stargate: Update code for new codec

This commit is contained in:
willclarktech 2020-10-14 18:27:45 +02:00
parent 05bcfa2e90
commit 131974822a
No known key found for this signature in database
GPG Key ID: 551A86E2E398ADF7
12 changed files with 239 additions and 138 deletions

View File

@ -1,12 +1,16 @@
import { encodeAminoPubkey } from "@cosmjs/launchpad";
/* eslint-disable @typescript-eslint/naming-convention */
import { fromBase64 } from "@cosmjs/encoding";
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
import { assert } from "@cosmjs/utils";
import Long from "long";
import { cosmos, google } from "../codec";
import { nonExistentAddress, pendingWithoutSimapp, simapp, unused, validator } from "../testutils.spec";
import { AuthExtension, setupAuthExtension } from "./auth";
import { QueryClient } from "./queryclient";
import { toAccAddress } from "./utils";
const { PubKey } = cosmos.crypto.secp256k1;
const { Any } = google.protobuf;
async function makeClientWithAuth(rpcUrl: string): Promise<[QueryClient & AuthExtension, TendermintClient]> {
const tmClient = await TendermintClient.connect(rpcUrl);
@ -18,12 +22,11 @@ describe("AuthExtension", () => {
it("works for unused account", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.account(unused.address);
assert(account);
expect(account).toEqual({
address: toAccAddress(unused.address),
address: unused.address,
// pubKey not set
accountNumber: Long.fromNumber(unused.accountNumber, true),
// sequence not set
@ -35,12 +38,19 @@ describe("AuthExtension", () => {
it("works for account with pubkey and non-zero sequence", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.account(validator.address);
assert(account);
const pubkey = PubKey.create({
key: fromBase64(validator.pubkey.value),
});
const pubkeyAny = Any.create({
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: Uint8Array.from(PubKey.encode(pubkey).finish()),
});
expect(account).toEqual({
address: toAccAddress(validator.address),
pubKey: encodeAminoPubkey(validator.pubkey),
address: validator.address,
pubKey: pubkeyAny,
// accountNumber not set
sequence: Long.fromNumber(validator.sequence, true),
});
@ -51,8 +61,8 @@ describe("AuthExtension", () => {
it("returns null for non-existent address", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.account(nonExistentAddress);
expect(account).toBeNull();
tmClient.disconnect();
@ -64,11 +74,11 @@ describe("AuthExtension", () => {
it("works for unused account", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.unverified.account(unused.address);
assert(account);
expect(account).toEqual({
address: toAccAddress(unused.address),
address: unused.address,
// pubKey not set
accountNumber: Long.fromNumber(unused.accountNumber, true),
// sequence not set
@ -80,12 +90,19 @@ describe("AuthExtension", () => {
it("works for account with pubkey and non-zero sequence", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.unverified.account(validator.address);
assert(account);
const pubkey = PubKey.create({
key: fromBase64(validator.pubkey.value),
});
const pubkeyAny = Any.create({
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: Uint8Array.from(PubKey.encode(pubkey).finish()),
});
expect(account).toEqual({
address: toAccAddress(validator.address),
pubKey: encodeAminoPubkey(validator.pubkey),
address: validator.address,
pubKey: pubkeyAny,
// accountNumber not set
sequence: Long.fromNumber(validator.sequence, true),
});

View File

@ -1,14 +1,17 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { assert } from "@cosmjs/utils";
import { cosmos, google } from "../codec";
import { QueryClient } from "./queryclient";
import { toAccAddress, toObject } from "./utils";
const { BaseAccount, Query } = cosmos.auth.v1beta1;
export interface AuthExtension {
readonly auth: {
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
readonly account: (address: string) => Promise<cosmos.auth.v1beta1.IBaseAccount | null>;
readonly unverified: {
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
readonly account: (address: string) => Promise<cosmos.auth.v1beta1.IBaseAccount | null>;
};
};
}
@ -16,9 +19,9 @@ export interface AuthExtension {
export function setupAuthExtension(base: QueryClient): AuthExtension {
// Use this service to get easy typed access to query methods
// This cannot be used to for proof verification
const queryService = cosmos.auth.Query.create((method: any, requestData, callback) => {
const queryService = Query.create((method: any, requestData, callback) => {
// Parts of the path are unavailable, so we hardcode them here. See https://github.com/protobufjs/protobuf.js/issues/1229
const path = `/cosmos.auth.Query/${method.name}`;
const path = `/cosmos.auth.v1beta1.Query/${method.name}`;
base
.queryUnverified(path, requestData)
.then((response) => callback(null, response))
@ -34,8 +37,8 @@ export function setupAuthExtension(base: QueryClient): AuthExtension {
if (responseData.length === 0) return null;
const account = google.protobuf.Any.decode(responseData);
switch (account.type_url) {
case "/cosmos.auth.BaseAccount": {
return toObject(cosmos.auth.BaseAccount.decode(account.value));
case "/cosmos.auth.v1beta1.BaseAccount": {
return toObject(BaseAccount.decode(account.value));
}
default:
throw new Error(`Unsupported type: '${account.type_url}'`);
@ -43,12 +46,12 @@ export function setupAuthExtension(base: QueryClient): AuthExtension {
},
unverified: {
account: async (address: string) => {
const { account } = await queryService.account({ address: toAccAddress(address) });
const { account } = await queryService.account({ address: address });
if (!account) return null;
switch (account.type_url) {
case "/cosmos.auth.BaseAccount": {
case "/cosmos.auth.v1beta1.BaseAccount": {
assert(account.value);
return toObject(cosmos.auth.BaseAccount.decode(account.value));
return toObject(BaseAccount.decode(account.value));
}
default:
throw new Error(`Unsupported type: '${account.type_url}'`);

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toAscii } from "@cosmjs/encoding";
import { assert } from "@cosmjs/utils";
@ -5,14 +6,17 @@ import { cosmos } from "../codec";
import { QueryClient } from "./queryclient";
import { toAccAddress, toObject } from "./utils";
const { Coin } = cosmos.base.v1beta1;
const { Query } = cosmos.bank.v1beta1;
export interface BankExtension {
readonly bank: {
readonly balance: (address: string, denom: string) => Promise<cosmos.ICoin | null>;
readonly balance: (address: string, denom: string) => Promise<cosmos.base.v1beta1.ICoin | null>;
readonly unverified: {
readonly balance: (address: string, denom: string) => Promise<cosmos.ICoin>;
readonly allBalances: (address: string) => Promise<cosmos.ICoin[]>;
readonly totalSupply: () => Promise<cosmos.ICoin[]>;
readonly supplyOf: (denom: string) => Promise<cosmos.ICoin>;
readonly balance: (address: string, denom: string) => Promise<cosmos.base.v1beta1.ICoin>;
readonly allBalances: (address: string) => Promise<cosmos.base.v1beta1.ICoin[]>;
readonly totalSupply: () => Promise<cosmos.base.v1beta1.ICoin[]>;
readonly supplyOf: (denom: string) => Promise<cosmos.base.v1beta1.ICoin>;
};
};
}
@ -20,9 +24,9 @@ export interface BankExtension {
export function setupBankExtension(base: QueryClient): BankExtension {
// Use this service to get easy typed access to query methods
// This cannot be used to for proof verification
const queryService = cosmos.bank.Query.create((method: any, requestData, callback) => {
const queryService = Query.create((method: any, requestData, callback) => {
// Parts of the path are unavailable, so we hardcode them here. See https://github.com/protobufjs/protobuf.js/issues/1229
const path = `/cosmos.bank.Query/${method.name}`;
const path = `/cosmos.bank.v1beta1.Query/${method.name}`;
base
.queryUnverified(path, requestData)
.then((response) => callback(null, response))
@ -40,16 +44,16 @@ export function setupBankExtension(base: QueryClient): BankExtension {
// https://github.com/cosmos/cosmos-sdk/blob/2879c0702c87dc9dd828a8c42b9224dc054e28ad/store/prefix/store.go#L37-L43
const key = Uint8Array.from([...toAscii("balances"), ...toAccAddress(address), ...toAscii(denom)]);
const responseData = await base.queryVerified("bank", key);
return responseData.length ? toObject(cosmos.Coin.decode(responseData)) : null;
return responseData.length ? toObject(Coin.decode(responseData)) : null;
},
unverified: {
balance: async (address: string, denom: string) => {
const { balance } = await queryService.balance({ address: toAccAddress(address), denom: denom });
const { balance } = await queryService.balance({ address: address, denom: denom });
assert(balance);
return toObject(balance);
},
allBalances: async (address: string) => {
const { balances } = await queryService.allBalances({ address: toAccAddress(address) });
const { balances } = await queryService.allBalances({ address: address });
return balances.map(toObject);
},
totalSupply: async () => {

View File

@ -88,13 +88,26 @@ describe("IbcExtension", () => {
});
});
describe("unrelayedPackets", () => {
describe("unreceivedPackets", () => {
it("can be called", async () => {
pending("Fails with 'Query failed with (1): internal'. Make it work.");
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithIbc(simapp.tendermintUrl);
const response = await client.ibc.unverified.unrelayedPackets("foo", "bar", [0, 1], true);
const response = await client.ibc.unverified.unreceivedPackets("foo", "bar", [0, 1]);
expect(response).toBeTruthy(); // TODO: implement checks
tmClient.disconnect();
});
});
describe("unrelayedAcks", () => {
it("can be called", async () => {
pending("Fails with 'Query failed with (1): internal'. Make it work.");
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithIbc(simapp.tendermintUrl);
const response = await client.ibc.unverified.unrelayedAcks("foo", "bar", [0, 1]);
expect(response).toBeTruthy(); // TODO: implement checks
tmClient.disconnect();

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toAscii } from "@cosmjs/encoding";
import { Uint64 } from "@cosmjs/math";
import Long from "long";
@ -6,9 +7,12 @@ import { ibc } from "../codec";
import { QueryClient } from "./queryclient";
import { toObject } from "./utils";
const { Query: ChannelQuery } = ibc.core.channel.v1;
const { Query: ConnectionQuery } = ibc.core.connection.v1;
export interface IbcExtension {
readonly ibc: {
readonly channel: (portId: string, channelId: string) => Promise<ibc.channel.IChannel | null>;
readonly channel: (portId: string, channelId: string) => Promise<ibc.core.channel.v1.IChannel | null>;
readonly packetCommitment: (portId: string, channelId: string, sequence: number) => Promise<Uint8Array>;
readonly packetAcknowledgement: (
portId: string,
@ -17,44 +21,51 @@ export interface IbcExtension {
) => Promise<Uint8Array>;
readonly nextSequenceReceive: (portId: string, channelId: string) => Promise<number | null>;
readonly unverified: {
// Queries for ibc.channel
readonly channel: (portId: string, channelId: string) => Promise<ibc.channel.IQueryChannelResponse>;
readonly channels: () => Promise<ibc.channel.IQueryChannelsResponse>;
// Queries for ibc.core.channel.v1
readonly channel: (
portId: string,
channelId: string,
) => Promise<ibc.core.channel.v1.IQueryChannelResponse>;
readonly channels: () => Promise<ibc.core.channel.v1.IQueryChannelsResponse>;
readonly connectionChannels: (
connection: string,
) => Promise<ibc.channel.IQueryConnectionChannelsResponse>;
) => Promise<ibc.core.channel.v1.IQueryConnectionChannelsResponse>;
readonly packetCommitment: (
portId: string,
channelId: string,
sequence: number,
) => Promise<ibc.channel.IQueryPacketCommitmentResponse>;
) => Promise<ibc.core.channel.v1.IQueryPacketCommitmentResponse>;
readonly packetCommitments: (
portId: string,
channelId: string,
) => Promise<ibc.channel.IQueryPacketCommitmentsResponse>;
) => Promise<ibc.core.channel.v1.IQueryPacketCommitmentsResponse>;
readonly packetAcknowledgement: (
portId: string,
channelId: string,
sequence: number,
) => Promise<ibc.channel.IQueryPacketAcknowledgementResponse>;
readonly unrelayedPackets: (
) => Promise<ibc.core.channel.v1.IQueryPacketAcknowledgementResponse>;
readonly unreceivedPackets: (
portId: string,
channelId: string,
packetCommitmentSequences: readonly number[],
acknowledgements: boolean,
) => Promise<ibc.channel.IQueryUnrelayedPacketsResponse>;
) => Promise<ibc.core.channel.v1.IQueryUnreceivedPacketsResponse>;
readonly unrelayedAcks: (
portId: string,
channelId: string,
packetCommitmentSequences: readonly number[],
) => Promise<ibc.core.channel.v1.IQueryUnrelayedAcksResponse>;
readonly nextSequenceReceive: (
portId: string,
channelId: string,
) => Promise<ibc.channel.IQueryNextSequenceReceiveResponse>;
) => Promise<ibc.core.channel.v1.IQueryNextSequenceReceiveResponse>;
// Queries for ibc.connection
// Queries for ibc.core.connection.v1
readonly connection: (connectionId: string) => Promise<ibc.connection.IQueryConnectionResponse>;
readonly connections: () => Promise<ibc.connection.IQueryConnectionsResponse>;
readonly connection: (connectionId: string) => Promise<ibc.core.connection.v1.IQueryConnectionResponse>;
readonly connections: () => Promise<ibc.core.connection.v1.IQueryConnectionsResponse>;
readonly clientConnections: (
clientId: string,
) => Promise<ibc.connection.IQueryClientConnectionsResponse>;
) => Promise<ibc.core.connection.v1.IQueryClientConnectionsResponse>;
};
};
}
@ -63,18 +74,18 @@ export function setupIbcExtension(base: QueryClient): IbcExtension {
// Use this service to get easy typed access to query methods
// This cannot be used to for proof verification
const channelQuerySerice = ibc.channel.Query.create((method: any, requestData, callback) => {
const channelQuerySerice = ChannelQuery.create((method: any, requestData, callback) => {
// Parts of the path are unavailable, so we hardcode them here. See https://github.com/protobufjs/protobuf.js/issues/1229
const path = `/ibc.channel.Query/${method.name}`;
const path = `/ibc.core.channel.v1.Query/${method.name}`;
base
.queryUnverified(path, requestData)
.then((response) => callback(null, response))
.catch((error) => callback(error));
});
const connectionQuerySerice = ibc.connection.Query.create((method: any, requestData, callback) => {
const connectionQuerySerice = ConnectionQuery.create((method: any, requestData, callback) => {
// Parts of the path are unavailable, so we hardcode them here. See https://github.com/protobufjs/protobuf.js/issues/1229
const path = `/ibc.connection.Query/${method.name}`;
const path = `/ibc.core.connection.v1.Query/${method.name}`;
base
.queryUnverified(path, requestData)
.then((response) => callback(null, response))
@ -88,7 +99,7 @@ export function setupIbcExtension(base: QueryClient): IbcExtension {
// key: https://github.com/cosmos/cosmos-sdk/blob/ef0a7344af345882729598bc2958a21143930a6b/x/ibc/24-host/keys.go#L117-L120
const key = toAscii(`channelEnds/ports/${portId}/channels/${channelId}`);
const responseData = await base.queryVerified("ibc", key);
return responseData.length ? toObject(ibc.channel.Channel.decode(responseData)) : null;
return responseData.length ? toObject(ibc.core.channel.v1.Channel.decode(responseData)) : null;
},
packetCommitment: async (portId: string, channelId: string, sequence: number) => {
// keeper: https://github.com/cosmos/cosmos-sdk/blob/3bafd8255a502e5a9cee07391cf8261538245dfd/x/ibc/04-channel/keeper/keeper.go#L128-L133
@ -115,7 +126,7 @@ export function setupIbcExtension(base: QueryClient): IbcExtension {
},
unverified: {
// Queries for ibc.channel
// Queries for ibc.core.channel.v1
channel: async (portId: string, channelId: string) => {
const response = await channelQuerySerice.channel({ portId: portId, channelId: channelId });
return toObject(response);
@ -151,17 +162,27 @@ export function setupIbcExtension(base: QueryClient): IbcExtension {
});
return toObject(response);
},
unrelayedPackets: async (
unreceivedPackets: async (
portId: string,
channelId: string,
packetCommitmentSequences: readonly number[],
acknowledgements: boolean,
) => {
const response = await channelQuerySerice.unrelayedPackets({
const response = await channelQuerySerice.unreceivedPackets({
portId: portId,
channelId: channelId,
packetCommitmentSequences: packetCommitmentSequences.map((s) => Long.fromNumber(s)),
});
return toObject(response);
},
unrelayedAcks: async (
portId: string,
channelId: string,
packetCommitmentSequences: readonly number[],
) => {
const response = await channelQuerySerice.unrelayedAcks({
portId: portId,
channelId: channelId,
packetCommitmentSequences: packetCommitmentSequences.map((s) => Long.fromNumber(s)),
acknowledgements: acknowledgements,
});
return toObject(response);
},
@ -173,7 +194,7 @@ export function setupIbcExtension(base: QueryClient): IbcExtension {
return toObject(response);
},
// Queries for ibc.connection
// Queries for ibc.core.connection.v1
connection: async (connectionId: string) => {
const response = await connectionQuerySerice.connection({ connectionId: connectionId });

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { toAscii } from "@cosmjs/encoding";
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
@ -6,6 +7,9 @@ import { nonNegativeIntegerMatcher, pendingWithoutSimapp, simapp, unused } from
import { QueryClient } from "./queryclient";
import { toAccAddress } from "./utils";
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);
return [QueryClient.withExtensions(tmClient), tmClient];
@ -23,7 +27,7 @@ describe("QueryClient", () => {
...toAscii(simapp.denomFee),
]);
const data = await client.queryVerified("bank", key);
const response = cosmos.Coin.decode(data);
const response = Coin.decode(data);
expect(response.amount).toMatch(nonNegativeIntegerMatcher);
expect(response.denom).toEqual(simapp.denomFee);
@ -40,7 +44,7 @@ describe("QueryClient", () => {
...toAscii(simapp.denomFee),
]);
const data = await client.queryVerified("bank", key);
const response = cosmos.Coin.decode(data);
const response = Coin.decode(data);
expect(response.amount).toMatch(nonNegativeIntegerMatcher);
expect(response.denom).toEqual(simapp.denomFee);
@ -54,10 +58,10 @@ describe("QueryClient", () => {
const [client, tmClient] = await makeClient(simapp.tendermintUrlWs);
const requestData = Uint8Array.from(
cosmos.bank.QueryAllBalancesRequest.encode({ address: toAccAddress(unused.address) }).finish(),
QueryAllBalancesRequest.encode({ address: unused.address }).finish(),
);
const data = await client.queryUnverified(`/cosmos.bank.Query/AllBalances`, requestData);
const response = cosmos.bank.QueryAllBalancesResponse.decode(data);
const data = await client.queryUnverified(`/cosmos.bank.v1beta1.Query/AllBalances`, requestData);
const response = QueryAllBalancesResponse.decode(data);
expect(response.balances.length).toEqual(2);
tmClient.disconnect();
@ -68,10 +72,10 @@ describe("QueryClient", () => {
const [client, tmClient] = await makeClient(simapp.tendermintUrlHttp);
const requestData = Uint8Array.from(
cosmos.bank.QueryAllBalancesRequest.encode({ address: toAccAddress(unused.address) }).finish(),
QueryAllBalancesRequest.encode({ address: unused.address }).finish(),
);
const data = await client.queryUnverified(`/cosmos.bank.Query/AllBalances`, requestData);
const response = cosmos.bank.QueryAllBalancesResponse.decode(data);
const data = await client.queryUnverified(`/cosmos.bank.v1beta1.Query/AllBalances`, requestData);
const response = QueryAllBalancesResponse.decode(data);
expect(response.balances.length).toEqual(2);
tmClient.disconnect();

View File

@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Bech32, fromBase64 } from "@cosmjs/encoding";
import { fromBase64 } from "@cosmjs/encoding";
import { Coin, coins } from "@cosmjs/launchpad";
import { DirectSecp256k1Wallet, makeAuthInfo, makeSignBytes, Registry } from "@cosmjs/proto-signing";
import { assert, sleep } from "@cosmjs/utils";
import { cosmos } from "./codec";
import { cosmos, google } from "./codec";
import {
BroadcastTxResponse,
isBroadcastTxFailure,
@ -13,8 +13,9 @@ import {
} from "./stargateclient";
import { faucet, makeRandomAddress, pendingWithoutSimapp, simapp, simappEnabled } from "./testutils.spec";
const { AuthInfo, Tx, TxBody } = cosmos.tx;
const { PublicKey } = cosmos.crypto;
const { TxRaw } = cosmos.tx.v1beta1;
const { PubKey } = cosmos.crypto.secp256k1;
const { Any } = google.protobuf;
interface TestTxSend {
readonly sender: string;
@ -36,16 +37,20 @@ async function sendTokens(
readonly tx: Uint8Array;
}> {
const [{ address: walletAddress, pubkey: pubkeyBytes }] = await wallet.getAccounts();
const publicKey = PublicKey.create({ secp256k1: pubkeyBytes });
const pubkey = PubKey.create({ key: pubkeyBytes });
const pubkeyAny = Any.create({
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: PubKey.encode(pubkey).finish(),
});
const txBodyFields = {
typeUrl: "/cosmos.tx.TxBody",
typeUrl: "/cosmos.tx.v1beta1.TxBody",
value: {
messages: [
{
typeUrl: "/cosmos.bank.MsgSend",
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: {
fromAddress: Bech32.decode(walletAddress).data,
toAddress: Bech32.decode(recipient).data,
fromAddress: walletAddress,
toAddress: recipient,
amount: amount,
},
},
@ -54,20 +59,25 @@ async function sendTokens(
},
};
const txBodyBytes = registry.encode(txBodyFields);
const txBody = TxBody.decode(txBodyBytes);
const authInfoBytes = makeAuthInfo([publicKey], 200000);
const { accountNumber, sequence } = (await client.getSequence(walletAddress))!;
const feeAmount = [
{
amount: "2000",
denom: "ucosm",
},
];
const gasLimit = 200000;
const authInfoBytes = makeAuthInfo([pubkeyAny], feeAmount, gasLimit, sequence);
const chainId = await client.getChainId();
const signDocBytes = makeSignBytes(txBodyBytes, authInfoBytes, chainId, accountNumber, sequence);
const signDocBytes = makeSignBytes(txBodyBytes, authInfoBytes, chainId, accountNumber);
const signature = await wallet.sign(walletAddress, signDocBytes);
// TODO: Why is this not a TxRaw? https://github.com/CosmWasm/cosmjs/issues/383
const txRaw = Tx.create({
body: txBody,
authInfo: AuthInfo.decode(authInfoBytes),
const txRaw = TxRaw.create({
bodyBytes: txBodyBytes,
authInfoBytes: authInfoBytes,
signatures: [fromBase64(signature.signature)],
});
const txRawBytes = Uint8Array.from(Tx.encode(txRaw).finish());
const txRawBytes = Uint8Array.from(TxRaw.encode(txRaw).finish());
const broadcastResponse = await client.broadcastTx(txRawBytes);
return {
broadcastResponse: broadcastResponse,

View File

@ -1,14 +1,14 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Bech32, fromBase64 } from "@cosmjs/encoding";
import { fromBase64 } from "@cosmjs/encoding";
import { DirectSecp256k1Wallet, makeAuthInfo, makeSignBytes, Registry } from "@cosmjs/proto-signing";
import { assert, sleep } from "@cosmjs/utils";
import { ReadonlyDate } from "readonly-date";
import { cosmos } from "./codec";
import { cosmos, google } from "./codec";
import { assertIsBroadcastTxSuccess, PrivateStargateClient, StargateClient } from "./stargateclient";
import {
faucet,
makeRandomAddressBytes,
makeRandomAddress,
nonExistentAddress,
pendingWithoutSimapp,
simapp,
@ -17,8 +17,9 @@ import {
validator,
} from "./testutils.spec";
const { AuthInfo, Tx, TxBody } = cosmos.tx;
const { PublicKey } = cosmos.crypto;
const { TxRaw } = cosmos.tx.v1beta1;
const { PubKey } = cosmos.crypto.secp256k1;
const { Any } = google.protobuf;
describe("StargateClient", () => {
describe("connect", () => {
@ -253,17 +254,21 @@ describe("StargateClient", () => {
const client = await StargateClient.connect(simapp.tendermintUrl);
const wallet = await DirectSecp256k1Wallet.fromMnemonic(faucet.mnemonic);
const [{ address, pubkey: pubkeyBytes }] = await wallet.getAccounts();
const publicKey = PublicKey.create({ secp256k1: pubkeyBytes });
const pubkey = PubKey.create({ key: pubkeyBytes });
const pubkeyAny = Any.create({
type_url: "/cosmos.crypto.secp256k1.PubKey",
value: PubKey.encode(pubkey).finish(),
});
const registry = new Registry();
const txBodyFields = {
typeUrl: "/cosmos.tx.TxBody",
typeUrl: "/cosmos.tx.v1beta1.TxBody",
value: {
messages: [
{
typeUrl: "/cosmos.bank.MsgSend",
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: {
fromAddress: Bech32.decode(address).data,
toAddress: makeRandomAddressBytes(),
fromAddress: address,
toAddress: makeRandomAddress(),
amount: [
{
denom: "ucosm",
@ -276,20 +281,25 @@ describe("StargateClient", () => {
},
};
const txBodyBytes = registry.encode(txBodyFields);
const txBody = TxBody.decode(txBodyBytes);
const authInfoBytes = makeAuthInfo([publicKey], 200000);
const { accountNumber, sequence } = (await client.getSequence(address))!;
const feeAmount = [
{
amount: "2000",
denom: "ucosm",
},
];
const gasLimit = 200000;
const authInfoBytes = makeAuthInfo([pubkeyAny], feeAmount, gasLimit, sequence);
const chainId = await client.getChainId();
const { accountNumber, sequence } = (await client.getSequence(address))!;
const signDocBytes = makeSignBytes(txBodyBytes, authInfoBytes, chainId, accountNumber, sequence);
const signDocBytes = makeSignBytes(txBodyBytes, authInfoBytes, chainId, accountNumber);
const signature = await wallet.sign(address, signDocBytes);
// TODO: Why is this not a TxRaw? https://github.com/CosmWasm/cosmjs/issues/383
const txRaw = Tx.create({
body: txBody,
authInfo: AuthInfo.decode(authInfoBytes),
const txRaw = TxRaw.create({
bodyBytes: txBodyBytes,
authInfoBytes: authInfoBytes,
signatures: [fromBase64(signature.signature)],
});
const txRawBytes = Uint8Array.from(Tx.encode(txRaw).finish());
const txRawBytes = Uint8Array.from(TxRaw.encode(txRaw).finish());
const txResult = await client.broadcastTx(txRawBytes);
assertIsBroadcastTxSuccess(txResult);

View File

@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Bech32, toHex } from "@cosmjs/encoding";
import { toHex } from "@cosmjs/encoding";
import {
Block,
Coin,
decodeAminoPubkey,
encodeSecp256k1Pubkey,
isSearchByHeightQuery,
isSearchByIdQuery,
PubKey,
@ -15,7 +15,7 @@ import { broadcastTxCommitSuccess, Client as TendermintClient, QueryString } fro
import { assert, assertDefined } from "@cosmjs/utils";
import Long from "long";
import { cosmos } from "./codec";
import { cosmos, google } from "./codec";
import { AuthExtension, BankExtension, QueryClient, setupAuthExtension, setupBankExtension } from "./queries";
/** A transaction that is indexed as part of the transaction history */
@ -85,20 +85,34 @@ function uint64FromProto(input: number | Long | null | undefined): Uint64 {
return Uint64.fromString(input.toString());
}
function accountFromProto(input: cosmos.auth.IBaseAccount, prefix: string): Account {
function decodePubkey(pubkey?: google.protobuf.IAny | null): PubKey | null {
if (!pubkey || !pubkey.value) {
return null;
}
switch (pubkey.type_url) {
case "/cosmos.crypto.secp256k1.PubKey": {
const { key } = cosmos.crypto.secp256k1.PubKey.decode(pubkey.value);
return encodeSecp256k1Pubkey(key);
}
default:
throw new Error("Unknown pubkey type");
}
}
function accountFromProto(input: cosmos.auth.v1beta1.IBaseAccount): Account {
const { address, pubKey, accountNumber, sequence } = input;
// Pubkey is still Amino-encoded in BaseAccount (https://github.com/cosmos/cosmos-sdk/issues/6886)
const pubkey = pubKey && pubKey.length ? decodeAminoPubkey(pubKey) : null;
const pubkey = decodePubkey(pubKey);
assert(address);
return {
address: Bech32.encode(prefix, address),
address: address,
pubkey: pubkey,
accountNumber: uint64FromProto(accountNumber).toNumber(),
sequence: uint64FromProto(sequence).toNumber(),
};
}
function coinFromProto(input: cosmos.ICoin): Coin {
function coinFromProto(input: cosmos.base.v1beta1.ICoin): Coin {
assertDefined(input.amount);
assertDefined(input.denom);
assert(input.amount !== null);
@ -146,10 +160,8 @@ export class StargateClient {
}
public async getAccount(searchAddress: string): Promise<Account | null> {
const { prefix } = Bech32.decode(searchAddress);
const account = await this.queryClient.auth.account(searchAddress);
return account ? accountFromProto(account, prefix) : null;
return account ? accountFromProto(account) : null;
}
public async getSequence(address: string): Promise<SequenceResponse | null> {

View File

@ -2,9 +2,9 @@ import { cosmos } from "../codec";
import { QueryClient } from "./queryclient";
export interface AuthExtension {
readonly auth: {
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
readonly account: (address: string) => Promise<cosmos.auth.v1beta1.IBaseAccount | null>;
readonly unverified: {
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
readonly account: (address: string) => Promise<cosmos.auth.v1beta1.IBaseAccount | null>;
};
};
}

View File

@ -2,12 +2,12 @@ import { cosmos } from "../codec";
import { QueryClient } from "./queryclient";
export interface BankExtension {
readonly bank: {
readonly balance: (address: string, denom: string) => Promise<cosmos.ICoin | null>;
readonly balance: (address: string, denom: string) => Promise<cosmos.base.v1beta1.ICoin | null>;
readonly unverified: {
readonly balance: (address: string, denom: string) => Promise<cosmos.ICoin>;
readonly allBalances: (address: string) => Promise<cosmos.ICoin[]>;
readonly totalSupply: () => Promise<cosmos.ICoin[]>;
readonly supplyOf: (denom: string) => Promise<cosmos.ICoin>;
readonly balance: (address: string, denom: string) => Promise<cosmos.base.v1beta1.ICoin>;
readonly allBalances: (address: string) => Promise<cosmos.base.v1beta1.ICoin[]>;
readonly totalSupply: () => Promise<cosmos.base.v1beta1.ICoin[]>;
readonly supplyOf: (denom: string) => Promise<cosmos.base.v1beta1.ICoin>;
};
};
}

View File

@ -2,7 +2,7 @@ import { ibc } from "../codec";
import { QueryClient } from "./queryclient";
export interface IbcExtension {
readonly ibc: {
readonly channel: (portId: string, channelId: string) => Promise<ibc.channel.IChannel | null>;
readonly channel: (portId: string, channelId: string) => Promise<ibc.core.channel.v1.IChannel | null>;
readonly packetCommitment: (portId: string, channelId: string, sequence: number) => Promise<Uint8Array>;
readonly packetAcknowledgement: (
portId: string,
@ -11,40 +11,47 @@ export interface IbcExtension {
) => Promise<Uint8Array>;
readonly nextSequenceReceive: (portId: string, channelId: string) => Promise<number | null>;
readonly unverified: {
readonly channel: (portId: string, channelId: string) => Promise<ibc.channel.IQueryChannelResponse>;
readonly channels: () => Promise<ibc.channel.IQueryChannelsResponse>;
readonly channel: (
portId: string,
channelId: string,
) => Promise<ibc.core.channel.v1.IQueryChannelResponse>;
readonly channels: () => Promise<ibc.core.channel.v1.IQueryChannelsResponse>;
readonly connectionChannels: (
connection: string,
) => Promise<ibc.channel.IQueryConnectionChannelsResponse>;
) => Promise<ibc.core.channel.v1.IQueryConnectionChannelsResponse>;
readonly packetCommitment: (
portId: string,
channelId: string,
sequence: number,
) => Promise<ibc.channel.IQueryPacketCommitmentResponse>;
) => Promise<ibc.core.channel.v1.IQueryPacketCommitmentResponse>;
readonly packetCommitments: (
portId: string,
channelId: string,
) => Promise<ibc.channel.IQueryPacketCommitmentsResponse>;
) => Promise<ibc.core.channel.v1.IQueryPacketCommitmentsResponse>;
readonly packetAcknowledgement: (
portId: string,
channelId: string,
sequence: number,
) => Promise<ibc.channel.IQueryPacketAcknowledgementResponse>;
readonly unrelayedPackets: (
) => Promise<ibc.core.channel.v1.IQueryPacketAcknowledgementResponse>;
readonly unreceivedPackets: (
portId: string,
channelId: string,
packetCommitmentSequences: readonly number[],
acknowledgements: boolean,
) => Promise<ibc.channel.IQueryUnrelayedPacketsResponse>;
) => Promise<ibc.core.channel.v1.IQueryUnreceivedPacketsResponse>;
readonly unrelayedAcks: (
portId: string,
channelId: string,
packetCommitmentSequences: readonly number[],
) => Promise<ibc.core.channel.v1.IQueryUnrelayedAcksResponse>;
readonly nextSequenceReceive: (
portId: string,
channelId: string,
) => Promise<ibc.channel.IQueryNextSequenceReceiveResponse>;
readonly connection: (connectionId: string) => Promise<ibc.connection.IQueryConnectionResponse>;
readonly connections: () => Promise<ibc.connection.IQueryConnectionsResponse>;
) => Promise<ibc.core.channel.v1.IQueryNextSequenceReceiveResponse>;
readonly connection: (connectionId: string) => Promise<ibc.core.connection.v1.IQueryConnectionResponse>;
readonly connections: () => Promise<ibc.core.connection.v1.IQueryConnectionsResponse>;
readonly clientConnections: (
clientId: string,
) => Promise<ibc.connection.IQueryClientConnectionsResponse>;
) => Promise<ibc.core.connection.v1.IQueryClientConnectionsResponse>;
};
};
}