From d183cc1e49273f4d6f8ca4c70011e598af41fdf9 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 23 Mar 2021 17:21:01 +0100 Subject: [PATCH 1/5] proto-signing: Use local coins --- packages/proto-signing/src/directsecp256k1hdwallet.spec.ts | 2 +- packages/proto-signing/src/directsecp256k1wallet.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/proto-signing/src/directsecp256k1hdwallet.spec.ts b/packages/proto-signing/src/directsecp256k1hdwallet.spec.ts index c13e2434..59b86612 100644 --- a/packages/proto-signing/src/directsecp256k1hdwallet.spec.ts +++ b/packages/proto-signing/src/directsecp256k1hdwallet.spec.ts @@ -1,7 +1,7 @@ import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto"; import { fromBase64, fromHex } from "@cosmjs/encoding"; -import { coins } from "@cosmjs/launchpad"; +import { coins } from "./coins"; import { DirectSecp256k1HdWallet } from "./directsecp256k1hdwallet"; import { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing"; import { faucet, testVectors } from "./testutils.spec"; diff --git a/packages/proto-signing/src/directsecp256k1wallet.spec.ts b/packages/proto-signing/src/directsecp256k1wallet.spec.ts index 40e0773f..a90d123c 100644 --- a/packages/proto-signing/src/directsecp256k1wallet.spec.ts +++ b/packages/proto-signing/src/directsecp256k1wallet.spec.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto"; import { fromBase64, fromHex } from "@cosmjs/encoding"; -import { coins } from "@cosmjs/launchpad"; +import { coins } from "./coins"; import { DirectSecp256k1Wallet } from "./directsecp256k1wallet"; import { makeAuthInfoBytes, makeSignBytes, makeSignDoc } from "./signing"; import { testVectors } from "./testutils.spec"; From a75b299ec519a6652a6a82b625f34fe4fa14efd7 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 23 Mar 2021 17:40:48 +0100 Subject: [PATCH 2/5] amino: Copy signature encoding from launchpad --- packages/amino/src/signature.spec.ts | 65 ++++++++++++++++++++++++++++ packages/amino/src/signature.ts | 44 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 packages/amino/src/signature.spec.ts create mode 100644 packages/amino/src/signature.ts diff --git a/packages/amino/src/signature.spec.ts b/packages/amino/src/signature.spec.ts new file mode 100644 index 00000000..d9cd1ad5 --- /dev/null +++ b/packages/amino/src/signature.spec.ts @@ -0,0 +1,65 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { fromBase64 } from "@cosmjs/encoding"; + +import { decodeSignature, encodeSecp256k1Signature, StdSignature } from "./signature"; + +describe("signature", () => { + describe("encodeSecp256k1Signature", () => { + it("encodes a full signature", () => { + const pubkey = fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP"); + const signature = fromBase64( + "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + ); + expect(encodeSecp256k1Signature(pubkey, signature)).toEqual({ + pub_key: { + type: "tendermint/PubKeySecp256k1", + value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", + }, + signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + }); + }); + + it("throws when getting uncompressed public keys", () => { + const pubkey = fromBase64( + "BE8EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQE7WHpoHoNswYeoFkuYpYSKK4mzFzMV/dB0DVAy4lnNU=", + ); + const signature = fromBase64( + "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + ); + expect(() => encodeSecp256k1Signature(pubkey, signature)).toThrowError( + /public key must be compressed secp256k1/i, + ); + }); + + it("throws if signature contains recovery byte", () => { + const pubkey = fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP"); + const signature = Uint8Array.from([ + ...fromBase64( + "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + ), + 99, + ]); + expect(() => encodeSecp256k1Signature(pubkey, signature)).toThrowError( + /signature must be 64 bytes long/i, + ); + }); + }); + + describe("decodeSignature", () => { + it("works for secp256k1", () => { + const signature: StdSignature = { + pub_key: { + type: "tendermint/PubKeySecp256k1", + value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", + }, + signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + }; + expect(decodeSignature(signature)).toEqual({ + pubkey: fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP"), + signature: fromBase64( + "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + ), + }); + }); + }); +}); diff --git a/packages/amino/src/signature.ts b/packages/amino/src/signature.ts new file mode 100644 index 00000000..280653a2 --- /dev/null +++ b/packages/amino/src/signature.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { fromBase64, toBase64 } from "@cosmjs/encoding"; + +import { encodeSecp256k1Pubkey } from "./encoding"; +import { Pubkey, pubkeyType } from "./pubkeys"; + +export interface StdSignature { + readonly pub_key: Pubkey; + readonly signature: string; +} + +/** + * Takes a binary pubkey and signature to create a signature object + * + * @param pubkey a compressed secp256k1 public key + * @param signature a 64 byte fixed length representation of secp256k1 signature components r and s + */ +export function encodeSecp256k1Signature(pubkey: Uint8Array, signature: Uint8Array): StdSignature { + if (signature.length !== 64) { + throw new Error( + "Signature must be 64 bytes long. Cosmos SDK uses a 2x32 byte fixed length encoding for the secp256k1 signature integers r and s.", + ); + } + + return { + pub_key: encodeSecp256k1Pubkey(pubkey), + signature: toBase64(signature), + }; +} + +export function decodeSignature( + signature: StdSignature, +): { readonly pubkey: Uint8Array; readonly signature: Uint8Array } { + switch (signature.pub_key.type) { + // Note: please don't add cases here without writing additional unit tests + case pubkeyType.secp256k1: + return { + pubkey: fromBase64(signature.pub_key.value), + signature: fromBase64(signature.signature), + }; + default: + throw new Error("Unsupported pubkey type"); + } +} From 3a5877452e3afc405b6f48ddf8f79c806c040053 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 23 Mar 2021 17:41:46 +0100 Subject: [PATCH 3/5] amino: Export signature functions/type --- packages/amino/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/amino/src/index.ts b/packages/amino/src/index.ts index 28b4d9e5..340fd598 100644 --- a/packages/amino/src/index.ts +++ b/packages/amino/src/index.ts @@ -19,3 +19,4 @@ export { pubkeyType, } from "./pubkeys"; export { createMultisigThresholdPubkey } from "./multisig"; +export { decodeSignature, encodeSecp256k1Signature, StdSignature } from "./signature"; From 7b880c9a66cb140e31cdff9ede1fad70ad47ec7b Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 23 Mar 2021 17:45:42 +0100 Subject: [PATCH 4/5] launchpad: Use signature functions/type from amino --- packages/launchpad/src/index.ts | 6 +- packages/launchpad/src/secp256k1hdwallet.ts | 3 +- packages/launchpad/src/secp256k1wallet.ts | 3 +- packages/launchpad/src/sequence.ts | 2 +- packages/launchpad/src/signature.spec.ts | 66 --------------------- packages/launchpad/src/signature.ts | 39 ------------ packages/launchpad/src/signer.ts | 3 +- packages/launchpad/src/tx.spec.ts | 4 +- packages/launchpad/src/tx.ts | 4 +- packages/launchpad/src/types.ts | 7 --- 10 files changed, 15 insertions(+), 122 deletions(-) delete mode 100644 packages/launchpad/src/signature.spec.ts delete mode 100644 packages/launchpad/src/signature.ts diff --git a/packages/launchpad/src/index.ts b/packages/launchpad/src/index.ts index 14db51fa..d97792e4 100644 --- a/packages/launchpad/src/index.ts +++ b/packages/launchpad/src/index.ts @@ -2,11 +2,14 @@ export { decodeAminoPubkey, decodeBech32Pubkey, + decodeSignature, encodeAminoPubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey, + encodeSecp256k1Signature, pubkeyToAddress, pubkeyType, + StdSignature, } from "@cosmjs/amino"; import { SinglePubkey } from "@cosmjs/amino"; /** @deprecated PubKey is deprecated. Use `SinglePubkey` or the more general `Pubkey` from `@cosmjs/amino`. */ @@ -139,11 +142,10 @@ export { } from "./msgs"; export { makeCosmoshubPath } from "./paths"; export { findSequenceForSignedTx } from "./sequence"; -export { encodeSecp256k1Signature, decodeSignature } from "./signature"; export { AccountData, Algo, AminoSignResponse, OfflineSigner } from "./signer"; export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient"; export { isStdTx, isWrappedStdTx, makeStdTx, CosmosSdkTx, StdTx, WrappedStdTx, WrappedTx } from "./tx"; -export { StdFee, StdSignature } from "./types"; +export { StdFee } from "./types"; export { executeKdf, KdfConfiguration } from "./wallet"; export { extractKdfConfiguration, Secp256k1HdWallet } from "./secp256k1hdwallet"; export { Secp256k1Wallet } from "./secp256k1wallet"; diff --git a/packages/launchpad/src/secp256k1hdwallet.ts b/packages/launchpad/src/secp256k1hdwallet.ts index fa7d8b9c..38e29a43 100644 --- a/packages/launchpad/src/secp256k1hdwallet.ts +++ b/packages/launchpad/src/secp256k1hdwallet.ts @@ -1,4 +1,4 @@ -import { rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; +import { encodeSecp256k1Signature, rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; import { Bip39, EnglishMnemonic, @@ -16,7 +16,6 @@ import { assert, isNonNullObject } from "@cosmjs/utils"; import { serializeSignDoc, StdSignDoc } from "./encoding"; import { makeCosmoshubPath } from "./paths"; -import { encodeSecp256k1Signature } from "./signature"; import { AccountData, AminoSignResponse, OfflineSigner } from "./signer"; import { decrypt, diff --git a/packages/launchpad/src/secp256k1wallet.ts b/packages/launchpad/src/secp256k1wallet.ts index 94552d84..247ba3d1 100644 --- a/packages/launchpad/src/secp256k1wallet.ts +++ b/packages/launchpad/src/secp256k1wallet.ts @@ -1,9 +1,8 @@ -import { rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; +import { encodeSecp256k1Signature, rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; import { Secp256k1, Sha256 } from "@cosmjs/crypto"; import { Bech32 } from "@cosmjs/encoding"; import { serializeSignDoc, StdSignDoc } from "./encoding"; -import { encodeSecp256k1Signature } from "./signature"; import { AccountData, AminoSignResponse, OfflineSigner } from "./signer"; /** diff --git a/packages/launchpad/src/sequence.ts b/packages/launchpad/src/sequence.ts index 3ed14221..c833107a 100644 --- a/packages/launchpad/src/sequence.ts +++ b/packages/launchpad/src/sequence.ts @@ -1,7 +1,7 @@ +import { decodeSignature } from "@cosmjs/amino"; import { Secp256k1, Secp256k1Signature, sha256 } from "@cosmjs/crypto"; import { makeSignDoc, serializeSignDoc } from "./encoding"; -import { decodeSignature } from "./signature"; import { WrappedStdTx } from "./tx"; /** diff --git a/packages/launchpad/src/signature.spec.ts b/packages/launchpad/src/signature.spec.ts deleted file mode 100644 index dd1d6a38..00000000 --- a/packages/launchpad/src/signature.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { fromBase64 } from "@cosmjs/encoding"; - -import { decodeSignature, encodeSecp256k1Signature } from "./signature"; -import { StdSignature } from "./types"; - -describe("signature", () => { - describe("encodeSecp256k1Signature", () => { - it("encodes a full signature", () => { - const pubkey = fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP"); - const signature = fromBase64( - "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", - ); - expect(encodeSecp256k1Signature(pubkey, signature)).toEqual({ - pub_key: { - type: "tendermint/PubKeySecp256k1", - value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", - }, - signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", - }); - }); - - it("throws when getting uncompressed public keys", () => { - const pubkey = fromBase64( - "BE8EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQE7WHpoHoNswYeoFkuYpYSKK4mzFzMV/dB0DVAy4lnNU=", - ); - const signature = fromBase64( - "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", - ); - expect(() => encodeSecp256k1Signature(pubkey, signature)).toThrowError( - /public key must be compressed secp256k1/i, - ); - }); - - it("throws if signature contains recovery byte", () => { - const pubkey = fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP"); - const signature = Uint8Array.from([ - ...fromBase64( - "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", - ), - 99, - ]); - expect(() => encodeSecp256k1Signature(pubkey, signature)).toThrowError( - /signature must be 64 bytes long/i, - ); - }); - }); - - describe("decodeSignature", () => { - it("works for secp256k1", () => { - const signature: StdSignature = { - pub_key: { - type: "tendermint/PubKeySecp256k1", - value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", - }, - signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", - }; - expect(decodeSignature(signature)).toEqual({ - pubkey: fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP"), - signature: fromBase64( - "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", - ), - }); - }); - }); -}); diff --git a/packages/launchpad/src/signature.ts b/packages/launchpad/src/signature.ts deleted file mode 100644 index 15f37f00..00000000 --- a/packages/launchpad/src/signature.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { encodeSecp256k1Pubkey, pubkeyType } from "@cosmjs/amino"; -import { fromBase64, toBase64 } from "@cosmjs/encoding"; - -import { StdSignature } from "./types"; - -/** - * Takes a binary pubkey and signature to create a signature object - * - * @param pubkey a compressed secp256k1 public key - * @param signature a 64 byte fixed length representation of secp256k1 signature components r and s - */ -export function encodeSecp256k1Signature(pubkey: Uint8Array, signature: Uint8Array): StdSignature { - if (signature.length !== 64) { - throw new Error( - "Signature must be 64 bytes long. Cosmos SDK uses a 2x32 byte fixed length encoding for the secp256k1 signature integers r and s.", - ); - } - - return { - pub_key: encodeSecp256k1Pubkey(pubkey), - signature: toBase64(signature), - }; -} - -export function decodeSignature( - signature: StdSignature, -): { readonly pubkey: Uint8Array; readonly signature: Uint8Array } { - switch (signature.pub_key.type) { - // Note: please don't add cases here without writing additional unit tests - case pubkeyType.secp256k1: - return { - pubkey: fromBase64(signature.pub_key.value), - signature: fromBase64(signature.signature), - }; - default: - throw new Error("Unsupported pubkey type"); - } -} diff --git a/packages/launchpad/src/signer.ts b/packages/launchpad/src/signer.ts index 57b9829c..a2f25eb1 100644 --- a/packages/launchpad/src/signer.ts +++ b/packages/launchpad/src/signer.ts @@ -1,5 +1,6 @@ +import { StdSignature } from "@cosmjs/amino"; + import { StdSignDoc } from "./encoding"; -import { StdSignature } from "./types"; export type Algo = "secp256k1" | "ed25519" | "sr25519"; diff --git a/packages/launchpad/src/tx.spec.ts b/packages/launchpad/src/tx.spec.ts index 21bf834f..848c0c56 100644 --- a/packages/launchpad/src/tx.spec.ts +++ b/packages/launchpad/src/tx.spec.ts @@ -1,8 +1,10 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import { StdSignature } from "@cosmjs/amino"; + import { coins } from "./coins"; import { makeSignDoc } from "./encoding"; import { makeStdTx } from "./tx"; -import { StdFee, StdSignature } from "./types"; +import { StdFee } from "./types"; describe("tx", () => { describe("makeStdTx", () => { diff --git a/packages/launchpad/src/tx.ts b/packages/launchpad/src/tx.ts index a466c9f4..e88ab00f 100644 --- a/packages/launchpad/src/tx.ts +++ b/packages/launchpad/src/tx.ts @@ -1,6 +1,8 @@ +import { StdSignature } from "@cosmjs/amino"; + import { StdSignDoc } from "./encoding"; import { Msg } from "./msgs"; -import { StdFee, StdSignature } from "./types"; +import { StdFee } from "./types"; /** * A Cosmos SDK StdTx diff --git a/packages/launchpad/src/types.ts b/packages/launchpad/src/types.ts index bb674bc1..91fc04c0 100644 --- a/packages/launchpad/src/types.ts +++ b/packages/launchpad/src/types.ts @@ -1,14 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { Pubkey } from "@cosmjs/amino"; - import { Coin } from "./coins"; export interface StdFee { readonly amount: readonly Coin[]; readonly gas: string; } - -export interface StdSignature { - readonly pub_key: Pubkey; - readonly signature: string; -} From af8b771f828947fa053cb0e4af4ecc81bb526204 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 23 Mar 2021 17:47:49 +0100 Subject: [PATCH 5/5] proto-signing: Use signature functions/type from amino --- packages/proto-signing/src/directsecp256k1hdwallet.ts | 3 +-- packages/proto-signing/src/directsecp256k1wallet.ts | 3 +-- packages/proto-signing/src/signer.ts | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/proto-signing/src/directsecp256k1hdwallet.ts b/packages/proto-signing/src/directsecp256k1hdwallet.ts index cfdbc8c4..0a3f02cf 100644 --- a/packages/proto-signing/src/directsecp256k1hdwallet.ts +++ b/packages/proto-signing/src/directsecp256k1hdwallet.ts @@ -1,4 +1,4 @@ -import { rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; +import { encodeSecp256k1Signature, rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; import { Bip39, EnglishMnemonic, @@ -10,7 +10,6 @@ import { Slip10Curve, } from "@cosmjs/crypto"; import { Bech32 } from "@cosmjs/encoding"; -import { encodeSecp256k1Signature } from "@cosmjs/launchpad"; import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx"; import { makeCosmoshubPath } from "./paths"; diff --git a/packages/proto-signing/src/directsecp256k1wallet.ts b/packages/proto-signing/src/directsecp256k1wallet.ts index 9207d9c6..2193bf61 100644 --- a/packages/proto-signing/src/directsecp256k1wallet.ts +++ b/packages/proto-signing/src/directsecp256k1wallet.ts @@ -1,7 +1,6 @@ -import { rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; +import { encodeSecp256k1Signature, rawSecp256k1PubkeyToRawAddress } from "@cosmjs/amino"; import { Secp256k1, sha256 } from "@cosmjs/crypto"; import { Bech32 } from "@cosmjs/encoding"; -import { encodeSecp256k1Signature } from "@cosmjs/launchpad"; import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx"; import { AccountData, DirectSignResponse, OfflineDirectSigner } from "./signer"; diff --git a/packages/proto-signing/src/signer.ts b/packages/proto-signing/src/signer.ts index 072f3255..65b6e6ab 100644 --- a/packages/proto-signing/src/signer.ts +++ b/packages/proto-signing/src/signer.ts @@ -1,4 +1,5 @@ -import { OfflineSigner as OfflineAminoSigner, StdSignature } from "@cosmjs/launchpad"; +import { StdSignature } from "@cosmjs/amino"; +import { OfflineSigner as OfflineAminoSigner } from "@cosmjs/launchpad"; import { SignDoc } from "./codec/cosmos/tx/v1beta1/tx";