Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for /block_search RPC endpoint #810

Closed
wants to merge 10 commits into from
2 changes: 2 additions & 0 deletions packages/tendermint-rpc/src/tendermint34/adaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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);
}
Expand Down
17 changes: 17 additions & 0 deletions packages/tendermint-rpc/src/tendermint34/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -30,6 +31,7 @@ export type Request =
| AbciInfoRequest
| AbciQueryRequest
| BlockRequest
| BlockSearchRequest
| BlockchainRequest
| BlockResultsRequest
| BroadcastTxRequest
Expand Down Expand Up @@ -60,6 +62,7 @@ export interface AbciQueryRequest {
readonly method: Method.AbciQuery;
readonly params: AbciQueryParams;
}

export interface AbciQueryParams {
readonly path: string;
readonly data: Uint8Array;
Expand Down Expand Up @@ -97,10 +100,23 @@ 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;
}

export interface BroadcastTxParams {
readonly tx: Uint8Array;
}
Expand Down Expand Up @@ -141,6 +157,7 @@ export interface TxRequest {
readonly method: Method.Tx;
readonly params: TxParams;
}

export interface TxParams {
readonly hash: Uint8Array;
readonly prove?: boolean;
Expand Down
5 changes: 5 additions & 0 deletions packages/tendermint-rpc/src/tendermint34/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down
41 changes: 41 additions & 0 deletions packages/tendermint-rpc/src/tendermint34/tendermint34client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,47 @@ export class Tendermint34Client {
return this.doCall(query, this.p.encodeBlockResults, this.r.decodeBlockResults);
}

/**
* Search for events that are in a block
*
* @see https://docs.tendermint.com/master/rpc/#/Info/block_search
*/
public async blockSearch(params: requests.BlockSearchParams): Promise<responses.BlockSearchResponse> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me (in terms of functionality).

My one question is that this is only available in tendermint v0.34.9+, correct? Minimally that should be documented in the comments along with a link to the PR. However, what happens when someone tries to run this on tendermint v0.34.8 node? There should be a clearly defined error case that distinguishes "network error" from "tendermint version too old" or it will be very hard for people to debug this.

Maybe @webmaster128 has some idea how to do this properly - it seems this error would be thrown in this.doCall

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a note regarding compatibility here would be nice.

I would not get into the version checking business again, which led to a lot of trouble sttarting with Tendermint 0.34. If the backend does not support an RPC method, it will return some kind of error that explains the problem somehow.

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)
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.
*
Expand Down