fix: update ConfirmedBlock handling to match solana upstream

This commit is contained in:
Tyera Eulberg 2019-11-19 15:38:06 -07:00 committed by Michael Vines
parent 77745a278d
commit a461d5f25f
5 changed files with 104 additions and 52 deletions

View File

@ -71,6 +71,13 @@ declare module '@solana/web3.js' {
rpc: string | null, rpc: string | null,
}; };
declare export type ConfirmedBlock = {
blockhash: Blockhash,
previousBlockhash: Blockhash,
parentSlot: number,
transactions: Array<[Transaction, SignatureSuccess | TransactionError | null]>,
};
declare export type KeyedAccountInfo = { declare export type KeyedAccountInfo = {
accountId: PublicKey, accountId: PublicKey,
accountInfo: AccountInfo, accountInfo: AccountInfo,
@ -136,6 +143,7 @@ declare module '@solana/web3.js' {
): Promise<RpcResponseAndContext<number>>; ): Promise<RpcResponseAndContext<number>>;
getBalance(publicKey: PublicKey, commitment: ?Commitment): Promise<number>; getBalance(publicKey: PublicKey, commitment: ?Commitment): Promise<number>;
getClusterNodes(): Promise<Array<ContactInfo>>; getClusterNodes(): Promise<Array<ContactInfo>>;
getConfirmedBlock(): Promise<ConfirmedBlock>;
getVoteAccounts(commitment: ?Commitment): Promise<VoteAccountStatus>; getVoteAccounts(commitment: ?Commitment): Promise<VoteAccountStatus>;
confirmTransactionAndContext( confirmTransactionAndContext(
signature: TransactionSignature, signature: TransactionSignature,

View File

@ -186,6 +186,22 @@ const Version = struct({
'solana-core': 'string', 'solana-core': 'string',
}); });
/**
* A ConfirmedBlock on the ledger
*
* @typedef {Object} ConfirmedBlock
* @property {Blockhash} blockhash Blockhash of this block
* @property {Blockhash} previousBlockhash Blockhash of this block's parent
* @property {number} parentSlot Slot index of this block's parent
* @property {Array<Array<object>>} transactions Vector of transactions paired with statuses
*/
type ConfirmedBlock = {
blockhash: Blockhash,
previousBlockhash: Blockhash,
parentSlot: number,
transactions: Array<[Transaction, GetSignatureStatusRpcResult]>,
};
function createRpcRequest(url): RpcRequest { function createRpcRequest(url): RpcRequest {
const server = jayson(async (request, callback) => { const server = jayson(async (request, callback) => {
const options = { const options = {
@ -412,16 +428,20 @@ const GetMinimumBalanceForRentExemptionRpcResult = jsonRpcResult('number');
* Expected JSON RPC response for the "getConfirmedBlock" message * Expected JSON RPC response for the "getConfirmedBlock" message
*/ */
export const GetConfirmedBlockRpcResult = jsonRpcResult( export const GetConfirmedBlockRpcResult = jsonRpcResult(
struct.list([ struct({
blockhash: struct.list(['number']),
previousBlockhash: struct.list(['number']),
parentSlot: 'number',
transactions: struct.list([
struct.tuple([ struct.tuple([
struct({ struct({
signatures: struct.list([struct.list(['number'])]), signatures: struct.list([struct.list(['number'])]),
message: struct({ message: struct({
account_keys: struct.list([struct.list(['number'])]), accountKeys: struct.list([struct.list(['number'])]),
header: struct({ header: struct({
num_required_signatures: 'number', numRequiredSignatures: 'number',
num_readonly_signed_accounts: 'number', numReadonlySignedAccounts: 'number',
num_readonly_unsigned_accounts: 'number', numReadonlyUnsignedAccounts: 'number',
}), }),
instructions: struct.list([ instructions: struct.list([
struct.union([ struct.union([
@ -433,16 +453,23 @@ export const GetConfirmedBlockRpcResult = jsonRpcResult(
data: struct.list([ data: struct.list([
struct.union([struct.list(['number']), 'number']), struct.union([struct.list(['number']), 'number']),
]), ]),
program_id_index: 'number', programIdIndex: 'number',
}), }),
]), ]),
]), ]),
recent_blockhash: struct.list(['number']), recentBlockhash: struct.list(['number']),
}), }),
}), }),
struct.union([struct({Ok: 'null'}), struct({Err: 'object'})]), struct.union([
'null',
struct({
status: GetSignatureStatusRpcResult,
fee: 'number',
}),
]), ]),
]), ]),
]),
}),
); );
/** /**
@ -1055,20 +1082,23 @@ export class Connection {
* Fetch a list of Transactions and transaction statuses from the cluster * Fetch a list of Transactions and transaction statuses from the cluster
* for a confirmed block * for a confirmed block
*/ */
async getConfirmedBlock( async getConfirmedBlock(slot: number): Promise<ConfirmedBlock> {
slot: number,
): Promise<
Array<[Transaction, SignatureSuccess] | [Transaction, TransactionError]>,
> {
const unsafeRes = await this._rpcRequest('getConfirmedBlock', [slot]); const unsafeRes = await this._rpcRequest('getConfirmedBlock', [slot]);
const result = GetConfirmedBlockRpcResult(unsafeRes); const result = GetConfirmedBlockRpcResult(unsafeRes);
if (result.error) { if (result.error) {
throw new Error(result.error.message); throw new Error(result.error.message);
} }
assert(typeof result.result !== 'undefined'); assert(typeof result.result !== 'undefined');
return result.result.map(result => { return {
blockhash: new PublicKey(result.result.blockhash).toString(),
previousBlockhash: new PublicKey(
result.result.previousBlockhash,
).toString(),
parentSlot: result.result.parentSlot,
transactions: result.result.transactions.map(result => {
return [Transaction.fromRpcResult(result[0]), result[1]]; return [Transaction.fromRpcResult(result[0]), result[1]];
}); }),
};
} }
/** /**

View File

@ -504,19 +504,19 @@ export class Transaction {
*/ */
static fromRpcResult(rpcResult: any): Transaction { static fromRpcResult(rpcResult: any): Transaction {
const signatures = rpcResult.signatures.slice(1); const signatures = rpcResult.signatures.slice(1);
const accounts = rpcResult.message.account_keys.slice(1); const accounts = rpcResult.message.accountKeys.slice(1);
const instructions = rpcResult.message.instructions.slice(1).map(ix => { const instructions = rpcResult.message.instructions.slice(1).map(ix => {
ix.accounts.shift(); ix.accounts.shift();
ix.data.shift(); ix.data.shift();
return ix; return ix;
}); });
const recentBlockhash = rpcResult.message.recent_blockhash; const recentBlockhash = rpcResult.message.recentBlockhash;
const numRequiredSignatures = const numRequiredSignatures =
rpcResult.message.header.num_required_signatures; rpcResult.message.header.numRequiredSignatures;
const numReadonlySignedAccounts = const numReadonlySignedAccounts =
rpcResult.message.header.num_readonly_signed_accounts; rpcResult.message.header.numReadonlySignedAccounts;
const numReadonlyUnsignedAccounts = const numReadonlyUnsignedAccounts =
rpcResult.message.header.num_readonly_unsigned_accounts; rpcResult.message.header.numReadonlyUnsignedAccounts;
return Transaction._populate( return Transaction._populate(
signatures, signatures,
accounts, accounts,

View File

@ -426,12 +426,26 @@ test('get confirmed block', async () => {
} }
const connection = new Connection(url); const connection = new Connection(url);
// These test cases need to be updated when upstream solana RPC api is fleshed out // Block 0 never has any transactions in automation localnet
const zeroTransactions = await connection.getConfirmedBlock(0); const block0 = await connection.getConfirmedBlock(0);
expect(zeroTransactions.length).toBe(0); const blockhash0 = block0.blockhash;
expect(block0.transactions.length).toBe(0);
expect(blockhash0).not.toBeNull();
expect(block0.previousBlockhash).not.toBeNull();
expect(block0.parentSlot).toBe(0);
const oneTransaction = await connection.getConfirmedBlock(1); // Find a block that has a transaction, usually Block 1
expect(oneTransaction.length).toBe(1); let x = 1;
while (x < 10) {
const block1 = await connection.getConfirmedBlock(x);
if (block1.transactions.length >= 1) {
expect(block1.previousBlockhash).toBe(blockhash0);
expect(block1.blockhash).not.toBeNull();
expect(block1.parentSlot).toBe(0);
break;
}
x++;
}
}); });
test('get recent blockhash', async () => { test('get recent blockhash', async () => {

View File

@ -105,7 +105,7 @@ test('transaction from rpc result', () => {
const rawBlockhash = new PublicKey(0).toBuffer(); const rawBlockhash = new PublicKey(0).toBuffer();
const rpcResult = { const rpcResult = {
message: { message: {
account_keys: [ accountKeys: [
[5], [5],
new PublicKey(1).toBuffer(), new PublicKey(1).toBuffer(),
new PublicKey(2).toBuffer(), new PublicKey(2).toBuffer(),
@ -114,19 +114,19 @@ test('transaction from rpc result', () => {
new PublicKey(5).toBuffer(), new PublicKey(5).toBuffer(),
], ],
header: { header: {
num_readonly_signed_accounts: 0, num_ReadonlySignedAccounts: 0,
num_readonly_unsigned_accounts: 3, numReadonlyUnsignedAccounts: 3,
num_required_signatures: 2, numRequiredSignatures: 2,
}, },
instructions: [ instructions: [
[1], [1],
{ {
accounts: [[3], 1, 2, 3], accounts: [[3], 1, 2, 3],
data: [[1], 0], data: [[1], 0],
program_id_index: 4, programIdIndex: 4,
}, },
], ],
recent_blockhash: rawBlockhash, recentBlockhash: rawBlockhash,
}, },
signatures: [[2], Array(64).fill(1), Array(64).fill(2)], signatures: [[2], Array(64).fill(1), Array(64).fill(2)],
}; };