From 2d14efa4cd95edc2a97f8cdcdb3cda4590e3ba43 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 4 Feb 2020 13:34:51 +0100 Subject: [PATCH] Deduplicate makeSignBytes --- packages/bcp/src/cosmwasmcodec.ts | 26 ++++++++--------- packages/bcp/src/types.ts | 6 +--- packages/bcp/types/types.d.ts | 3 +- packages/sdk/src/encoding.ts | 26 +++++++++++++++-- packages/sdk/src/index.ts | 2 +- packages/sdk/src/restclient.spec.ts | 44 +++++++++-------------------- packages/sdk/src/types.ts | 3 ++ packages/sdk/types/encoding.d.ts | 10 +++++-- packages/sdk/types/index.d.ts | 2 +- packages/sdk/types/types.d.ts | 2 ++ 10 files changed, 66 insertions(+), 58 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index 7602830f..88285bff 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { marshalTx, sortJson, unmarshalTx } from "@cosmwasm/sdk"; +import { makeSignBytes, marshalTx, types, unmarshalTx } from "@cosmwasm/sdk"; import { Address, ChainId, @@ -14,7 +14,6 @@ import { TxCodec, UnsignedTransaction, } from "@iov/bcp"; -import { Encoding } from "@iov/encoding"; import { CosmosBech32Prefix, isValidAddress, pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; @@ -22,8 +21,6 @@ import { parseTx } from "./decode"; import { buildSignedTx, buildUnsignedTx } from "./encode"; import { nonceToAccountNumber, nonceToSequence, TokenInfos } from "./types"; -const { toUtf8 } = Encoding; - export class CosmWasmCodec implements TxCodec { private readonly prefix: CosmosBech32Prefix; private readonly tokens: TokenInfos; @@ -34,18 +31,19 @@ export class CosmWasmCodec implements TxCodec { } public bytesToSign(unsigned: UnsignedTransaction, nonce: Nonce): SigningJob { - const memo = (unsigned as any).memo; const built = buildUnsignedTx(unsigned, this.tokens); - const signMsg = sortJson({ - account_number: nonceToAccountNumber(nonce).toString(), - chain_id: Caip5.decode(unsigned.chainId), - fee: (built.value as any).fee, - memo: memo, - msgs: (built.value as any).msg, - sequence: nonceToSequence(nonce).toString(), - }); - const signBytes = toUtf8(JSON.stringify(signMsg)); + const nonceInfo: types.NonceInfo = { + account_number: nonceToAccountNumber(nonce), + sequence: nonceToSequence(nonce), + }; + const signBytes = makeSignBytes( + built.value.msg[0], + built.value.fee, + Caip5.decode(unsigned.chainId), + built.value.memo || "", + nonceInfo, + ); return { bytes: signBytes as SignableBytes, diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index 2f1ee35a..71ec2be8 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -23,13 +23,9 @@ const maxAcct = 1 << 23; // tslint:disable-next-line:no-bitwise const maxSeq = 1 << 20; -// NonceInfo is the data we need from account to create a nonce -// Use this so no confusion about order of arguments -export type NonceInfo = Pick; - // this (lossily) encodes the two pieces of info (uint64) needed to sign into // one (53-bit) number. Cross your fingers. -export function accountToNonce({ account_number: account, sequence }: NonceInfo): Nonce { +export function accountToNonce({ account_number: account, sequence }: types.NonceInfo): Nonce { // we allow 23 bits (8 million) for accounts, and 20 bits (1 million) for tx/account // let's fix this soon if (account > maxAcct) { diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index d89725f4..e5a6e631 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -15,7 +15,6 @@ export interface TokenInfo { readonly fractionalDigits: number; } export declare type TokenInfos = ReadonlyArray; -export declare type NonceInfo = Pick; -export declare function accountToNonce({ account_number: account, sequence }: NonceInfo): Nonce; +export declare function accountToNonce({ account_number: account, sequence }: types.NonceInfo): Nonce; export declare function nonceToAccountNumber(nonce: Nonce): number; export declare function nonceToSequence(nonce: Nonce): number; diff --git a/packages/sdk/src/encoding.ts b/packages/sdk/src/encoding.ts index 8f10a78d..fe4d34a0 100644 --- a/packages/sdk/src/encoding.ts +++ b/packages/sdk/src/encoding.ts @@ -1,11 +1,11 @@ import { Secp256k1 } from "@iov/crypto"; import { Encoding } from "@iov/encoding"; -import { StdSignature, StdTx } from "./types"; +import { Msg, NonceInfo, StdFee, StdSignature, StdTx } from "./types"; -const { toBase64 } = Encoding; +const { toBase64, toUtf8 } = Encoding; -export function sortJson(json: any): any { +function sortJson(json: any): any { if (typeof json !== "object" || json === null) { return json; } @@ -28,6 +28,26 @@ export function marshalTx(tx: StdTx): Uint8Array { return Encoding.toUtf8(json); } +export function makeSignBytes( + msg: Msg, + fee: StdFee, + chainId: string, + memo: string, + account: NonceInfo, +): Uint8Array { + const signMsg = sortJson({ + // eslint-disable-next-line @typescript-eslint/camelcase + account_number: account.account_number.toString(), + // eslint-disable-next-line @typescript-eslint/camelcase + chain_id: chainId, + fee: fee, + memo: memo, + msgs: msg, + sequence: account.sequence.toString(), + }); + return toUtf8(JSON.stringify(signMsg)); +} + export function encodeSecp256k1Signature(pubkey: Uint8Array, signature: Uint8Array): StdSignature { return { // eslint-disable-next-line @typescript-eslint/camelcase diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 1a2e024c..9016d432 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,6 +1,6 @@ import * as types from "./types"; export { unmarshalTx } from "./decoding"; -export { encodeSecp256k1Signature, marshalTx, sortJson } from "./encoding"; +export { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; export { types }; diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index e753904b..4f5d3a58 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -3,13 +3,13 @@ import { ChainId, PrehashType, SignableBytes } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; import { HdPaths, Secp256k1HdWallet } from "@iov/keycontrol"; -import { encodeSecp256k1Signature, marshalTx, sortJson } from "./encoding"; +import { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding"; import { RestClient } from "./restclient"; import contract from "./testdata/contract.json"; import data from "./testdata/cosmoshub.json"; -import { MsgStoreCode, StdTx } from "./types"; +import { MsgStoreCode, StdFee, StdTx } from "./types"; -const { fromBase64, toUtf8 } = Encoding; +const { fromBase64 } = Encoding; const httpUrl = "http://localhost:1317"; const defaultNetworkId = "testing"; @@ -75,41 +75,25 @@ describe("RestClient", () => { builder: "v0.0.1", }, }; - - const unsigned: StdTx = { - msg: [theMsg], - memo: memo, - signatures: [], - fee: { - amount: [ - { - amount: "5000", - denom: "ucosm", - }, - ], - gas: "89000000", - }, + const fee: StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "89000000", }; const client = new RestClient(httpUrl); const account = (await client.authAccounts(faucetAddress)).result.value; - - const signMsg = sortJson({ - account_number: account.account_number.toString(), - chain_id: defaultNetworkId, - fee: unsigned.fee, - memo: memo, - msgs: unsigned.msg, - sequence: account.sequence.toString(), - }); - - const signBytes = toUtf8(JSON.stringify(signMsg)) as SignableBytes; + const signBytes = makeSignBytes(theMsg, fee, defaultNetworkId, memo, account) as SignableBytes; const rawSignature = await wallet.createTransactionSignature(signer, signBytes, PrehashType.Sha256); const signature = encodeSecp256k1Signature(signer.pubkey.data, rawSignature); const tx: StdTx = { - msg: unsigned.msg, - fee: unsigned.fee, + msg: [theMsg], + fee: fee, memo: memo, signatures: [signature], }; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 07066b29..243088b4 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -97,3 +97,6 @@ export interface BaseAccount { readonly account_number: number; readonly sequence: number; } + +/** The data we need from BaseAccount to create a nonce */ +export type NonceInfo = Pick; diff --git a/packages/sdk/types/encoding.d.ts b/packages/sdk/types/encoding.d.ts index 22972faf..e7f23d60 100644 --- a/packages/sdk/types/encoding.d.ts +++ b/packages/sdk/types/encoding.d.ts @@ -1,4 +1,10 @@ -import { StdSignature, StdTx } from "./types"; -export declare function sortJson(json: any): any; +import { Msg, NonceInfo, StdFee, StdSignature, StdTx } from "./types"; export declare function marshalTx(tx: StdTx): Uint8Array; +export declare function makeSignBytes( + msg: Msg, + fee: StdFee, + chainId: string, + memo: string, + account: NonceInfo, +): Uint8Array; export declare function encodeSecp256k1Signature(pubkey: Uint8Array, signature: Uint8Array): StdSignature; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index cddb9250..8c8e4b4b 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,5 +1,5 @@ import * as types from "./types"; export { unmarshalTx } from "./decoding"; -export { encodeSecp256k1Signature, marshalTx, sortJson } from "./encoding"; +export { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; export { types }; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index b1b43a0d..3eaa73c3 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -69,4 +69,6 @@ export interface BaseAccount { readonly account_number: number; readonly sequence: number; } +/** The data we need from BaseAccount to create a nonce */ +export declare type NonceInfo = Pick; export {};