Merge pull request #573 from cosmos/532-stargate-searchTx-queries
Support more Stargate searchTx queries
This commit is contained in:
@@ -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,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user