diff --git a/CHANGELOG.md b/CHANGELOG.md index 33106c95..99187b7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ `BroadcastTxsResponse.logs` to `unknown[]`. - @cosmjs/launchpad: Export `StdSignDoc` and create helpers to make and serialize a `StdSignDoc`: `makeStdSignDoc` and `serializeSignDoc`. +- @cosmjs/launchpad: Let `OfflineSigner.sign` take an `StdSignDoc` instead of an + encoded message. - @cosmjs/launchpad-ledger: Add package supporting Ledger device integration for Launchpad. Two new classes are provided: `LedgerSigner` (for most use cases) and `LaunchpadLedger` for more fine-grained access. diff --git a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts index b9d6d359..bf6ae681 100644 --- a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts @@ -6,7 +6,7 @@ import { isBroadcastTxFailure, isMsgSend, LcdClient, - makeSignBytes, + makeStdSignDoc, MsgSend, Secp256k1Wallet, } from "@cosmjs/launchpad"; @@ -103,8 +103,8 @@ describe("CosmWasmClient.searchTx", () => { }; const { accountNumber, sequence } = await client.getSequence(); const chainId = await client.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(alice.address0, signBytes); + const signDoc = makeStdSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(alice.address0, signDoc); const tx: CosmosSdkTx = { type: "cosmos-sdk/StdTx", value: { diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index 868aa02a..fcdf8e6d 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -3,7 +3,7 @@ import { Sha256 } from "@cosmjs/crypto"; import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@cosmjs/encoding"; import { assertIsBroadcastTxSuccess, - makeSignBytes, + makeStdSignDoc, MsgSend, Secp256k1Wallet, StdFee, @@ -236,8 +236,8 @@ describe("CosmWasmClient", () => { const chainId = await client.getChainId(); const { accountNumber, sequence } = await client.getSequence(alice.address0); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(alice.address0, signBytes); + const signDoc = makeStdSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(alice.address0, signDoc); const signedTx = { msg: [sendMsg], fee: fee, diff --git a/packages/cosmwasm/src/lcdapi/wasm.spec.ts b/packages/cosmwasm/src/lcdapi/wasm.spec.ts index 0614d990..83790e1a 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.spec.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.spec.ts @@ -10,7 +10,7 @@ import { coin, coins, LcdClient, - makeSignBytes, + makeStdSignDoc, OfflineSigner, Secp256k1Wallet, setupAuthExtension, @@ -125,8 +125,8 @@ async function executeContract( }; const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value; - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await signer.sign(alice.address0, signBytes); + const signDoc = makeStdSignDoc([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const signature = await signer.sign(alice.address0, signDoc); const signedTx = makeSignedTx(theMsg, fee, memo, signature); return client.broadcastTx(signedTx); } diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index d0fff774..0b27a2c3 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -11,7 +11,7 @@ import { GasLimits, GasPrice, isBroadcastTxFailure, - makeSignBytes, + makeStdSignDoc, Msg, MsgSend, OfflineSigner, @@ -360,8 +360,8 @@ export class SigningCosmWasmClient extends CosmWasmClient { public async signAndBroadcast(msgs: readonly Msg[], fee: StdFee, memo = ""): Promise { const { accountNumber, sequence } = await this.getSequence(); const chainId = await this.getChainId(); - const signBytes = makeSignBytes(msgs, fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); + const signDoc = makeStdSignDoc(msgs, fee, chainId, memo, accountNumber, sequence); + const signature = await this.signer.sign(this.senderAddress, signDoc); const signedTx: StdTx = { msg: msgs, fee: fee, diff --git a/packages/launchpad-ledger/demo/index.html b/packages/launchpad-ledger/demo/index.html index 7a04b1cd..7da23c11 100644 --- a/packages/launchpad-ledger/demo/index.html +++ b/packages/launchpad-ledger/demo/index.html @@ -34,7 +34,7 @@
-
diff --git a/packages/launchpad-ledger/src/demo/node.ts b/packages/launchpad-ledger/src/demo/node.ts index ef3b3bb1..6cd967e2 100644 --- a/packages/launchpad-ledger/src/demo/node.ts +++ b/packages/launchpad-ledger/src/demo/node.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { toBase64 } from "@cosmjs/encoding"; -import { makeCosmoshubPath, makeSignBytes, StdFee, StdSignature } from "@cosmjs/launchpad"; +import { makeCosmoshubPath, makeStdSignDoc, StdFee, StdSignature } from "@cosmjs/launchpad"; import { LedgerSigner } from "../ledgersigner"; @@ -48,7 +48,7 @@ export async function sign( }, }, ]; - const signBytes = makeSignBytes( + const signDoc = makeStdSignDoc( msgs, defaultFee, defaultChainId, @@ -56,5 +56,5 @@ export async function sign( accountNumber, defaultSequence, ); - return signer.sign(fromAddress, signBytes); + return signer.sign(fromAddress, signDoc); } diff --git a/packages/launchpad-ledger/src/demo/web.ts b/packages/launchpad-ledger/src/demo/web.ts index e656a871..c9297c47 100644 --- a/packages/launchpad-ledger/src/demo/web.ts +++ b/packages/launchpad-ledger/src/demo/web.ts @@ -1,5 +1,7 @@ -import { toBase64, toUtf8 } from "@cosmjs/encoding"; -import { AccountData, makeCosmoshubPath } from "@cosmjs/launchpad"; +import { toBase64 } from "@cosmjs/encoding"; +import { AccountData, makeCosmoshubPath, StdSignDoc } from "@cosmjs/launchpad"; +import { Uint53 } from "@cosmjs/math"; +import { assert } from "@cosmjs/utils"; import { LedgerSigner } from "../ledgersigner"; @@ -8,28 +10,37 @@ declare const document: any; let accounts: readonly AccountData[] = []; -function createMessage(accountNumber: number, address: string): string { - return `{ - "account_number": ${accountNumber}, - "chain_id": "testing", - "fee": { - "amount": [{ "amount": 100, "denom": "ucosm" }], - "gas": 250 +function createSignDoc(accountNumber: number, address: string): string { + const signDoc: StdSignDoc = { + // eslint-disable-next-line @typescript-eslint/naming-convention + chain_id: "testing", + // eslint-disable-next-line @typescript-eslint/naming-convention + account_number: `${accountNumber}`, + sequence: "0", + fee: { + amount: [{ amount: "100", denom: "ucosm" }], + gas: "250", }, - "memo": "Some memo", - "msgs": [{ - "type": "cosmos-sdk/MsgSend", - "value": { - "amount": [{ - "amount": "1234567", - "denom": "ucosm" - }], - "from_address": "${address}", - "to_address": "${address}" - } - }], - "sequence": 0 - }`; + memo: "Some memo", + msgs: [ + { + type: "cosmos-sdk/MsgSend", + value: { + amount: [ + { + amount: "1234567", + denom: "ucosm", + }, + ], + // eslint-disable-next-line @typescript-eslint/naming-convention + from_address: address, + // eslint-disable-next-line @typescript-eslint/naming-convention + to_address: address, + }, + }, + ], + }; + return JSON.stringify(signDoc, null, 2); } const signer = new LedgerSigner({ @@ -37,7 +48,9 @@ const signer = new LedgerSigner({ hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2)], }); -window.updateMessage = (accountNumber: number) => { +window.updateMessage = (accountNumberInput: unknown) => { + assert(typeof accountNumberInput === "string"); + const accountNumber = Uint53.fromString(accountNumberInput).toNumber(); const account = accounts[accountNumber]; if (account === undefined) { return; @@ -46,15 +59,15 @@ window.updateMessage = (accountNumber: number) => { const address = accounts[accountNumber].address; const addressInput = document.getElementById("address"); addressInput.value = address; - const messageTextArea = document.getElementById("message"); - messageTextArea.textContent = createMessage(accountNumber, address); + const signDocTextArea = document.getElementById("sign-doc"); + signDocTextArea.textContent = createSignDoc(accountNumber, address); }; window.getAccounts = async function getAccounts(): Promise { const accountNumberInput = document.getElementById("account-number"); const addressInput = document.getElementById("address"); const accountsDiv = document.getElementById("accounts"); - const messageTextArea = document.getElementById("message"); + const signDocTextArea = document.getElementById("sign-doc"); accountsDiv.textContent = "Loading..."; try { @@ -69,7 +82,7 @@ window.getAccounts = async function getAccounts(): Promise { accountNumberInput.value = accountNumber; const address = accounts[0].address; addressInput.value = address; - messageTextArea.textContent = createMessage(accountNumber, address); + signDocTextArea.textContent = createSignDoc(accountNumber, address); } catch (error) { accountsDiv.textContent = error; } @@ -81,9 +94,9 @@ window.sign = async function sign(): Promise { try { const address = document.getElementById("address").value; - const rawMessage = document.getElementById("message").textContent; - const message = JSON.stringify(JSON.parse(rawMessage)); - const signature = await signer.sign(address, toUtf8(message)); + const signDocJson = document.getElementById("sign-doc").textContent; + const signDoc: StdSignDoc = JSON.parse(signDocJson); + const signature = await signer.sign(address, signDoc); signatureDiv.textContent = JSON.stringify(signature, null, "\t"); } catch (error) { signatureDiv.textContent = error; diff --git a/packages/launchpad-ledger/src/ledgersigner.ts b/packages/launchpad-ledger/src/ledgersigner.ts index da6892ae..c09fc03b 100644 --- a/packages/launchpad-ledger/src/ledgersigner.ts +++ b/packages/launchpad-ledger/src/ledgersigner.ts @@ -5,7 +5,9 @@ import { makeCosmoshubPath, OfflineSigner, StdSignature, + StdSignDoc, } from "@cosmjs/launchpad"; +import { serializeSignDoc } from "@cosmjs/launchpad"; import { LaunchpadLedger, LaunchpadLedgerOptions } from "./launchpadledger"; @@ -34,14 +36,15 @@ export class LedgerSigner implements OfflineSigner { return this.accounts; } - public async sign(address: string, message: Uint8Array): Promise { + public async sign(signerAddress: string, signDoc: StdSignDoc): Promise { const accounts = this.accounts || (await this.getAccounts()); - const accountIndex = accounts.findIndex((account) => account.address === address); + const accountIndex = accounts.findIndex((account) => account.address === signerAddress); if (accountIndex === -1) { - throw new Error(`Address ${address} not found in wallet`); + throw new Error(`Address ${signerAddress} not found in wallet`); } + const message = serializeSignDoc(signDoc); const accountForAddress = accounts[accountIndex]; const hdPath = this.hdPaths[accountIndex]; const signature = await this.ledger.sign(message, hdPath); diff --git a/packages/launchpad-ledger/types/ledgersigner.d.ts b/packages/launchpad-ledger/types/ledgersigner.d.ts index 1ebfdcdd..ffc9a609 100644 --- a/packages/launchpad-ledger/types/ledgersigner.d.ts +++ b/packages/launchpad-ledger/types/ledgersigner.d.ts @@ -1,4 +1,4 @@ -import { AccountData, OfflineSigner, StdSignature } from "@cosmjs/launchpad"; +import { AccountData, OfflineSigner, StdSignature, StdSignDoc } from "@cosmjs/launchpad"; import { LaunchpadLedgerOptions } from "./launchpadledger"; export declare class LedgerSigner implements OfflineSigner { private readonly ledger; @@ -6,5 +6,5 @@ export declare class LedgerSigner implements OfflineSigner { private accounts?; constructor(options?: LaunchpadLedgerOptions); getAccounts(): Promise; - sign(address: string, message: Uint8Array): Promise; + sign(signerAddress: string, signDoc: StdSignDoc): Promise; } diff --git a/packages/launchpad/src/cosmosclient.searchtx.spec.ts b/packages/launchpad/src/cosmosclient.searchtx.spec.ts index f3bbda31..8d247634 100644 --- a/packages/launchpad/src/cosmosclient.searchtx.spec.ts +++ b/packages/launchpad/src/cosmosclient.searchtx.spec.ts @@ -3,7 +3,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { coins } from "./coins"; import { CosmosClient, isBroadcastTxFailure } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; +import { makeStdSignDoc } from "./encoding"; import { LcdClient } from "./lcdapi"; import { isMsgSend, MsgSend } from "./msgs"; import { Secp256k1Wallet } from "./secp256k1wallet"; @@ -55,8 +55,8 @@ describe("CosmosClient.searchTx", () => { }; const { accountNumber, sequence } = await client.getSequence(); const chainId = await client.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(walletAddress, signBytes); + const signDoc = makeStdSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(walletAddress, signDoc); const tx: CosmosSdkTx = { type: "cosmos-sdk/StdTx", value: { diff --git a/packages/launchpad/src/cosmosclient.spec.ts b/packages/launchpad/src/cosmosclient.spec.ts index 03eb672f..47368a87 100644 --- a/packages/launchpad/src/cosmosclient.spec.ts +++ b/packages/launchpad/src/cosmosclient.spec.ts @@ -3,7 +3,7 @@ import { sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; import { assertIsBroadcastTxSuccess, CosmosClient, PrivateCosmosClient } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; +import { makeStdSignDoc } from "./encoding"; import { findAttribute } from "./logs"; import { MsgSend } from "./msgs"; import { Secp256k1Wallet } from "./secp256k1wallet"; @@ -229,8 +229,8 @@ describe("CosmosClient", () => { const chainId = await client.getChainId(); const { accountNumber, sequence } = await client.getSequence(faucet.address); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(walletAddress, signBytes); + const signDoc = makeStdSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(walletAddress, signDoc); const signedTx = { msg: [sendMsg], fee: fee, diff --git a/packages/launchpad/src/lcdapi/distribution.spec.ts b/packages/launchpad/src/lcdapi/distribution.spec.ts index 1f6165dd..bc68176e 100644 --- a/packages/launchpad/src/lcdapi/distribution.spec.ts +++ b/packages/launchpad/src/lcdapi/distribution.spec.ts @@ -4,7 +4,7 @@ import { sleep } from "@cosmjs/utils"; import { coin, coins } from "../coins"; import { assertIsBroadcastTxSuccess } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeStdSignDoc } from "../encoding"; import { MsgDelegate } from "../msgs"; import { Secp256k1Wallet } from "../secp256k1wallet"; import { SigningCosmosClient } from "../signingcosmosclient"; @@ -45,8 +45,8 @@ describe("DistributionExtension", () => { }; const memo = "Test delegation for wasmd"; const { accountNumber, sequence } = await client.getSequence(); - const signBytes = makeSignBytes([msg], defaultFee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(faucet.address, signBytes); + const signDoc = makeStdSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(faucet.address, signDoc); const tx = { msg: [msg], fee: defaultFee, diff --git a/packages/launchpad/src/lcdapi/gov.spec.ts b/packages/launchpad/src/lcdapi/gov.spec.ts index c6cd10d1..0af73b64 100644 --- a/packages/launchpad/src/lcdapi/gov.spec.ts +++ b/packages/launchpad/src/lcdapi/gov.spec.ts @@ -3,7 +3,7 @@ import { sleep } from "@cosmjs/utils"; import { coins } from "../coins"; import { assertIsBroadcastTxSuccess } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeStdSignDoc } from "../encoding"; import { Secp256k1Wallet } from "../secp256k1wallet"; import { SigningCosmosClient } from "../signingcosmosclient"; import { @@ -50,7 +50,7 @@ describe("GovExtension", () => { }; const proposalMemo = "Test proposal for wasmd"; const { accountNumber: proposalAccountNumber, sequence: proposalSequence } = await client.getSequence(); - const proposalSignBytes = makeSignBytes( + const proposalSignDoc = makeStdSignDoc( [proposalMsg], defaultFee, chainId, @@ -58,7 +58,7 @@ describe("GovExtension", () => { proposalAccountNumber, proposalSequence, ); - const proposalSignature = await wallet.sign(faucet.address, proposalSignBytes); + const proposalSignature = await wallet.sign(faucet.address, proposalSignDoc); const proposalTx = { msg: [proposalMsg], fee: defaultFee, @@ -82,7 +82,7 @@ describe("GovExtension", () => { }; const voteMemo = "Test vote for wasmd"; const { accountNumber: voteAccountNumber, sequence: voteSequence } = await client.getSequence(); - const voteSignBytes = makeSignBytes( + const voteSignDoc = makeStdSignDoc( [voteMsg], defaultFee, chainId, @@ -90,7 +90,7 @@ describe("GovExtension", () => { voteAccountNumber, voteSequence, ); - const voteSignature = await wallet.sign(faucet.address, voteSignBytes); + const voteSignature = await wallet.sign(faucet.address, voteSignDoc); const voteTx = { msg: [voteMsg], fee: defaultFee, diff --git a/packages/launchpad/src/lcdapi/lcdclient.spec.ts b/packages/launchpad/src/lcdapi/lcdclient.spec.ts index 42418e18..a1043d4a 100644 --- a/packages/launchpad/src/lcdapi/lcdclient.spec.ts +++ b/packages/launchpad/src/lcdapi/lcdclient.spec.ts @@ -3,7 +3,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { Coin } from "../coins"; import { isBroadcastTxFailure } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeStdSignDoc } from "../encoding"; import { parseLogs } from "../logs"; import { MsgSend } from "../msgs"; import { Secp256k1Wallet } from "../secp256k1wallet"; @@ -239,8 +239,8 @@ describe("LcdClient", () => { }; const { accountNumber, sequence } = await client.getSequence(); const chainId = await client.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(walletAddress, signBytes); + const signDoc = makeStdSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(walletAddress, signDoc); const signedTx = { msg: [sendMsg], fee: fee, @@ -537,8 +537,8 @@ describe("LcdClient", () => { const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number, sequence } = (await client.auth.account(faucet.address)).result.value; - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await wallet.sign(walletAddress, signBytes); + const signDoc = makeStdSignDoc([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const signature = await wallet.sign(walletAddress, signDoc); const signedTx = makeSignedTx(theMsg, fee, memo, signature); const result = await client.broadcastTx(signedTx); expect(result.code).toBeUndefined(); @@ -594,12 +594,12 @@ describe("LcdClient", () => { const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; const { account_number: an3, sequence: sequence3 } = (await client.auth.account(address3)).result.value; - const signBytes1 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an2, sequence2); - const signBytes3 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an3, sequence3); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); - const signature3 = await account3.sign(address3, signBytes3); + const signDoc1 = makeStdSignDoc([theMsg], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeStdSignDoc([theMsg], fee, wasmd.chainId, memo, an2, sequence2); + const signDoc3 = makeStdSignDoc([theMsg], fee, wasmd.chainId, memo, an3, sequence3); + const signature1 = await account1.sign(address1, signDoc1); + const signature2 = await account2.sign(address2, signDoc2); + const signature3 = await account3.sign(address3, signDoc3); const signedTx = { msg: [theMsg], fee: fee, @@ -658,13 +658,13 @@ describe("LcdClient", () => { const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number, sequence } = (await client.auth.account(walletAddress)).result.value; - const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); - const signature1 = await wallet.sign(walletAddress, signBytes); + const signDoc = makeStdSignDoc([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); + const signature = await wallet.sign(walletAddress, signDoc); const signedTx = { msg: [msg1, msg2], fee: fee, memo: memo, - signatures: [signature1], + signatures: [signature], }; const broadcastResult = await client.broadcastTx(signedTx); expect(broadcastResult.code).toBeUndefined(); @@ -722,10 +722,10 @@ describe("LcdClient", () => { const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; - const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); + const signDoc1 = makeStdSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeStdSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); + const signature1 = await account1.sign(address1, signDoc1); + const signature2 = await account2.sign(address2, signDoc2); const signedTx = { msg: [msg2, msg1], fee: fee, @@ -793,10 +793,10 @@ describe("LcdClient", () => { const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; - const signBytes1 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); + const signDoc1 = makeStdSignDoc([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeStdSignDoc([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2); + const signature1 = await account1.sign(address1, signDoc1); + const signature2 = await account2.sign(address2, signDoc2); const signedTx = { msg: [msg1, msg2], fee: fee, @@ -859,10 +859,10 @@ describe("LcdClient", () => { const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; - const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); + const signDoc1 = makeStdSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeStdSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); + const signature1 = await account1.sign(address1, signDoc1); + const signature2 = await account2.sign(address2, signDoc2); const signedTx = { msg: [msg2, msg1], fee: fee, diff --git a/packages/launchpad/src/lcdapi/staking.spec.ts b/packages/launchpad/src/lcdapi/staking.spec.ts index db75603c..5efcff6c 100644 --- a/packages/launchpad/src/lcdapi/staking.spec.ts +++ b/packages/launchpad/src/lcdapi/staking.spec.ts @@ -3,7 +3,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { coin, coins } from "../coins"; import { assertIsBroadcastTxSuccess } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeStdSignDoc } from "../encoding"; import { MsgDelegate, MsgUndelegate } from "../msgs"; import { Secp256k1Wallet } from "../secp256k1wallet"; import { SigningCosmosClient } from "../signingcosmosclient"; @@ -46,8 +46,8 @@ describe("StakingExtension", () => { }; const memo = "Test delegation for wasmd"; const { accountNumber, sequence } = await client.getSequence(); - const signBytes = makeSignBytes([msg], defaultFee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(faucet.address, signBytes); + const signDoc = makeStdSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(faucet.address, signDoc); const tx = { msg: [msg], fee: defaultFee, @@ -69,8 +69,8 @@ describe("StakingExtension", () => { }; const memo = "Test undelegation for wasmd"; const { accountNumber, sequence } = await client.getSequence(); - const signBytes = makeSignBytes([msg], defaultFee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(faucet.address, signBytes); + const signDoc = makeStdSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence); + const signature = await wallet.sign(faucet.address, signDoc); const tx = { msg: [msg], fee: defaultFee, diff --git a/packages/launchpad/src/secp256k1wallet.spec.ts b/packages/launchpad/src/secp256k1wallet.spec.ts index a16e8534..d2080321 100644 --- a/packages/launchpad/src/secp256k1wallet.spec.ts +++ b/packages/launchpad/src/secp256k1wallet.spec.ts @@ -1,6 +1,8 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; -import { fromBase64, fromHex, toAscii } from "@cosmjs/encoding"; +import { fromBase64, fromHex } from "@cosmjs/encoding"; +import { serializeSignDoc, StdSignDoc } from "./encoding"; import { extractKdfConfiguration, Secp256k1Wallet } from "./secp256k1wallet"; import { base64Matcher } from "./testutils.spec"; import { executeKdf, KdfConfiguration } from "./wallet"; @@ -110,11 +112,18 @@ describe("Secp256k1Wallet", () => { describe("sign", () => { it("resolves to valid signature if enabled", async () => { const wallet = await Secp256k1Wallet.fromMnemonic(defaultMnemonic); - const message = toAscii("foo bar"); - const signature = await wallet.sign(defaultAddress, message); + const signDoc: StdSignDoc = { + msgs: [], + fee: { amount: [], gas: "23" }, + chain_id: "foochain", + memo: "hello, world", + account_number: "7", + sequence: "54", + }; + const signature = await wallet.sign(defaultAddress, signDoc); const valid = await Secp256k1.verifySignature( Secp256k1Signature.fromFixedLength(fromBase64(signature.signature)), - new Sha256(message).digest(), + new Sha256(serializeSignDoc(signDoc)).digest(), defaultPubkey, ); expect(valid).toEqual(true); diff --git a/packages/launchpad/src/secp256k1wallet.ts b/packages/launchpad/src/secp256k1wallet.ts index e788250a..6ffab6fa 100644 --- a/packages/launchpad/src/secp256k1wallet.ts +++ b/packages/launchpad/src/secp256k1wallet.ts @@ -5,6 +5,7 @@ import { pathToString, Random, Secp256k1, + Sha256, Slip10, Slip10Curve, stringToPath, @@ -13,8 +14,9 @@ import { fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding"; import { assert, isNonNullObject } from "@cosmjs/utils"; import { rawSecp256k1PubkeyToAddress } from "./address"; +import { serializeSignDoc, StdSignDoc } from "./encoding"; import { encodeSecp256k1Signature } from "./signature"; -import { AccountData, OfflineSigner, PrehashType } from "./signer"; +import { AccountData, OfflineSigner } from "./signer"; import { StdSignature } from "./types"; import { decrypt, @@ -23,7 +25,6 @@ import { executeKdf, KdfConfiguration, makeCosmoshubPath, - prehash, supportedAlgorithms, } from "./wallet"; @@ -255,16 +256,12 @@ export class Secp256k1Wallet implements OfflineSigner { ]; } - public async sign( - address: string, - message: Uint8Array, - prehashType: PrehashType = "sha256", - ): Promise { - if (address !== this.address) { - throw new Error(`Address ${address} not found in wallet`); + public async sign(signerAddress: string, signDoc: StdSignDoc): Promise { + if (signerAddress !== this.address) { + throw new Error(`Address ${signerAddress} not found in wallet`); } - const hashedMessage = prehash(message, prehashType); - const signature = await Secp256k1.createSignature(hashedMessage, this.privkey); + const message = new Sha256(serializeSignDoc(signDoc)).digest(); + const signature = await Secp256k1.createSignature(message, this.privkey); const signatureBytes = new Uint8Array([...signature.r(32), ...signature.s(32)]); return encodeSecp256k1Signature(this.pubkey, signatureBytes); } diff --git a/packages/launchpad/src/signer.ts b/packages/launchpad/src/signer.ts index 93e1ce59..1217d8f6 100644 --- a/packages/launchpad/src/signer.ts +++ b/packages/launchpad/src/signer.ts @@ -1,3 +1,4 @@ +import { StdSignDoc } from "./encoding"; import { StdSignature } from "./types"; export type PrehashType = "sha256" | "sha512" | null; @@ -19,6 +20,13 @@ export interface OfflineSigner { /** * Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled. + * + * @param signerAddress The address of the account that should sign the transaction + * @param signDoc The content that should be signed */ - readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise; + readonly sign: ( + signerAddress: string, + signDoc: StdSignDoc, + prehashType?: PrehashType, + ) => Promise; } diff --git a/packages/launchpad/src/signingcosmosclient.ts b/packages/launchpad/src/signingcosmosclient.ts index 718e7e48..fa9ba345 100644 --- a/packages/launchpad/src/signingcosmosclient.ts +++ b/packages/launchpad/src/signingcosmosclient.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Coin } from "./coins"; import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; +import { makeStdSignDoc } from "./encoding"; import { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas"; import { BroadcastMode } from "./lcdapi"; import { Msg, MsgSend } from "./msgs"; @@ -88,8 +88,8 @@ export class SigningCosmosClient extends CosmosClient { public async signAndBroadcast(msgs: readonly Msg[], fee: StdFee, memo = ""): Promise { const { accountNumber, sequence } = await this.getSequence(); const chainId = await this.getChainId(); - const signBytes = makeSignBytes(msgs, fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); + const signDoc = makeStdSignDoc(msgs, fee, chainId, memo, accountNumber, sequence); + const signature = await this.signer.sign(this.senderAddress, signDoc); const signedTx: StdTx = { msg: msgs, fee: fee, diff --git a/packages/launchpad/types/secp256k1wallet.d.ts b/packages/launchpad/types/secp256k1wallet.d.ts index feed1eef..aa241a86 100644 --- a/packages/launchpad/types/secp256k1wallet.d.ts +++ b/packages/launchpad/types/secp256k1wallet.d.ts @@ -1,5 +1,6 @@ import { HdPath } from "@cosmjs/crypto"; -import { AccountData, OfflineSigner, PrehashType } from "./signer"; +import { StdSignDoc } from "./encoding"; +import { AccountData, OfflineSigner } from "./signer"; import { StdSignature } from "./types"; import { EncryptionConfiguration, KdfConfiguration } from "./wallet"; /** @@ -86,7 +87,7 @@ export declare class Secp256k1Wallet implements OfflineSigner { get mnemonic(): string; private get address(); getAccounts(): Promise; - sign(address: string, message: Uint8Array, prehashType?: PrehashType): Promise; + sign(signerAddress: string, signDoc: StdSignDoc): Promise; /** * Generates an encrypted serialization of this wallet. * diff --git a/packages/launchpad/types/signer.d.ts b/packages/launchpad/types/signer.d.ts index d21ed77a..2b0948a4 100644 --- a/packages/launchpad/types/signer.d.ts +++ b/packages/launchpad/types/signer.d.ts @@ -1,3 +1,4 @@ +import { StdSignDoc } from "./encoding"; import { StdSignature } from "./types"; export declare type PrehashType = "sha256" | "sha512" | null; export declare type Algo = "secp256k1" | "ed25519" | "sr25519"; @@ -14,6 +15,13 @@ export interface OfflineSigner { readonly getAccounts: () => Promise; /** * Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled. + * + * @param signerAddress The address of the account that should sign the transaction + * @param signDoc The content that should be signed */ - readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise; + readonly sign: ( + signerAddress: string, + signDoc: StdSignDoc, + prehashType?: PrehashType, + ) => Promise; }