Skip to content

Commit

Permalink
fix: add integration test and fix various exposed bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
CriesofCarrots committed Jan 2, 2020
1 parent dc4669f commit ad4ef09
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 21 deletions.
3 changes: 0 additions & 3 deletions module.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,6 @@ declare module '@solana/web3.js' {
validatorExit(): Promise<boolean>;
}

// === src/config-program.js ===
declare export var CONFIG_PROGRAM_ID;

// === src/stake-program.js ===
declare export class StakeProgram {
static programId: PublicKey;
Expand Down
6 changes: 0 additions & 6 deletions src/config-program.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
export {Account} from './account';
export {BpfLoader} from './bpf-loader';
export {BudgetProgram} from './budget-program';
export {CONFIG_PROGRAM_ID} from './config-program';
export {Connection} from './connection';
export {Loader} from './loader';
export {PublicKey} from './publickey';
export {
STAKE_CONFIG_ID,
Authorized,
Lockup,
StakeAuthorizationLayout,
Expand Down
6 changes: 5 additions & 1 deletion src/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ export const authorized = (property: string = 'authorized') => {
*/
export const lockup = (property: string = 'lockup') => {
return BufferLayout.struct(
[BufferLayout.ns64('epoch'), publicKey('custodian')],
[
BufferLayout.ns64('unixTimestamp'),
BufferLayout.ns64('epoch'),
publicKey('custodian'),
],
property,
);
};
Expand Down
20 changes: 13 additions & 7 deletions src/stake-program.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import * as BufferLayout from 'buffer-layout';
import hasha from 'hasha';

import {CONFIG_PROGRAM_ID} from './config-program';
import {encodeData} from './instruction';
import type {InstructionType} from './instruction';
import * as Layout from './layout';
Expand All @@ -18,6 +17,10 @@ import {
import {Transaction, TransactionInstruction} from './transaction';
import type {TransactionInstructionCtorFields} from './transaction';

export const STAKE_CONFIG_ID = new PublicKey(
'StakeConfig11111111111111111111111111111111',
);

export class Authorized {
staker: PublicKey;
withdrawer: PublicKey;
Expand All @@ -32,13 +35,15 @@ export class Authorized {
}

export class Lockup {
unixTimestamp: number;
epoch: number;
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.custodian = custodian;
}
Expand Down Expand Up @@ -126,14 +131,14 @@ export const StakeInstructionLayout = Object.freeze({
index: 4,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
BufferLayout.ns64('amount'),
BufferLayout.ns64('lamports'),
]),
},
Withdraw: {
index: 5,
layout: BufferLayout.struct([
BufferLayout.u32('instruction'),
BufferLayout.ns64('amount'),
BufferLayout.ns64('lamports'),
]),
},
Deactivate: {
Expand Down Expand Up @@ -197,7 +202,7 @@ export class StakeProgram {
* Max space of a Stake account
*/
static get space(): number {
return 2000;
return 2008;
}

/**
Expand All @@ -215,6 +220,7 @@ export class StakeProgram {
withdrawer: authorized.withdrawer.toBuffer(),
},
lockup: {
unixTimestamp: lockup.unixTimestamp,
epoch: lockup.epoch,
custodian: lockup.custodian.toBuffer(),
},
Expand Down Expand Up @@ -293,7 +299,7 @@ export class StakeProgram {
{pubkey: stakeAccount, isSigner: false, isWritable: true},
{pubkey: votePubkey, 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},
],
programId: this.programId,
Expand Down
134 changes: 131 additions & 3 deletions test/stake-program.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import {
Account,
Authorized,
Connection,
Lockup,
PublicKey,
sendAndConfirmRecentTransaction,
LAMPORTS_PER_SOL,
StakeAuthorizationLayout,
StakeInstruction,
StakeInstructionLayout,
Expand All @@ -13,6 +16,13 @@ import {
SystemProgram,
Transaction,
} 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', () => {
const from = new Account();
Expand All @@ -30,7 +40,7 @@ test('createAccountWithSeed', () => {
newAccountPubkey,
seed,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, from.publicKey),
new Lockup(0, 0, from.publicKey),
123,
);

Expand All @@ -52,7 +62,7 @@ test('createAccount', () => {
from.publicKey,
newAccount.publicKey,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, from.publicKey),
new Lockup(0, 0, from.publicKey),
123,
);

Expand Down Expand Up @@ -179,7 +189,7 @@ test('StakeInstructions', () => {
newAccountPubkey,
seed,
new Authorized(authorized.publicKey, authorized.publicKey),
new Lockup(0, from.publicKey),
new Lockup(0, 0, from.publicKey),
amount,
);
const createWithSeedTransaction = new Transaction({recentBlockhash}).add(
Expand Down Expand Up @@ -220,3 +230,121 @@ test('StakeInstructions', () => {
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);
});

0 comments on commit ad4ef09

Please sign in to comment.