673 lines
22 KiB
TypeScript
673 lines
22 KiB
TypeScript
/* eslint-disable no-dupe-class-members, @typescript-eslint/ban-types, @typescript-eslint/naming-convention */
|
|
import { iavlSpec, ics23, tendermintSpec, verifyExistence, verifyNonExistence } from "@confio/ics23";
|
|
import { toAscii, toHex } from "@cosmjs/encoding";
|
|
import { firstEvent } from "@cosmjs/stream";
|
|
import { CometClient, tendermint34 } from "@cosmjs/tendermint-rpc";
|
|
import { arrayContentEquals, assert, assertDefined, isNonNullObject, sleep } from "@cosmjs/utils";
|
|
import { ProofOps } from "cosmjs-types/tendermint/crypto/proof";
|
|
import { Stream } from "xstream";
|
|
|
|
type QueryExtensionSetup<P> = (base: QueryClient) => P;
|
|
|
|
function checkAndParseOp(op: tendermint34.ProofOp, kind: string, key: Uint8Array): ics23.CommitmentProof {
|
|
if (op.type !== kind) {
|
|
throw new Error(`Op expected to be ${kind}, got "${op.type}`);
|
|
}
|
|
if (!arrayContentEquals(key, op.key)) {
|
|
throw new Error(`Proven key different than queried key.\nQuery: ${toHex(key)}\nProven: ${toHex(op.key)}`);
|
|
}
|
|
return ics23.CommitmentProof.decode(op.data);
|
|
}
|
|
|
|
export interface ProvenQuery {
|
|
readonly key: Uint8Array;
|
|
readonly value: Uint8Array;
|
|
readonly proof: ProofOps;
|
|
readonly height: number;
|
|
}
|
|
|
|
export interface QueryStoreResponse {
|
|
/** The response key from Tendermint. This is the same as the query key in the request. */
|
|
readonly key: Uint8Array;
|
|
readonly value: Uint8Array;
|
|
readonly height: number;
|
|
}
|
|
|
|
/**
|
|
* The response of an ABCI query to Tendermint.
|
|
* This is a subset of `tendermint34.AbciQueryResponse` in order
|
|
* to abstract away Tendermint versions.
|
|
*/
|
|
export interface QueryAbciResponse {
|
|
readonly value: Uint8Array;
|
|
readonly height: number;
|
|
}
|
|
|
|
export class QueryClient {
|
|
/** Constructs a QueryClient with 0 extensions */
|
|
public static withExtensions(cometClient: CometClient): QueryClient;
|
|
|
|
/** Constructs a QueryClient with 1 extension */
|
|
public static withExtensions<A extends object>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
): QueryClient & A;
|
|
|
|
/** Constructs a QueryClient with 2 extensions */
|
|
public static withExtensions<A extends object, B extends object>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
): QueryClient & A & B;
|
|
|
|
/** Constructs a QueryClient with 3 extensions */
|
|
public static withExtensions<A extends object, B extends object, C extends object>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
): QueryClient & A & B & C;
|
|
|
|
/** Constructs a QueryClient with 4 extensions */
|
|
public static withExtensions<A extends object, B extends object, C extends object, D extends object>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
): QueryClient & A & B & C & D;
|
|
|
|
/** Constructs a QueryClient with 5 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
): QueryClient & A & B & C & D & E;
|
|
|
|
/** Constructs a QueryClient with 6 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
): QueryClient & A & B & C & D & E & F;
|
|
|
|
/** Constructs a QueryClient with 7 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
): QueryClient & A & B & C & D & E & F & G;
|
|
|
|
/** Constructs a QueryClient with 8 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
): QueryClient & A & B & C & D & E & F & G & H;
|
|
|
|
/** Constructs a QueryClient with 9 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I;
|
|
|
|
/** Constructs a QueryClient with 10 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J;
|
|
|
|
/** Constructs a QueryClient with 11 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K;
|
|
|
|
/** Constructs a QueryClient with 12 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
L extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
setupExtensionL: QueryExtensionSetup<L>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L;
|
|
|
|
/** Constructs a QueryClient with 13 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
L extends object,
|
|
M extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
setupExtensionL: QueryExtensionSetup<L>,
|
|
setupExtensionM: QueryExtensionSetup<M>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L & M;
|
|
|
|
/** Constructs a QueryClient with 14 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
L extends object,
|
|
M extends object,
|
|
N extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
setupExtensionL: QueryExtensionSetup<L>,
|
|
setupExtensionM: QueryExtensionSetup<M>,
|
|
setupExtensionN: QueryExtensionSetup<N>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L & M & N;
|
|
|
|
/** Constructs a QueryClient with 15 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
L extends object,
|
|
M extends object,
|
|
N extends object,
|
|
O extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
setupExtensionL: QueryExtensionSetup<L>,
|
|
setupExtensionM: QueryExtensionSetup<M>,
|
|
setupExtensionN: QueryExtensionSetup<N>,
|
|
setupExtensionO: QueryExtensionSetup<O>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L & M & N & O;
|
|
|
|
/** Constructs a QueryClient with 16 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
L extends object,
|
|
M extends object,
|
|
N extends object,
|
|
O extends object,
|
|
P extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
setupExtensionL: QueryExtensionSetup<L>,
|
|
setupExtensionM: QueryExtensionSetup<M>,
|
|
setupExtensionN: QueryExtensionSetup<N>,
|
|
setupExtensionO: QueryExtensionSetup<O>,
|
|
setupExtensionP: QueryExtensionSetup<P>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L & M & N & O & P;
|
|
|
|
/** Constructs a QueryClient with 17 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
L extends object,
|
|
M extends object,
|
|
N extends object,
|
|
O extends object,
|
|
P extends object,
|
|
Q extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
setupExtensionL: QueryExtensionSetup<L>,
|
|
setupExtensionM: QueryExtensionSetup<M>,
|
|
setupExtensionN: QueryExtensionSetup<N>,
|
|
setupExtensionO: QueryExtensionSetup<O>,
|
|
setupExtensionP: QueryExtensionSetup<P>,
|
|
setupExtensionQ: QueryExtensionSetup<Q>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L & M & N & O & P & Q;
|
|
|
|
/** Constructs a QueryClient with 18 extensions */
|
|
public static withExtensions<
|
|
A extends object,
|
|
B extends object,
|
|
C extends object,
|
|
D extends object,
|
|
E extends object,
|
|
F extends object,
|
|
G extends object,
|
|
H extends object,
|
|
I extends object,
|
|
J extends object,
|
|
K extends object,
|
|
L extends object,
|
|
M extends object,
|
|
N extends object,
|
|
O extends object,
|
|
P extends object,
|
|
Q extends object,
|
|
R extends object,
|
|
>(
|
|
cometClient: CometClient,
|
|
setupExtensionA: QueryExtensionSetup<A>,
|
|
setupExtensionB: QueryExtensionSetup<B>,
|
|
setupExtensionC: QueryExtensionSetup<C>,
|
|
setupExtensionD: QueryExtensionSetup<D>,
|
|
setupExtensionE: QueryExtensionSetup<E>,
|
|
setupExtensionF: QueryExtensionSetup<F>,
|
|
setupExtensionG: QueryExtensionSetup<G>,
|
|
setupExtensionH: QueryExtensionSetup<H>,
|
|
setupExtensionI: QueryExtensionSetup<I>,
|
|
setupExtensionJ: QueryExtensionSetup<J>,
|
|
setupExtensionK: QueryExtensionSetup<K>,
|
|
setupExtensionL: QueryExtensionSetup<L>,
|
|
setupExtensionM: QueryExtensionSetup<M>,
|
|
setupExtensionN: QueryExtensionSetup<N>,
|
|
setupExtensionO: QueryExtensionSetup<O>,
|
|
setupExtensionP: QueryExtensionSetup<P>,
|
|
setupExtensionQ: QueryExtensionSetup<Q>,
|
|
setupExtensionR: QueryExtensionSetup<R>,
|
|
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L & M & N & O & P & Q & R;
|
|
|
|
public static withExtensions(
|
|
cometClient: CometClient,
|
|
...extensionSetups: Array<QueryExtensionSetup<object>>
|
|
): any {
|
|
const client = new QueryClient(cometClient);
|
|
const extensions = extensionSetups.map((setupExtension) => setupExtension(client));
|
|
for (const extension of extensions) {
|
|
assert(isNonNullObject(extension), `Extension must be a non-null object`);
|
|
for (const [moduleKey, moduleValue] of Object.entries(extension)) {
|
|
assert(
|
|
isNonNullObject(moduleValue),
|
|
`Module must be a non-null object. Found type ${typeof moduleValue} for module "${moduleKey}".`,
|
|
);
|
|
const current = (client as any)[moduleKey] || {};
|
|
(client as any)[moduleKey] = {
|
|
...current,
|
|
...moduleValue,
|
|
};
|
|
}
|
|
}
|
|
return client;
|
|
}
|
|
|
|
private readonly cometClient: CometClient;
|
|
|
|
public constructor(cometClient: CometClient) {
|
|
this.cometClient = cometClient;
|
|
}
|
|
|
|
/**
|
|
* Queries the database store with a proof, which is then verified.
|
|
*
|
|
* Please note: the current implementation trusts block headers it gets from the PRC endpoint.
|
|
*/
|
|
public async queryStoreVerified(
|
|
store: string,
|
|
queryKey: Uint8Array,
|
|
desiredHeight?: number,
|
|
): Promise<QueryStoreResponse> {
|
|
const { height, proof, key, value } = await this.queryRawProof(store, queryKey, desiredHeight);
|
|
|
|
const subProof = checkAndParseOp(proof.ops[0], "ics23:iavl", queryKey);
|
|
const storeProof = checkAndParseOp(proof.ops[1], "ics23:simple", toAscii(store));
|
|
|
|
// this must always be existence, if the store is not a typo
|
|
assert(storeProof.exist);
|
|
assert(storeProof.exist.value);
|
|
|
|
// this may be exist or non-exist, depends on response
|
|
if (!value || value.length === 0) {
|
|
// non-existence check
|
|
assert(subProof.nonexist);
|
|
// the subproof must map the desired key to the "value" of the storeProof
|
|
verifyNonExistence(subProof.nonexist, iavlSpec, storeProof.exist.value, queryKey);
|
|
} else {
|
|
// existence check
|
|
assert(subProof.exist);
|
|
assert(subProof.exist.value);
|
|
// the subproof must map the desired key to the "value" of the storeProof
|
|
verifyExistence(subProof.exist, iavlSpec, storeProof.exist.value, queryKey, value);
|
|
}
|
|
|
|
// the store proof must map its declared value (root of subProof) to the appHash of the next block
|
|
const header = await this.getNextHeader(height);
|
|
verifyExistence(storeProof.exist, tendermintSpec, header.appHash, toAscii(store), storeProof.exist.value);
|
|
|
|
return { key, value, height };
|
|
}
|
|
|
|
public async queryRawProof(
|
|
store: string,
|
|
queryKey: Uint8Array,
|
|
desiredHeight?: number,
|
|
): Promise<ProvenQuery> {
|
|
const { key, value, height, proof, code, log } = await this.cometClient.abciQuery({
|
|
// we need the StoreKey for the module, not the module name
|
|
// https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L12
|
|
path: `/store/${store}/key`,
|
|
data: queryKey,
|
|
prove: true,
|
|
height: desiredHeight,
|
|
});
|
|
|
|
if (code) {
|
|
throw new Error(`Query failed with (${code}): ${log}`);
|
|
}
|
|
|
|
if (!arrayContentEquals(queryKey, key)) {
|
|
throw new Error(`Response key ${toHex(key)} doesn't match query key ${toHex(queryKey)}`);
|
|
}
|
|
|
|
if (!height) {
|
|
throw new Error("No query height returned");
|
|
}
|
|
if (!proof || proof.ops.length !== 2) {
|
|
throw new Error(`Expected 2 proof ops, got ${proof?.ops.length ?? 0}. Are you using stargate?`);
|
|
}
|
|
|
|
// we don't need the results, but we can ensure the data is the proper format
|
|
checkAndParseOp(proof.ops[0], "ics23:iavl", key);
|
|
checkAndParseOp(proof.ops[1], "ics23:simple", toAscii(store));
|
|
|
|
return {
|
|
key: key,
|
|
value: value,
|
|
height: height,
|
|
// need to clone this: readonly input / writeable output
|
|
proof: {
|
|
ops: [...proof.ops],
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Performs an ABCI query to Tendermint without requesting a proof.
|
|
*
|
|
* If the `desiredHeight` is set, a particular height is requested. Otherwise
|
|
* the latest height is requested. The response contains the actual height of
|
|
* the query.
|
|
*/
|
|
public async queryAbci(
|
|
path: string,
|
|
request: Uint8Array,
|
|
desiredHeight?: number,
|
|
): Promise<QueryAbciResponse> {
|
|
const response = await this.cometClient.abciQuery({
|
|
path: path,
|
|
data: request,
|
|
prove: false,
|
|
height: desiredHeight,
|
|
});
|
|
|
|
if (response.code) {
|
|
throw new Error(`Query failed with (${response.code}): ${response.log}`);
|
|
}
|
|
|
|
if (!response.height) {
|
|
throw new Error("No query height returned");
|
|
}
|
|
|
|
return {
|
|
value: response.value,
|
|
height: response.height,
|
|
};
|
|
}
|
|
|
|
// this must return the header for height+1
|
|
// throws an error if height is 0 or undefined
|
|
private async getNextHeader(height?: number): Promise<tendermint34.Header> {
|
|
assertDefined(height);
|
|
if (height === 0) {
|
|
throw new Error("Query returned height 0, cannot prove it");
|
|
}
|
|
|
|
const searchHeight = height + 1;
|
|
let nextHeader: tendermint34.Header | undefined;
|
|
let headersSubscription: Stream<tendermint34.NewBlockHeaderEvent> | undefined;
|
|
try {
|
|
headersSubscription = this.cometClient.subscribeNewBlockHeader();
|
|
} catch {
|
|
// Ignore exception caused by non-WebSocket Tendermint clients
|
|
}
|
|
|
|
if (headersSubscription) {
|
|
const firstHeader = await firstEvent(headersSubscription);
|
|
// The first header we get might not be n+1 but n+2 or even higher. In such cases we fall back on a query.
|
|
if (firstHeader.height === searchHeight) {
|
|
nextHeader = firstHeader;
|
|
}
|
|
}
|
|
|
|
while (!nextHeader) {
|
|
// start from current height to avoid backend error for minHeight in the future
|
|
const correctHeader = (await this.cometClient.blockchain(height, searchHeight)).blockMetas
|
|
.map((meta) => meta.header)
|
|
.find((h) => h.height === searchHeight);
|
|
if (correctHeader) {
|
|
nextHeader = correctHeader;
|
|
} else {
|
|
await sleep(1000);
|
|
}
|
|
}
|
|
|
|
assert(nextHeader.height === searchHeight, "Got wrong header. This is a bug in the logic above.");
|
|
return nextHeader;
|
|
}
|
|
}
|