Add CosmWasmClient.searchTx

This commit is contained in:
Simon Warta 2020-02-12 11:27:46 +01:00
parent 737d5570b9
commit 1e122a7546
5 changed files with 210 additions and 4 deletions

View File

@ -1,3 +1,5 @@
import { assert } from "@iov/utils";
import { CosmWasmClient } from "./cosmwasmclient";
import { makeSignBytes, marshalTx } from "./encoding";
import { findAttribute } from "./logs";
@ -5,7 +7,7 @@ import { Secp256k1Pen } from "./pen";
import { RestClient } from "./restclient";
import cosmoshub from "./testdata/cosmoshub.json";
import { getRandomizedHackatom, makeRandomAddress } from "./testutils.spec";
import { Coin, MsgSend, StdFee } from "./types";
import { Coin, CosmosSdkTx, MsgSend, StdFee } from "./types";
const httpUrl = "http://localhost:1317";
@ -118,6 +120,134 @@ describe("CosmWasmClient", () => {
});
});
describe("searchTx", () => {
let posted:
| {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
readonly height: number;
readonly tx: CosmosSdkTx;
}
| undefined;
beforeAll(async () => {
if (cosmosEnabled()) {
pendingWithoutCosmos();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = CosmWasmClient.makeReadOnly(httpUrl);
const memo = "My first contract on chain";
const sendMsg: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
// eslint-disable-next-line @typescript-eslint/camelcase
from_address: faucet.address,
// eslint-disable-next-line @typescript-eslint/camelcase
to_address: makeRandomAddress(),
amount: [
{
denom: "ucosm",
amount: "1234567",
},
],
},
};
const fee: StdFee = {
amount: [
{
amount: "5000",
denom: "ucosm",
},
],
gas: "890000",
};
const chainId = await client.chainId();
const { accountNumber, sequence } = await client.getNonce(faucet.address);
const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await pen.sign(signBytes);
const signedTx = {
msg: [sendMsg],
fee: fee,
memo: memo,
signatures: [signature],
};
const result = await client.postTx(marshalTx(signedTx));
const txDetails = await new RestClient(httpUrl).txsById(result.transactionHash);
posted = {
sender: sendMsg.value.from_address,
recipient: sendMsg.value.to_address,
hash: result.transactionHash,
height: Number.parseInt(txDetails.height, 10),
tx: txDetails.tx,
};
}
});
it("can search by ID", async () => {
pendingWithoutCosmos();
assert(posted, "value must be set in beforeAll()");
const client = CosmWasmClient.makeReadOnly(httpUrl);
const result = await client.searchTx({ id: posted.hash });
expect(result.length).toEqual(1);
expect(result[0]).toEqual(
jasmine.objectContaining({
height: posted.height.toString(),
txhash: posted.hash,
tx: posted.tx,
}),
);
});
it("can search by height", async () => {
pendingWithoutCosmos();
assert(posted, "value must be set in beforeAll()");
const client = CosmWasmClient.makeReadOnly(httpUrl);
const result = await client.searchTx({ height: posted.height });
expect(result.length).toEqual(1);
expect(result[0]).toEqual(
jasmine.objectContaining({
height: posted.height.toString(),
txhash: posted.hash,
tx: posted.tx,
}),
);
});
it("can search by sender", async () => {
pendingWithoutCosmos();
assert(posted, "value must be set in beforeAll()");
const client = CosmWasmClient.makeReadOnly(httpUrl);
const result = await client.searchTx({ sentFromOrTo: posted.sender });
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result[result.length - 1]).toEqual(
jasmine.objectContaining({
height: posted.height.toString(),
txhash: posted.hash,
tx: posted.tx,
}),
);
});
it("can search by recipient", async () => {
pendingWithoutCosmos();
assert(posted, "value must be set in beforeAll()");
const client = CosmWasmClient.makeReadOnly(httpUrl);
const result = await client.searchTx({ sentFromOrTo: posted.recipient });
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result[result.length - 1]).toEqual(
jasmine.objectContaining({
height: posted.height.toString(),
txhash: posted.hash,
tx: posted.tx,
}),
);
});
});
describe("upload", () => {
it("works", async () => {
pendingWithoutCosmos();

View File

@ -3,7 +3,7 @@ import { Encoding } from "@iov/encoding";
import { makeSignBytes, marshalTx } from "./encoding";
import { findAttribute, Log, parseLogs } from "./logs";
import { RestClient } from "./restclient";
import { RestClient, TxsResponse } from "./restclient";
import {
Coin,
CosmosSdkTx,
@ -65,6 +65,32 @@ export interface PostTxResult {
readonly transactionHash: string;
}
export interface SearchByIdQuery {
readonly id: string;
}
export interface SearchByHeightQuery {
readonly height: number;
}
export interface SearchBySentFromOrToQuery {
readonly sentFromOrTo: string;
}
export type SearchTxQuery = SearchByIdQuery | SearchByHeightQuery | SearchBySentFromOrToQuery;
function isSearchByIdQuery(query: SearchTxQuery): query is SearchByIdQuery {
return (query as SearchByIdQuery).id !== undefined;
}
function isSearchByHeightQuery(query: SearchTxQuery): query is SearchByHeightQuery {
return (query as SearchByHeightQuery).height !== undefined;
}
function isSearchBySentFromOrToQuery(query: SearchTxQuery): query is SearchBySentFromOrToQuery {
return (query as SearchBySentFromOrToQuery).sentFromOrTo !== undefined;
}
export interface ExecuteResult {
readonly logs: readonly Log[];
}
@ -131,6 +157,26 @@ export class CosmWasmClient {
};
}
public async searchTx(query: SearchTxQuery): Promise<readonly TxsResponse[]> {
// TODO: we need proper pagination support
function limited(originalQuery: string): string {
return `${originalQuery}&limit=75`;
}
if (isSearchByIdQuery(query)) {
return [await this.restClient.txsById(query.id)];
} else if (isSearchByHeightQuery(query)) {
return (await this.restClient.txs(`tx.height=${query.height}`)).txs;
} else if (isSearchBySentFromOrToQuery(query)) {
const sent = (await this.restClient.txs(limited(`message.sender=${query.sentFromOrTo}`))).txs;
const sentHashes = sent.map(t => t.txhash);
const received = (await this.restClient.txs(limited(`transfer.recipient=${query.sentFromOrTo}`))).txs;
return [...sent, ...received.filter(t => !sentHashes.includes(t.txhash))];
} else {
throw new Error("Unknown query type");
}
}
public async postTx(tx: Uint8Array): Promise<PostTxResult> {
const result = await this.restClient.postTx(tx);
if (result.code) {

View File

@ -7,7 +7,16 @@ export { unmarshalTx } from "./decoding";
export { makeSignBytes, marshalTx } from "./encoding";
export { RestClient, TxsResponse } from "./restclient";
export { encodeSecp256k1Signature } from "./signature";
export { CosmWasmClient, ExecuteResult, GetNonceResult, PostTxResult } from "./cosmwasmclient";
export {
CosmWasmClient,
ExecuteResult,
GetNonceResult,
PostTxResult,
SearchByHeightQuery,
SearchByIdQuery,
SearchBySentFromOrToQuery,
SearchTxQuery,
} from "./cosmwasmclient";
export { makeCosmoshubPath, Pen, PrehashType, Secp256k1Pen } from "./pen";
export {
CosmosPubkeyBech32Prefix,

View File

@ -1,4 +1,5 @@
import { Log } from "./logs";
import { TxsResponse } from "./restclient";
import { Coin, CosmosSdkTx, StdSignature } from "./types";
export interface SigningCallback {
(signBytes: Uint8Array): Promise<StdSignature>;
@ -13,6 +14,16 @@ export interface PostTxResult {
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-exmpty upper-case hex */
readonly transactionHash: string;
}
export interface SearchByIdQuery {
readonly id: string;
}
export interface SearchByHeightQuery {
readonly height: number;
}
export interface SearchBySentFromOrToQuery {
readonly sentFromOrTo: string;
}
export declare type SearchTxQuery = SearchByIdQuery | SearchByHeightQuery | SearchBySentFromOrToQuery;
export interface ExecuteResult {
readonly logs: readonly Log[];
}
@ -35,6 +46,7 @@ export declare class CosmWasmClient {
* @param address returns data for this address. When unset, the client's sender adddress is used.
*/
getNonce(address?: string): Promise<GetNonceResult>;
searchTx(query: SearchTxQuery): Promise<readonly TxsResponse[]>;
postTx(tx: Uint8Array): Promise<PostTxResult>;
/** Uploads code and returns a code ID */
upload(wasmCode: Uint8Array, memo?: string): Promise<number>;

View File

@ -6,7 +6,16 @@ export { unmarshalTx } from "./decoding";
export { makeSignBytes, marshalTx } from "./encoding";
export { RestClient, TxsResponse } from "./restclient";
export { encodeSecp256k1Signature } from "./signature";
export { CosmWasmClient, ExecuteResult, GetNonceResult, PostTxResult } from "./cosmwasmclient";
export {
CosmWasmClient,
ExecuteResult,
GetNonceResult,
PostTxResult,
SearchByHeightQuery,
SearchByIdQuery,
SearchBySentFromOrToQuery,
SearchTxQuery,
} from "./cosmwasmclient";
export { makeCosmoshubPath, Pen, PrehashType, Secp256k1Pen } from "./pen";
export {
CosmosPubkeyBech32Prefix,