Merge pull request #573 from cosmos/532-stargate-searchTx-queries

Support more Stargate searchTx queries
This commit is contained in:
Will Clark
2020-12-15 13:13:16 +01:00
committed by GitHub
3 changed files with 171 additions and 7 deletions
@@ -17,9 +17,16 @@ import {
isBroadcastTxSuccess,
StargateClient,
} from "./stargateclient";
import { faucet, makeRandomAddress, pendingWithoutSimapp, simapp, simappEnabled } from "./testutils.spec";
import {
faucet,
fromOneElementArray,
makeRandomAddress,
pendingWithoutSimapp,
simapp,
simappEnabled,
} from "./testutils.spec";
const { TxRaw } = cosmos.tx.v1beta1;
const { Tx, TxRaw } = cosmos.tx.v1beta1;
interface TestTxSend {
readonly sender: string;
@@ -242,4 +249,146 @@ describe("StargateClient.searchTx", () => {
);
});
});
describe("with SearchBySentFromOrToQuery", () => {
it("can search by sender", async () => {
pendingWithoutSimapp();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.sender });
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const filteredMsgs = tx.body!.messages!.filter(({ type_url: typeUrl, value }) => {
if (typeUrl !== "/cosmos.bank.v1beta1.MsgSend") return false;
const decoded = registry.decode({ typeUrl: typeUrl, value: value! });
return decoded.fromAddress === sendSuccessful?.sender;
});
expect(filteredMsgs.length).toBeGreaterThanOrEqual(1);
}
// Check details of most recent result
expect(results[results.length - 1]).toEqual(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
tx: sendSuccessful.tx,
}),
);
});
it("can search by recipient", async () => {
pendingWithoutSimapp();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const results = await client.searchTx({ sentFromOrTo: sendSuccessful.recipient });
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const filteredMsgs = tx.body!.messages!.filter(({ type_url: typeUrl, value }) => {
if (typeUrl !== "/cosmos.bank.v1beta1.MsgSend") return false;
const decoded = registry.decode({ typeUrl: typeUrl, value: value! });
return decoded.toAddress === sendSuccessful?.recipient;
});
expect(filteredMsgs.length).toBeGreaterThanOrEqual(1);
}
// Check details of most recent result
expect(results[results.length - 1]).toEqual(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
tx: sendSuccessful.tx,
}),
);
});
it("can search by recipient and filter by minHeight", async () => {
pendingWithoutSimapp();
assert(sendSuccessful);
const client = await StargateClient.connect(simapp.tendermintUrl);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { minHeight: 0 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { minHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(0);
}
});
it("can search by recipient and filter by maxHeight", async () => {
pendingWithoutSimapp();
assert(sendSuccessful);
const client = await StargateClient.connect(simapp.tendermintUrl);
const query = { sentFromOrTo: sendSuccessful.recipient };
{
const result = await client.searchTx(query, { maxHeight: 9999999999999 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height + 1 });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height });
expect(result.length).toEqual(1);
}
{
const result = await client.searchTx(query, { maxHeight: sendSuccessful.height - 1 });
expect(result.length).toEqual(0);
}
});
});
describe("with SearchByTagsQuery", () => {
it("can search by transfer.recipient", async () => {
pendingWithoutSimapp();
assert(sendSuccessful, "value must be set in beforeAll()");
const client = await StargateClient.connect(simapp.tendermintUrl);
const results = await client.searchTx({
tags: [{ key: "transfer.recipient", value: sendSuccessful.recipient }],
});
expect(results.length).toBeGreaterThanOrEqual(1);
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const { type_url: typeUrl, value } = fromOneElementArray(tx.body!.messages!);
expect(typeUrl).toEqual("/cosmos.bank.v1beta1.MsgSend");
const decoded = registry.decode({ typeUrl: typeUrl!, value: value! });
expect(decoded.toAddress).toEqual(sendSuccessful.recipient);
}
// Check details of most recent result
expect(results[results.length - 1]).toEqual(
jasmine.objectContaining({
height: sendSuccessful.height,
hash: sendSuccessful.hash,
tx: sendSuccessful.tx,
}),
);
});
});
});
+14 -5
View File
@@ -213,6 +213,10 @@ export class StargateClient {
if (maxHeight < minHeight) return []; // optional optimization
function withFilters(originalQuery: string): string {
return `${originalQuery} AND tx.height>=${minHeight} AND tx.height<=${maxHeight}`;
}
let txs: readonly IndexedTx[];
if (isSearchByIdQuery(query)) {
@@ -223,13 +227,18 @@ export class StargateClient {
? await this.txsQuery(`tx.height=${query.height}`)
: [];
} else if (isSearchBySentFromOrToQuery(query)) {
throw new Error(
"This type of search query is not yet implemented. See https://github.com/cosmos/cosmjs/issues/533.",
const sentQuery = withFilters(`message.module='bank' AND transfer.sender='${query.sentFromOrTo}'`);
const receivedQuery = withFilters(
`message.module='bank' AND transfer.recipient='${query.sentFromOrTo}'`,
);
const [sent, received] = await Promise.all(
[sentQuery, receivedQuery].map((rawQuery) => this.txsQuery(rawQuery)),
);
const sentHashes = sent.map((t) => t.hash);
txs = [...sent, ...received.filter((t) => !sentHashes.includes(t.hash))];
} else if (isSearchByTagsQuery(query)) {
throw new Error(
"This type of search query is not yet implemented. See https://github.com/cosmos/cosmjs/issues/532.",
);
const rawQuery = withFilters(query.tags.map((t) => `${t.key}='${t.value}'`).join(" AND "));
txs = await this.txsQuery(rawQuery);
} else {
throw new Error("Unknown query type");
}
+6
View File
@@ -32,6 +32,12 @@ export function makeRandomAddress(): string {
return Bech32.encode("cosmos", makeRandomAddressBytes());
}
/** Returns first element. Throws if array has a different length than 1. */
export function fromOneElementArray<T>(elements: ArrayLike<T>): T {
if (elements.length !== 1) throw new Error(`Expected exactly one element but got ${elements.length}`);
return elements[0];
}
export const simapp = {
tendermintUrl: "localhost:26658",
tendermintUrlWs: "ws://localhost:26658",