Add SigningCosmosClient.appendSignature

This commit is contained in:
Simon Warta 2020-12-21 14:59:42 +01:00
parent cb752e89d8
commit 4c60f883fa
5 changed files with 108 additions and 0 deletions

View File

@ -22,6 +22,8 @@
remove such functionality from `CosmosClient.searchTx`. remove such functionality from `CosmosClient.searchTx`.
- @cosmjs/launchpad: Add `SigningCosmosClient.sign` method for signing without - @cosmjs/launchpad: Add `SigningCosmosClient.sign` method for signing without
broadcasting. broadcasting.
- @cosmjs/launchpad: Add `SigningCosmosClient.appendSignature` method creating
transactions with multiple signatures.
- @cosmjs/launchpad: Add support for undefined memo in `makeSignDoc`. - @cosmjs/launchpad: Add support for undefined memo in `makeSignDoc`.
- @cosmjs/launchpad-ledger: `LedgerSigner.sign` method renamed `signAmino`. - @cosmjs/launchpad-ledger: `LedgerSigner.sign` method renamed `signAmino`.
- @cosmjs/proto-signing: Add new package for handling transaction signing with - @cosmjs/proto-signing: Add new package for handling transaction signing with

View File

@ -14,6 +14,7 @@ import {
makeRandomAddress, makeRandomAddress,
pendingWithoutLaunchpad, pendingWithoutLaunchpad,
} from "./testutils.spec"; } from "./testutils.spec";
import { makeCosmoshubPath } from "./wallet";
describe("SigningCosmosClient", () => { describe("SigningCosmosClient", () => {
describe("makeReadOnly", () => { describe("makeReadOnly", () => {
@ -222,4 +223,65 @@ describe("SigningCosmosClient", () => {
assertIsBroadcastTxSuccess(broadcastResult); assertIsBroadcastTxSuccess(broadcastResult);
}); });
}); });
describe("appendSignature", () => {
it("works", async () => {
pendingWithoutLaunchpad();
const wallet0 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0));
const wallet1 = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1));
const client0 = new SigningCosmosClient(launchpad.endpoint, faucet.address0, wallet0);
const client1 = new SigningCosmosClient(launchpad.endpoint, faucet.address1, wallet1);
const msg1: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address0,
to_address: makeRandomAddress(),
amount: coins(1234567, "ucosm"),
},
};
const msg2: MsgSend = {
type: "cosmos-sdk/MsgSend",
value: {
from_address: faucet.address1,
to_address: makeRandomAddress(),
amount: coins(1234567, "ucosm"),
},
};
const fee = {
amount: coins(2000, "ucosm"),
gas: "160000", // 2*80k
};
const memo = "This must be authorized by the two of us";
const signed = await client0.sign([msg1, msg2], fee, memo);
expect(signed.msg).toEqual([msg1, msg2]);
expect(signed.fee).toEqual(fee);
expect(signed.memo).toEqual(memo);
expect(signed.signatures).toEqual([
{
pub_key: faucet.pubkey0,
signature: jasmine.stringMatching(base64Matcher),
},
]);
const cosigned = await client1.appendSignature(signed);
expect(cosigned.msg).toEqual([msg1, msg2]);
expect(cosigned.fee).toEqual(fee);
expect(cosigned.memo).toEqual(memo);
expect(cosigned.signatures).toEqual([
{
pub_key: faucet.pubkey0,
signature: jasmine.stringMatching(base64Matcher),
},
{
pub_key: faucet.pubkey1,
signature: jasmine.stringMatching(base64Matcher),
},
]);
const broadcastResult = await client0.broadcastTx(cosigned);
assertIsBroadcastTxSuccess(broadcastResult);
});
});
}); });

View File

@ -1,4 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import equals from "fast-deep-equal";
import { Coin } from "./coins"; import { Coin } from "./coins";
import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./cosmosclient"; import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./cosmosclient";
import { makeSignDoc } from "./encoding"; import { makeSignDoc } from "./encoding";
@ -102,4 +104,25 @@ export class SigningCosmosClient extends CosmosClient {
const { signed, signature } = await this.signer.signAmino(this.senderAddress, signDoc); const { signed, signature } = await this.signer.signAmino(this.senderAddress, signDoc);
return makeStdTx(signed, signature); return makeStdTx(signed, signature);
} }
/**
* Gets account number and sequence from the API, creates a sign doc,
* creates a single signature and appends it to the existing signatures.
*/
public async appendSignature(signedTx: StdTx): Promise<StdTx> {
const { msg: msgs, fee, memo } = signedTx;
const { accountNumber, sequence } = await this.getSequence();
const chainId = await this.getChainId();
const signDoc = makeSignDoc(msgs, fee, chainId, memo, accountNumber, sequence);
const { signed, signature: additionalSignature } = await this.signer.signAmino(
this.senderAddress,
signDoc,
);
if (!equals(signDoc, signed)) {
throw new Error(
"The signed document differs from the one of the original transaction. This is not allowed since the resulting transaction will be invalid.",
);
}
return makeStdTx(signed, [...signedTx.signatures, additionalSignature]);
}
} }

View File

@ -42,6 +42,22 @@ export const faucet = {
type: "tendermint/PubKeySecp256k1", type: "tendermint/PubKeySecp256k1",
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ", value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
}, },
pubkey1: {
type: "tendermint/PubKeySecp256k1",
value: "AiDosfIbBi54XJ1QjCeApumcy/FjdtF+YhywPf3DKTx7",
},
pubkey2: {
type: "tendermint/PubKeySecp256k1",
value: "AzQg33JZqH7vSsm09esZY5bZvmzYwE/SY78cA0iLxpD7",
},
pubkey3: {
type: "tendermint/PubKeySecp256k1",
value: "A3gOAlB6aiRTCPvWMQg2+ZbGYNsLd8qlvV28m8p2UhY2",
},
pubkey4: {
type: "tendermint/PubKeySecp256k1",
value: "Aum2063ub/ErUnIUB36sK55LktGUStgcbSiaAnL1wadu",
},
address0: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", address0: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6",
address1: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5", address1: "cosmos10dyr9899g6t0pelew4nvf4j5c3jcgv0r73qga5",
address2: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k", address2: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",

View File

@ -58,4 +58,9 @@ export declare class SigningCosmosClient extends CosmosClient {
* creates a single signature and assembles the signed transaction. * creates a single signature and assembles the signed transaction.
*/ */
sign(msgs: readonly Msg[], fee: StdFee, memo?: string): Promise<StdTx>; sign(msgs: readonly Msg[], fee: StdFee, memo?: string): Promise<StdTx>;
/**
* Gets account number and sequence from the API, creates a sign doc,
* creates a single signature and appends it to the existing signatures.
*/
appendSignature(signedTx: StdTx): Promise<StdTx>;
} }