mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-10 21:49:15 +00:00
Backport block search
This commit is contained in:
parent
5f694f2ed6
commit
65db62d3ee
@ -6,6 +6,14 @@ and this project adheres to
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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
|
## [0.25.4] - 2021-05-31
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -23,6 +23,7 @@ export interface Params {
|
|||||||
readonly encodeBlock: (req: requests.BlockRequest) => JsonRpcRequest;
|
readonly encodeBlock: (req: requests.BlockRequest) => JsonRpcRequest;
|
||||||
readonly encodeBlockchain: (req: requests.BlockchainRequest) => JsonRpcRequest;
|
readonly encodeBlockchain: (req: requests.BlockchainRequest) => JsonRpcRequest;
|
||||||
readonly encodeBlockResults: (req: requests.BlockResultsRequest) => JsonRpcRequest;
|
readonly encodeBlockResults: (req: requests.BlockResultsRequest) => JsonRpcRequest;
|
||||||
|
readonly encodeBlockSearch: (req: requests.BlockSearchRequest) => JsonRpcRequest;
|
||||||
readonly encodeBroadcastTx: (req: requests.BroadcastTxRequest) => JsonRpcRequest;
|
readonly encodeBroadcastTx: (req: requests.BroadcastTxRequest) => JsonRpcRequest;
|
||||||
readonly encodeCommit: (req: requests.CommitRequest) => JsonRpcRequest;
|
readonly encodeCommit: (req: requests.CommitRequest) => JsonRpcRequest;
|
||||||
readonly encodeGenesis: (req: requests.GenesisRequest) => JsonRpcRequest;
|
readonly encodeGenesis: (req: requests.GenesisRequest) => JsonRpcRequest;
|
||||||
@ -39,6 +40,7 @@ export interface Responses {
|
|||||||
readonly decodeAbciQuery: (response: JsonRpcSuccessResponse) => responses.AbciQueryResponse;
|
readonly decodeAbciQuery: (response: JsonRpcSuccessResponse) => responses.AbciQueryResponse;
|
||||||
readonly decodeBlock: (response: JsonRpcSuccessResponse) => responses.BlockResponse;
|
readonly decodeBlock: (response: JsonRpcSuccessResponse) => responses.BlockResponse;
|
||||||
readonly decodeBlockResults: (response: JsonRpcSuccessResponse) => responses.BlockResultsResponse;
|
readonly decodeBlockResults: (response: JsonRpcSuccessResponse) => responses.BlockResultsResponse;
|
||||||
|
readonly decodeBlockSearch: (response: JsonRpcSuccessResponse) => responses.BlockSearchResponse;
|
||||||
readonly decodeBlockchain: (response: JsonRpcSuccessResponse) => responses.BlockchainResponse;
|
readonly decodeBlockchain: (response: JsonRpcSuccessResponse) => responses.BlockchainResponse;
|
||||||
readonly decodeBroadcastTxSync: (response: JsonRpcSuccessResponse) => responses.BroadcastTxSyncResponse;
|
readonly decodeBroadcastTxSync: (response: JsonRpcSuccessResponse) => responses.BroadcastTxSyncResponse;
|
||||||
readonly decodeBroadcastTxAsync: (response: JsonRpcSuccessResponse) => responses.BroadcastTxAsyncResponse;
|
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 {
|
interface RpcAbciQueryParams {
|
||||||
readonly path: string;
|
readonly path: string;
|
||||||
/** hex encoded */
|
/** hex encoded */
|
||||||
@ -118,6 +133,10 @@ export class Params {
|
|||||||
return createJsonRpcRequest(req.method, encodeHeightParam(req.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 {
|
public static encodeBroadcastTx(req: requests.BroadcastTxRequest): JsonRpcRequest {
|
||||||
return createJsonRpcRequest(req.method, encodeBroadcastTxParams(req.params));
|
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 {
|
export class Responses {
|
||||||
public static decodeAbciInfo(response: JsonRpcSuccessResponse): responses.AbciInfoResponse {
|
public static decodeAbciInfo(response: JsonRpcSuccessResponse): responses.AbciInfoResponse {
|
||||||
return decodeAbciInfo(assertObject((response.result as AbciInfoResult).response));
|
return decodeAbciInfo(assertObject((response.result as AbciInfoResult).response));
|
||||||
@ -800,6 +812,10 @@ export class Responses {
|
|||||||
return decodeBlockResults(response.result as RpcBlockResultsResponse);
|
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 {
|
public static decodeBlockchain(response: JsonRpcSuccessResponse): responses.BlockchainResponse {
|
||||||
return decodeBlockchain(response.result as RpcBlockchainResponse);
|
return decodeBlockchain(response.result as RpcBlockchainResponse);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ export {
|
|||||||
AbciQueryRequest,
|
AbciQueryRequest,
|
||||||
BlockRequest,
|
BlockRequest,
|
||||||
BlockchainRequest,
|
BlockchainRequest,
|
||||||
|
BlockSearchParams,
|
||||||
|
BlockSearchRequest,
|
||||||
BlockResultsRequest,
|
BlockResultsRequest,
|
||||||
BroadcastTxRequest,
|
BroadcastTxRequest,
|
||||||
BroadcastTxParams,
|
BroadcastTxParams,
|
||||||
@ -38,6 +40,7 @@ export {
|
|||||||
BlockParams,
|
BlockParams,
|
||||||
BlockResponse,
|
BlockResponse,
|
||||||
BlockResultsResponse,
|
BlockResultsResponse,
|
||||||
|
BlockSearchResponse,
|
||||||
BroadcastTxAsyncResponse,
|
BroadcastTxAsyncResponse,
|
||||||
BroadcastTxCommitResponse,
|
BroadcastTxCommitResponse,
|
||||||
broadcastTxCommitSuccess,
|
broadcastTxCommitSuccess,
|
||||||
|
@ -12,6 +12,7 @@ export enum Method {
|
|||||||
/** Get block headers for minHeight <= height <= maxHeight. */
|
/** Get block headers for minHeight <= height <= maxHeight. */
|
||||||
Blockchain = "blockchain",
|
Blockchain = "blockchain",
|
||||||
BlockResults = "block_results",
|
BlockResults = "block_results",
|
||||||
|
BlockSearch = "block_search",
|
||||||
BroadcastTxAsync = "broadcast_tx_async",
|
BroadcastTxAsync = "broadcast_tx_async",
|
||||||
BroadcastTxSync = "broadcast_tx_sync",
|
BroadcastTxSync = "broadcast_tx_sync",
|
||||||
BroadcastTxCommit = "broadcast_tx_commit",
|
BroadcastTxCommit = "broadcast_tx_commit",
|
||||||
@ -30,6 +31,7 @@ export type Request =
|
|||||||
| AbciInfoRequest
|
| AbciInfoRequest
|
||||||
| AbciQueryRequest
|
| AbciQueryRequest
|
||||||
| BlockRequest
|
| BlockRequest
|
||||||
|
| BlockSearchRequest
|
||||||
| BlockchainRequest
|
| BlockchainRequest
|
||||||
| BlockResultsRequest
|
| BlockResultsRequest
|
||||||
| BroadcastTxRequest
|
| 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 {
|
export interface BroadcastTxRequest {
|
||||||
readonly method: Method.BroadcastTxAsync | Method.BroadcastTxSync | Method.BroadcastTxCommit;
|
readonly method: Method.BroadcastTxAsync | Method.BroadcastTxSync | Method.BroadcastTxCommit;
|
||||||
readonly params: BroadcastTxParams;
|
readonly params: BroadcastTxParams;
|
||||||
|
@ -60,6 +60,11 @@ export interface BlockResultsResponse {
|
|||||||
readonly endBlockEvents: readonly Event[];
|
readonly endBlockEvents: readonly Event[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BlockSearchResponse {
|
||||||
|
readonly blocks: readonly BlockResponse[];
|
||||||
|
readonly totalCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BlockchainResponse {
|
export interface BlockchainResponse {
|
||||||
readonly lastHeight: number;
|
readonly lastHeight: number;
|
||||||
readonly blockMetas: readonly BlockMeta[];
|
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", () => {
|
describe("blockchain", () => {
|
||||||
it("returns latest in descending order by default", async () => {
|
it("returns latest in descending order by default", async () => {
|
||||||
pendingWithoutTendermint();
|
pendingWithoutTendermint();
|
||||||
|
@ -98,6 +98,53 @@ export class Tendermint34Client {
|
|||||||
return this.doCall(query, this.p.encodeBlockResults, this.r.decodeBlockResults);
|
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.
|
* Queries block headers filtered by minHeight <= height <= maxHeight.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user