fix: query Nonce account

This commit is contained in:
Tyera Eulberg 2020-01-02 18:54:43 -07:00 committed by Michael Vines
parent c9cc44ae4f
commit 600a295b11
4 changed files with 246 additions and 1 deletions

View File

@ -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';
@ -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
*/

View File

@ -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,

View File

@ -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
web3.js/test/nonce.test.js Normal file
View File

@ -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);
});