Merge pull request #538 from cosmos/reduce-dependency-on-tendermint-version-detection

Reduce dependency on tendermint version detection
This commit is contained in:
Will Clark 2020-11-24 13:08:02 +01:00 committed by GitHub
commit 030bff75fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 274 additions and 122 deletions

View File

@ -13,6 +13,15 @@
- @cosmjs/proto-signing: Add new package for handling transaction signing with
protobuf encoding.
- @cosmjs/stargate: Add new package for Cosmos SDK Stargate support.
- @cosmjs/tendermint-rpc: Make `Client.detectVersion` private and let it return
a version instead of a client.
- @cosmjs/tendermint-rpc: Make the constructor of `Client` private. Add
`Client.create` for creating a Tendermint client given an RPC client and an
optional adaptor.
- @cosmjs/tendermint-rpc: Add an optional adaptor argument to `Client.connect`
which allows skipping the auto-detection.
- @cosmjs/tendermint-rpc: Remove export `v0_33` in favour of `adaptor33` and
`adaptor34`. Export the `Adaptor` type.
## 0.23.1 (2020-10-27)

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { encodePubkey } from "@cosmjs/proto-signing";
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { assert } from "@cosmjs/utils";
import Long from "long";
@ -12,7 +12,7 @@ import { QueryClient } from "./queryclient";
const { Any } = google.protobuf;
async function makeClientWithAuth(rpcUrl: string): Promise<[QueryClient & AuthExtension, TendermintClient]> {
const tmClient = await TendermintClient.connect(rpcUrl);
const tmClient = await TendermintClient.connect(rpcUrl, adaptor34);
return [QueryClient.withExtensions(tmClient, setupAuthExtension), tmClient];
}

View File

@ -1,4 +1,4 @@
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import {
nonExistentAddress,
@ -11,7 +11,7 @@ import { BankExtension, setupBankExtension } from "./bank";
import { QueryClient } from "./queryclient";
async function makeClientWithBank(rpcUrl: string): Promise<[QueryClient & BankExtension, TendermintClient]> {
const tmClient = await TendermintClient.connect(rpcUrl);
const tmClient = await TendermintClient.connect(rpcUrl, adaptor34);
return [QueryClient.withExtensions(tmClient, setupBankExtension), tmClient];
}

View File

@ -1,4 +1,4 @@
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import Long from "long";
import { cosmos, ibc } from "../codec";
@ -8,7 +8,7 @@ import * as ibcTest from "./ibctestdata.spec";
import { QueryClient } from "./queryclient";
async function makeClientWithIbc(rpcUrl: string): Promise<[QueryClient & IbcExtension, TendermintClient]> {
const tmClient = await TendermintClient.connect(rpcUrl);
const tmClient = await TendermintClient.connect(rpcUrl, adaptor34);
return [QueryClient.withExtensions(tmClient, setupIbcExtension), tmClient];
}

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toAscii } from "@cosmjs/encoding";
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { cosmos } from "../codec";
import { nonNegativeIntegerMatcher, pendingWithoutSimapp, simapp, unused } from "../testutils.spec";
@ -11,7 +11,7 @@ const { Coin } = cosmos.base.v1beta1;
const { QueryAllBalancesRequest, QueryAllBalancesResponse } = cosmos.bank.v1beta1;
async function makeClient(rpcUrl: string): Promise<[QueryClient, TendermintClient]> {
const tmClient = await TendermintClient.connect(rpcUrl);
const tmClient = await TendermintClient.connect(rpcUrl, adaptor34);
return [QueryClient.withExtensions(tmClient), tmClient];
}

View File

@ -21,7 +21,7 @@ import {
OfflineSigner,
Registry,
} from "@cosmjs/proto-signing";
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { cosmos } from "./codec";
import { getMsgType } from "./encoding";
@ -54,7 +54,7 @@ export class SigningStargateClient extends StargateClient {
signer: OfflineSigner,
options: SigningStargateClientOptions = {},
): Promise<SigningStargateClient> {
const tmClient = await TendermintClient.connect(endpoint);
const tmClient = await TendermintClient.connect(endpoint, adaptor34);
return new SigningStargateClient(tmClient, signer, options);
}

View File

@ -40,6 +40,7 @@ describe("StargateClient", () => {
pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl);
expect(await client.getChainId()).toEqual(simapp.chainId);
client.disconnect();
});
it("caches chain ID", async () => {
@ -52,6 +53,8 @@ describe("StargateClient", () => {
expect(await client.getChainId()).toEqual(simapp.chainId); // from cache
expect(getCodeSpy).toHaveBeenCalledTimes(1);
client.disconnect();
});
});
@ -66,6 +69,8 @@ describe("StargateClient", () => {
const height2 = await client.getHeight();
expect(height2).toBeGreaterThanOrEqual(height1 + 1);
expect(height2).toBeLessThanOrEqual(height1 + 2);
client.disconnect();
});
});
@ -160,6 +165,8 @@ describe("StargateClient", () => {
expect(new ReadonlyDate(response.header.time).getTime()).toBeGreaterThanOrEqual(
ReadonlyDate.now() - 5_000,
);
client.disconnect();
});
it("works for block by height", async () => {
@ -183,6 +190,8 @@ describe("StargateClient", () => {
expect(new ReadonlyDate(response.header.time).getTime()).toBeGreaterThanOrEqual(
ReadonlyDate.now() - 5_000,
);
client.disconnect();
});
});
@ -242,13 +251,18 @@ describe("StargateClient", () => {
denom: simapp.denomStaking,
},
]);
client.disconnect();
});
it("returns an empty list for non-existent account", async () => {
pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl);
const balances = await client.getAllBalancesUnverified(nonExistentAddress);
expect(balances).toEqual([]);
client.disconnect();
});
});
@ -309,6 +323,8 @@ describe("StargateClient", () => {
const { rawLog, transactionHash } = txResult;
expect(rawLog).toMatch(/{"key":"amount","value":"1234567ucosm"}/);
expect(transactionHash).toMatch(/^[0-9A-F]{64}$/);
client.disconnect();
});
});
});

View File

@ -13,7 +13,12 @@ import {
} from "@cosmjs/launchpad";
import { Uint53, Uint64 } from "@cosmjs/math";
import { decodePubkey } from "@cosmjs/proto-signing";
import { broadcastTxCommitSuccess, Client as TendermintClient, QueryString } from "@cosmjs/tendermint-rpc";
import {
adaptor34,
broadcastTxCommitSuccess,
Client as TendermintClient,
QueryString,
} from "@cosmjs/tendermint-rpc";
import { assert, assertDefined } from "@cosmjs/utils";
import Long from "long";
@ -121,7 +126,7 @@ export class StargateClient {
private chainId: string | undefined;
public static async connect(endpoint: string): Promise<StargateClient> {
const tmClient = await TendermintClient.connect(endpoint);
const tmClient = await TendermintClient.connect(endpoint, adaptor34);
return new StargateClient(tmClient);
}

View File

@ -37,7 +37,7 @@
"test-safari": "yarn pack-web && karma start --single-run --browsers Safari",
"test": "yarn build-or-skip && yarn test-node",
"coverage": "nyc --reporter=text --reporter=lcov yarn test --quiet",
"move-types": "shx rm -rf ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts && shx rm ./types/**/*.spec.d.ts",
"move-types": "shx rm -rf ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts ./types/*/*.spec.d.ts ./types/*/*/*.spec.d.ts",
"format-types": "prettier --write --loglevel warn \"./types/**/*.d.ts\"",
"prebuild": "shx rm -rf ./build",
"build": "tsc",

View File

@ -1,26 +0,0 @@
/* eslint-disable @typescript-eslint/naming-convention */
// This module exposes translators for multiple tendermint versions
// Pick a version that matches the server to properly encode the data types
import { Adaptor } from "./adaptor";
import { v0_33 } from "./v0-33";
const hashes = {
v0_34: [
"ca2c9df", // v0.34.0-rc6
"", // See https://github.com/cosmos/cosmos-sdk/issues/7963
],
};
/**
* Returns an Adaptor implementation for a given tendermint version.
* Throws when version is not supported.
*
* @param version full Tendermint version string, e.g. "0.20.1"
*/
export function adaptorForVersion(version: string): Adaptor {
if (version.startsWith("0.33.") || version.startsWith("0.34.") || hashes.v0_34.includes(version)) {
return v0_33;
} else {
throw new Error(`Unsupported tendermint version: ${version}`);
}
}

View File

@ -0,0 +1,49 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Adaptor } from "../adaptor";
import { v0_33 } from "./v0-33";
/**
* Adaptor for Tendermint 0.33.
*
* Use this to skip auto-detection:
*
* ```
* import { adaptor33, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
* // ...
* const client = await TendermintClient.connect(url, adaptor33);
* ```
*/
export const adaptor33 = v0_33;
/**
* Adaptor for Tendermint 0.34.
*
* Use this to skip auto-detection:
*
* ```
* import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
* // ...
* const client = await TendermintClient.connect(url, adaptor34);
* ```
*/
export const adaptor34 = v0_33; // With this alias we can swap out the implementation without affecting caller code.
const hashes = {
v0_34: [
"ca2c9df", // v0.34.0-rc6
],
};
/**
* Returns an Adaptor implementation for a given tendermint version.
* Throws when version is not supported.
*
* @param version full Tendermint version string, e.g. "0.20.1"
*/
export function adaptorForVersion(version: string): Adaptor {
if (version.startsWith("0.33.") || version.startsWith("0.34.") || hashes.v0_34.includes(version)) {
return v0_33;
} else {
throw new Error(`Unsupported tendermint version: ${version}`);
}
}

View File

@ -1,8 +1,8 @@
import { fromBase64, fromHex } from "@cosmjs/encoding";
import { ReadonlyDate } from "readonly-date";
import { ReadonlyDateWithNanoseconds } from "../responses";
import { TxBytes } from "../types";
import { ReadonlyDateWithNanoseconds } from "../../responses";
import { TxBytes } from "../../types";
import { hashBlock, hashTx } from "./hasher";
describe("Hasher", () => {

View File

@ -1,8 +1,15 @@
import { Sha256 } from "@cosmjs/crypto";
import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "../encodings";
import { Header } from "../responses";
import { BlockHash, TxBytes, TxHash } from "../types";
import {
encodeBlockId,
encodeBytes,
encodeInt,
encodeString,
encodeTime,
encodeVersion,
} from "../../encodings";
import { Header } from "../../responses";
import { BlockHash, TxBytes, TxHash } from "../../types";
// hash is sha256
// https://github.com/tendermint/tendermint/blob/master/UPGRADING.md#v0260

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Adaptor } from "../adaptor";
import { Adaptor } from "../../adaptor";
import { hashBlock, hashTx } from "./hasher";
import { Params } from "./requests";
import { Responses } from "./responses";

View File

@ -2,9 +2,17 @@
import { toHex } from "@cosmjs/encoding";
import { JsonRpcRequest } from "@cosmjs/json-rpc";
import { assertNotEmpty, Base64, Base64String, HexString, Integer, IntegerString, may } from "../encodings";
import { createJsonRpcRequest } from "../jsonrpc";
import * as requests from "../requests";
import {
assertNotEmpty,
Base64,
Base64String,
HexString,
Integer,
IntegerString,
may,
} from "../../encodings";
import { createJsonRpcRequest } from "../../jsonrpc";
import * as requests from "../../requests";
interface HeightParam {
readonly height?: number;

View File

@ -9,6 +9,7 @@ import {
assertNumber,
assertObject,
assertSet,
assertString,
Base64,
Base64String,
DateTime,
@ -20,10 +21,10 @@ import {
IntegerString,
may,
optional,
} from "../encodings";
import * as responses from "../responses";
import { SubscriptionEvent } from "../rpcclients";
import { IpPortString, TxBytes, TxHash, ValidatorPubkey, ValidatorSignature } from "../types";
} from "../../encodings";
import * as responses from "../../responses";
import { SubscriptionEvent } from "../../rpcclients";
import { IpPortString, TxBytes, TxHash, ValidatorPubkey, ValidatorSignature } from "../../types";
import { hashTx } from "./hasher";
interface AbciInfoResult {
@ -502,9 +503,7 @@ function decodeNodeInfo(data: RpcNodeInfo): responses.NodeInfo {
id: fromHex(assertNotEmpty(data.id)),
listenAddr: assertNotEmpty(data.listen_addr),
network: assertNotEmpty(data.network),
// TODO: Reactivate check when https://github.com/cosmos/cosmos-sdk/issues/7963 is resolved
// version: assertNotEmpty(data.version),
version: data.version,
version: assertString(data.version), // Can be empty (https://github.com/cosmos/cosmos-sdk/issues/7963)
channels: assertNotEmpty(data.channels),
moniker: assertNotEmpty(data.moniker),
other: dictionaryToStringMap(data.other),

View File

@ -6,7 +6,7 @@ import { ReadonlyDate } from "readonly-date";
import { Stream } from "xstream";
import { Adaptor } from "./adaptor";
import { adaptorForVersion } from "./adaptorforversion";
import { adaptorForVersion } from "./adaptors";
import { Client } from "./client";
import { ExpectedValues, tendermintInstances } from "./config.spec";
import { buildQuery } from "./requests";
@ -42,24 +42,26 @@ function randomString(): string {
}
function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expected: ExpectedValues): void {
it("can connect to tendermint with known version", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
expect(await client.abciInfo()).toBeTruthy();
client.disconnect();
});
describe("create", () => {
it("can auto-discover Tendermint version and communicate", async () => {
pendingWithoutTendermint();
const client = await Client.create(rpcFactory());
const info = await client.abciInfo();
expect(info).toBeTruthy();
client.disconnect();
});
it("can auto-discover tendermint version and connect", async () => {
pendingWithoutTendermint();
const client = await Client.detectVersion(rpcFactory());
const info = await client.abciInfo();
expect(info).toBeTruthy();
client.disconnect();
it("can connect to Tendermint with known version", async () => {
pendingWithoutTendermint();
const client = await Client.create(rpcFactory(), adaptor);
expect(await client.abciInfo()).toBeTruthy();
client.disconnect();
});
});
it("can get genesis", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const genesis = await client.genesis();
expect(genesis).toBeTruthy();
client.disconnect();
@ -67,7 +69,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("can broadcast a transaction", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const tx = buildKvTx(randomString(), randomString());
const response = await client.broadcastTxCommit({ tx: tx });
@ -85,7 +87,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("gets the same tx hash from backend as calculated locally", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const tx = buildKvTx(randomString(), randomString());
const calculatedTxHash = adaptor.hashTx(tx);
@ -97,7 +99,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("can query the state", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const key = randomString();
const value = randomString();
@ -116,7 +118,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("can call a bunch of methods", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
expect(await client.block()).toBeTruthy();
expect(await client.commit(4)).toBeTruthy();
@ -130,7 +132,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
describe("status", () => {
it("works", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const status = await client.status();
@ -159,7 +161,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
describe("blockResults", () => {
it("works", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const height = 3;
const results = await client.blockResults(height);
@ -175,7 +177,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
describe("blockchain", () => {
it("returns latest in descending order by default", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
// Run in parallel to increase chance there is no block between the calls
const [status, blockchain] = await Promise.all([client.status(), client.blockchain()]);
@ -192,7 +194,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("can limit by maxHeight", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(undefined, height - 1);
@ -206,7 +208,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("works with maxHeight in the future", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(undefined, height + 20);
@ -220,7 +222,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("can limit by minHeight and maxHeight", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(height - 2, height - 1);
@ -234,7 +236,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("contains all the info", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const height = (await client.status()).syncInfo.latestBlockHeight;
const blockchain = await client.blockchain(height - 1, height - 1);
@ -264,7 +266,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
describe("tx", () => {
it("can query a tx properly", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const find = randomString();
const me = randomString();
@ -321,7 +323,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
beforeAll(async () => {
if (tendermintEnabled()) {
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
// eslint-disable-next-line no-inner-declarations
async function sendTx(): Promise<void> {
@ -347,7 +349,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("can paginate over txSearch results", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
@ -366,7 +368,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte
it("can get all search results in one call", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const query = buildQuery({ tags: [{ key: "app.key", value: key }] });
@ -391,7 +393,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expec
(async () => {
const events: responses.NewBlockHeaderEvent[] = [];
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const stream = client.subscribeNewBlockHeader();
expect(stream).toBeTruthy();
const subscription = stream.subscribe({
@ -449,7 +451,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expec
const transactionData2 = buildKvTx(randomString(), randomString());
const events: responses.NewBlockEvent[] = [];
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const stream = client.subscribeNewBlock();
const subscription = stream.subscribe({
next: (event) => {
@ -505,7 +507,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expec
pendingWithoutTendermint();
const events: responses.TxEvent[] = [];
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const stream = client.subscribeTx();
const subscription = stream.subscribe({
next: (event) => {
@ -549,7 +551,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expec
const transactionData2 = buildKvTx(randomString(), randomString());
const events: responses.TxEvent[] = [];
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const query = buildQuery({ tags: [{ key: "app.creator", value: expected.appCreator }] });
const stream = client.subscribeTx(query);
expect(stream).toBeTruthy();
@ -587,7 +589,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expec
it("can unsubscribe and re-subscribe to the same stream", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const stream = client.subscribeNewBlockHeader();
const event1 = await firstEvent(stream);
@ -620,7 +622,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expec
it("can subscribe twice", async () => {
pendingWithoutTendermint();
const client = new Client(rpcFactory(), adaptor);
const client = await Client.create(rpcFactory(), adaptor);
const stream1 = client.subscribeNewBlockHeader();
const stream2 = client.subscribeNewBlockHeader();

View File

@ -1,7 +1,7 @@
import { Stream } from "xstream";
import { Adaptor, Decoder, Encoder, Params, Responses } from "./adaptor";
import { adaptorForVersion } from "./adaptorforversion";
import { adaptorForVersion } from "./adaptors";
import { createJsonRpcRequest } from "./jsonrpc";
import * as requests from "./requests";
import * as responses from "./responses";
@ -14,13 +14,34 @@ import {
} from "./rpcclients";
export class Client {
public static async connect(url: string): Promise<Client> {
/**
* Creates a new Tendermint client for the given endpoint.
*
* Uses HTTP when the URL schema is http or https. Uses WebSockets otherwise.
*
* If the adaptor is not set an auto-detection is attempted.
*/
public static async connect(url: string, adaptor?: Adaptor): Promise<Client> {
const useHttp = url.startsWith("http://") || url.startsWith("https://");
const client = useHttp ? new HttpClient(url) : new WebsocketClient(url);
return this.detectVersion(client);
const rpcClient = useHttp ? new HttpClient(url) : new WebsocketClient(url);
return Client.create(rpcClient, adaptor);
}
public static async detectVersion(client: RpcClient): Promise<Client> {
/**
* Creates a new Tendermint client given an RPC client.
*
* If the adaptor is not set an auto-detection is attempted.
*/
public static async create(rpcClient: RpcClient, adaptor?: Adaptor): Promise<Client> {
// 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 Client(rpcClient, adaptor || adaptorForVersion(version));
}
private static async detectVersion(client: RpcClient): Promise<string> {
const req = createJsonRpcRequest(requests.Method.Status);
const response = await client.execute(req);
const result = response.result;
@ -33,15 +54,17 @@ export class Client {
if (typeof version !== "string") {
throw new Error("Unrecognized version format: must be string");
}
return new Client(client, adaptorForVersion(version));
return version;
}
private readonly client: RpcClient;
private readonly p: Params;
private readonly r: Responses;
public constructor(client: RpcClient, adaptor: Adaptor) {
/**
* Use `Client.connect` or `Client.create` to create an instance.
*/
private constructor(client: RpcClient, adaptor: Adaptor) {
this.client = client;
this.p = adaptor.params;
this.r = adaptor.responses;

View File

@ -40,6 +40,20 @@ export function assertBoolean(value: boolean): boolean {
return value;
}
/**
* A runtime checker that ensures a given value is a string.
*
* This is used when you want to verify that data at runtime matches the expected type.
* This implies assertSet.
*/
export function assertString(value: string): string {
assertSet(value);
if (typeof (value as unknown) !== "string") {
throw new Error("Value must be a string");
}
return value;
}
/**
* A runtime checker that ensures a given value is a number
*

View File

@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
// exported to access version-specific hashing
export { v0_33 } from "./v0-33";
export { Adaptor } from "./adaptor";
export { adaptor33, adaptor34 } from "./adaptors";
export { Client } from "./client";
export {
AbciInfoRequest,

View File

@ -1,8 +0,0 @@
import { Adaptor } from "./adaptor";
/**
* Returns an Adaptor implementation for a given tendermint version.
* Throws when version is not supported.
*
* @param version full Tendermint version string, e.g. "0.20.1"
*/
export declare function adaptorForVersion(version: string): Adaptor;

View File

@ -0,0 +1,32 @@
import { Adaptor } from "../adaptor";
/**
* Adaptor for Tendermint 0.33.
*
* Use this to skip auto-detection:
*
* ```
* import { adaptor33, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
* // ...
* const client = await TendermintClient.connect(url, adaptor33);
* ```
*/
export declare const adaptor33: Adaptor;
/**
* Adaptor for Tendermint 0.34.
*
* Use this to skip auto-detection:
*
* ```
* import { adaptor34, Client as TendermintClient } from "@cosmjs/tendermint-rpc";
* // ...
* const client = await TendermintClient.connect(url, adaptor34);
* ```
*/
export declare const adaptor34: Adaptor;
/**
* Returns an Adaptor implementation for a given tendermint version.
* Throws when version is not supported.
*
* @param version full Tendermint version string, e.g. "0.20.1"
*/
export declare function adaptorForVersion(version: string): Adaptor;

View File

@ -1,4 +1,4 @@
import { Header } from "../responses";
import { BlockHash, TxBytes, TxHash } from "../types";
import { Header } from "../../responses";
import { BlockHash, TxBytes, TxHash } from "../../types";
export declare function hashTx(tx: TxBytes): TxHash;
export declare function hashBlock(header: Header): BlockHash;

View File

@ -0,0 +1,2 @@
import { Adaptor } from "../../adaptor";
export declare const v0_33: Adaptor;

View File

@ -1,5 +1,5 @@
import { JsonRpcRequest } from "@cosmjs/json-rpc";
import * as requests from "../requests";
import * as requests from "../../requests";
export declare class Params {
static encodeAbciInfo(req: requests.AbciInfoRequest): JsonRpcRequest;
static encodeAbciQuery(req: requests.AbciQueryRequest): JsonRpcRequest;

View File

@ -1,7 +1,7 @@
import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import { Base64String } from "../encodings";
import * as responses from "../responses";
import { SubscriptionEvent } from "../rpcclients";
import { Base64String } from "../../encodings";
import * as responses from "../../responses";
import { SubscriptionEvent } from "../../rpcclients";
export interface RpcProofOp {
readonly type: string;
readonly key: Base64String;

View File

@ -4,12 +4,28 @@ import * as requests from "./requests";
import * as responses from "./responses";
import { RpcClient } from "./rpcclients";
export declare class Client {
static connect(url: string): Promise<Client>;
static detectVersion(client: RpcClient): Promise<Client>;
/**
* Creates a new Tendermint client for the given endpoint.
*
* Uses HTTP when the URL schema is http or https. Uses WebSockets otherwise.
*
* If the adaptor is not set an auto-detection is attempted.
*/
static connect(url: string, adaptor?: Adaptor): Promise<Client>;
/**
* Creates a new Tendermint client given an RPC client.
*
* If the adaptor is not set an auto-detection is attempted.
*/
static create(rpcClient: RpcClient, adaptor?: Adaptor): Promise<Client>;
private static detectVersion;
private readonly client;
private readonly p;
private readonly r;
constructor(client: RpcClient, adaptor: Adaptor);
/**
* Use `Client.connect` or `Client.create` to create an instance.
*/
private constructor();
disconnect(): void;
abciInfo(): Promise<responses.AbciInfoResponse>;
abciQuery(params: requests.AbciQueryParams): Promise<responses.AbciQueryResponse>;

View File

@ -17,6 +17,13 @@ export declare function assertSet<T>(value: T): T;
* This implies assertSet.
*/
export declare function assertBoolean(value: boolean): boolean;
/**
* A runtime checker that ensures a given value is a string.
*
* This is used when you want to verify that data at runtime matches the expected type.
* This implies assertSet.
*/
export declare function assertString(value: string): string;
/**
* A runtime checker that ensures a given value is a number
*

View File

@ -1,4 +1,5 @@
export { v0_33 } from "./v0-33";
export { Adaptor } from "./adaptor";
export { adaptor33, adaptor34 } from "./adaptors";
export { Client } from "./client";
export {
AbciInfoRequest,

View File

@ -1,2 +0,0 @@
import { Adaptor } from "../adaptor";
export declare const v0_33: Adaptor;