feat: adds getBlockProduction RPC call

This commit is contained in:
stellaw1 2022-02-22 18:49:27 -08:00 committed by Steven Luscher
parent 31b707b625
commit c08cfafd6c
2 changed files with 209 additions and 0 deletions

View File

@ -754,6 +754,48 @@ export type BlockSignatures = {
blockTime: number | null;
};
/**
* recent block production information
*/
export type BlockProduction = Readonly<{
/** a dictionary of validator identities, as base-58 encoded strings. Value is a two element array containing the number of leader slots and the number of blocks produced */
byIdentity: Readonly<Record<string, ReadonlyArray<number>>>;
/** Block production slot range */
range: Readonly<{
/** first slot of the block production information (inclusive) */
firstSlot: number;
/** last slot of block production information (inclusive) */
lastSlot: number;
}>;
}>;
export type GetBlockProductionConfig = {
/** Optional commitment level */
commitment?: Commitment;
/** Slot range to return block production for. If parameter not provided, defaults to current epoch. */
range?: {
/** first slot to return block production information for (inclusive) */
firstSlot: number;
/** last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot */
lastSlot?: number;
};
/** Only return results for this validator identity (base-58 encoded) */
identity?: string;
};
/**
* Expected JSON RPC response for the "getBlockProduction" message
*/
const BlockProductionResponseStruct = jsonRpcResultAndContext(
pick({
byIdentity: record(string(), array(number())),
range: pick({
firstSlot: number(),
lastSlot: number(),
}),
}),
);
/**
* A performance sample
*/
@ -3230,6 +3272,51 @@ export class Connection {
};
}
/*
* Returns the current block height of the node
*/
async getBlockHeight(commitment?: Commitment): Promise<number> {
const args = this._buildArgs([], commitment);
const unsafeRes = await this._rpcRequest('getBlockHeight', args);
const res = create(unsafeRes, jsonRpcResult(number()));
if ('error' in res) {
throw new Error(
'failed to get block height information: ' + res.error.message,
);
}
return res.result;
}
/*
* Returns recent block production information from the current or previous epoch
*/
async getBlockProduction(
configOrCommitment?: GetBlockProductionConfig | Commitment,
): Promise<RpcResponseAndContext<BlockProduction>> {
let extra: Omit<GetBlockProductionConfig, 'commitment'> | undefined;
let commitment: Commitment | undefined;
if (typeof configOrCommitment === 'string') {
commitment = configOrCommitment;
} else if (configOrCommitment) {
const {commitment: c, ...rest} = configOrCommitment;
commitment = c;
extra = rest;
}
const args = this._buildArgs([], commitment, 'base64', extra);
const unsafeRes = await this._rpcRequest('getBlockProduction', args);
const res = create(unsafeRes, BlockProductionResponseStruct);
if ('error' in res) {
throw new Error(
'failed to get block production information: ' + res.error.message,
);
}
return res.result;
}
/**
* Fetch a confirmed or finalized transaction from the cluster.
*/

View File

@ -1513,6 +1513,128 @@ describe('Connection', function () {
expect(result).to.be.empty;
});
it('get block height', async () => {
const commitment: Commitment = 'confirmed';
await mockRpcResponse({
method: 'getBlockHeight',
params: [{commitment: commitment}],
value: 10,
});
const blockHeight = await connection.getBlockHeight(commitment);
expect(blockHeight).to.be.a('number');
});
it('get block production', async () => {
const commitment: Commitment = 'processed';
// Find slot of the lowest confirmed block
await mockRpcResponse({
method: 'getFirstAvailableBlock',
params: [],
value: 1,
});
let firstSlot = await connection.getFirstAvailableBlock();
// Find current block height
await mockRpcResponse({
method: 'getBlockHeight',
params: [{commitment: commitment}],
value: 10,
});
let lastSlot = await connection.getBlockHeight(commitment);
const blockProductionConfig = {
commitment: commitment,
range: {
firstSlot,
lastSlot,
},
};
const blockProductionRet = {
byIdentity: {
'85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr': [12, 10],
},
range: {
firstSlot,
lastSlot,
},
};
//mock RPC call with config specified
await mockRpcResponse({
method: 'getBlockProduction',
params: [blockProductionConfig],
value: blockProductionRet,
withContext: true,
});
//mock RPC call with commitment only
await mockRpcResponse({
method: 'getBlockProduction',
params: [{commitment: commitment}],
value: blockProductionRet,
withContext: true,
});
const result = await connection.getBlockProduction(blockProductionConfig);
if (!result) {
expect(result).to.be.ok;
return;
}
expect(result.context).to.be.ok;
expect(result.value).to.be.ok;
const resultContextSlot = result.context.slot;
expect(resultContextSlot).to.be.a('number');
const resultIdentityDictionary = result.value.byIdentity;
expect(resultIdentityDictionary).to.be.a('object');
for (var key in resultIdentityDictionary) {
expect(key).to.be.a('string');
expect(resultIdentityDictionary[key]).to.be.a('array');
expect(resultIdentityDictionary[key][0]).to.be.a('number');
expect(resultIdentityDictionary[key][1]).to.be.a('number');
}
const resultSlotRange = result.value.range;
expect(resultSlotRange.firstSlot).to.equal(firstSlot);
expect(resultSlotRange.lastSlot).to.equal(lastSlot);
const resultCommitmentOnly = await connection.getBlockProduction(
commitment,
);
if (!resultCommitmentOnly) {
expect(resultCommitmentOnly).to.be.ok;
return;
}
expect(resultCommitmentOnly.context).to.be.ok;
expect(resultCommitmentOnly.value).to.be.ok;
const resultCOContextSlot = result.context.slot;
expect(resultCOContextSlot).to.be.a('number');
const resultCOIdentityDictionary = result.value.byIdentity;
expect(resultCOIdentityDictionary).to.be.a('object');
for (var property in resultCOIdentityDictionary) {
expect(property).to.be.a('string');
expect(resultCOIdentityDictionary[property]).to.be.a('array');
expect(resultCOIdentityDictionary[property][0]).to.be.a('number');
expect(resultCOIdentityDictionary[property][1]).to.be.a('number');
}
const resultCOSlotRange = result.value.range;
expect(resultCOSlotRange.firstSlot).to.equal(firstSlot);
expect(resultCOSlotRange.lastSlot).to.equal(lastSlot);
});
it('get transaction', async () => {
await mockRpcResponse({
method: 'getSlot',