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

View File

@ -186,6 +186,22 @@ const Version = struct({
'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 {
const server = jayson(async (request, callback) => {
const options = {
@ -412,37 +428,48 @@ const GetMinimumBalanceForRentExemptionRpcResult = jsonRpcResult('number');
* Expected JSON RPC response for the "getConfirmedBlock" message
*/
export const GetConfirmedBlockRpcResult = jsonRpcResult(
struct.list([
struct.tuple([
struct({
signatures: struct.list([struct.list(['number'])]),
message: struct({
account_keys: struct.list([struct.list(['number'])]),
header: struct({
num_required_signatures: 'number',
num_readonly_signed_accounts: 'number',
num_readonly_unsigned_accounts: 'number',
}),
instructions: struct.list([
struct.union([
struct.list(['number']),
struct({
accounts: struct.list([
struct.union([struct.list(['number']), 'number']),
]),
data: struct.list([
struct.union([struct.list(['number']), 'number']),
]),
program_id_index: 'number',
}),
struct({
blockhash: struct.list(['number']),
previousBlockhash: struct.list(['number']),
parentSlot: 'number',
transactions: struct.list([
struct.tuple([
struct({
signatures: struct.list([struct.list(['number'])]),
message: struct({
accountKeys: struct.list([struct.list(['number'])]),
header: struct({
numRequiredSignatures: 'number',
numReadonlySignedAccounts: 'number',
numReadonlyUnsignedAccounts: 'number',
}),
instructions: struct.list([
struct.union([
struct.list(['number']),
struct({
accounts: struct.list([
struct.union([struct.list(['number']), 'number']),
]),
data: struct.list([
struct.union([struct.list(['number']), '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
* for a confirmed block
*/
async getConfirmedBlock(
slot: number,
): Promise<
Array<[Transaction, SignatureSuccess] | [Transaction, TransactionError]>,
> {
async getConfirmedBlock(slot: number): Promise<ConfirmedBlock> {
const unsafeRes = await this._rpcRequest('getConfirmedBlock', [slot]);
const result = GetConfirmedBlockRpcResult(unsafeRes);
if (result.error) {
throw new Error(result.error.message);
}
assert(typeof result.result !== 'undefined');
return result.result.map(result => {
return [Transaction.fromRpcResult(result[0]), result[1]];
});
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]];
}),
};
}
/**

View File

@ -504,19 +504,19 @@ export class Transaction {
*/
static fromRpcResult(rpcResult: any): Transaction {
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 => {
ix.accounts.shift();
ix.data.shift();
return ix;
});
const recentBlockhash = rpcResult.message.recent_blockhash;
const recentBlockhash = rpcResult.message.recentBlockhash;
const numRequiredSignatures =
rpcResult.message.header.num_required_signatures;
rpcResult.message.header.numRequiredSignatures;
const numReadonlySignedAccounts =
rpcResult.message.header.num_readonly_signed_accounts;
rpcResult.message.header.numReadonlySignedAccounts;
const numReadonlyUnsignedAccounts =
rpcResult.message.header.num_readonly_unsigned_accounts;
rpcResult.message.header.numReadonlyUnsignedAccounts;
return Transaction._populate(
signatures,
accounts,

View File

@ -426,12 +426,26 @@ test('get confirmed block', async () => {
}
const connection = new Connection(url);
// These test cases need to be updated when upstream solana RPC api is fleshed out
const zeroTransactions = await connection.getConfirmedBlock(0);
expect(zeroTransactions.length).toBe(0);
// Block 0 never has any transactions in automation localnet
const block0 = await connection.getConfirmedBlock(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);
expect(oneTransaction.length).toBe(1);
// Find a block that has a transaction, usually Block 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 () => {

View File

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