From b68fe1aa6aa831a7a069528acc9977565217bdad Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 17:08:48 +0100 Subject: [PATCH 01/16] Copy amino interface definitions to package - need some cleanup --- packages/sdk/src/types.ts | 55 +++++++++++++++++++++++++++++++++-- packages/sdk/types/types.d.ts | 45 ++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 83be077d..9db452f5 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -1,8 +1,59 @@ import amino from "@tendermint/amino-js"; -export type AminoTx = amino.Tx & { readonly value: amino.StdTx }; +// We will move all needed *interfaces* from amino-js here +// This means bcp can just import them from here (if needed at all) +export interface Tx { + type: string; + value: any; +} -export function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx { +export interface StdTx { + readonly msg: ReadonlyArray; + readonly fee: StdFee; + readonly signatures: ReadonlyArray; + readonly memo: string | undefined; +} + +export interface Msg { + type: string; + // TODO: make better union type + readonly value: MsgSend; +} + +export interface MsgSend { + /** Bech32 account address */ + readonly from_address: string; + /** Bech32 account address */ + readonly to_address: string; + readonly amount: ReadonlyArray; +} + + +export interface StdFee { + readonly amount: ReadonlyArray; + readonly gas: string; +} + +export interface Coin { + denom: string; + amount: string; +} + +export interface StdSignature { + pub_key: PubKey; + signature: string; +} + +export interface PubKey { + /** Amino registered name, e.g. `"tendermint/PubKeySecp256k1"` */ + type: string; + /** Base64-encoded key bytes */ + value: string; +} + +export type AminoTx = Tx & { readonly value: StdTx }; + +export function isAminoStdTx(txValue: unknown): txValue is amino.StdTx { const { memo, msg, fee, signatures } = txValue as amino.StdTx; return ( typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index e41597e4..654dccc3 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -1,8 +1,47 @@ import amino from "@tendermint/amino-js"; -export declare type AminoTx = amino.Tx & { - readonly value: amino.StdTx; +export interface Tx { + type: string; + value: any; +} +export interface StdTx { + readonly msg: ReadonlyArray; + readonly fee: StdFee; + readonly signatures: ReadonlyArray; + readonly memo: string | undefined; +} +export interface Msg { + type: string; + readonly value: MsgSend; +} +export interface MsgSend { + /** Bech32 account address */ + readonly from_address: string; + /** Bech32 account address */ + readonly to_address: string; + readonly amount: ReadonlyArray; +} +export interface StdFee { + readonly amount: ReadonlyArray; + readonly gas: string; +} +export interface Coin { + denom: string; + amount: string; +} +export interface StdSignature { + pub_key: PubKey; + signature: string; +} +export interface PubKey { + /** Amino registered name, e.g. `"tendermint/PubKeySecp256k1"` */ + type: string; + /** Base64-encoded key bytes */ + value: string; +} +export declare type AminoTx = Tx & { + readonly value: StdTx; }; -export declare function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx; +export declare function isAminoStdTx(txValue: unknown): txValue is amino.StdTx; export interface TokenInfo { readonly denom: string; readonly ticker: string; From 8b262e6b127dc517e7984e69f2ea35a3980d1272 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 17:11:44 +0100 Subject: [PATCH 02/16] Update imports from bcp to sdk --- packages/bcp/src/cosmwasmconnection.ts | 6 +++--- packages/bcp/src/decode.ts | 4 ++-- packages/bcp/src/encode.ts | 6 +++--- packages/bcp/src/types.ts | 4 ++-- packages/bcp/types/cosmwasmconnection.d.ts | 4 ++-- packages/bcp/types/encode.d.ts | 6 +++--- packages/bcp/types/types.d.ts | 4 ++-- packages/sdk/src/index.ts | 3 ++- packages/sdk/types/index.d.ts | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index dd288a7d..a87e91a0 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { RestClient, TokenInfo, TxsResponse } from "@cosmwasm/sdk"; +import { RestClient, TxsResponse, types } from "@cosmwasm/sdk"; import { Account, AccountQuery, @@ -68,7 +68,7 @@ function buildQueryString({ return components.filter(Boolean).join("&"); } -export type TokenConfiguration = readonly (TokenInfo & { readonly name: string })[]; +export type TokenConfiguration = readonly (types.TokenInfo & { readonly name: string })[]; export class CosmWasmConnection implements BlockchainConnection { // we must know prefix and tokens a priori to understand the chain @@ -90,7 +90,7 @@ export class CosmWasmConnection implements BlockchainConnection { private readonly restClient: RestClient; private readonly chainData: ChainData; private readonly _prefix: CosmosBech32Prefix; - private readonly tokenInfo: readonly TokenInfo[]; + private readonly tokenInfo: readonly types.TokenInfo[]; // these are derived from arguments (cached for use in multiple functions) private readonly primaryToken: Token; diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index f06b1e49..c3323e97 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -1,4 +1,4 @@ -import { coinToDecimal, isAminoStdTx, TxsResponse } from "@cosmwasm/sdk"; +import { coinToDecimal, TxsResponse, types } from "@cosmwasm/sdk"; import { Address, Algorithm, @@ -96,7 +96,7 @@ export function parseFee(fee: amino.StdFee, tokens: TokenInfos): Fee { export function parseTx(tx: amino.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { const txValue = tx.value; - if (!isAminoStdTx(txValue)) { + if (!types.isAminoStdTx(txValue)) { throw new Error("Only Amino StdTx is supported"); } if (txValue.msg.length !== 1) { diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index 5fb4b842..5da2da07 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { AminoTx, decimalToCoin } from "@cosmwasm/sdk"; +import { decimalToCoin, types } from "@cosmwasm/sdk"; import { Algorithm, Amount, @@ -67,7 +67,7 @@ export function encodeFullSignature(fullSignature: FullSignature): amino.StdSign }; } -export function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): AminoTx { +export function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): types.AminoTx { if (!isSendTransaction(tx)) { throw new Error("Received transaction of unsupported kind"); } @@ -96,7 +96,7 @@ export function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): Am }; } -export function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): AminoTx { +export function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): types.AminoTx { const built = buildUnsignedTx(tx.transaction, tokens); return { ...built, diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index 07003e90..eb90b68f 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -1,7 +1,7 @@ -import { TokenInfo } from "@cosmwasm/sdk"; +import { types } from "@cosmwasm/sdk"; import { Nonce } from "@iov/bcp"; -export type TokenInfos = ReadonlyArray; +export type TokenInfos = ReadonlyArray; // tslint:disable-next-line:no-bitwise const maxAcct = 1 << 23; diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index 4a8a3d83..b84557df 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -1,4 +1,4 @@ -import { TokenInfo } from "@cosmwasm/sdk"; +import { types } from "@cosmwasm/sdk"; import { Account, AccountQuery, @@ -22,7 +22,7 @@ import { } from "@iov/bcp"; import { Stream } from "xstream"; import { CosmosBech32Prefix } from "./address"; -export declare type TokenConfiguration = readonly (TokenInfo & { +export declare type TokenConfiguration = readonly (types.TokenInfo & { readonly name: string; })[]; export declare class CosmWasmConnection implements BlockchainConnection { diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index 1068443a..99feed23 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -1,4 +1,4 @@ -import { AminoTx } from "@cosmwasm/sdk"; +import { types } from "@cosmwasm/sdk"; import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTransaction } from "@iov/bcp"; import amino from "@tendermint/amino-js"; import { TokenInfos } from "./types"; @@ -6,5 +6,5 @@ export declare function encodePubkey(pubkey: PubkeyBundle): amino.PubKey; export declare function encodeAmount(amount: Amount, tokens: TokenInfos): amino.Coin; export declare function encodeFee(fee: Fee, tokens: TokenInfos): amino.StdFee; export declare function encodeFullSignature(fullSignature: FullSignature): amino.StdSignature; -export declare function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): AminoTx; -export declare function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): AminoTx; +export declare function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): types.AminoTx; +export declare function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): types.AminoTx; diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index 8bdcbf3b..b419ece0 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -1,6 +1,6 @@ -import { TokenInfo } from "@cosmwasm/sdk"; +import { types } from "@cosmwasm/sdk"; import { Nonce } from "@iov/bcp"; -export declare type TokenInfos = ReadonlyArray; +export declare type TokenInfos = ReadonlyArray; export interface NonceInfo { readonly account_number: string; readonly sequence: string; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 888fb6c3..e664c4a5 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,4 +1,5 @@ export { coinToDecimal } from "./decoding"; export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; -export { AminoTx, isAminoStdTx, TokenInfo } from "./types"; +// export { AminoTx, isAminoStdTx, TokenInfo } from "./types"; +export { default as types } from "./types"; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index 888fb6c3..d85cdb7b 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,4 +1,4 @@ export { coinToDecimal } from "./decoding"; export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; -export { AminoTx, isAminoStdTx, TokenInfo } from "./types"; +export { default as types } from "./types"; From 62b5c88c58739b39e225430862ee4a677898c920 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 17:19:48 +0100 Subject: [PATCH 03/16] bcp builds properly --- packages/bcp/src/decode.spec.ts | 6 +++--- packages/bcp/src/decode.ts | 17 ++++++++--------- packages/bcp/src/encode.ts | 9 ++++----- packages/bcp/types/decode.d.ts | 15 +++++++-------- packages/bcp/types/encode.d.ts | 9 ++++----- packages/sdk/src/types.ts | 7 ++----- packages/sdk/types/types.d.ts | 3 +-- 7 files changed, 29 insertions(+), 37 deletions(-) diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index 3c825555..90d9fc66 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/camelcase */ import { Address, Algorithm, TokenTicker } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; -import amino from "@tendermint/amino-js"; +import { types } from "@cosmwasm/sdk"; import { decodeAmount, @@ -113,7 +113,7 @@ describe("decode", () => { describe("decodeAmount", () => { it("works", () => { - const amount: amino.Coin = { + const amount: types.Coin = { denom: "uatom", amount: "11657995", }; @@ -123,7 +123,7 @@ describe("decode", () => { describe("parseMsg", () => { it("works", () => { - const msg: amino.Msg = { + const msg: types.Msg = { type: "cosmos-sdk/MsgSend", value: { from_address: "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r", diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index c3323e97..6fa4ead7 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -18,13 +18,12 @@ import { UnsignedTransaction, } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; -import amino from "@tendermint/amino-js"; import { TokenInfos } from "./types"; const { fromBase64 } = Encoding; -export function decodePubkey(pubkey: amino.PubKey): PubkeyBundle { +export function decodePubkey(pubkey: types.PubKey): PubkeyBundle { switch (pubkey.type) { // https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/secp256k1/secp256k1.go#L23 case "tendermint/PubKeySecp256k1": @@ -47,7 +46,7 @@ export function decodeSignature(signature: string): SignatureBytes { return fromBase64(signature) as SignatureBytes; } -export function decodeFullSignature(signature: amino.StdSignature, nonce: number): FullSignature { +export function decodeFullSignature(signature: types.StdSignature, nonce: number): FullSignature { return { nonce: nonce as Nonce, pubkey: decodePubkey(signature.pub_key), @@ -55,7 +54,7 @@ export function decodeFullSignature(signature: amino.StdSignature, nonce: number }; } -export function decodeAmount(tokens: TokenInfos, coin: amino.Coin): Amount { +export function decodeAmount(tokens: TokenInfos, coin: types.Coin): Amount { const [value, ticker] = coinToDecimal(tokens, coin); return { quantity: value.atomics, @@ -64,14 +63,14 @@ export function decodeAmount(tokens: TokenInfos, coin: amino.Coin): Amount { }; } -export function parseMsg(msg: amino.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction { +export function parseMsg(msg: types.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction { if (msg.type !== "cosmos-sdk/MsgSend") { throw new Error("Unknown message type in transaction"); } - if (!(msg.value as amino.MsgSend).from_address) { + if (!(msg.value as types.MsgSend).from_address) { throw new Error("Only MsgSend is supported"); } - const msgValue = msg.value as amino.MsgSend; + const msgValue = msg.value as types.MsgSend; if (msgValue.amount.length !== 1) { throw new Error("Only MsgSend with one amount is supported"); } @@ -84,7 +83,7 @@ export function parseMsg(msg: amino.Msg, chainId: ChainId, tokens: TokenInfos): }; } -export function parseFee(fee: amino.StdFee, tokens: TokenInfos): Fee { +export function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee { if (fee.amount.length !== 1) { throw new Error("Only fee with one amount is supported"); } @@ -94,7 +93,7 @@ export function parseFee(fee: amino.StdFee, tokens: TokenInfos): Fee { }; } -export function parseTx(tx: amino.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { +export function parseTx(tx: types.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { const txValue = tx.value; if (!types.isAminoStdTx(txValue)) { throw new Error("Only Amino StdTx is supported"); diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index 5da2da07..d8793ad0 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -12,13 +12,12 @@ import { } from "@iov/bcp"; import { Secp256k1 } from "@iov/crypto"; import { Decimal, Encoding } from "@iov/encoding"; -import amino from "@tendermint/amino-js"; import { TokenInfos } from "./types"; const { toBase64 } = Encoding; -export function encodePubkey(pubkey: PubkeyBundle): amino.PubKey { +export function encodePubkey(pubkey: PubkeyBundle): types.PubKey { switch (pubkey.algo) { case Algorithm.Secp256k1: return { @@ -35,7 +34,7 @@ export function encodePubkey(pubkey: PubkeyBundle): amino.PubKey { } } -export function encodeAmount(amount: Amount, tokens: TokenInfos): amino.Coin { +export function encodeAmount(amount: Amount, tokens: TokenInfos): types.Coin { return decimalToCoin( tokens, Decimal.fromAtomics(amount.quantity, amount.fractionalDigits), @@ -43,7 +42,7 @@ export function encodeAmount(amount: Amount, tokens: TokenInfos): amino.Coin { ); } -export function encodeFee(fee: Fee, tokens: TokenInfos): amino.StdFee { +export function encodeFee(fee: Fee, tokens: TokenInfos): types.StdFee { if (fee.tokens === undefined) { throw new Error("Cannot encode fee without tokens"); } @@ -56,7 +55,7 @@ export function encodeFee(fee: Fee, tokens: TokenInfos): amino.StdFee { }; } -export function encodeFullSignature(fullSignature: FullSignature): amino.StdSignature { +export function encodeFullSignature(fullSignature: FullSignature): types.StdSignature { return { pub_key: { type: "tendermint/PubKeySecp256k1", diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index 68122fd9..af8f43be 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -1,4 +1,4 @@ -import { TxsResponse } from "@cosmwasm/sdk"; +import { TxsResponse, types } from "@cosmwasm/sdk"; import { Amount, ChainId, @@ -12,16 +12,15 @@ import { SignedTransaction, UnsignedTransaction, } from "@iov/bcp"; -import amino from "@tendermint/amino-js"; import { TokenInfos } from "./types"; -export declare function decodePubkey(pubkey: amino.PubKey): PubkeyBundle; +export declare function decodePubkey(pubkey: types.PubKey): PubkeyBundle; export declare function decodeSignature(signature: string): SignatureBytes; -export declare function decodeFullSignature(signature: amino.StdSignature, nonce: number): FullSignature; -export declare function decodeAmount(tokens: TokenInfos, coin: amino.Coin): Amount; -export declare function parseMsg(msg: amino.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction; -export declare function parseFee(fee: amino.StdFee, tokens: TokenInfos): Fee; +export declare function decodeFullSignature(signature: types.StdSignature, nonce: number): FullSignature; +export declare function decodeAmount(tokens: TokenInfos, coin: types.Coin): Amount; +export declare function parseMsg(msg: types.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction; +export declare function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee; export declare function parseTx( - tx: amino.Tx, + tx: types.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos, diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index 99feed23..72e10ba8 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -1,10 +1,9 @@ import { types } from "@cosmwasm/sdk"; import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTransaction } from "@iov/bcp"; -import amino from "@tendermint/amino-js"; import { TokenInfos } from "./types"; -export declare function encodePubkey(pubkey: PubkeyBundle): amino.PubKey; -export declare function encodeAmount(amount: Amount, tokens: TokenInfos): amino.Coin; -export declare function encodeFee(fee: Fee, tokens: TokenInfos): amino.StdFee; -export declare function encodeFullSignature(fullSignature: FullSignature): amino.StdSignature; +export declare function encodePubkey(pubkey: PubkeyBundle): types.PubKey; +export declare function encodeAmount(amount: Amount, tokens: TokenInfos): types.Coin; +export declare function encodeFee(fee: Fee, tokens: TokenInfos): types.StdFee; +export declare function encodeFullSignature(fullSignature: FullSignature): types.StdSignature; export declare function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): types.AminoTx; export declare function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): types.AminoTx; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 9db452f5..899a6719 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -1,5 +1,3 @@ -import amino from "@tendermint/amino-js"; - // We will move all needed *interfaces* from amino-js here // This means bcp can just import them from here (if needed at all) export interface Tx { @@ -28,7 +26,6 @@ export interface MsgSend { readonly amount: ReadonlyArray; } - export interface StdFee { readonly amount: ReadonlyArray; readonly gas: string; @@ -53,8 +50,8 @@ export interface PubKey { export type AminoTx = Tx & { readonly value: StdTx }; -export function isAminoStdTx(txValue: unknown): txValue is amino.StdTx { - const { memo, msg, fee, signatures } = txValue as amino.StdTx; +export function isAminoStdTx(txValue: unknown): txValue is StdTx { + const { memo, msg, fee, signatures } = txValue as StdTx; return ( typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) ); diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 654dccc3..ee1adefa 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -1,4 +1,3 @@ -import amino from "@tendermint/amino-js"; export interface Tx { type: string; value: any; @@ -41,7 +40,7 @@ export interface PubKey { export declare type AminoTx = Tx & { readonly value: StdTx; }; -export declare function isAminoStdTx(txValue: unknown): txValue is amino.StdTx; +export declare function isAminoStdTx(txValue: unknown): txValue is StdTx; export interface TokenInfo { readonly denom: string; readonly ticker: string; From f5fbb02ba4a18ec57829a763bbcb461c9b44b435 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 18:18:21 +0100 Subject: [PATCH 04/16] Improve parsing, two types of pubkey decoding --- packages/bcp/src/address.spec.ts | 2 +- packages/bcp/src/address.ts | 32 +++++++++++++++---------- packages/bcp/src/cosmwasmconnection.ts | 8 +------ packages/bcp/src/decode.ts | 4 ++-- packages/bcp/types/address.d.ts | 6 ++--- packages/sdk/src/index.ts | 5 ++-- packages/sdk/src/restclient.ts | 6 ++--- packages/sdk/src/types.ts | 33 +++++++++++++++++--------- packages/sdk/types/index.d.ts | 2 +- packages/sdk/types/restclient.d.ts | 5 ++-- packages/sdk/types/types.d.ts | 29 +++++++++++++--------- 11 files changed, 76 insertions(+), 56 deletions(-) diff --git a/packages/bcp/src/address.spec.ts b/packages/bcp/src/address.spec.ts index 1d138206..21fcb192 100644 --- a/packages/bcp/src/address.spec.ts +++ b/packages/bcp/src/address.spec.ts @@ -33,8 +33,8 @@ describe("address", () => { expect( decodeCosmosPubkey("cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5"), ).toEqual({ - prefix: "cosmospub", data: fromBase64("A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"), + algo: Algorithm.Secp256k1, }); }); }); diff --git a/packages/bcp/src/address.ts b/packages/bcp/src/address.ts index d36a50d4..fa5e7ff2 100644 --- a/packages/bcp/src/address.ts +++ b/packages/bcp/src/address.ts @@ -1,4 +1,4 @@ -import { Address, Algorithm, PubkeyBundle } from "@iov/bcp"; +import { Address, Algorithm, PubkeyBundle, PubkeyBytes } from "@iov/bcp"; import { Ripemd160, Secp256k1, Sha256 } from "@iov/crypto"; import { Bech32, Encoding } from "@iov/encoding"; import equal from "fast-deep-equal"; @@ -8,7 +8,10 @@ export type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmo export type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; // As discussed in https://github.com/binance-chain/javascript-sdk/issues/163 -const pubkeyAminoPrefix = Encoding.fromHex("eb5ae98721"); +// Prefixes listed here: https://github.com/tendermint/tendermint/blob/d419fffe18531317c28c29a292ad7d253f6cafdf/docs/spec/blockchain/encoding.md#public-key-cryptography +const pubkeyAminoPrefixSecp256k1 = Encoding.fromHex("eb5ae98721"); +const pubkeyAminoPrefixEd25519 = Encoding.fromHex("1624de64"); +const pubkeyAminoPrefixLength = pubkeyAminoPrefixSecp256k1.length; function isCosmosAddressBech32Prefix(prefix: string): prefix is CosmosAddressBech32Prefix { return ["cosmos", "cosmosvalcons", "cosmosvaloper"].includes(prefix); @@ -33,22 +36,27 @@ export function decodeCosmosAddress( export function decodeCosmosPubkey( encodedPubkey: string, -): { readonly prefix: CosmosPubkeyBech32Prefix; readonly data: Uint8Array } { +): { readonly algo: Algorithm; readonly data: PubkeyBytes } { const { prefix, data } = Bech32.decode(encodedPubkey); if (!isCosmosPubkeyBech32Prefix(prefix)) { throw new Error(`Invalid bech32 prefix. Must be one of cosmos, cosmosvalcons, or cosmosvaloper.`); } - if (!equal(data.slice(0, pubkeyAminoPrefix.length), pubkeyAminoPrefix)) { - throw new Error("Pubkey does not have the expected amino prefix " + Encoding.toHex(pubkeyAminoPrefix)); + const aminoPrefix = data.slice(0, pubkeyAminoPrefixLength); + const rest = data.slice(pubkeyAminoPrefixLength); + if (equal(aminoPrefix, pubkeyAminoPrefixSecp256k1)) { + if (rest.length !== 33) { + throw new Error("Invalid rest data length. Expected 33 bytes (compressed secp256k1 pubkey)."); + } + return { algo: Algorithm.Secp256k1, data: rest as PubkeyBytes }; + } else if (equal(aminoPrefix, pubkeyAminoPrefixEd25519)) { + if (rest.length !== 32) { + throw new Error("Invalid rest data length. Expected 32 bytes (ed25519 pubkey)."); + } + return { algo: Algorithm.Ed25519, data: rest as PubkeyBytes }; + } else { + throw new Error("Unsupported Pubkey type. Amino prefix: " + Encoding.toHex(aminoPrefix)); } - - const rest = data.slice(pubkeyAminoPrefix.length); - if (rest.length !== 33) { - throw new Error("Invalid rest data length. Expected 33 bytes (compressed secp256k1 pubkey)."); - } - - return { prefix: prefix, data: rest }; } export function isValidAddress(address: string): boolean { diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index a87e91a0..d44d143e 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -151,13 +151,7 @@ export class CosmWasmConnection implements BlockchainConnection { this.tokenInfo.find(token => token.denom === denom), ); - const pubkey = !account.public_key - ? undefined - : { - algo: Algorithm.Secp256k1, - // amino-js has wrong (outdated) types - data: decodeCosmosPubkey(account.public_key as any).data as PubkeyBytes, - }; + const pubkey = !account.public_key ? undefined : decodeCosmosPubkey(account.public_key); return { address: address, balance: supportedCoins.map(coin => decodeAmount(this.tokenInfo, coin)), diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 6fa4ead7..a1db9910 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -1,4 +1,4 @@ -import { coinToDecimal, TxsResponse, types } from "@cosmwasm/sdk"; +import { coinToDecimal, isAminoStdTx, TxsResponse, types } from "@cosmwasm/sdk"; import { Address, Algorithm, @@ -95,7 +95,7 @@ export function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee { export function parseTx(tx: types.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { const txValue = tx.value; - if (!types.isAminoStdTx(txValue)) { + if (!isAminoStdTx(txValue)) { throw new Error("Only Amino StdTx is supported"); } if (txValue.msg.length !== 1) { diff --git a/packages/bcp/types/address.d.ts b/packages/bcp/types/address.d.ts index 252add6e..fd3a99c3 100644 --- a/packages/bcp/types/address.d.ts +++ b/packages/bcp/types/address.d.ts @@ -1,4 +1,4 @@ -import { Address, PubkeyBundle } from "@iov/bcp"; +import { Address, Algorithm, PubkeyBundle, PubkeyBytes } from "@iov/bcp"; export declare type CosmosAddressBech32Prefix = "cosmos" | "cosmosvalcons" | "cosmosvaloper"; export declare type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmosvaloperpub"; export declare type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; @@ -11,8 +11,8 @@ export declare function decodeCosmosAddress( export declare function decodeCosmosPubkey( encodedPubkey: string, ): { - readonly prefix: CosmosPubkeyBech32Prefix; - readonly data: Uint8Array; + readonly algo: Algorithm; + readonly data: PubkeyBytes; }; export declare function isValidAddress(address: string): boolean; export declare function pubkeyToAddress(pubkey: PubkeyBundle, prefix: CosmosBech32Prefix): Address; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index e664c4a5..21c4f489 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,5 +1,6 @@ export { coinToDecimal } from "./decoding"; export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; -// export { AminoTx, isAminoStdTx, TokenInfo } from "./types"; -export { default as types } from "./types"; +// types.X are all the types we re-export +// Note: this doesn't work for functions, just typescript types, so we must explicitly re-export functions +export { default as types, isAminoStdTx } from "./types"; diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 3bd74f70..bd9e4eed 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -1,7 +1,7 @@ -import amino, { unmarshalTx } from "@tendermint/amino-js"; +import { unmarshalTx } from "@tendermint/amino-js"; import axios, { AxiosInstance } from "axios"; -import { AminoTx } from "./types"; +import { AminoTx, BaseAccount } from "./types"; interface NodeInfo { readonly network: string; @@ -35,7 +35,7 @@ interface BlocksResponse { interface AuthAccountsResponse { readonly result: { - readonly value: amino.BaseAccount; + readonly value: BaseAccount; }; } diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 899a6719..b05cd289 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -1,8 +1,9 @@ // We will move all needed *interfaces* from amino-js here // This means bcp can just import them from here (if needed at all) export interface Tx { - type: string; - value: any; + readonly type: string; + // TODO + readonly value: any; } export interface StdTx { @@ -13,7 +14,7 @@ export interface StdTx { } export interface Msg { - type: string; + readonly type: string; // TODO: make better union type readonly value: MsgSend; } @@ -32,20 +33,30 @@ export interface StdFee { } export interface Coin { - denom: string; - amount: string; + readonly denom: string; + readonly amount: string; } export interface StdSignature { - pub_key: PubKey; - signature: string; + readonly pub_key: PubKey; + readonly signature: string; } export interface PubKey { - /** Amino registered name, e.g. `"tendermint/PubKeySecp256k1"` */ - type: string; - /** Base64-encoded key bytes */ - value: string; + readonly type: string; + readonly value: string; +} + +// AccountPubKey is bech32-encoded amino-binary encoded PubKey interface. oof. +export type AccountPubKey = string; + +export interface BaseAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: ReadonlyArray; + readonly public_key: AccountPubKey; + readonly account_number: string; + readonly sequence: string; } export type AminoTx = Tx & { readonly value: StdTx }; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index d85cdb7b..e96d3116 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,4 +1,4 @@ export { coinToDecimal } from "./decoding"; export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; -export { default as types } from "./types"; +export { default as types, isAminoStdTx } from "./types"; diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index ace2f82b..d952d480 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -1,5 +1,4 @@ -import amino from "@tendermint/amino-js"; -import { AminoTx } from "./types"; +import { AminoTx, BaseAccount } from "./types"; interface NodeInfo { readonly network: string; } @@ -27,7 +26,7 @@ interface BlocksResponse { } interface AuthAccountsResponse { readonly result: { - readonly value: amino.BaseAccount; + readonly value: BaseAccount; }; } export interface TxsResponse { diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index ee1adefa..26602e06 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -1,6 +1,6 @@ export interface Tx { - type: string; - value: any; + readonly type: string; + readonly value: any; } export interface StdTx { readonly msg: ReadonlyArray; @@ -9,7 +9,7 @@ export interface StdTx { readonly memo: string | undefined; } export interface Msg { - type: string; + readonly type: string; readonly value: MsgSend; } export interface MsgSend { @@ -24,18 +24,25 @@ export interface StdFee { readonly gas: string; } export interface Coin { - denom: string; - amount: string; + readonly denom: string; + readonly amount: string; } export interface StdSignature { - pub_key: PubKey; - signature: string; + readonly pub_key: PubKey; + readonly signature: string; } export interface PubKey { - /** Amino registered name, e.g. `"tendermint/PubKeySecp256k1"` */ - type: string; - /** Base64-encoded key bytes */ - value: string; + readonly type: string; + readonly value: string; +} +export declare type AccountPubKey = string; +export interface BaseAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: ReadonlyArray; + readonly public_key: AccountPubKey; + readonly account_number: string; + readonly sequence: string; } export declare type AminoTx = Tx & { readonly value: StdTx; From d2030f4f51b731ac2e5c647e59e22ef04b02a41e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 18:20:29 +0100 Subject: [PATCH 05/16] Add sample tx decode data --- packages/bcp/src/decode.spec.ts | 176 ++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index 90d9fc66..54baa8ae 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -181,3 +181,179 @@ describe("decode", () => { }); }); }); + +/* + +Some output from sample rest queries: + +$ wasmcli tx send $(wasmcli keys show validator -a) $(wasmcli keys show fred -a) 98765stake -y +{ + "height": "4", + "txhash": "8A4613D62884EF8BB9BCCDDA3833D560701908BF17FE82A570EECCBACEF94A91", + "raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos16qu479grzwanyzav6xvtzncgdjkwhqw7vy2pje\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k\"},{\"key\":\"amount\",\"value\":\"98765stake\"}]}]}]", + "logs": [ + { + "msg_index": 0, + "log": "", + "events": [ + { + "type": "message", + "attributes": [ + { + "key": "action", + "value": "send" + }, + { + "key": "sender", + "value": "cosmos16qu479grzwanyzav6xvtzncgdjkwhqw7vy2pje" + }, + { + "key": "module", + "value": "bank" + } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "recipient", + "value": "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k" + }, + { + "key": "amount", + "value": "98765stake" + } + ] + } + ] + } + ], + "gas_wanted": "200000", + "gas_used": "53254" +} + + +$ wasmcli query tx 8A4613D62884EF8BB9BCCDDA3833D560701908BF17FE82A570EECCBACEF94A91 +{ + "height": "4", + "txhash": "8A4613D62884EF8BB9BCCDDA3833D560701908BF17FE82A570EECCBACEF94A91", + "raw_log": "[{\"msg_index\":0,\"log\":\"\",\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"send\"},{\"key\":\"sender\",\"value\":\"cosmos16qu479grzwanyzav6xvtzncgdjkwhqw7vy2pje\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k\"},{\"key\":\"amount\",\"value\":\"98765stake\"}]}]}]", + "logs": [ + { + "msg_index": 0, + "log": "", + "events": [ + { + "type": "message", + "attributes": [ + { + "key": "action", + "value": "send" + }, + { + "key": "sender", + "value": "cosmos16qu479grzwanyzav6xvtzncgdjkwhqw7vy2pje" + }, + { + "key": "module", + "value": "bank" + } + ] + }, + { + "type": "transfer", + "attributes": [ + { + "key": "recipient", + "value": "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k" + }, + { + "key": "amount", + "value": "98765stake" + } + ] + } + ] + } + ], + "gas_wanted": "200000", + "gas_used": "53254", + "tx": { + "type": "cosmos-sdk/StdTx", + "value": { + "msg": [ + { + "type": "cosmos-sdk/MsgSend", + "value": { + "from_address": "cosmos16qu479grzwanyzav6xvtzncgdjkwhqw7vy2pje", + "to_address": "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k", + "amount": [ + { + "denom": "stake", + "amount": "98765" + } + ] + } + } + ], + "fee": { + "amount": [], + "gas": "200000" + }, + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A11L8EitFnA6YsZ2QSnbMNmK+qI2kxyevDtSfhPqOwcp" + }, + "signature": "qCeKoqZeaL0LThKrUXHLgu72jwTiF+DseSBjcKHtcONE0kIdybwYJpuYg3Jj71hmfync+daHNdqgJlPRma0pPA==" + } + ], + "memo": "" + } + }, + "timestamp": "2020-02-03T17:06:58Z" +} + + +$ wasmcli query account $(wasmcli keys show fred -a) +{ + "type": "cosmos-sdk/Account", + "value": { + "address": "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k", + "coins": [ + { + "denom": "stake", + "amount": "98765" + } + ], + "public_key": "", + "account_number": 7, + "sequence": 0 + } +} + + +$ wasmcli query account $(wasmcli keys show validator -a) +{ + "type": "cosmos-sdk/Account", + "value": { + "address": "cosmos16qu479grzwanyzav6xvtzncgdjkwhqw7vy2pje", + "coins": [ + { + "denom": "stake", + "amount": "899901235" + }, + { + "denom": "validatortoken", + "amount": "1000000000" + } + ], + "public_key": "cosmospub1addwnpepqdw5huzg45t8qwnzcemyz2wmxrvc474zx6f3e84u8df8uyl28vrjjnp9v4p", + "account_number": 3, + "sequence": 2 + } +} + + */ From 1491b13e83adbf12e3228c410ede1de16f871103 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 18:25:05 +0100 Subject: [PATCH 06/16] Format and lint --- packages/bcp/src/cosmwasmconnection.ts | 2 -- packages/bcp/src/decode.spec.ts | 2 +- packages/sdk/src/types.ts | 4 ++-- packages/sdk/types/types.d.ts | 4 ++-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index d44d143e..f83b1e8f 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -4,7 +4,6 @@ import { Account, AccountQuery, AddressQuery, - Algorithm, BlockchainConnection, BlockHeader, BlockId, @@ -20,7 +19,6 @@ import { Nonce, PostableBytes, PostTxResponse, - PubkeyBytes, PubkeyQuery, Token, TokenTicker, diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index 54baa8ae..fbe6becb 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { types } from "@cosmwasm/sdk"; import { Address, Algorithm, TokenTicker } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; -import { types } from "@cosmwasm/sdk"; import { decodeAmount, diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index b05cd289..a99179df 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -3,7 +3,7 @@ export interface Tx { readonly type: string; // TODO - readonly value: any; + readonly value: unknown; } export interface StdTx { @@ -16,7 +16,7 @@ export interface StdTx { export interface Msg { readonly type: string; // TODO: make better union type - readonly value: MsgSend; + readonly value: MsgSend | unknown; } export interface MsgSend { diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 26602e06..1f0af1a5 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -1,6 +1,6 @@ export interface Tx { readonly type: string; - readonly value: any; + readonly value: unknown; } export interface StdTx { readonly msg: ReadonlyArray; @@ -10,7 +10,7 @@ export interface StdTx { } export interface Msg { readonly type: string; - readonly value: MsgSend; + readonly value: MsgSend | unknown; } export interface MsgSend { /** Bech32 account address */ From 80be33b621e247bea8ffc5c6b9b76d8deafc04b9 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 18:47:59 +0100 Subject: [PATCH 07/16] Fix types re-export --- packages/bcp/src/decode.ts | 4 ++-- packages/sdk/src/index.ts | 6 +++--- packages/sdk/types/index.d.ts | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index a1db9910..6fa4ead7 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -1,4 +1,4 @@ -import { coinToDecimal, isAminoStdTx, TxsResponse, types } from "@cosmwasm/sdk"; +import { coinToDecimal, TxsResponse, types } from "@cosmwasm/sdk"; import { Address, Algorithm, @@ -95,7 +95,7 @@ export function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee { export function parseTx(tx: types.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { const txValue = tx.value; - if (!isAminoStdTx(txValue)) { + if (!types.isAminoStdTx(txValue)) { throw new Error("Only Amino StdTx is supported"); } if (txValue.msg.length !== 1) { diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 21c4f489..a38d5033 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,6 +1,6 @@ export { coinToDecimal } from "./decoding"; export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; -// types.X are all the types we re-export -// Note: this doesn't work for functions, just typescript types, so we must explicitly re-export functions -export { default as types, isAminoStdTx } from "./types"; + +import * as types from "./types"; +export { types }; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index e96d3116..13b0904b 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,4 +1,6 @@ export { coinToDecimal } from "./decoding"; export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; -export { default as types, isAminoStdTx } from "./types"; +import * as types from "./types"; +export { types }; +export { isAminoStdTx } from "./types"; From e50733eb71b13d5923c451865f228d3da89ba226 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 19:44:38 +0100 Subject: [PATCH 08/16] RestClient posts with JSON-encoded StdTx --- packages/sdk/src/restclient.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index bd9e4eed..f715393e 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -1,7 +1,9 @@ -import { unmarshalTx } from "@tendermint/amino-js"; +import { Encoding } from "@iov/encoding"; import axios, { AxiosInstance } from "axios"; -import { AminoTx, BaseAccount } from "./types"; +import { AminoTx, BaseAccount, isAminoStdTx } from "./types"; + +const { fromUtf8 } = Encoding; interface NodeInfo { readonly network: string; @@ -157,10 +159,15 @@ export class RestClient { return responseData as TxsResponse; } + // tx must be JSON encoded StdTx (no wrapper) public async postTx(tx: Uint8Array): Promise { - const unmarshalled = unmarshalTx(tx, true); + // TODO: check this is StdTx + const decoded = JSON.parse(fromUtf8(tx)); + if (!isAminoStdTx(decoded)) { + throw new Error("Must be json encoded StdTx"); + } const params = { - tx: unmarshalled.value, + tx: decoded, mode: this.mode, }; const responseData = await this.post("/txs", params); From 7e7f18a1b3d126ee869c87a15b4313631069247f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 19:59:20 +0100 Subject: [PATCH 09/16] Remove all use of amino, identifier is disabled --- packages/bcp/package.json | 1 - packages/bcp/src/cosmwasmcodec.spec.ts | 6 +++--- packages/bcp/src/cosmwasmcodec.ts | 18 +++++++++++------- packages/bcp/src/decode.spec.ts | 2 +- packages/bcp/src/decode.ts | 5 ++--- packages/bcp/types/cosmwasmcodec.d.ts | 2 +- packages/bcp/types/decode.d.ts | 2 +- packages/sdk/package.json | 1 - packages/sdk/src/decoding.ts | 11 +++++++++++ packages/sdk/src/encoding.ts | 8 ++++++++ packages/sdk/src/index.ts | 2 ++ packages/sdk/src/types.ts | 18 +++++++++--------- packages/sdk/types/decoding.d.ts | 2 ++ packages/sdk/types/encoding.d.ts | 2 ++ packages/sdk/types/index.d.ts | 2 ++ 15 files changed, 55 insertions(+), 27 deletions(-) diff --git a/packages/bcp/package.json b/packages/bcp/package.json index ae98612d..1cc926a9 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -43,7 +43,6 @@ "@iov/crypto": "^2.0.0-alpha.7", "@iov/encoding": "^2.0.0-alpha.7", "@iov/stream": "^2.0.0-alpha.7", - "@tendermint/amino-js": "^0.7.0-alpha.1", "fast-deep-equal": "^3.1.1", "readonly-date": "^1.0.0", "xstream": "^11.11.0" diff --git a/packages/bcp/src/cosmwasmcodec.spec.ts b/packages/bcp/src/cosmwasmcodec.spec.ts index 1829b936..405a2db8 100644 --- a/packages/bcp/src/cosmwasmcodec.spec.ts +++ b/packages/bcp/src/cosmwasmcodec.spec.ts @@ -19,7 +19,7 @@ describe("cosmWasmCodec", () => { expect(bytesToSign).toEqual(expected); }); - it("properly encodes transactions", () => { + xit("properly encodes transactions", () => { const encoded = cosmWasmCodec.bytesToPost(signedTxJson); expect(encoded).toEqual(signedTxBin); }); @@ -30,12 +30,12 @@ describe("cosmWasmCodec", () => { ); }); - it("properly decodes transactions", () => { + xit("properly decodes transactions", () => { const decoded = cosmWasmCodec.parseBytes(signedTxBin as PostableBytes, chainId, nonce); expect(decoded).toEqual(signedTxJson); }); - it("generates transaction id", () => { + xit("generates transaction id", () => { const id = cosmWasmCodec.identifier(signedTxJson); expect(id).toMatch(/^[0-9A-F]{64}$/); expect(id).toEqual(txId); diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index 30650ae0..e3745343 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -15,7 +15,7 @@ import { } from "@iov/bcp"; import { Sha256 } from "@iov/crypto"; import { Encoding } from "@iov/encoding"; -import { marshalTx, unmarshalTx } from "@tendermint/amino-js"; +import { unmarshalTx, marshalTx } from "@cosmwasm/sdk"; import { CosmosBech32Prefix, isValidAddress, pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; @@ -72,16 +72,20 @@ export class CosmWasmCodec implements TxCodec { }; } + // PostableBytes are JSON-encoded StdTx public bytesToPost(signed: SignedTransaction): PostableBytes { + // TODO: change this as well (return StdTx, not AminoTx)? const built = buildSignedTx(signed, this.tokens); - const bytes = marshalTx(built, true); - return bytes as PostableBytes; + return marshalTx(built.value) as PostableBytes; } - public identifier(signed: SignedTransaction): TransactionId { - const bytes = this.bytesToPost(signed); - const hash = new Sha256(bytes).digest(); - return toHex(hash).toUpperCase() as TransactionId; + // TODO: this needs some marshalling going on... + // Do we need to support this?? + public identifier(_signed: SignedTransaction): TransactionId { + throw new Error("Not yet implemented, requires amino encoding- talk to Ethan"); + // const bytes = this.bytesToPost(signed); + // const hash = new Sha256(bytes).digest(); + // return toHex(hash).toUpperCase() as TransactionId; } public parseBytes(bytes: PostableBytes, chainId: ChainId, nonce?: Nonce): SignedTransaction { diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index fbe6becb..c783b5a4 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -157,7 +157,7 @@ describe("decode", () => { describe("parseTx", () => { it("works", () => { - expect(parseTx(data.tx, chainId, nonce, defaultTokens)).toEqual(signedTxJson); + expect(parseTx(data.tx.value, chainId, nonce, defaultTokens)).toEqual(signedTxJson); }); }); diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 8239a9e2..0ae3cffd 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -102,8 +102,7 @@ export function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee { }; } -export function parseTx(tx: types.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { - const txValue = tx.value; +export function parseTx(txValue: types.StdTx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { if (!types.isAminoStdTx(txValue)) { throw new Error("Only Amino StdTx is supported"); } @@ -137,7 +136,7 @@ export function parseTxsResponse( ): ConfirmedAndSignedTransaction { const height = parseInt(response.height, 10); return { - ...parseTx(response.tx, chainId, nonce, tokens), + ...parseTx(response.tx.value, chainId, nonce, tokens), height: height, confirmations: currentHeight - height + 1, transactionId: response.txhash as TransactionId, diff --git a/packages/bcp/types/cosmwasmcodec.d.ts b/packages/bcp/types/cosmwasmcodec.d.ts index 1373ef99..9ce8a6f0 100644 --- a/packages/bcp/types/cosmwasmcodec.d.ts +++ b/packages/bcp/types/cosmwasmcodec.d.ts @@ -18,7 +18,7 @@ export declare class CosmWasmCodec implements TxCodec { constructor(prefix: CosmosBech32Prefix, tokens: TokenInfos); bytesToSign(unsigned: UnsignedTransaction, nonce: Nonce): SigningJob; bytesToPost(signed: SignedTransaction): PostableBytes; - identifier(signed: SignedTransaction): TransactionId; + identifier(_signed: SignedTransaction): TransactionId; parseBytes(bytes: PostableBytes, chainId: ChainId, nonce?: Nonce): SignedTransaction; identityToAddress(identity: Identity): Address; isValidAddress(address: string): boolean; diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index 60f9b198..2fb8dcab 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -22,7 +22,7 @@ export declare function decodeAmount(tokens: TokenInfos, coin: types.Coin): Amou export declare function parseMsg(msg: types.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction; export declare function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee; export declare function parseTx( - tx: types.Tx, + txValue: types.StdTx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos, diff --git a/packages/sdk/package.json b/packages/sdk/package.json index e3a7ccf6..ab71861e 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -39,7 +39,6 @@ }, "dependencies": { "@iov/encoding": "^2.0.0-alpha.7", - "@tendermint/amino-js": "^0.7.0-alpha.1", "axios": "^0.19.0" }, "devDependencies": { diff --git a/packages/sdk/src/decoding.ts b/packages/sdk/src/decoding.ts index e69de29b..11560d12 100644 --- a/packages/sdk/src/decoding.ts +++ b/packages/sdk/src/decoding.ts @@ -0,0 +1,11 @@ +import { Encoding } from "@iov/encoding"; + +import { isAminoStdTx, StdTx } from "./types"; + +export function unmarshalTx(data: Uint8Array): StdTx { + const decoded = JSON.parse(Encoding.fromUtf8(data)); + if (!isAminoStdTx(decoded)) { + throw new Error("Must be json encoded StdTx"); + } + return decoded; +} diff --git a/packages/sdk/src/encoding.ts b/packages/sdk/src/encoding.ts index e69de29b..6f6d9be3 100644 --- a/packages/sdk/src/encoding.ts +++ b/packages/sdk/src/encoding.ts @@ -0,0 +1,8 @@ +import { Encoding } from "@iov/encoding"; + +import { StdTx } from "./types"; + +export function marshalTx(tx: StdTx): Uint8Array { + const json = JSON.stringify(tx); + return Encoding.toUtf8(json); +} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index d97b584b..d84968e5 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,4 +1,6 @@ import * as types from "./types"; +export { unmarshalTx } from "./decoding"; +export { marshalTx } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; export { types }; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 7dea1856..1d5e9c86 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -13,6 +13,15 @@ export interface StdTx { readonly memo: string | undefined; } +export type AminoTx = Tx & { readonly value: StdTx }; + +export function isAminoStdTx(txValue: unknown): txValue is StdTx { + const { memo, msg, fee, signatures } = txValue as StdTx; + return ( + typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) + ); +} + export interface Msg { readonly type: string; // TODO: make better union type @@ -58,12 +67,3 @@ export interface BaseAccount { readonly account_number: string; readonly sequence: string; } - -export type AminoTx = Tx & { readonly value: StdTx }; - -export function isAminoStdTx(txValue: unknown): txValue is StdTx { - const { memo, msg, fee, signatures } = txValue as StdTx; - return ( - typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) - ); -} diff --git a/packages/sdk/types/decoding.d.ts b/packages/sdk/types/decoding.d.ts index e69de29b..fd0c5746 100644 --- a/packages/sdk/types/decoding.d.ts +++ b/packages/sdk/types/decoding.d.ts @@ -0,0 +1,2 @@ +import { StdTx } from "./types"; +export declare function unmarshalTx(data: Uint8Array): StdTx; diff --git a/packages/sdk/types/encoding.d.ts b/packages/sdk/types/encoding.d.ts index e69de29b..7a33940a 100644 --- a/packages/sdk/types/encoding.d.ts +++ b/packages/sdk/types/encoding.d.ts @@ -0,0 +1,2 @@ +import { StdTx } from "./types"; +export declare function marshalTx(tx: StdTx): Uint8Array; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index b441abf5..4420e59b 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,3 +1,5 @@ import * as types from "./types"; +export { unmarshalTx } from "./decoding"; +export { marshalTx } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; export { types }; From 3da586561447c56b691af790e082f6aacc9f5569 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 20:06:15 +0100 Subject: [PATCH 10/16] Linter cleanup --- packages/bcp/src/cosmwasmcodec.ts | 5 ++--- packages/bcp/src/decode.ts | 7 ++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index e3745343..2ca6e273 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { marshalTx, unmarshalTx } from "@cosmwasm/sdk"; import { Address, ChainId, @@ -13,9 +14,7 @@ import { TxCodec, UnsignedTransaction, } from "@iov/bcp"; -import { Sha256 } from "@iov/crypto"; import { Encoding } from "@iov/encoding"; -import { unmarshalTx, marshalTx } from "@cosmwasm/sdk"; import { CosmosBech32Prefix, isValidAddress, pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; @@ -23,7 +22,7 @@ import { parseTx } from "./decode"; import { buildSignedTx, buildUnsignedTx } from "./encode"; import { nonceToAccountNumber, nonceToSequence, TokenInfos } from "./types"; -const { toHex, toUtf8 } = Encoding; +const { toUtf8 } = Encoding; function sortJson(json: any): any { if (typeof json !== "object" || json === null) { diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 0ae3cffd..2835b0fa 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -102,7 +102,12 @@ export function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee { }; } -export function parseTx(txValue: types.StdTx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction { +export function parseTx( + txValue: types.StdTx, + chainId: ChainId, + nonce: Nonce, + tokens: TokenInfos, +): SignedTransaction { if (!types.isAminoStdTx(txValue)) { throw new Error("Only Amino StdTx is supported"); } From b99340bf76c57291885a80a32c851bac280f8427 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 20:22:02 +0100 Subject: [PATCH 11/16] Add encode logic to rest client --- packages/sdk/src/restclient.ts | 19 +++++++++++++++++-- packages/sdk/types/restclient.d.ts | 9 +++++++-- packages/sdk/types/types.d.ts | 8 ++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index f715393e..e3794ee1 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -1,7 +1,7 @@ import { Encoding } from "@iov/encoding"; import axios, { AxiosInstance } from "axios"; -import { AminoTx, BaseAccount, isAminoStdTx } from "./types"; +import { AminoTx, BaseAccount, isAminoStdTx, StdTx } from "./types"; const { fromUtf8 } = Encoding; @@ -66,13 +66,19 @@ interface PostTxsResponse { readonly raw_log?: string; } +interface EncodeTxResponse { + // base64-encoded amino-binary encoded representation + readonly tx: string; +} + type RestClientResponse = | NodeInfoResponse | BlocksResponse | AuthAccountsResponse | TxsResponse | SearchTxsResponse - | PostTxsResponse; + | PostTxsResponse + | EncodeTxResponse; type BroadcastMode = "block" | "sync" | "async"; @@ -133,6 +139,15 @@ export class RestClient { return responseData as BlocksResponse; } + // encodeTx returns the amino-encoding of the transaction + public async encodeTx(tx: StdTx): Promise { + const responseData = await this.post("/txs/encode", tx); + if (!(responseData as any).tx) { + throw new Error("Unexpected response data format"); + } + return Encoding.fromBase64((responseData as EncodeTxResponse).tx); + } + public async authAccounts(address: string, height?: string): Promise { const path = height === undefined ? `/auth/accounts/${address}` : `/auth/accounts/${address}?tx.height=${height}`; diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index d952d480..02c0fa53 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -1,4 +1,4 @@ -import { AminoTx, BaseAccount } from "./types"; +import { AminoTx, BaseAccount, StdTx } from "./types"; interface NodeInfo { readonly network: string; } @@ -50,13 +50,17 @@ interface PostTxsResponse { readonly code?: number; readonly raw_log?: string; } +interface EncodeTxResponse { + readonly tx: string; +} declare type RestClientResponse = | NodeInfoResponse | BlocksResponse | AuthAccountsResponse | TxsResponse | SearchTxsResponse - | PostTxsResponse; + | PostTxsResponse + | EncodeTxResponse; declare type BroadcastMode = "block" | "sync" | "async"; export declare class RestClient { private readonly client; @@ -67,6 +71,7 @@ export declare class RestClient { nodeInfo(): Promise; blocksLatest(): Promise; blocks(height: number): Promise; + encodeTx(tx: StdTx): Promise; authAccounts(address: string, height?: string): Promise; txs(query: string): Promise; txsById(id: string): Promise; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index a5e5672d..4f1bb326 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -8,6 +8,10 @@ export interface StdTx { readonly signatures: ReadonlyArray; readonly memo: string | undefined; } +export declare type AminoTx = Tx & { + readonly value: StdTx; +}; +export declare function isAminoStdTx(txValue: unknown): txValue is StdTx; export interface Msg { readonly type: string; readonly value: MsgSend | unknown; @@ -44,7 +48,3 @@ export interface BaseAccount { readonly account_number: string; readonly sequence: string; } -export declare type AminoTx = Tx & { - readonly value: StdTx; -}; -export declare function isAminoStdTx(txValue: unknown): txValue is StdTx; From 449a83120a815012175f19be57b92c37c1e4c42d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 20:25:08 +0100 Subject: [PATCH 12/16] WIP: test identifier via rest client amino encoding --- packages/bcp/src/cosmwasmcodec.spec.ts | 8 +------- packages/bcp/src/cosmwasmconnection.spec.ts | 12 ++++++++++++ packages/bcp/src/cosmwasmconnection.ts | 14 ++++++++++++-- packages/bcp/types/cosmwasmconnection.d.ts | 1 + 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.spec.ts b/packages/bcp/src/cosmwasmcodec.spec.ts index 405a2db8..d2ceeba0 100644 --- a/packages/bcp/src/cosmwasmcodec.spec.ts +++ b/packages/bcp/src/cosmwasmcodec.spec.ts @@ -2,7 +2,7 @@ import { PostableBytes, PrehashType } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; import { cosmWasmCodec } from "./cosmwasmcodec"; -import { chainId, nonce, sendTxJson, signedTxBin, signedTxJson, txId } from "./testdata.spec"; +import { chainId, nonce, sendTxJson, signedTxBin, signedTxJson } from "./testdata.spec"; const { toUtf8 } = Encoding; @@ -35,12 +35,6 @@ describe("cosmWasmCodec", () => { expect(decoded).toEqual(signedTxJson); }); - xit("generates transaction id", () => { - const id = cosmWasmCodec.identifier(signedTxJson); - expect(id).toMatch(/^[0-9A-F]{64}$/); - expect(id).toEqual(txId); - }); - it("round trip works", () => { const encoded = cosmWasmCodec.bytesToPost(signedTxJson); const decoded = cosmWasmCodec.parseBytes(encoded, chainId, nonce); diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 8ef2a08e..a87b431e 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -17,6 +17,7 @@ import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol"; import { CosmosBech32Prefix } from "./address"; import { CosmWasmCodec, cosmWasmCodec } from "./cosmwasmcodec"; import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; +import { signedTxJson, txId } from "./testdata.spec"; import { nonceToSequence } from "./types"; const { fromBase64, toHex } = Encoding; @@ -133,6 +134,17 @@ describe("CosmWasmConnection", () => { }); }); + describe("encodeTx", () => { + it("properly calculates tx hash", async () => { + pendingWithoutCosmos(); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const postable = cosmWasmCodec.bytesToPost(signedTxJson); + const id = await connection.identifier(postable); + expect(id).toMatch(/^[0-9A-F]{64}$/); + expect(id).toEqual(txId); + }); + }); + describe("getAccount", () => { it("gets an empty account by address", async () => { pendingWithoutCosmos(); diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 7abf2904..d3c09490 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { RestClient, TxsResponse } from "@cosmwasm/sdk"; +import { RestClient, TxsResponse, unmarshalTx } from "@cosmwasm/sdk"; import { Account, AccountQuery, @@ -27,7 +27,8 @@ import { TransactionState, UnsignedTransaction, } from "@iov/bcp"; -import { Uint53 } from "@iov/encoding"; +import { Sha256 } from "@iov/crypto"; +import { Encoding, Uint53 } from "@iov/encoding"; import { DefaultValueProducer, ValueAndUpdates } from "@iov/stream"; import equal from "fast-deep-equal"; import { ReadonlyDate } from "readonly-date"; @@ -38,6 +39,8 @@ import { Caip5 } from "./caip5"; import { decodeAmount, parseTxsResponse } from "./decode"; import { accountToNonce, TokenInfo } from "./types"; +const { toHex } = Encoding; + interface ChainData { readonly chainId: ChainId; } @@ -138,6 +141,13 @@ export class CosmWasmConnection implements BlockchainConnection { return this.supportedTokens; } + public async identifier(signed: PostableBytes): Promise { + const tx = unmarshalTx(signed); + const bytes = await this.restClient.encodeTx(tx); + const hash = new Sha256(bytes).digest(); + return toHex(hash).toUpperCase() as TransactionId; + } + public async getAccount(query: AccountQuery): Promise { const address = isPubkeyQuery(query) ? pubkeyToAddress(query.pubkey, this.prefix) : query.address; const { result } = await this.restClient.authAccounts(address); diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index c3c2f616..982ac756 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -45,6 +45,7 @@ export declare class CosmWasmConnection implements BlockchainConnection { height(): Promise; getToken(searchTicker: TokenTicker): Promise; getAllTokens(): Promise; + identifier(signed: PostableBytes): Promise; getAccount(query: AccountQuery): Promise; watchAccount(_account: AccountQuery): Stream; getNonce(query: AddressQuery | PubkeyQuery): Promise; From 0031d6860d7cf8f8ee032ad683ad072f642c4750 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 21:31:46 +0100 Subject: [PATCH 13/16] Fix encodeTx implementation, Connection.identifier now works --- packages/sdk/src/restclient.ts | 3 ++- packages/sdk/types/restclient.d.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index e3794ee1..b89e7fcb 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -140,7 +140,8 @@ export class RestClient { } // encodeTx returns the amino-encoding of the transaction - public async encodeTx(tx: StdTx): Promise { + public async encodeTx(stdTx: StdTx): Promise { + const tx = {"type": "cosmos-sdk/StdTx", "value": stdTx}; const responseData = await this.post("/txs/encode", tx); if (!(responseData as any).tx) { throw new Error("Unexpected response data format"); diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index 02c0fa53..b818be1e 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -71,7 +71,7 @@ export declare class RestClient { nodeInfo(): Promise; blocksLatest(): Promise; blocks(height: number): Promise; - encodeTx(tx: StdTx): Promise; + encodeTx(stdTx: StdTx): Promise; authAccounts(address: string, height?: string): Promise; txs(query: string): Promise; txsById(id: string): Promise; From c62702ba30eef3cf410115f981faa883fffb967f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 21:33:39 +0100 Subject: [PATCH 14/16] Fix lint issues --- packages/sdk/src/restclient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index b89e7fcb..c7d17e94 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -141,7 +141,7 @@ export class RestClient { // encodeTx returns the amino-encoding of the transaction public async encodeTx(stdTx: StdTx): Promise { - const tx = {"type": "cosmos-sdk/StdTx", "value": stdTx}; + const tx = { type: "cosmos-sdk/StdTx", value: stdTx }; const responseData = await this.post("/txs/encode", tx); if (!(responseData as any).tx) { throw new Error("Unexpected response data format"); From 1fbfa311c3605ac5383ae26489924c6c17a7873e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 22:42:40 +0100 Subject: [PATCH 15/16] Minor cleanups from pr --- packages/bcp/src/cosmwasmconnection.spec.ts | 4 ++-- packages/bcp/src/cosmwasmconnection.ts | 2 +- packages/bcp/types/cosmwasmconnection.d.ts | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index a87b431e..100ddc3b 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -134,8 +134,8 @@ describe("CosmWasmConnection", () => { }); }); - describe("encodeTx", () => { - it("properly calculates tx hash", async () => { + describe("identifier", () => { + it("calculates tx hash from PostableBytes", async () => { pendingWithoutCosmos(); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); const postable = cosmWasmCodec.bytesToPost(signedTxJson); diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index d3c09490..84177c3a 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -69,7 +69,7 @@ function buildQueryString({ return components.filter(Boolean).join("&"); } -export type TokenConfiguration = (TokenInfo & { readonly name: string })[]; +export type TokenConfiguration = ReadonlyArray; export class CosmWasmConnection implements BlockchainConnection { // we must know prefix and tokens a priori to understand the chain diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index 982ac756..e5d1a7c6 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -22,9 +22,11 @@ import { import { Stream } from "xstream"; import { CosmosBech32Prefix } from "./address"; import { TokenInfo } from "./types"; -export declare type TokenConfiguration = (TokenInfo & { - readonly name: string; -})[]; +export declare type TokenConfiguration = ReadonlyArray< + TokenInfo & { + readonly name: string; + } +>; export declare class CosmWasmConnection implements BlockchainConnection { static establish( url: string, From f52bb378b4a902465fab2e00a92aea84af798ce8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 3 Feb 2020 22:53:05 +0100 Subject: [PATCH 16/16] Re-enable test cases --- packages/bcp/src/cosmwasmcodec.spec.ts | 10 +++++----- packages/bcp/src/testdata.spec.ts | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.spec.ts b/packages/bcp/src/cosmwasmcodec.spec.ts index d2ceeba0..8f5604be 100644 --- a/packages/bcp/src/cosmwasmcodec.spec.ts +++ b/packages/bcp/src/cosmwasmcodec.spec.ts @@ -2,7 +2,7 @@ import { PostableBytes, PrehashType } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; import { cosmWasmCodec } from "./cosmwasmcodec"; -import { chainId, nonce, sendTxJson, signedTxBin, signedTxJson } from "./testdata.spec"; +import { chainId, nonce, sendTxJson, signedTxBin, signedTxEncodedJson, signedTxJson } from "./testdata.spec"; const { toUtf8 } = Encoding; @@ -19,9 +19,9 @@ describe("cosmWasmCodec", () => { expect(bytesToSign).toEqual(expected); }); - xit("properly encodes transactions", () => { + it("properly encodes transactions", () => { const encoded = cosmWasmCodec.bytesToPost(signedTxJson); - expect(encoded).toEqual(signedTxBin); + expect(encoded).toEqual(signedTxEncodedJson); }); it("throws when trying to decode a transaction without a nonce", () => { @@ -30,8 +30,8 @@ describe("cosmWasmCodec", () => { ); }); - xit("properly decodes transactions", () => { - const decoded = cosmWasmCodec.parseBytes(signedTxBin as PostableBytes, chainId, nonce); + it("properly decodes transactions", () => { + const decoded = cosmWasmCodec.parseBytes(signedTxEncodedJson as PostableBytes, chainId, nonce); expect(decoded).toEqual(signedTxJson); }); diff --git a/packages/bcp/src/testdata.spec.ts b/packages/bcp/src/testdata.spec.ts index bf627e38..bec40b5b 100644 --- a/packages/bcp/src/testdata.spec.ts +++ b/packages/bcp/src/testdata.spec.ts @@ -16,7 +16,7 @@ import { Encoding } from "@iov/encoding"; import data from "./testdata/cosmoshub.json"; -const { fromBase64 } = Encoding; +const { fromBase64, toUtf8 } = Encoding; export const pubJson: PubkeyBundle = { algo: Algorithm.Secp256k1, @@ -62,3 +62,7 @@ export const signedTxJson: SignedTransaction = { export const signedTxBin = fromBase64(data.tx_data); export const txId = data.id as TransactionId; + +export const signedTxEncodedJson = toUtf8( + `{"msg":[{"type":"cosmos-sdk/MsgSend","value":{"from_address":"cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq","to_address":"cosmos1nynns8ex9fq6sjjfj8k79ymkdz4sqth06xexae","amount":[{"denom":"uatom","amount":"35997500"}]}}],"memo":"","signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A5qFcJBJvEK/fOmEAY0DHNWwSRZ9TEfNZyH8VoVvDtAq"},"signature":"NK1Oy4EUGAsoC03c1wi9GG03JC/39LEdautC5Jk643oIbEPqeXHMwaqbdvO/Jws0X/NAXaN8SAy2KNY5Qml+5Q=="}],"fee":{"amount":[{"denom":"uatom","amount":"2500"}],"gas":"100000"}}`, +);