diff --git a/src/cosmoscodec.ts b/src/cosmoscodec.ts index 31d6e3cf..88a74ea8 100644 --- a/src/cosmoscodec.ts +++ b/src/cosmoscodec.ts @@ -54,8 +54,6 @@ export class CosmosCodec implements TxCodec { } public bytesToSign(unsigned: UnsignedTransaction, nonce: Nonce): SigningJob { - console.log("nonce:", nonce); - // ohhh... this is a bug!!! const accountNumber = 0; const memo = (unsigned as any).memo; const built = buildUnsignedTx(unsigned, this.tokens); diff --git a/src/types.spec.ts b/src/types.spec.ts new file mode 100644 index 00000000..87f07fe7 --- /dev/null +++ b/src/types.spec.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { accountToNonce, nonceToAccountNumber, nonceToSequence } from "./types"; + +describe("nonceEncoding", () => { + it("works for input in range", () => { + const nonce = accountToNonce("1234", "7890"); + expect(nonceToAccountNumber(nonce)).toEqual("1234"); + expect(nonceToSequence(nonce)).toEqual("7890"); + }); + + it("errors on input too large", () => { + expect(() => accountToNonce("1234567890", "7890")).toThrow(); + expect(() => accountToNonce("178", "97320247923")).toThrow(); + }); +}); diff --git a/src/types.ts b/src/types.ts index 5e44a030..2ebcec1c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { Amount, Token } from "@iov/bcp"; +import { Amount, Nonce, Token } from "@iov/bcp"; import amino from "@tendermint/amino-js"; export type AminoTx = amino.Tx & { readonly value: amino.StdTx }; @@ -40,3 +40,42 @@ export function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount { quantity: coin.amount, }; } + +// tslint:disable-next-line:no-bitwise +const maxAcct = 1 << 23; +// tslint:disable-next-line:no-bitwise +const maxSeq = 1 << 20; + +// this (lossily) encodes the two pieces of info (uint64) needed to sign into +// one (53-bit) number. Cross your fingers. +export function accountToNonce(accountNumber: string, sequence: string): Nonce { + const acct = parseInt(accountNumber, 10); + const seq = parseInt(sequence, 10); + + // we allow 23 bits (8 million) for accounts, and 20 bits (1 million) for tx/account + // let's fix this soon + if (acct > maxAcct) { + throw new Error("Account number is greater than 2^23, must update Nonce handler"); + } + if (seq > maxSeq) { + throw new Error("Sequence is greater than 2^20, must update Nonce handler"); + } + + const val = acct * maxSeq + seq; + return val as Nonce; +} + +// this extracts info from nonce for signing +export function nonceToAccountNumber(nonce: Nonce): string { + const acct = nonce / maxSeq; + if (acct > maxAcct) { + throw new Error("Invalid Nonce, account number is higher than can safely be encoded in Nonce"); + } + return Math.round(acct).toString(); +} + +// this extracts info from nonce for signing +export function nonceToSequence(nonce: Nonce): string { + const seq = nonce % maxSeq; + return Math.round(seq).toString(); +} diff --git a/types/types.d.ts b/types/types.d.ts index 62d5ab9b..eda75b7b 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -1,4 +1,4 @@ -import { Amount, Token } from "@iov/bcp"; +import { Amount, Token, Nonce } from "@iov/bcp"; import amino from "@tendermint/amino-js"; export declare type AminoTx = amino.Tx & { readonly value: amino.StdTx; @@ -10,3 +10,6 @@ export interface TokenInfo extends Token { export declare type TokenInfos = ReadonlyArray; export declare function amountToCoin(lookup: ReadonlyArray, amount: Amount): amino.Coin; export declare function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount; +export declare function accountToNonce(accountNumber: string, sequence: string): Nonce; +export declare function nonceToAccountNumber(nonce: Nonce): string; +export declare function nonceToSequence(nonce: Nonce): string;