Merge pull request #1376 from cosmos/tendermint-0.37

Add a Tendermint 0.37 client
This commit is contained in:
Simon Warta 2023-03-06 11:57:25 +01:00 committed by GitHub
commit 2c1f779dbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 444 additions and 210 deletions

View File

@ -21,6 +21,8 @@ and this project adheres to
- @cosmjs/proto-signing: Remove `fromJSON`/`toJSON` from `TsProtoGeneratedType`
such that generated types are not required to generate those anymore. The
methods were provided by ts-proto but we never needed them. ([#1329])
- @cosmjs/stargate: Rename `fromTendermint34Event` to `fromTendermintEvent` and
let it support both Tendermint 0.34 and 0.37 events as input.
[#1002]: https://github.com/cosmos/cosmjs/issues/1002
[#1240]: https://github.com/cosmos/cosmjs/pull/1240
@ -29,10 +31,23 @@ and this project adheres to
[#1329]: https://github.com/cosmos/cosmjs/pull/1329
### Added
- @cosmjs/stargate: Add `granteeGrants` and `granterGrants` queries to
`AuthzExtension` ([#1308]).
- @cosmjs/tendermint-rpc: Add new `Tendermint37Client` and remove unused
`Tendermint35Client`; Add `TendermintClient` as a union type for
`Tendermint34Client` or `Tendermint37Client` and
`isTendermint34Client`/`isTendermint37Client` to get the specific type
([#1376]).
- @cosmjs/stargate: Add constructors `StargateClient.create` and
`SigningStargateClient.createWithSigner` to construct with a given Tendermint
client ([#1376]).
- @cosmjs/cosmwasm-stargate: Add constructors `CosmWasmClient.create` and
`SigningCosmWasmClient.createWithSigner` to construct with a given Tendermint
client ([#1376]).
[#1308]: https://github.com/cosmos/cosmjs/pull/1308
[#1376]: https://github.com/cosmos/cosmjs/pull/1376
## [0.29.5] - 2022-12-07

View File

@ -0,0 +1,40 @@
import { coins, makeCosmoshubPath } from "@cosmjs/amino";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { assertIsDeliverTxSuccess, calculateFee, GasPrice, SigningStargateClient } from "@cosmjs/stargate";
import { Tendermint37Client } from "@cosmjs/tendermint-rpc";
// Network config
const prefix = "wasm";
const rpcEndpoint = "http://146.190.50.102:26657"; // or 137.184.83.82:26657
const gasPrice = GasPrice.fromString("0.001stake");
// Wallet wasm16jd84xm6yerfaafvtp7s6tpetdqkpu6wxumszp
const mnemonic = "royal next favorite duck plastic august rent knee strong weather father opinion";
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: prefix });
const [account] = await wallet.getAccounts();
console.log("Signer address:", account.address);
// Setup client. In contrast to most other examples out there, we create the Tendermint client
// explicitly. Otherwise the 0.34 client will be used.
const tmClient = await Tendermint37Client.connect(rpcEndpoint);
const version = (await tmClient.status()).nodeInfo.version;
console.log("Tendermint version:", version);
const client = await SigningStargateClient.createWithSigner(tmClient, wallet, { gasPrice: gasPrice });
// Get my balance
const balance = await client.getAllBalances(account.address);
console.log("Balance:", balance);
// Send a transaction
const recipient = "wasm142u9fgcjdlycfcez3lw8x6x5h7rfjlnfaallkd";
const result = await client.sendTokens(
account.address,
recipient,
coins(1, "stake"),
1.5, // In the current testnet the default multiplier of 1.3 is not sufficient 🤷‍♂️
"Have fun with this gift",
);
assertIsDeliverTxSuccess(result);
console.log("Successfully broadcasted:", result);
client.disconnect();

View File

@ -10,7 +10,7 @@ import {
BroadcastTxError,
Coin,
DeliverTxResponse,
fromTendermint34Event,
fromTendermintEvent,
IndexedTx,
isSearchByHeightQuery,
isSearchBySentFromOrToQuery,
@ -25,7 +25,12 @@ import {
TimeoutError,
TxExtension,
} from "@cosmjs/stargate";
import { HttpEndpoint, Tendermint34Client, toRfc3339WithNanoseconds } from "@cosmjs/tendermint-rpc";
import {
HttpEndpoint,
Tendermint34Client,
TendermintClient,
toRfc3339WithNanoseconds,
} from "@cosmjs/tendermint-rpc";
import { assert, sleep } from "@cosmjs/utils";
import {
CodeInfoResponse,
@ -77,26 +82,40 @@ export interface ContractCodeHistoryEntry {
/** Use for testing only */
export interface PrivateCosmWasmClient {
readonly tmClient: Tendermint34Client | undefined;
readonly tmClient: TendermintClient | undefined;
readonly queryClient:
| (QueryClient & AuthExtension & BankExtension & TxExtension & WasmExtension)
| undefined;
}
export class CosmWasmClient {
private readonly tmClient: Tendermint34Client | undefined;
private readonly tmClient: TendermintClient | undefined;
private readonly queryClient:
| (QueryClient & AuthExtension & BankExtension & TxExtension & WasmExtension)
| undefined;
private readonly codesCache = new Map<number, CodeDetails>();
private chainId: string | undefined;
/**
* Creates an instance by connecting to the given Tendermint RPC endpoint.
*
* For now this uses the Tendermint 0.34 client. If you need Tendermint 0.37
* support, see `create`.
*/
public static async connect(endpoint: string | HttpEndpoint): Promise<CosmWasmClient> {
const tmClient = await Tendermint34Client.connect(endpoint);
return CosmWasmClient.create(tmClient);
}
/**
* Creates an instance from a manually created Tendermint client.
* Use this to use `Tendermint37Client` instead of `Tendermint34Client`.
*/
public static async create(tmClient: TendermintClient): Promise<CosmWasmClient> {
return new CosmWasmClient(tmClient);
}
protected constructor(tmClient: Tendermint34Client | undefined) {
protected constructor(tmClient: TendermintClient | undefined) {
if (tmClient) {
this.tmClient = tmClient;
this.queryClient = QueryClient.withExtensions(
@ -109,11 +128,11 @@ export class CosmWasmClient {
}
}
protected getTmClient(): Tendermint34Client | undefined {
protected getTmClient(): TendermintClient | undefined {
return this.tmClient;
}
protected forceGetTmClient(): Tendermint34Client {
protected forceGetTmClient(): TendermintClient {
if (!this.tmClient) {
throw new Error(
"Tendermint client not available. You cannot use online functionality in offline mode.",
@ -464,7 +483,7 @@ export class CosmWasmClient {
height: tx.height,
hash: toHex(tx.hash).toUpperCase(),
code: tx.result.code,
events: tx.result.events.map(fromTendermint34Event),
events: tx.result.events.map(fromTendermintEvent),
rawLog: tx.result.log || "",
tx: tx.tx,
gasUsed: tx.result.gasUsed,

View File

@ -31,7 +31,7 @@ import {
SignerData,
StdFee,
} from "@cosmjs/stargate";
import { HttpEndpoint, Tendermint34Client } from "@cosmjs/tendermint-rpc";
import { HttpEndpoint, Tendermint34Client, TendermintClient } from "@cosmjs/tendermint-rpc";
import { assert, assertDefined } from "@cosmjs/utils";
import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import { MsgDelegate, MsgUndelegate } from "cosmjs-types/cosmos/staking/v1beta1/tx";
@ -183,12 +183,30 @@ export class SigningCosmWasmClient extends CosmWasmClient {
private readonly aminoTypes: AminoTypes;
private readonly gasPrice: GasPrice | undefined;
/**
* Creates an instance by connecting to the given Tendermint RPC endpoint.
*
* For now this uses the Tendermint 0.34 client. If you need Tendermint 0.37
* support, see `createWithSigner`.
*/
public static async connectWithSigner(
endpoint: string | HttpEndpoint,
signer: OfflineSigner,
options: SigningCosmWasmClientOptions = {},
): Promise<SigningCosmWasmClient> {
const tmClient = await Tendermint34Client.connect(endpoint);
return SigningCosmWasmClient.createWithSigner(tmClient, signer, options);
}
/**
* Creates an instance from a manually created Tendermint client.
* Use this to use `Tendermint37Client` instead of `Tendermint34Client`.
*/
public static async createWithSigner(
tmClient: TendermintClient,
signer: OfflineSigner,
options: SigningCosmWasmClientOptions = {},
): Promise<SigningCosmWasmClient> {
return new SigningCosmWasmClient(tmClient, signer, options);
}
@ -209,7 +227,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
}
protected constructor(
tmClient: Tendermint34Client | undefined,
tmClient: TendermintClient | undefined,
signer: OfflineSigner,
options: SigningCosmWasmClientOptions,
) {

View File

@ -1,5 +1,5 @@
import { fromUtf8 } from "@cosmjs/encoding";
import { tendermint34 } from "@cosmjs/tendermint-rpc";
import { tendermint34, tendermint37 } from "@cosmjs/tendermint-rpc";
/**
* An event attribute.
@ -30,16 +30,16 @@ export interface Event {
}
/**
* Takes a Tendemrint 0.34 event with binary encoded key and value
* Takes a Tendermint 0.34 or 0.37 event with binary encoded key and value
* and converts it into an `Event` with string attributes.
*/
export function fromTendermint34Event(event: tendermint34.Event): Event {
export function fromTendermintEvent(event: tendermint34.Event | tendermint37.Event): Event {
return {
type: event.type,
attributes: event.attributes.map(
(attr): Attribute => ({
key: fromUtf8(attr.key, true),
value: fromUtf8(attr.value, true),
key: typeof attr.key == "string" ? attr.key : fromUtf8(attr.key, true),
value: typeof attr.value == "string" ? attr.value : fromUtf8(attr.value, true),
}),
),
};

View File

@ -1,6 +1,6 @@
export { Account, accountFromAny, AccountParser } from "./accounts";
export { AminoConverter, AminoConverters, AminoTypes } from "./aminotypes";
export { Attribute, Event, fromTendermint34Event } from "./events";
export { Attribute, Event, fromTendermintEvent } from "./events";
export { calculateFee, GasPrice } from "./fee";
export * as logs from "./logs";
export {

View File

@ -2,7 +2,7 @@
import { iavlSpec, ics23, tendermintSpec, verifyExistence, verifyNonExistence } from "@confio/ics23";
import { toAscii, toHex } from "@cosmjs/encoding";
import { firstEvent } from "@cosmjs/stream";
import { tendermint34, Tendermint34Client } from "@cosmjs/tendermint-rpc";
import { tendermint34, TendermintClient } 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";
@ -45,24 +45,24 @@ export interface QueryAbciResponse {
export class QueryClient {
/** Constructs a QueryClient with 0 extensions */
public static withExtensions(tmClient: Tendermint34Client): QueryClient;
public static withExtensions(tmClient: TendermintClient): QueryClient;
/** Constructs a QueryClient with 1 extension */
public static withExtensions<A extends object>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
): QueryClient & A;
/** Constructs a QueryClient with 2 extensions */
public static withExtensions<A extends object, B extends object>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
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>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -70,7 +70,7 @@ export class QueryClient {
/** Constructs a QueryClient with 4 extensions */
public static withExtensions<A extends object, B extends object, C extends object, D extends object>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -85,7 +85,7 @@ export class QueryClient {
D extends object,
E extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -102,7 +102,7 @@ export class QueryClient {
E extends object,
F extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -121,7 +121,7 @@ export class QueryClient {
F extends object,
G extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -142,7 +142,7 @@ export class QueryClient {
G extends object,
H extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -165,7 +165,7 @@ export class QueryClient {
H extends object,
I extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -190,7 +190,7 @@ export class QueryClient {
I extends object,
J extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -217,7 +217,7 @@ export class QueryClient {
J extends object,
K extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -246,7 +246,7 @@ export class QueryClient {
K extends object,
L extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -277,7 +277,7 @@ export class QueryClient {
L extends object,
M extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -310,7 +310,7 @@ export class QueryClient {
M extends object,
N extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -345,7 +345,7 @@ export class QueryClient {
N extends object,
O extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -382,7 +382,7 @@ export class QueryClient {
O extends object,
P extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -421,7 +421,7 @@ export class QueryClient {
P extends object,
Q extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -462,7 +462,7 @@ export class QueryClient {
Q extends object,
R extends object,
>(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
setupExtensionA: QueryExtensionSetup<A>,
setupExtensionB: QueryExtensionSetup<B>,
setupExtensionC: QueryExtensionSetup<C>,
@ -484,7 +484,7 @@ export class QueryClient {
): QueryClient & A & B & C & D & E & F & G & H & I & J & K & L & M & N & O & P & Q & R;
public static withExtensions(
tmClient: Tendermint34Client,
tmClient: TendermintClient,
...extensionSetups: Array<QueryExtensionSetup<object>>
): any {
const client = new QueryClient(tmClient);
@ -506,9 +506,9 @@ export class QueryClient {
return client;
}
private readonly tmClient: Tendermint34Client;
private readonly tmClient: TendermintClient;
public constructor(tmClient: Tendermint34Client) {
public constructor(tmClient: TendermintClient) {
this.tmClient = tmClient;
}

View File

@ -12,7 +12,7 @@ import {
Registry,
TxBodyEncodeObject,
} from "@cosmjs/proto-signing";
import { HttpEndpoint, Tendermint34Client } from "@cosmjs/tendermint-rpc";
import { HttpEndpoint, Tendermint34Client, TendermintClient } from "@cosmjs/tendermint-rpc";
import { assert, assertDefined } from "@cosmjs/utils";
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
@ -114,12 +114,30 @@ export class SigningStargateClient extends StargateClient {
private readonly aminoTypes: AminoTypes;
private readonly gasPrice: GasPrice | undefined;
/**
* Creates an instance by connecting to the given Tendermint RPC endpoint.
*
* For now this uses the Tendermint 0.34 client. If you need Tendermint 0.37
* support, see `createWithSigner`.
*/
public static async connectWithSigner(
endpoint: string | HttpEndpoint,
signer: OfflineSigner,
options: SigningStargateClientOptions = {},
): Promise<SigningStargateClient> {
const tmClient = await Tendermint34Client.connect(endpoint);
return SigningStargateClient.createWithSigner(tmClient, signer, options);
}
/**
* Creates an instance from a manually created Tendermint client.
* Use this to use `Tendermint37Client` instead of `Tendermint34Client`.
*/
public static async createWithSigner(
tmClient: TendermintClient,
signer: OfflineSigner,
options: SigningStargateClientOptions = {},
): Promise<SigningStargateClient> {
return new SigningStargateClient(tmClient, signer, options);
}
@ -140,7 +158,7 @@ export class SigningStargateClient extends StargateClient {
}
protected constructor(
tmClient: Tendermint34Client | undefined,
tmClient: TendermintClient | undefined,
signer: OfflineSigner,
options: SigningStargateClientOptions,
) {

View File

@ -2,7 +2,12 @@
import { addCoins } from "@cosmjs/amino";
import { toHex } from "@cosmjs/encoding";
import { Uint53 } from "@cosmjs/math";
import { HttpEndpoint, Tendermint34Client, toRfc3339WithNanoseconds } from "@cosmjs/tendermint-rpc";
import {
HttpEndpoint,
Tendermint34Client,
TendermintClient,
toRfc3339WithNanoseconds,
} from "@cosmjs/tendermint-rpc";
import { assert, sleep } from "@cosmjs/utils";
import { MsgData } from "cosmjs-types/cosmos/base/abci/v1beta1/abci";
import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
@ -10,7 +15,7 @@ import { QueryDelegatorDelegationsResponse } from "cosmjs-types/cosmos/staking/v
import { DelegationResponse } from "cosmjs-types/cosmos/staking/v1beta1/staking";
import { Account, accountFromAny, AccountParser } from "./accounts";
import { Event, fromTendermint34Event } from "./events";
import { Event, fromTendermintEvent } from "./events";
import {
AuthExtension,
BankExtension,
@ -171,7 +176,7 @@ export class BroadcastTxError extends Error {
/** Use for testing only */
export interface PrivateStargateClient {
readonly tmClient: Tendermint34Client | undefined;
readonly tmClient: TendermintClient | undefined;
}
export interface StargateClientOptions {
@ -179,22 +184,39 @@ export interface StargateClientOptions {
}
export class StargateClient {
private readonly tmClient: Tendermint34Client | undefined;
private readonly tmClient: TendermintClient | undefined;
private readonly queryClient:
| (QueryClient & AuthExtension & BankExtension & StakingExtension & TxExtension)
| undefined;
private chainId: string | undefined;
private readonly accountParser: AccountParser;
/**
* Creates an instance by connecting to the given Tendermint RPC endpoint.
*
* For now this uses the Tendermint 0.34 client. If you need Tendermint 0.37
* support, see `create`.
*/
public static async connect(
endpoint: string | HttpEndpoint,
options: StargateClientOptions = {},
): Promise<StargateClient> {
const tmClient = await Tendermint34Client.connect(endpoint);
return StargateClient.create(tmClient, options);
}
/**
* Creates an instance from a manually created Tendermint client.
* Use this to use `Tendermint37Client` instead of `Tendermint34Client`.
*/
public static async create(
tmClient: TendermintClient,
options: StargateClientOptions = {},
): Promise<StargateClient> {
return new StargateClient(tmClient, options);
}
protected constructor(tmClient: Tendermint34Client | undefined, options: StargateClientOptions) {
protected constructor(tmClient: TendermintClient | undefined, options: StargateClientOptions) {
if (tmClient) {
this.tmClient = tmClient;
this.queryClient = QueryClient.withExtensions(
@ -209,11 +231,11 @@ export class StargateClient {
this.accountParser = accountParser;
}
protected getTmClient(): Tendermint34Client | undefined {
protected getTmClient(): TendermintClient | undefined {
return this.tmClient;
}
protected forceGetTmClient(): Tendermint34Client {
protected forceGetTmClient(): TendermintClient {
if (!this.tmClient) {
throw new Error(
"Tendermint client not available. You cannot use online functionality in offline mode.",
@ -471,7 +493,7 @@ export class StargateClient {
height: tx.height,
hash: toHex(tx.hash).toUpperCase(),
code: tx.result.code,
events: tx.result.events.map(fromTendermint34Event),
events: tx.result.events.map(fromTendermintEvent),
rawLog: tx.result.log || "",
tx: tx.tx,
gasUsed: tx.result.gasUsed,

View File

@ -95,10 +95,9 @@ export {
} from "./tendermint34";
export * as tendermint34 from "./tendermint34";
export { Tendermint34Client } from "./tendermint34";
// Tendermint 0.35 support is not public. The implementation may break or be removed at any point in time.
// See https://github.com/cosmos/cosmjs/issues/1225 for more context.
// export * as tendermint35 from "./tendermint35";
// export { Tendermint35Client } from "./tendermint35";
export * as tendermint37 from "./tendermint37";
export { Tendermint37Client } from "./tendermint37";
export { isTendermint34Client, isTendermint37Client, TendermintClient } from "./tendermintclient";
export {
BlockIdFlag,
CommitSignature,

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toAscii } from "@cosmjs/encoding";
import { toAscii, toHex } from "@cosmjs/encoding";
import { firstEvent, toListPromise } from "@cosmjs/stream";
import { sleep } from "@cosmjs/utils";
import { assert, sleep } from "@cosmjs/utils";
import { ReadonlyDate } from "readonly-date";
import { Stream } from "xstream";
@ -344,7 +344,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(undefined, height - 1);
expect(blockchain.lastHeight).toEqual(height);
expect(blockchain.lastHeight).toBeGreaterThanOrEqual(height);
expect(blockchain.blockMetas.length).toBeGreaterThanOrEqual(2);
expect(blockchain.blockMetas[0].header.height).toEqual(height - 1); // upper limit included
expect(blockchain.blockMetas[1].header.height).toEqual(height - 2);
@ -435,25 +435,6 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
expect(r.height).toEqual(height);
expect(r.proof).toBeTruthy();
// txSearch - you must enable the indexer when running
// tendermint, else you get empty results
const query = buildQuery({ tags: [{ key: "app.key", value: find }] });
const s = await client.txSearch({ query: query, page: 1, per_page: 30 });
// should find the tx
expect(s.totalCount).toEqual(1);
// should return same info as querying directly,
// except without the proof
expect(s.txs[0]).toEqual({ ...r, proof: undefined });
// ensure txSearchAll works as well
const sall = await client.txSearchAll({ query: query });
// should find the tx
expect(sall.totalCount).toEqual(1);
// should return same info as querying directly,
// except without the proof
expect(sall.txs[0]).toEqual({ ...r, proof: undefined });
// and let's query the block itself to see this transaction
const block = await client.block(height);
expect(block.block.txs.length).toEqual(1);
@ -464,25 +445,28 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
});
describe("txSearch", () => {
const key = randomString();
const txKey = randomString(); // a key used for multiple transactions
let tx1: Uint8Array | undefined;
let broadcast1: responses.BroadcastTxCommitResponse | undefined;
beforeAll(async () => {
if (tendermintEnabled()) {
const client = await Tendermint34Client.create(rpcFactory());
// eslint-disable-next-line no-inner-declarations
async function sendTx(): Promise<void> {
async function sendTx(): Promise<[Uint8Array, responses.BroadcastTxCommitResponse]> {
const me = randomString();
const tx = buildKvTx(key, me);
const tx = buildKvTx(txKey, me);
const txRes = await client.broadcastTxCommit({ tx: tx });
expect(responses.broadcastTxCommitSuccess(txRes)).toEqual(true);
expect(txRes.height).toBeTruthy();
expect(txRes.hash.length).not.toEqual(0);
expect(txRes.hash.length).toEqual(32);
return [tx, txRes];
}
// send 3 txs
await sendTx();
[tx1, broadcast1] = await sendTx();
await sendTx();
await sendTx();
@ -492,6 +476,68 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
}
});
it("finds a single tx by hash", async () => {
pendingWithoutTendermint();
assert(tx1 && broadcast1);
const client = await Tendermint34Client.create(rpcFactory());
const result = await client.txSearch({ query: `tx.hash='${toHex(broadcast1.hash)}'` });
expect(result.totalCount).toEqual(1);
expect(result.txs[0]).toEqual({
hash: broadcast1.hash,
height: broadcast1.height,
index: 0,
tx: tx1,
result: broadcast1.deliverTx!,
proof: undefined,
});
client.disconnect();
});
it("finds a single tx by tags", async () => {
pendingWithoutTendermint();
const client = await Tendermint34Client.create(rpcFactory());
const txKey2 = randomString();
const txValue2 = randomString();
const tx = buildKvTx(txKey2, txValue2);
const txRes = await client.broadcastTxCommit({ tx: tx });
expect(responses.broadcastTxCommitSuccess(txRes)).toEqual(true);
await tendermintSearchIndexUpdated();
// txSearch - you must enable the indexer when running
// tendermint, else you get empty results
const query = buildQuery({ tags: [{ key: "app.key", value: txKey2 }] });
const search = await client.txSearch({ query: query, page: 1, per_page: 30 });
// should find the tx
expect(search.totalCount).toEqual(1);
// should return same info as querying directly,
// except without the proof
expect(search.txs[0]).toEqual({
hash: txRes.hash,
height: txRes.height,
index: 0,
tx: tx,
result: txRes.deliverTx!,
proof: undefined,
});
// Ensure txSearchAll works as well. This should be moved in a dedicated "txSearchAll" test block.
const searchAll = await client.txSearchAll({ query: query });
expect(searchAll.totalCount).toEqual(1);
expect(searchAll.txs[0]).toEqual({
hash: txRes.hash,
height: txRes.height,
index: 0,
tx: tx,
result: txRes.deliverTx!,
proof: undefined,
});
});
it("returns transactions in ascending order by default", async () => {
// NOTE: The Tendermint docs claim the default ordering is "desc" but it is actually "asc"
// Docs: https://docs.tendermint.com/master/rpc/#/Info/tx_search
@ -499,15 +545,15 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
pendingWithoutTendermint();
const client = await Tendermint34Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
const s = await client.txSearch({ query: query });
const result = await client.txSearch({ query: query });
expect(s.totalCount).toEqual(3);
s.txs.slice(1).reduce((lastHeight, { height }) => {
expect(result.totalCount).toEqual(3);
result.txs.slice(1).reduce((lastHeight, { height }) => {
expect(height).toBeGreaterThanOrEqual(lastHeight);
return height;
}, s.txs[0].height);
}, result.txs[0].height);
client.disconnect();
});
@ -516,7 +562,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
pendingWithoutTendermint();
const client = await Tendermint34Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
const s1 = await client.txSearch({ query: query, order_by: "desc" });
const s2 = await client.txSearch({ query: query, order_by: "asc" });
@ -531,7 +577,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
pendingWithoutTendermint();
const client = await Tendermint34Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
// expect one page of results
const s1 = await client.txSearch({ query: query, page: 1, per_page: 2 });
@ -550,15 +596,14 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
pendingWithoutTendermint();
const client = await Tendermint34Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
const sall = await client.txSearchAll({ query: query, per_page: 2 });
expect(sall.totalCount).toEqual(3);
expect(sall.txs.length).toEqual(3);
// make sure there are in order from lowest to highest height
const [tx1, tx2, tx3] = sall.txs;
expect(tx2.height).toEqual(tx1.height + 1);
expect(tx3.height).toEqual(tx2.height + 1);
expect(sall.txs[1].height).toEqual(sall.txs[0].height + 1);
expect(sall.txs[2].height).toEqual(sall.txs[1].height + 1);
client.disconnect();
});

View File

@ -5,7 +5,7 @@ import { Adaptor } from "./types";
export { Decoder, Encoder, Params, Responses } from "./types";
export const adaptor35: Adaptor = {
export const adaptor37: Adaptor = {
params: Params,
responses: Responses,
hashTx: hashTx,

View File

@ -74,13 +74,13 @@ function encodeBroadcastTxParams(params: requests.BroadcastTxParams): RpcBroadca
}
interface RpcTxParams {
/** hex encoded */
/** base64 encoded */
readonly hash: string;
readonly prove?: boolean;
}
function encodeTxParams(params: requests.TxParams): RpcTxParams {
return {
hash: toHex(assertNotEmpty(params.hash)),
hash: toBase64(assertNotEmpty(params.hash)),
prove: params.prove,
};
}

View File

@ -1,5 +1,5 @@
// Note: all exports in this module are publicly available via
// `import { tendermint35 } from "@cosmjs/tendermint-rpc"`
// `import { tendermint37 } from "@cosmjs/tendermint-rpc"`
export {
AbciInfoRequest,
@ -76,4 +76,4 @@ export {
Vote,
VoteType,
} from "./responses";
export { Tendermint35Client } from "./tendermint35client";
export { Tendermint37Client } from "./tendermint37client";

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toAscii } from "@cosmjs/encoding";
import { toAscii, toHex } from "@cosmjs/encoding";
import { firstEvent, toListPromise } from "@cosmjs/stream";
import { sleep } from "@cosmjs/utils";
import { assert, sleep } from "@cosmjs/utils";
import { ReadonlyDate } from "readonly-date";
import { Stream } from "xstream";
@ -16,16 +16,16 @@ import {
tendermintInstances,
tendermintSearchIndexUpdated,
} from "../testutil.spec";
import { adaptor35 } from "./adaptor";
import { adaptor37 } from "./adaptor";
import { buildQuery } from "./requests";
import * as responses from "./responses";
import { Tendermint35Client } from "./tendermint35client";
import { Tendermint37Client } from "./tendermint37client";
function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues): void {
describe("create", () => {
it("can auto-discover Tendermint version and communicate", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const info = await client.abciInfo();
expect(info).toBeTruthy();
client.disconnect();
@ -33,7 +33,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can connect to Tendermint with known version", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
expect(await client.abciInfo()).toBeTruthy();
client.disconnect();
});
@ -41,7 +41,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can get genesis", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const genesis = await client.genesis();
expect(genesis).toBeTruthy();
client.disconnect();
@ -50,7 +50,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("broadcastTxCommit", () => {
it("can broadcast a transaction", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const tx = buildKvTx(randomString(), randomString());
const response = await client.broadcastTxCommit({ tx: tx });
@ -70,7 +70,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("broadcastTxSync", () => {
it("can broadcast a transaction", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const tx = buildKvTx(randomString(), randomString());
const response = await client.broadcastTxSync({ tx: tx });
@ -86,7 +86,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("broadcastTxAsync", () => {
it("can broadcast a transaction", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const tx = buildKvTx(randomString(), randomString());
const response = await client.broadcastTxAsync({ tx: tx });
@ -98,9 +98,9 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("gets the same tx hash from backend as calculated locally", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const tx = buildKvTx(randomString(), randomString());
const calculatedTxHash = adaptor35.hashTx(tx);
const calculatedTxHash = adaptor37.hashTx(tx);
const response = await client.broadcastTxCommit({ tx: tx });
expect(response.hash).toEqual(calculatedTxHash);
@ -111,7 +111,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("abciQuery", () => {
it("can query the state", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const key = randomString();
const value = randomString();
@ -137,7 +137,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can get a commit", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const response = await client.commit(4);
expect(response).toBeTruthy();
@ -152,7 +152,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can get validators", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const response = await client.validators({});
expect(response).toBeTruthy();
@ -170,7 +170,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can get all validators", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const response = await client.validatorsAll();
expect(response).toBeTruthy();
@ -188,7 +188,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can call a bunch of methods", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
expect(await client.block()).toBeTruthy();
expect(await client.genesis()).toBeTruthy();
@ -200,7 +200,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("status", () => {
it("works", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const status = await client.status();
@ -230,7 +230,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("numUnconfirmedTxs", () => {
it("works", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const response = await client.numUnconfirmedTxs();
@ -244,7 +244,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("blockResults", () => {
it("works", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const height = 3;
const results = await client.blockResults(height);
@ -260,7 +260,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("blockSearch", () => {
beforeAll(async () => {
if (tendermintEnabled()) {
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
// eslint-disable-next-line no-inner-declarations
async function sendTx(): Promise<void> {
@ -285,7 +285,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can paginate over blockSearch results", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" });
@ -304,7 +304,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can get all search results in one call", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" });
@ -323,7 +323,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("blockchain", () => {
it("returns latest in descending order by default", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
// Run in parallel to increase chance there is no block between the calls
const [status, blockchain] = await Promise.all([client.status(), client.blockchain()]);
@ -340,7 +340,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can limit by maxHeight", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(undefined, height - 1);
@ -354,7 +354,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("works with maxHeight in the future", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(undefined, height + 20);
@ -369,7 +369,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can limit by minHeight and maxHeight", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(height - 2, height - 1);
@ -383,7 +383,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("contains all the info", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(height - 1, height - 1);
@ -412,7 +412,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
describe("tx", () => {
it("can query a tx properly", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const find = randomString();
const me = randomString();
@ -436,25 +436,6 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
expect(r.height).toEqual(height);
expect(r.proof).toBeTruthy();
// txSearch - you must enable the indexer when running
// tendermint, else you get empty results
const query = buildQuery({ tags: [{ key: "app.key", value: find }] });
const s = await client.txSearch({ query: query, page: 1, per_page: 30 });
// should find the tx
expect(s.totalCount).toEqual(1);
// should return same info as querying directly,
// except without the proof
expect(s.txs[0]).toEqual({ ...r, proof: undefined });
// ensure txSearchAll works as well
const sall = await client.txSearchAll({ query: query });
// should find the tx
expect(sall.totalCount).toEqual(1);
// should return same info as querying directly,
// except without the proof
expect(sall.txs[0]).toEqual({ ...r, proof: undefined });
// and let's query the block itself to see this transaction
const block = await client.block(height);
expect(block.block.txs.length).toEqual(1);
@ -465,25 +446,28 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
});
describe("txSearch", () => {
const key = randomString();
const txKey = randomString(); // a key used for multiple transactions
let tx1: Uint8Array | undefined;
let broadcast1: responses.BroadcastTxCommitResponse | undefined;
beforeAll(async () => {
if (tendermintEnabled()) {
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
// eslint-disable-next-line no-inner-declarations
async function sendTx(): Promise<void> {
async function sendTx(): Promise<[Uint8Array, responses.BroadcastTxCommitResponse]> {
const me = randomString();
const tx = buildKvTx(key, me);
const tx = buildKvTx(txKey, me);
const txRes = await client.broadcastTxCommit({ tx: tx });
expect(responses.broadcastTxCommitSuccess(txRes)).toEqual(true);
expect(txRes.height).toBeTruthy();
expect(txRes.hash.length).not.toEqual(0);
expect(txRes.hash.length).toEqual(32);
return [tx, txRes];
}
// send 3 txs
await sendTx();
[tx1, broadcast1] = await sendTx();
await sendTx();
await sendTx();
@ -493,22 +477,86 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
}
});
it("returns transactions in descending order by default", async () => {
it("finds a single tx by hash", async () => {
pendingWithoutTendermint();
assert(tx1 && broadcast1);
const client = await Tendermint37Client.create(rpcFactory());
const result = await client.txSearch({ query: `tx.hash='${toHex(broadcast1.hash)}'` });
expect(result.totalCount).toEqual(1);
expect(result.txs[0]).toEqual({
hash: broadcast1.hash,
height: broadcast1.height,
index: 0,
tx: tx1,
result: broadcast1.deliverTx!,
proof: undefined,
});
client.disconnect();
});
it("finds a single tx by tags", async () => {
pendingWithoutTendermint();
const client = await Tendermint37Client.create(rpcFactory());
const txKey2 = randomString();
const txValue2 = randomString();
const tx = buildKvTx(txKey2, txValue2);
const txRes = await client.broadcastTxCommit({ tx: tx });
expect(responses.broadcastTxCommitSuccess(txRes)).toEqual(true);
await tendermintSearchIndexUpdated();
// txSearch - you must enable the indexer when running
// tendermint, else you get empty results
const query = buildQuery({ tags: [{ key: "app.key", value: txKey2 }] });
const search = await client.txSearch({ query: query, page: 1, per_page: 30 });
// should find the tx
expect(search.totalCount).toEqual(1);
// should return same info as querying directly,
// except without the proof
expect(search.txs[0]).toEqual({
hash: txRes.hash,
height: txRes.height,
index: 0,
tx: tx,
result: txRes.deliverTx!,
proof: undefined,
});
// Ensure txSearchAll works as well. This should be moved in a dedicated "txSearchAll" test block.
const searchAll = await client.txSearchAll({ query: query });
expect(searchAll.totalCount).toEqual(1);
expect(searchAll.txs[0]).toEqual({
hash: txRes.hash,
height: txRes.height,
index: 0,
tx: tx,
result: txRes.deliverTx!,
proof: undefined,
});
});
it("returns transactions in ascending order by default", async () => {
// NOTE: The Tendermint docs states the default ordering is "desc". Until
// 0.35 it was actually "asc" but from 0.35 on it is "desc".
// Then it was changed back to "asc" in 0.37.
// Docs: https://docs.tendermint.com/master/rpc/#/Info/tx_search
// Code 0.34: https://github.com/tendermint/tendermint/blob/v0.34.10/rpc/core/tx.go#L89
// Code 0.35: https://github.com/tendermint/tendermint/blob/v0.35.6/internal/rpc/core/tx.go#L93
// Code 0.37: https://github.com/cometbft/cometbft/blob/v0.37.0-rc3/rpc/core/tx.go#L87
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
const result = await client.txSearch({ query: query });
expect(result.totalCount).toEqual(3);
result.txs.slice(1).reduce((lastHeight, { height }) => {
expect(height).toBeLessThanOrEqual(lastHeight);
expect(height).toBeGreaterThanOrEqual(lastHeight);
return height;
}, result.txs[0].height);
@ -517,9 +565,9 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can set the order", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
const result1 = await client.txSearch({ query: query, order_by: "desc" });
const result2 = await client.txSearch({ query: query, order_by: "asc" });
@ -532,9 +580,9 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can paginate over txSearch results", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
// expect one page of results
const s1 = await client.txSearch({ query: query, page: 1, per_page: 2 });
@ -551,17 +599,16 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues)
it("can get all search results in one call", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
const query = buildQuery({ tags: [{ key: "app.key", value: txKey }] });
const sall = await client.txSearchAll({ query: query, per_page: 2 });
expect(sall.totalCount).toEqual(3);
expect(sall.txs.length).toEqual(3);
// make sure there are in order from highest to lowest height
const [tx1, tx2, tx3] = sall.txs;
expect(tx2.height).toBeLessThan(tx1.height);
expect(tx3.height).toBeLessThan(tx2.height);
expect(sall.txs[1].height).toEqual(sall.txs[0].height + 1);
expect(sall.txs[2].height).toEqual(sall.txs[1].height + 1);
client.disconnect();
});
@ -576,7 +623,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue
(async () => {
const events: responses.NewBlockHeaderEvent[] = [];
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const stream = client.subscribeNewBlockHeader();
expect(stream).toBeTruthy();
const subscription = stream.subscribe({
@ -635,7 +682,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue
const transactionData2 = buildKvTx(randomString(), randomString());
const events: responses.NewBlockEvent[] = [];
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const stream = client.subscribeNewBlock();
const subscription = stream.subscribe({
next: (event) => {
@ -694,7 +741,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue
pendingWithoutTendermint();
const events: responses.TxEvent[] = [];
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const stream = client.subscribeTx();
const subscription = stream.subscribe({
next: (event) => {
@ -738,7 +785,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue
const transactionData2 = buildKvTx(randomString(), randomString());
const events: responses.TxEvent[] = [];
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const query = buildQuery({ tags: [{ key: "app.creator", value: expected.appCreator }] });
const stream = client.subscribeTx(query);
expect(stream).toBeTruthy();
@ -776,7 +823,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue
it("can unsubscribe and re-subscribe to the same stream", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const stream = client.subscribeNewBlockHeader();
const event1 = await firstEvent(stream);
@ -809,7 +856,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue
it("can subscribe twice", async () => {
pendingWithoutTendermint();
const client = await Tendermint35Client.create(rpcFactory());
const client = await Tendermint37Client.create(rpcFactory());
const stream1 = client.subscribeNewBlockHeader();
const stream2 = client.subscribeNewBlockHeader();
@ -821,15 +868,15 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue
});
}
describe("Tendermint35Client", () => {
const { url, expected } = tendermintInstances[35];
describe("Tendermint37Client", () => {
const { url, expected } = tendermintInstances[37];
it("can connect to a given url", async () => {
pendingWithoutTendermint();
// default connection
{
const client = await Tendermint35Client.connect(url);
const client = await Tendermint37Client.connect(url);
const info = await client.abciInfo();
expect(info).toBeTruthy();
client.disconnect();
@ -837,7 +884,7 @@ describe("Tendermint35Client", () => {
// http connection
{
const client = await Tendermint35Client.connect("http://" + url);
const client = await Tendermint37Client.connect("http://" + url);
const info = await client.abciInfo();
expect(info).toBeTruthy();
client.disconnect();
@ -845,7 +892,7 @@ describe("Tendermint35Client", () => {
// ws connection
{
const client = await Tendermint35Client.connect("ws://" + url);
const client = await Tendermint37Client.connect("ws://" + url);
const info = await client.abciInfo();
expect(info).toBeTruthy();
client.disconnect();

View File

@ -10,41 +10,36 @@ import {
SubscriptionEvent,
WebsocketClient,
} from "../rpcclients";
import { adaptor35, Decoder, Encoder, Params, Responses } from "./adaptor";
import { adaptor37, Decoder, Encoder, Params, Responses } from "./adaptor";
import * as requests from "./requests";
import * as responses from "./responses";
/**
* Please note the Tendermint 0.35 client is currently not exported and may break or be removed at any point in time.
*
* @see https://github.com/cosmos/cosmjs/issues/1225
*/
export class Tendermint35Client {
export class Tendermint37Client {
/**
* Creates a new Tendermint client for the given endpoint.
*
* Uses HTTP when the URL schema is http or https. Uses WebSockets otherwise.
*/
public static async connect(endpoint: string | HttpEndpoint): Promise<Tendermint35Client> {
public static async connect(endpoint: string | HttpEndpoint): Promise<Tendermint37Client> {
if (typeof endpoint === "object") {
return Tendermint35Client.create(new HttpClient(endpoint));
return Tendermint37Client.create(new HttpClient(endpoint));
} else {
const useHttp = endpoint.startsWith("http://") || endpoint.startsWith("https://");
const rpcClient = useHttp ? new HttpClient(endpoint) : new WebsocketClient(endpoint);
return Tendermint35Client.create(rpcClient);
return Tendermint37Client.create(rpcClient);
}
}
/**
* Creates a new Tendermint client given an RPC client.
*/
public static async create(rpcClient: RpcClient): Promise<Tendermint35Client> {
public static async create(rpcClient: RpcClient): Promise<Tendermint37Client> {
// For some very strange reason I don't understand, tests start to fail on some systems
// (our CI) when skipping the status call before doing other queries. Sleeping a little
// while did not help. Thus we query the version as a way to say "hi" to the backend,
// even in cases where we don't use the result.
const _version = await this.detectVersion(rpcClient);
return new Tendermint35Client(rpcClient);
return new Tendermint37Client(rpcClient);
}
private static async detectVersion(client: RpcClient): Promise<string> {
@ -68,12 +63,12 @@ export class Tendermint35Client {
private readonly r: Responses;
/**
* Use `Tendermint34Client.connect` or `Tendermint34Client.create` to create an instance.
* Use `Tendermint37Client.connect` or `Tendermint37Client.create` to create an instance.
*/
private constructor(client: RpcClient) {
this.client = client;
this.p = adaptor35.params;
this.r = adaptor35.responses;
this.p = adaptor37.params;
this.r = adaptor37.responses;
}
public disconnect(): void {

View File

@ -0,0 +1,13 @@
import { Tendermint34Client } from "./tendermint34";
import { Tendermint37Client } from "./tendermint37";
/** A TendermintClient is either a Tendermint34Client or a Tendermint37Client */
export type TendermintClient = Tendermint34Client | Tendermint37Client;
export function isTendermint34Client(client: TendermintClient): client is Tendermint34Client {
return client instanceof Tendermint34Client;
}
export function isTendermint37Client(client: TendermintClient): client is Tendermint37Client {
return client instanceof Tendermint37Client;
}

View File

@ -49,13 +49,13 @@ export const tendermintInstances = {
appVersion: 1,
},
},
35: {
url: "localhost:11135",
version: "0.35.x",
37: {
url: "localhost:11137",
version: "0.37.x",
blockTime: 500,
expected: {
chainId: /^dockerchain$/,
version: /^$/, // Unfortunately we don't get info here
version: /^0\.37\.0-alpha\.3$/,
appCreator: "Cosmoshi Netowoko",
p2pVersion: 8,
blockVersion: 11,

View File

@ -2,18 +2,25 @@
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
# Find latest patch releases at https://hub.docker.com/r/tendermint/tendermint/tags/
declare -a TM_VERSIONS
TM_VERSIONS[34]=v0.34.19
TM_VERSIONS[35]=v0.35.6
# Find latest patch releases at
# - https://hub.docker.com/r/tendermint/tendermint/tags/
# - https://hub.docker.com/r/cometbft/cometbft/tags/
declare -a TM_IMAGES
TM_IMAGES[34]="tendermint/tendermint:v0.34.19"
TM_IMAGES[37]="cometbft/cometbft:v0.37.0-rc3"
declare -a TM_ROOTS
TM_ROOTS[34]="/tendermint"
TM_ROOTS[37]="/cometbft"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for KEY in "${!TM_VERSIONS[@]}"; do
export TENDERMINT_VERSION="${TM_VERSIONS[$KEY]}"
for KEY in "${!TM_IMAGES[@]}"; do
export TENDERMINT_IMAGE="${TM_IMAGES[$KEY]}"
export TENDERMINT_ROOT="${TM_ROOTS[$KEY]}"
export TENDERMINT_PORT="111$KEY"
export TENDERMINT_NAME="tendermint-$KEY"
echo "Starting $TENDERMINT_NAME ($TENDERMINT_VERSION) on port $TENDERMINT_PORT ..."
echo "Starting $TENDERMINT_NAME ($TENDERMINT_IMAGE) on port $TENDERMINT_PORT ..."
"$SCRIPT_DIR/start.sh"
done

View File

@ -2,13 +2,9 @@
set -o errexit -o nounset -o pipefail
command -v shellcheck >/dev/null && shellcheck "$0"
declare -a TM_VERSIONS
TM_VERSIONS[34]=v0.34.19
TM_VERSIONS[35]=v0.35.6
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for KEY in "${!TM_VERSIONS[@]}"; do
for KEY in 34 37; do
export TENDERMINT_NAME="tendermint-$KEY"
echo "Stopping $TENDERMINT_NAME ..."

View File

@ -6,7 +6,7 @@ gnused="$(command -v gsed || echo sed)"
# Tendermint settings must be specified
# Choose version from https://hub.docker.com/r/tendermint/tendermint/tags/
for SETTING in "TENDERMINT_VERSION" "TENDERMINT_PORT" "TENDERMINT_NAME"; do
for SETTING in "TENDERMINT_IMAGE" "TENDERMINT_PORT" "TENDERMINT_NAME"; do
if test -z "$(eval echo "\$$SETTING")"; then
echo "\$$SETTING must be set when running this script"
exit 1
@ -20,8 +20,8 @@ LOGFILE="$TMP_DIR/tendermint.log"
docker run --rm \
--user="$UID" \
-v "${TMP_DIR}:/tendermint" \
"tendermint/tendermint:${TENDERMINT_VERSION}" \
-v "${TMP_DIR}:${TENDERMINT_ROOT}" \
"${TENDERMINT_IMAGE}" \
init validator
# make sure we allow cors origins, only possible by modifying the config file
@ -36,11 +36,11 @@ docker run --rm \
docker run --rm \
--user="$UID" \
--name "$TENDERMINT_NAME" \
-p "${TENDERMINT_PORT}:26657" -v "${TMP_DIR}:/tendermint" \
-p "${TENDERMINT_PORT}:26657" -v "${TMP_DIR}:${TENDERMINT_ROOT}" \
-e "TM_TX_INDEX_INDEX_ALL_KEYS=true" \
-e "PROXY_APP=kvstore" \
-e "LOG_LEVEL=state:info,rpc:info,*:error" \
"tendermint/tendermint:${TENDERMINT_VERSION}" node \
"${TENDERMINT_IMAGE}" node \
--rpc.laddr=tcp://0.0.0.0:26657 \
>"$LOGFILE" 2>&1 &