Let OfflineSigner.sign take a StdSignDoc instead of an encoded message

This commit is contained in:
Simon Warta
2020-09-24 13:54:13 +02:00
parent 6c116f3d5a
commit bc890bee73
22 changed files with 160 additions and 119 deletions
@@ -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: {
+3 -3
View File
@@ -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,
@@ -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,
+5 -5
View File
@@ -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,
+26 -26
View File
@@ -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,
@@ -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,
+13 -4
View File
@@ -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);
+8 -11
View File
@@ -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<StdSignature> {
if (address !== this.address) {
throw new Error(`Address ${address} not found in wallet`);
public async sign(signerAddress: string, signDoc: StdSignDoc): Promise<StdSignature> {
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);
}
+9 -1
View File
@@ -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<StdSignature>;
readonly sign: (
signerAddress: string,
signDoc: StdSignDoc,
prehashType?: PrehashType,
) => Promise<StdSignature>;
}
@@ -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<BroadcastTxResult> {
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,