Skip to content

Commit

Permalink
fix: query Nonce account
Browse files Browse the repository at this point in the history
  • Loading branch information
CriesofCarrots committed Jan 8, 2020
1 parent b4fa9cb commit 52b7b51
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 1 deletion.
52 changes: 51 additions & 1 deletion src/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import jayson from 'jayson/lib/client/browser';
import {struct} from 'superstruct';
import {Client as RpcWebSocketClient} from 'rpc-websockets';

import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from './timing';
import {NonceAccount} from './nonce-account';
import {PublicKey} from './publickey';
import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from './timing';
import {Transaction} from './transaction';
import {sleep} from './util/sleep';
import type {Blockhash} from './blockhash';
Expand Down Expand Up @@ -1149,6 +1150,55 @@ export class Connection {
};
}

/**
* Fetch the contents of a Nonce account from the cluster
*/
async getNonceAndContext(
nonceAccount: PublicKey,
commitment: ?Commitment,
): Promise<RpcResponseAndContext<NonceAccount>> {
const args = this._argsWithCommitment(
[nonceAccount.toBase58()],
commitment,
);
const unsafeRes = await this._rpcRequest('getAccountInfo', args);
const res = GetAccountInfoAndContextRpcResult(unsafeRes);
if (res.error) {
throw new Error(res.error.message);
}
assert(typeof res.result !== 'undefined');

const isV021 =
typeof res.result.context !== 'undefined' &&
typeof res.result.value !== 'undefined';

const slot = isV021 ? res.result.context.slot : NaN;
const resultValue = isV021 ? res.result.value : res.result;

if (!resultValue) {
throw new Error('Invalid request');
}

const value = NonceAccount.fromAccountData(Buffer.from(resultValue.data));

return {
context: {
slot,
},
value,
};
}
async getNonce(
nonceAccount: PublicKey,
commitment: ?Commitment,
): Promise<NonceAccount> {
return await this.getNonceAndContext(nonceAccount, commitment)
.then(x => x.value)
.catch(e => {
throw e;
});
}

/**
* Request an allocation of lamports to the specified account
*/
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export {BpfLoader} from './bpf-loader';
export {BudgetProgram} from './budget-program';
export {Connection} from './connection';
export {Loader} from './loader';
export {NonceAccount} from './nonce-account';
export {PublicKey} from './publickey';
export {
STAKE_CONFIG_ID,
Expand Down
40 changes: 40 additions & 0 deletions src/nonce-account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// @flow
import * as BufferLayout from 'buffer-layout';

import type {Blockhash} from './blockhash';
import * as Layout from './layout';
import {PublicKey} from './publickey';

/**
* See https://github.com/solana-labs/solana/blob/0ea2843ec9cdc517572b8e62c959f41b55cf4453/sdk/src/nonce_state.rs#L29-L32
*
* @private
*/
const NonceAccountLayout = BufferLayout.struct([
BufferLayout.u32('state'),
Layout.publicKey('authorizedPubkey'),
Layout.publicKey('hash'),
]);

/**
* NonceAccount class
*/
export class NonceAccount {
authorizedPubkey: PublicKey;
nonce: Blockhash;

/**
* Deserialize NonceAccount from the account data.
*
* @param buffer account data
* @return NonceAccount
*/
static fromAccountData(buffer: Buffer): NonceAccount {
const nonceAccount = NonceAccountLayout.decode(buffer, 0);
nonceAccount.authorizedPubkey = new PublicKey(
nonceAccount.authorizedPubkey,
);
nonceAccount.nonce = new PublicKey(nonceAccount.nonce).toString();
return nonceAccount;
}
}
154 changes: 154 additions & 0 deletions test/nonce.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// @flow

import bs58 from 'bs58';

import {Account, Connection, SystemProgram} from '../src';
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash';
import {url} from './url';

if (!mockRpcEnabled) {
// Testing max commitment level takes around 20s to complete
jest.setTimeout(30000);
}

test('create and query nonce account', async () => {
const from = new Account();
const nonceAccount = new Account();
const connection = new Connection(url, 'recent');

mockRpc.push([
url,
{
method: 'getMinimumBalanceForRentExemption',
params: [68, {commitment: 'recent'}],
},
{
error: null,
result: 50,
},
]);

const minimumAmount = await connection.getMinimumBalanceForRentExemption(
SystemProgram.nonceSpace,
'recent',
);

mockRpc.push([
url,
{
method: 'requestAirdrop',
params: [
from.publicKey.toBase58(),
minimumAmount * 2,
{commitment: 'recent'},
],
},
{
error: null,
result:
'1WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
},
]);

await connection.requestAirdrop(from.publicKey, minimumAmount * 2);

mockRpc.push([
url,
{
method: 'getBalance',
params: [from.publicKey.toBase58(), {commitment: 'recent'}],
},
{
error: null,
result: minimumAmount * 2,
},
]);

const balance = await connection.getBalance(from.publicKey);
expect(balance).toBe(minimumAmount * 2);

mockGetRecentBlockhash('recent');
mockRpc.push([
url,
{
method: 'sendTransaction',
},
{
error: null,
result:
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
},
]);

const transaction = SystemProgram.createNonceAccount(
from.publicKey,
nonceAccount.publicKey,
from.publicKey,
minimumAmount,
);
await connection.sendTransaction(transaction, from, nonceAccount);

const expectedData = Buffer.alloc(68);
expectedData.writeInt32LE(1, 0);
from.publicKey.toBuffer().copy(expectedData, 4);
const mockNonce = new Account();
mockNonce.publicKey.toBuffer().copy(expectedData, 36);

mockRpc.push([
url,
{
method: 'getAccountInfo',
params: [nonceAccount.publicKey.toBase58(), {commitment: 'recent'}],
},
{
error: null,
result: {
owner: [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
lamports: minimumAmount,
data: [...expectedData],
executable: false,
},
},
]);
//
const nonceAccountData = await connection.getNonce(
nonceAccount.publicKey,
'recent',
);
expect(nonceAccountData.authorizedPubkey).toEqual(from.publicKey);
expect(bs58.decode(nonceAccountData.nonce).length).toBeGreaterThan(30);
});

0 comments on commit 52b7b51

Please sign in to comment.