mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-10 13:47:12 +00:00
Backport block search
This commit is contained in:
parent
5f694f2ed6
commit
65db62d3ee
@ -6,6 +6,14 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- @cosmjs/tendermint-rpc: `Tendermint34Client.blockSearch` and
|
||||
`Tendermint34Client.blockSearchAll` were added to allow searching blocks in
|
||||
Tendermint 0.34.9+ backends. This is a backport of [#815].
|
||||
|
||||
[#815]: https://github.com/cosmos/cosmjs/pull/815
|
||||
|
||||
## [0.25.4] - 2021-05-31
|
||||
|
||||
### Fixed
|
||||
|
@ -23,6 +23,7 @@ export interface Params {
|
||||
readonly encodeBlock: (req: requests.BlockRequest) => JsonRpcRequest;
|
||||
readonly encodeBlockchain: (req: requests.BlockchainRequest) => JsonRpcRequest;
|
||||
readonly encodeBlockResults: (req: requests.BlockResultsRequest) => JsonRpcRequest;
|
||||
readonly encodeBlockSearch: (req: requests.BlockSearchRequest) => JsonRpcRequest;
|
||||
readonly encodeBroadcastTx: (req: requests.BroadcastTxRequest) => JsonRpcRequest;
|
||||
readonly encodeCommit: (req: requests.CommitRequest) => JsonRpcRequest;
|
||||
readonly encodeGenesis: (req: requests.GenesisRequest) => JsonRpcRequest;
|
||||
@ -39,6 +40,7 @@ export interface Responses {
|
||||
readonly decodeAbciQuery: (response: JsonRpcSuccessResponse) => responses.AbciQueryResponse;
|
||||
readonly decodeBlock: (response: JsonRpcSuccessResponse) => responses.BlockResponse;
|
||||
readonly decodeBlockResults: (response: JsonRpcSuccessResponse) => responses.BlockResultsResponse;
|
||||
readonly decodeBlockSearch: (response: JsonRpcSuccessResponse) => responses.BlockSearchResponse;
|
||||
readonly decodeBlockchain: (response: JsonRpcSuccessResponse) => responses.BlockchainResponse;
|
||||
readonly decodeBroadcastTxSync: (response: JsonRpcSuccessResponse) => responses.BroadcastTxSyncResponse;
|
||||
readonly decodeBroadcastTxAsync: (response: JsonRpcSuccessResponse) => responses.BroadcastTxAsyncResponse;
|
||||
|
@ -30,6 +30,21 @@ function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams):
|
||||
};
|
||||
}
|
||||
|
||||
interface RpcBlockSearchParams {
|
||||
readonly query: string;
|
||||
readonly page?: string;
|
||||
readonly per_page?: string;
|
||||
readonly order_by?: string;
|
||||
}
|
||||
function encodeBlockSearchParams(params: requests.BlockSearchParams): RpcBlockSearchParams {
|
||||
return {
|
||||
query: params.query,
|
||||
page: may(Integer.encode, params.page),
|
||||
per_page: may(Integer.encode, params.per_page),
|
||||
order_by: params.order_by,
|
||||
};
|
||||
}
|
||||
|
||||
interface RpcAbciQueryParams {
|
||||
readonly path: string;
|
||||
/** hex encoded */
|
||||
@ -118,6 +133,10 @@ export class Params {
|
||||
return createJsonRpcRequest(req.method, encodeHeightParam(req.params));
|
||||
}
|
||||
|
||||
public static encodeBlockSearch(req: requests.BlockSearchRequest): JsonRpcRequest {
|
||||
return createJsonRpcRequest(req.method, encodeBlockSearchParams(req.params));
|
||||
}
|
||||
|
||||
public static encodeBroadcastTx(req: requests.BroadcastTxRequest): JsonRpcRequest {
|
||||
return createJsonRpcRequest(req.method, encodeBroadcastTxParams(req.params));
|
||||
}
|
||||
|
@ -783,6 +783,18 @@ function decodeBlockResponse(data: RpcBlockResponse): responses.BlockResponse {
|
||||
};
|
||||
}
|
||||
|
||||
interface RpcBlockSearchResponse {
|
||||
readonly blocks: readonly RpcBlockResponse[];
|
||||
readonly total_count: string;
|
||||
}
|
||||
|
||||
function decodeBlockSearch(data: RpcBlockSearchResponse): responses.BlockSearchResponse {
|
||||
return {
|
||||
totalCount: Integer.parse(assertNotEmpty(data.total_count)),
|
||||
blocks: assertArray(data.blocks).map(decodeBlockResponse),
|
||||
};
|
||||
}
|
||||
|
||||
export class Responses {
|
||||
public static decodeAbciInfo(response: JsonRpcSuccessResponse): responses.AbciInfoResponse {
|
||||
return decodeAbciInfo(assertObject((response.result as AbciInfoResult).response));
|
||||
@ -800,6 +812,10 @@ export class Responses {
|
||||
return decodeBlockResults(response.result as RpcBlockResultsResponse);
|
||||
}
|
||||
|
||||
public static decodeBlockSearch(response: JsonRpcSuccessResponse): responses.BlockSearchResponse {
|
||||
return decodeBlockSearch(response.result as RpcBlockSearchResponse);
|
||||
}
|
||||
|
||||
public static decodeBlockchain(response: JsonRpcSuccessResponse): responses.BlockchainResponse {
|
||||
return decodeBlockchain(response.result as RpcBlockchainResponse);
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ export {
|
||||
AbciQueryRequest,
|
||||
BlockRequest,
|
||||
BlockchainRequest,
|
||||
BlockSearchParams,
|
||||
BlockSearchRequest,
|
||||
BlockResultsRequest,
|
||||
BroadcastTxRequest,
|
||||
BroadcastTxParams,
|
||||
@ -38,6 +40,7 @@ export {
|
||||
BlockParams,
|
||||
BlockResponse,
|
||||
BlockResultsResponse,
|
||||
BlockSearchResponse,
|
||||
BroadcastTxAsyncResponse,
|
||||
BroadcastTxCommitResponse,
|
||||
broadcastTxCommitSuccess,
|
||||
|
@ -12,6 +12,7 @@ export enum Method {
|
||||
/** Get block headers for minHeight <= height <= maxHeight. */
|
||||
Blockchain = "blockchain",
|
||||
BlockResults = "block_results",
|
||||
BlockSearch = "block_search",
|
||||
BroadcastTxAsync = "broadcast_tx_async",
|
||||
BroadcastTxSync = "broadcast_tx_sync",
|
||||
BroadcastTxCommit = "broadcast_tx_commit",
|
||||
@ -30,6 +31,7 @@ export type Request =
|
||||
| AbciInfoRequest
|
||||
| AbciQueryRequest
|
||||
| BlockRequest
|
||||
| BlockSearchRequest
|
||||
| BlockchainRequest
|
||||
| BlockResultsRequest
|
||||
| BroadcastTxRequest
|
||||
@ -97,6 +99,18 @@ export interface BlockResultsRequest {
|
||||
};
|
||||
}
|
||||
|
||||
export interface BlockSearchRequest {
|
||||
readonly method: Method.BlockSearch;
|
||||
readonly params: BlockSearchParams;
|
||||
}
|
||||
|
||||
export interface BlockSearchParams {
|
||||
readonly query: string;
|
||||
readonly page?: number;
|
||||
readonly per_page?: number;
|
||||
readonly order_by?: string;
|
||||
}
|
||||
|
||||
export interface BroadcastTxRequest {
|
||||
readonly method: Method.BroadcastTxAsync | Method.BroadcastTxSync | Method.BroadcastTxCommit;
|
||||
readonly params: BroadcastTxParams;
|
||||
|
@ -60,6 +60,11 @@ export interface BlockResultsResponse {
|
||||
readonly endBlockEvents: readonly Event[];
|
||||
}
|
||||
|
||||
export interface BlockSearchResponse {
|
||||
readonly blocks: readonly BlockResponse[];
|
||||
readonly totalCount: number;
|
||||
}
|
||||
|
||||
export interface BlockchainResponse {
|
||||
readonly lastHeight: number;
|
||||
readonly blockMetas: readonly BlockMeta[];
|
||||
|
@ -204,6 +204,69 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
|
||||
});
|
||||
});
|
||||
|
||||
describe("blockSearch", () => {
|
||||
beforeAll(async () => {
|
||||
if (tendermintEnabled()) {
|
||||
const client = await Tendermint34Client.create(rpcFactory());
|
||||
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
async function sendTx(): Promise<void> {
|
||||
const tx = buildKvTx(randomString(), randomString());
|
||||
|
||||
const txRes = await client.broadcastTxCommit({ tx: tx });
|
||||
expect(responses.broadcastTxCommitSuccess(txRes)).toEqual(true);
|
||||
expect(txRes.height).toBeTruthy();
|
||||
expect(txRes.hash.length).not.toEqual(0);
|
||||
}
|
||||
|
||||
// send 3 txs
|
||||
await sendTx();
|
||||
await sendTx();
|
||||
await sendTx();
|
||||
|
||||
client.disconnect();
|
||||
|
||||
await tendermintSearchIndexUpdated();
|
||||
}
|
||||
});
|
||||
|
||||
it("can paginate over blockSearch results", async () => {
|
||||
pendingWithoutTendermint();
|
||||
const client = await Tendermint34Client.create(rpcFactory());
|
||||
|
||||
const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" });
|
||||
|
||||
// expect one page of results
|
||||
const s1 = await client.blockSearch({ query: query, page: 1, per_page: 2 });
|
||||
expect(s1.totalCount).toEqual(3);
|
||||
expect(s1.blocks.length).toEqual(2);
|
||||
|
||||
// second page
|
||||
const s2 = await client.blockSearch({ query: query, page: 2, per_page: 2 });
|
||||
expect(s2.totalCount).toEqual(3);
|
||||
expect(s2.blocks.length).toEqual(1);
|
||||
|
||||
client.disconnect();
|
||||
});
|
||||
|
||||
it("can get all search results in one call", async () => {
|
||||
pendingWithoutTendermint();
|
||||
const client = await Tendermint34Client.create(rpcFactory());
|
||||
|
||||
const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" });
|
||||
|
||||
const sall = await client.blockSearchAll({ query: query, per_page: 2 });
|
||||
expect(sall.totalCount).toEqual(3);
|
||||
expect(sall.blocks.length).toEqual(3);
|
||||
// make sure there are in order from lowest to highest height
|
||||
const [b1, b2, b3] = sall.blocks;
|
||||
expect(b2.block.header.height).toEqual(b1.block.header.height + 1);
|
||||
expect(b3.block.header.height).toEqual(b2.block.header.height + 1);
|
||||
|
||||
client.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("blockchain", () => {
|
||||
it("returns latest in descending order by default", async () => {
|
||||
pendingWithoutTendermint();
|
||||
|
@ -98,6 +98,53 @@ export class Tendermint34Client {
|
||||
return this.doCall(query, this.p.encodeBlockResults, this.r.decodeBlockResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for events that are in a block.
|
||||
*
|
||||
* NOTE
|
||||
* This method will error on any node that is running a Tendermint version lower than 0.34.9.
|
||||
*
|
||||
* @see https://docs.tendermint.com/master/rpc/#/Info/block_search
|
||||
*/
|
||||
public async blockSearch(params: requests.BlockSearchParams): Promise<responses.BlockSearchResponse> {
|
||||
const query: requests.BlockSearchRequest = { params: params, method: requests.Method.BlockSearch };
|
||||
const resp = await this.doCall(query, this.p.encodeBlockSearch, this.r.decodeBlockSearch);
|
||||
return {
|
||||
...resp,
|
||||
// make sure we sort by height, as tendermint may be sorting by string value of the height
|
||||
blocks: [...resp.blocks].sort((a, b) => a.block.header.height - b.block.header.height),
|
||||
};
|
||||
}
|
||||
|
||||
// this should paginate through all blockSearch options to ensure it returns all results.
|
||||
// starts with page 1 or whatever was provided (eg. to start on page 7)
|
||||
//
|
||||
// NOTE
|
||||
// This method will error on any node that is running a Tendermint version lower than 0.34.9.
|
||||
public async blockSearchAll(params: requests.BlockSearchParams): Promise<responses.BlockSearchResponse> {
|
||||
let page = params.page || 1;
|
||||
const blocks: responses.BlockResponse[] = [];
|
||||
let done = false;
|
||||
|
||||
while (!done) {
|
||||
const resp = await this.blockSearch({ ...params, page: page });
|
||||
blocks.push(...resp.blocks);
|
||||
if (blocks.length < resp.totalCount) {
|
||||
page++;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
// make sure we sort by height, as tendermint may be sorting by string value of the height
|
||||
// and the earlier items may be in a higher page than the later items
|
||||
blocks.sort((a, b) => a.block.header.height - b.block.header.height);
|
||||
|
||||
return {
|
||||
totalCount: blocks.length,
|
||||
blocks: blocks,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries block headers filtered by minHeight <= height <= maxHeight.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user