fix: add integration test and fix various exposed bugs

This commit is contained in:
Tyera Eulberg
2019-12-26 15:23:17 -07:00
committed by Michael Vines
parent 07c0670f65
commit 3595892fab
6 changed files with 150 additions and 21 deletions

View File

@ -203,9 +203,6 @@ declare module '@solana/web3.js' {
validatorExit(): Promise<boolean>; validatorExit(): Promise<boolean>;
} }
// === src/config-program.js ===
declare export var CONFIG_PROGRAM_ID;
// === src/stake-program.js === // === src/stake-program.js ===
declare export class StakeProgram { declare export class StakeProgram {
static programId: PublicKey; static programId: PublicKey;

View File

@ -1,6 +0,0 @@
// @flow
import {PublicKey} from './publickey';
export const CONFIG_PROGRAM_ID = new PublicKey(
'Config1111111111111111111111111111111111111',
);

View File

@ -2,11 +2,11 @@
export {Account} from './account'; export {Account} from './account';
export {BpfLoader} from './bpf-loader'; export {BpfLoader} from './bpf-loader';
export {BudgetProgram} from './budget-program'; export {BudgetProgram} from './budget-program';
export {CONFIG_PROGRAM_ID} from './config-program';
export {Connection} from './connection'; export {Connection} from './connection';
export {Loader} from './loader'; export {Loader} from './loader';
export {PublicKey} from './publickey'; export {PublicKey} from './publickey';
export { export {
STAKE_CONFIG_ID,
Authorized, Authorized,
Lockup, Lockup,
StakeAuthorizationLayout, StakeAuthorizationLayout,

View File

@ -69,7 +69,11 @@ export const authorized = (property: string = 'authorized') => {
*/ */
export const lockup = (property: string = 'lockup') => { export const lockup = (property: string = 'lockup') => {
return BufferLayout.struct( return BufferLayout.struct(
[BufferLayout.ns64('epoch'), publicKey('custodian')], [
BufferLayout.ns64('unixTimestamp'),
BufferLayout.ns64('epoch'),
publicKey('custodian'),
],
property, property,
); );
}; };

View File

@ -3,7 +3,6 @@
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import hasha from 'hasha'; import hasha from 'hasha';
import {CONFIG_PROGRAM_ID} from './config-program';
import {encodeData} from './instruction'; import {encodeData} from './instruction';
import type {InstructionType} from './instruction'; import type {InstructionType} from './instruction';
import * as Layout from './layout'; import * as Layout from './layout';
@ -18,6 +17,10 @@ import {
import {Transaction, TransactionInstruction} from './transaction'; import {Transaction, TransactionInstruction} from './transaction';
import type {TransactionInstructionCtorFields} from './transaction'; import type {TransactionInstructionCtorFields} from './transaction';
export const STAKE_CONFIG_ID = new PublicKey(
'StakeConfig11111111111111111111111111111111',
);
export class Authorized { export class Authorized {
staker: PublicKey; staker: PublicKey;
withdrawer: PublicKey; withdrawer: PublicKey;
@ -32,13 +35,15 @@ export class Authorized {
} }
export class Lockup { export class Lockup {
unixTimestamp: number;
epoch: number; epoch: number;
custodian: PublicKey; custodian: PublicKey;
/** /**
* Create a new Authorized object * Create a new Lockup object
*/ */
constructor(epoch: number, custodian: PublicKey) { constructor(unixTimestamp: number, epoch: number, custodian: PublicKey) {
this.unixTimestamp = unixTimestamp;
this.epoch = epoch; this.epoch = epoch;
this.custodian = custodian; this.custodian = custodian;
} }
@ -126,14 +131,14 @@ export const StakeInstructionLayout = Object.freeze({
index: 4, index: 4,
layout: BufferLayout.struct([ layout: BufferLayout.struct([
BufferLayout.u32('instruction'), BufferLayout.u32('instruction'),
BufferLayout.ns64('amount'), BufferLayout.ns64('lamports'),
]), ]),
}, },
Withdraw: { Withdraw: {
index: 5, index: 5,
layout: BufferLayout.struct([ layout: BufferLayout.struct([
BufferLayout.u32('instruction'), BufferLayout.u32('instruction'),
BufferLayout.ns64('amount'), BufferLayout.ns64('lamports'),
]), ]),
}, },
Deactivate: { Deactivate: {
@ -197,7 +202,7 @@ export class StakeProgram {
* Max space of a Stake account * Max space of a Stake account
*/ */
static get space(): number { static get space(): number {
return 2000; return 2008;
} }
/** /**
@ -215,6 +220,7 @@ export class StakeProgram {
withdrawer: authorized.withdrawer.toBuffer(), withdrawer: authorized.withdrawer.toBuffer(),
}, },
lockup: { lockup: {
unixTimestamp: lockup.unixTimestamp,
epoch: lockup.epoch, epoch: lockup.epoch,
custodian: lockup.custodian.toBuffer(), custodian: lockup.custodian.toBuffer(),
}, },
@ -293,7 +299,7 @@ export class StakeProgram {
{pubkey: stakeAccount, isSigner: false, isWritable: true}, {pubkey: stakeAccount, isSigner: false, isWritable: true},
{pubkey: votePubkey, isSigner: false, isWritable: false}, {pubkey: votePubkey, isSigner: false, isWritable: false},
{pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false}, {pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
{pubkey: CONFIG_PROGRAM_ID, isSigner: false, isWritable: false}, {pubkey: STAKE_CONFIG_ID, isSigner: false, isWritable: false},
{pubkey: authorizedPubkey, isSigner: true, isWritable: false}, {pubkey: authorizedPubkey, isSigner: true, isWritable: false},
], ],
programId: this.programId, programId: this.programId,

View File

@ -3,8 +3,11 @@
import { import {
Account, Account,
Authorized, Authorized,
Connection,
Lockup, Lockup,
PublicKey, PublicKey,
sendAndConfirmRecentTransaction,
LAMPORTS_PER_SOL,
StakeAuthorizationLayout, StakeAuthorizationLayout,
StakeInstruction, StakeInstruction,
StakeInstructionLayout, StakeInstructionLayout,
@ -13,6 +16,13 @@ import {
SystemProgram, SystemProgram,
Transaction, Transaction,
} from '../src'; } from '../src';
import {mockRpcEnabled} from './__mocks__/node-fetch';
import {url} from './url';
if (!mockRpcEnabled) {
// Testing max commitment level takes around 20s to complete
jest.setTimeout(30000);
}
test('createAccountWithSeed', () => { test('createAccountWithSeed', () => {
const from = new Account(); const from = new Account();
@ -30,7 +40,7 @@ test('createAccountWithSeed', () => {
newAccountPubkey, newAccountPubkey,
seed, seed,
new Authorized(authorized.publicKey, authorized.publicKey), new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, from.publicKey), new Lockup(0, 0, from.publicKey),
123, 123,
); );
@ -52,7 +62,7 @@ test('createAccount', () => {
from.publicKey, from.publicKey,
newAccount.publicKey, newAccount.publicKey,
new Authorized(authorized.publicKey, authorized.publicKey), new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, from.publicKey), new Lockup(0, 0, from.publicKey),
123, 123,
); );
@ -179,7 +189,7 @@ test('StakeInstructions', () => {
newAccountPubkey, newAccountPubkey,
seed, seed,
new Authorized(authorized.publicKey, authorized.publicKey), new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, from.publicKey), new Lockup(0, 0, from.publicKey),
amount, amount,
); );
const createWithSeedTransaction = new Transaction({recentBlockhash}).add( const createWithSeedTransaction = new Transaction({recentBlockhash}).add(
@ -220,3 +230,121 @@ test('StakeInstructions', () => {
StakeInstructionLayout.DelegateStake, StakeInstructionLayout.DelegateStake,
); );
}); });
test('live staking actions', async () => {
if (mockRpcEnabled) {
console.log('non-live test skipped');
return;
}
const connection = new Connection(url, 'recent');
const voteAccounts = await connection.getVoteAccounts();
const voteAccount = voteAccounts.current.concat(voteAccounts.delinquent)[0];
const votePubkey = new PublicKey(voteAccount.votePubkey);
const from = new Account();
const authorized = new Account();
await connection.requestAirdrop(from.publicKey, LAMPORTS_PER_SOL);
await connection.requestAirdrop(authorized.publicKey, LAMPORTS_PER_SOL);
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
StakeProgram.space,
'recent',
);
// Create Stake account with seed
const seed = 'test string';
const newAccountPubkey = PublicKey.createWithSeed(
from.publicKey,
seed,
StakeProgram.programId,
);
let createAndInitializeWithSeed = StakeProgram.createAccountWithSeed(
from.publicKey,
newAccountPubkey,
seed,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, 0, new PublicKey('0x00')),
2 * minimumAmount + 42,
);
await sendAndConfirmRecentTransaction(
connection,
createAndInitializeWithSeed,
from,
);
let originalStakeBalance = await connection.getBalance(newAccountPubkey);
expect(originalStakeBalance).toEqual(2 * minimumAmount + 42);
let delegation = StakeProgram.delegate(
newAccountPubkey,
authorized.publicKey,
votePubkey,
);
await sendAndConfirmRecentTransaction(connection, delegation, authorized);
// Test that withdraw fails before deactivation
const recipient = new Account();
let withdraw = StakeProgram.withdraw(
newAccountPubkey,
authorized.publicKey,
recipient.publicKey,
1000,
);
await expect(
sendAndConfirmRecentTransaction(connection, withdraw, authorized),
).rejects.toThrow();
// Authorize to new account
const newAuthorized = new Account();
await connection.requestAirdrop(newAuthorized.publicKey, LAMPORTS_PER_SOL);
let authorize = StakeProgram.authorize(
newAccountPubkey,
authorized.publicKey,
newAuthorized.publicKey,
StakeAuthorizationLayout.Withdrawer,
);
await sendAndConfirmRecentTransaction(connection, authorize, authorized);
authorize = StakeProgram.authorize(
newAccountPubkey,
authorized.publicKey,
newAuthorized.publicKey,
StakeAuthorizationLayout.Staker,
);
await sendAndConfirmRecentTransaction(connection, authorize, authorized);
// Test old authorized can't deactivate
let deactivateNotAuthorized = StakeProgram.deactivate(
newAccountPubkey,
authorized.publicKey,
);
await expect(
sendAndConfirmRecentTransaction(
connection,
deactivateNotAuthorized,
authorized,
),
).rejects.toThrow();
// Deactivate stake
let deactivate = StakeProgram.deactivate(
newAccountPubkey,
newAuthorized.publicKey,
);
await sendAndConfirmRecentTransaction(connection, deactivate, newAuthorized);
// Test that withdraw succeeds after deactivation
withdraw = StakeProgram.withdraw(
newAccountPubkey,
newAuthorized.publicKey,
recipient.publicKey,
minimumAmount + 20,
);
await sendAndConfirmRecentTransaction(connection, withdraw, newAuthorized);
const balance = await connection.getBalance(newAccountPubkey);
expect(balance).toEqual(minimumAmount + 22);
const recipientBalance = await connection.getBalance(recipient.publicKey);
expect(recipientBalance).toEqual(minimumAmount + 20);
});