From 450480791bf9506484d40348ebb28a3261c6a019 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 26 Jan 2022 13:44:53 +0100 Subject: [PATCH 1/8] adding show address in ledgerSigner --- .gitignore | 1 + CHANGELOG.md | 1 + packages/ledger-amino/src/launchpadledger.ts | 11 +++++++++++ packages/ledger-amino/src/ledgersigner.ts | 19 +++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/.gitignore b/.gitignore index 89489961..b7c70c8f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ node_modules/ # IDE-specific .vscode/ +.idea/ # demo mnemonics from cli *.key diff --git a/CHANGELOG.md b/CHANGELOG.md index 9139b3a5..c620743f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to JavaScript objects and the JSON representation of `cosmwasm_std::Binary` (base64). - @cosmjs/cosmwasm-stargate: Export `WasmExtension` and `setupWasmExtension`. +- @cosmjs/ledger-amino: Added `showAddress` in LedgerSigner. ### Changed diff --git a/packages/ledger-amino/src/launchpadledger.ts b/packages/ledger-amino/src/launchpadledger.ts index 37d2d0bf..962d3369 100644 --- a/packages/ledger-amino/src/launchpadledger.ts +++ b/packages/ledger-amino/src/launchpadledger.ts @@ -4,6 +4,7 @@ import { fromUtf8 } from "@cosmjs/encoding"; import { assert } from "@cosmjs/utils"; import Transport from "@ledgerhq/hw-transport"; import CosmosApp, { + AddressAndPublicKeyResponse, AppInfoResponse, PublicKeyResponse, SignResponse, @@ -166,6 +167,16 @@ export class LaunchpadLedger { await this.verifyCosmosAppIsOpen(); } + public async verifyAddress(hdPath?: HdPath): Promise { + await this.verifyDeviceIsReady(); + + const hdPathToUse = hdPath || this.hdPaths[0]; + // ledger-cosmos-js hardens the first three indices + const response = await this.app.showAddressAndPubKey(unharden(hdPathToUse), this.prefix); + this.handleLedgerErrors(response); + return response as AddressAndPublicKeyResponse; + } + private handleLedgerErrors( /* eslint-disable @typescript-eslint/naming-convention */ { diff --git a/packages/ledger-amino/src/ledgersigner.ts b/packages/ledger-amino/src/ledgersigner.ts index dc3cd0d8..1e165020 100644 --- a/packages/ledger-amino/src/ledgersigner.ts +++ b/packages/ledger-amino/src/ledgersigner.ts @@ -4,6 +4,7 @@ import { encodeSecp256k1Signature, makeCosmoshubPath, OfflineAminoSigner, + Secp256k1Pubkey, serializeSignDoc, StdSignDoc, } from "@cosmjs/amino"; @@ -12,6 +13,11 @@ import Transport from "@ledgerhq/hw-transport"; import { LaunchpadLedger, LaunchpadLedgerOptions } from "./launchpadledger"; +export interface AddressAndPubkey { + readonly address: string; + readonly pubkey: Secp256k1Pubkey; +} + export class LedgerSigner implements OfflineAminoSigner { private readonly ledger: LaunchpadLedger; private readonly hdPaths: readonly HdPath[]; @@ -37,6 +43,19 @@ export class LedgerSigner implements OfflineAminoSigner { return this.accounts; } + public async showAddress(path: HdPath): Promise { + const response = await this.ledger.verifyAddress(path); + + if (response.error_message) { + throw new Error(response.error_message); + } + + return { + address: response.address, + pubkey: response.compressed_pk, + }; + } + public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise { const accounts = this.accounts || (await this.getAccounts()); const accountIndex = accounts.findIndex((account) => account.address === signerAddress); From 08bb7e4e7390d5d7a7d4a22e9e61b7d80691afc7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 25 Jan 2022 14:48:47 +0100 Subject: [PATCH 2/8] Add missing pubkey encoding --- packages/ledger-amino/src/ledgersigner.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/ledger-amino/src/ledgersigner.ts b/packages/ledger-amino/src/ledgersigner.ts index 1e165020..1af8726f 100644 --- a/packages/ledger-amino/src/ledgersigner.ts +++ b/packages/ledger-amino/src/ledgersigner.ts @@ -1,6 +1,7 @@ import { AccountData, AminoSignResponse, + encodeSecp256k1Pubkey, encodeSecp256k1Signature, makeCosmoshubPath, OfflineAminoSigner, @@ -52,7 +53,7 @@ export class LedgerSigner implements OfflineAminoSigner { return { address: response.address, - pubkey: response.compressed_pk, + pubkey: encodeSecp256k1Pubkey(response.compressed_pk), }; } From e48101f8a7c225be89b937400548068787633381 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 26 Jan 2022 13:45:15 +0100 Subject: [PATCH 3/8] Refactor to LaunchpadLedger.showAddress --- CHANGELOG.md | 3 ++- packages/ledger-amino/src/index.ts | 2 +- packages/ledger-amino/src/launchpadledger.ts | 16 +++++++++++++--- packages/ledger-amino/src/ledgersigner.ts | 20 ++------------------ 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c620743f..647324b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to JavaScript objects and the JSON representation of `cosmwasm_std::Binary` (base64). - @cosmjs/cosmwasm-stargate: Export `WasmExtension` and `setupWasmExtension`. -- @cosmjs/ledger-amino: Added `showAddress` in LedgerSigner. +- @cosmjs/ledger-amino: Added `LedgerSigner.showAddress` and + `LaunchpadLedger.showAddress` to show the user's address in the Ledger screen. ### Changed diff --git a/packages/ledger-amino/src/index.ts b/packages/ledger-amino/src/index.ts index 155dfd14..4261666b 100644 --- a/packages/ledger-amino/src/index.ts +++ b/packages/ledger-amino/src/index.ts @@ -1,2 +1,2 @@ -export { LaunchpadLedger } from "./launchpadledger"; +export { AddressAndPubkey, LaunchpadLedger } from "./launchpadledger"; export { LedgerSigner } from "./ledgersigner"; diff --git a/packages/ledger-amino/src/launchpadledger.ts b/packages/ledger-amino/src/launchpadledger.ts index 962d3369..d2f17d0c 100644 --- a/packages/ledger-amino/src/launchpadledger.ts +++ b/packages/ledger-amino/src/launchpadledger.ts @@ -1,4 +1,4 @@ -import { makeCosmoshubPath } from "@cosmjs/amino"; +import { encodeSecp256k1Pubkey, makeCosmoshubPath, Secp256k1Pubkey } from "@cosmjs/amino"; import { HdPath, Secp256k1Signature } from "@cosmjs/crypto"; import { fromUtf8 } from "@cosmjs/encoding"; import { assert } from "@cosmjs/utils"; @@ -52,6 +52,11 @@ export interface LaunchpadLedgerOptions { readonly minLedgerAppVersion?: string; } +export interface AddressAndPubkey { + readonly address: string; + readonly pubkey: Secp256k1Pubkey; +} + export class LaunchpadLedger { private readonly testModeAllowed: boolean; private readonly hdPaths: readonly HdPath[]; @@ -167,14 +172,19 @@ export class LaunchpadLedger { await this.verifyCosmosAppIsOpen(); } - public async verifyAddress(hdPath?: HdPath): Promise { + public async showAddress(hdPath?: HdPath): Promise { await this.verifyDeviceIsReady(); const hdPathToUse = hdPath || this.hdPaths[0]; // ledger-cosmos-js hardens the first three indices const response = await this.app.showAddressAndPubKey(unharden(hdPathToUse), this.prefix); this.handleLedgerErrors(response); - return response as AddressAndPublicKeyResponse; + // eslint-disable-next-line @typescript-eslint/naming-convention + const { address, compressed_pk } = response as AddressAndPublicKeyResponse; + return { + address: address, + pubkey: encodeSecp256k1Pubkey(compressed_pk), + }; } private handleLedgerErrors( diff --git a/packages/ledger-amino/src/ledgersigner.ts b/packages/ledger-amino/src/ledgersigner.ts index 1af8726f..a5200148 100644 --- a/packages/ledger-amino/src/ledgersigner.ts +++ b/packages/ledger-amino/src/ledgersigner.ts @@ -1,23 +1,16 @@ import { AccountData, AminoSignResponse, - encodeSecp256k1Pubkey, encodeSecp256k1Signature, makeCosmoshubPath, OfflineAminoSigner, - Secp256k1Pubkey, serializeSignDoc, StdSignDoc, } from "@cosmjs/amino"; import { HdPath } from "@cosmjs/crypto"; import Transport from "@ledgerhq/hw-transport"; -import { LaunchpadLedger, LaunchpadLedgerOptions } from "./launchpadledger"; - -export interface AddressAndPubkey { - readonly address: string; - readonly pubkey: Secp256k1Pubkey; -} +import { AddressAndPubkey, LaunchpadLedger, LaunchpadLedgerOptions } from "./launchpadledger"; export class LedgerSigner implements OfflineAminoSigner { private readonly ledger: LaunchpadLedger; @@ -45,16 +38,7 @@ export class LedgerSigner implements OfflineAminoSigner { } public async showAddress(path: HdPath): Promise { - const response = await this.ledger.verifyAddress(path); - - if (response.error_message) { - throw new Error(response.error_message); - } - - return { - address: response.address, - pubkey: encodeSecp256k1Pubkey(response.compressed_pk), - }; + return this.ledger.showAddress(path); } public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise { From 75d5722df53ecd73e6b956a7f13919f5ec3d876c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 25 Jan 2022 16:36:29 +0100 Subject: [PATCH 4/8] Write some showAddress docs --- packages/ledger-amino/src/launchpadledger.ts | 8 ++++++++ packages/ledger-amino/src/ledgersigner.ts | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/ledger-amino/src/launchpadledger.ts b/packages/ledger-amino/src/launchpadledger.ts index d2f17d0c..f5c90710 100644 --- a/packages/ledger-amino/src/launchpadledger.ts +++ b/packages/ledger-amino/src/launchpadledger.ts @@ -172,6 +172,14 @@ export class LaunchpadLedger { await this.verifyCosmosAppIsOpen(); } + /** + * Shows the user's address in the device and returns an address/pubkey pair. + * + * The address will be shown with the native prefix of the app (e.g. cosmos, persistence, desmos) + * and does not support the usage of other address prefixes. + * + * @param path The HD path to show the address for. If unset, this is the first account. + */ public async showAddress(hdPath?: HdPath): Promise { await this.verifyDeviceIsReady(); diff --git a/packages/ledger-amino/src/ledgersigner.ts b/packages/ledger-amino/src/ledgersigner.ts index a5200148..7ed6d80a 100644 --- a/packages/ledger-amino/src/ledgersigner.ts +++ b/packages/ledger-amino/src/ledgersigner.ts @@ -37,7 +37,15 @@ export class LedgerSigner implements OfflineAminoSigner { return this.accounts; } - public async showAddress(path: HdPath): Promise { + /** + * Shows the user's address in the device and returns an address/pubkey pair. + * + * The address will be shown with the native prefix of the app (e.g. cosmos, persistence, desmos) + * and does not support the usage of other address prefixes. + * + * @param path The HD path to show the address for. If unset, this is the first account. + */ + public async showAddress(path?: HdPath): Promise { return this.ledger.showAddress(path); } From c3f062dfb11d39e5619a551b25783b07e18d6c76 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 25 Jan 2022 16:50:03 +0100 Subject: [PATCH 5/8] Disable @typescript-eslint/explicit-module-boundary-types for .js files --- .eslintrc.base.js | 1 + packages/amino/.eslintrc.js | 1 + packages/cli/.eslintrc.js | 1 + packages/cosmwasm-stargate/.eslintrc.js | 1 + packages/crypto/.eslintrc.js | 1 + packages/encoding/.eslintrc.js | 1 + packages/faucet-client/.eslintrc.js | 1 + packages/faucet/.eslintrc.js | 1 + packages/json-rpc/.eslintrc.js | 1 + packages/launchpad/.eslintrc.js | 1 + packages/ledger-amino/.eslintrc.js | 1 + packages/math/.eslintrc.js | 1 + packages/proto-signing/.eslintrc.js | 1 + packages/socket/.eslintrc.js | 1 + packages/stargate/.eslintrc.js | 1 + packages/stream/.eslintrc.js | 1 + packages/tendermint-rpc/.eslintrc.js | 1 + packages/utils/.eslintrc.js | 1 + 18 files changed, 18 insertions(+) diff --git a/.eslintrc.base.js b/.eslintrc.base.js index 3a1d6b54..b76dd7be 100644 --- a/.eslintrc.base.js +++ b/.eslintrc.base.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/amino/.eslintrc.js b/packages/amino/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/amino/.eslintrc.js +++ b/packages/amino/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/cli/.eslintrc.js b/packages/cli/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/cli/.eslintrc.js +++ b/packages/cli/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/cosmwasm-stargate/.eslintrc.js b/packages/cosmwasm-stargate/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/cosmwasm-stargate/.eslintrc.js +++ b/packages/cosmwasm-stargate/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/crypto/.eslintrc.js b/packages/crypto/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/crypto/.eslintrc.js +++ b/packages/crypto/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/encoding/.eslintrc.js b/packages/encoding/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/encoding/.eslintrc.js +++ b/packages/encoding/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/faucet-client/.eslintrc.js b/packages/faucet-client/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/faucet-client/.eslintrc.js +++ b/packages/faucet-client/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/faucet/.eslintrc.js b/packages/faucet/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/faucet/.eslintrc.js +++ b/packages/faucet/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/json-rpc/.eslintrc.js b/packages/json-rpc/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/json-rpc/.eslintrc.js +++ b/packages/json-rpc/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/launchpad/.eslintrc.js b/packages/launchpad/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/launchpad/.eslintrc.js +++ b/packages/launchpad/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/ledger-amino/.eslintrc.js b/packages/ledger-amino/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/ledger-amino/.eslintrc.js +++ b/packages/ledger-amino/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/math/.eslintrc.js b/packages/math/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/math/.eslintrc.js +++ b/packages/math/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/proto-signing/.eslintrc.js b/packages/proto-signing/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/proto-signing/.eslintrc.js +++ b/packages/proto-signing/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/socket/.eslintrc.js b/packages/socket/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/socket/.eslintrc.js +++ b/packages/socket/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/stargate/.eslintrc.js b/packages/stargate/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/stargate/.eslintrc.js +++ b/packages/stargate/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/stream/.eslintrc.js b/packages/stream/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/stream/.eslintrc.js +++ b/packages/stream/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/tendermint-rpc/.eslintrc.js b/packages/tendermint-rpc/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/tendermint-rpc/.eslintrc.js +++ b/packages/tendermint-rpc/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { diff --git a/packages/utils/.eslintrc.js b/packages/utils/.eslintrc.js index 3a1d6b54..b76dd7be 100644 --- a/packages/utils/.eslintrc.js +++ b/packages/utils/.eslintrc.js @@ -81,6 +81,7 @@ module.exports = { "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", }, }, { From 7fec3a287536c2b955ee07379de98e4256b1053c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 25 Jan 2022 16:50:32 +0100 Subject: [PATCH 6/8] Improve demo script for node to show adresses --- packages/ledger-amino/demo/node.js | 67 +++++++++++++++++++++++--- packages/ledger-amino/src/demo/node.ts | 60 ----------------------- 2 files changed, 59 insertions(+), 68 deletions(-) delete mode 100644 packages/ledger-amino/src/demo/node.ts diff --git a/packages/ledger-amino/demo/node.js b/packages/ledger-amino/demo/node.js index fd945388..1cb49e1d 100644 --- a/packages/ledger-amino/demo/node.js +++ b/packages/ledger-amino/demo/node.js @@ -1,19 +1,64 @@ -const demo = require("../build/demo/node"); +const { makeCosmoshubPath, makeSignDoc } = require("@cosmjs/amino"); +const { pathToString } = require("@cosmjs/crypto"); +const { toBase64 } = require("@cosmjs/encoding"); +// eslint-disable-next-line @typescript-eslint/naming-convention +const { LedgerSigner } = require("@cosmjs/ledger-amino"); +// eslint-disable-next-line @typescript-eslint/naming-convention +const TransportNodeHid = require("@ledgerhq/hw-transport-node-hid").default; + +const interactiveTimeout = 120_000; +const accountNumbers = [0, 1, 2, 10]; +const paths = accountNumbers.map(makeCosmoshubPath); + +const defaultChainId = "testing"; +const defaultFee = { + amount: [{ amount: "100", denom: "ucosm" }], + gas: "250", +}; +const defaultMemo = "Some memo"; +const defaultSequence = "0"; + +async function signMsgSend(signer, accountNumber, fromAddress, toAddress) { + const msg = { + type: "cosmos-sdk/MsgSend", + value: { + amount: [ + { + amount: "1234567", + denom: "ucosm", + }, + ], + // eslint-disable-next-line @typescript-eslint/naming-convention + from_address: fromAddress, + // eslint-disable-next-line @typescript-eslint/naming-convention + to_address: toAddress, + }, + }; + const signDoc = makeSignDoc([msg], defaultFee, defaultChainId, defaultMemo, accountNumber, defaultSequence); + const { signature } = await signer.signAmino(fromAddress, signDoc); + return signature; +} async function run() { - const signer = await demo.createSigner(); + const ledgerTransport = await TransportNodeHid.create(interactiveTimeout, interactiveTimeout); + const signer = new LedgerSigner(ledgerTransport, { testModeAllowed: true, hdPaths: paths }); - const accountNumbers = [0, 1, 2, 10]; - const accounts = await demo.getAccounts(signer); + const accounts = await signer.getAccounts(); + const printableAccounts = accounts.map((account) => ({ ...account, pubkey: toBase64(account.pubkey) })); console.info("Accounts from Ledger device:"); - console.table(accounts.map((account, i) => ({ ...account, accountNumber: accountNumbers[i] }))); + console.table(printableAccounts.map((account, i) => ({ ...account, hdPath: pathToString(paths[i]) }))); + + console.info("Showing address of first account on device"); + await signer.showAddress(); + console.info("Showing address of 3rd account on device"); + await signer.showAddress(paths[2]); // Path of 3rd account const accountNumber0 = 0; const address0 = accounts[accountNumber0].address; console.info( `Signing on Ledger device with account index ${accountNumber0} (${address0}). Please review and approve on the device now.`, ); - const signature0 = await demo.sign(signer, accountNumber0, address0, address0); + const signature0 = await signMsgSend(signer, accountNumber0, address0, address0); console.info("Signature:", signature0); // It seems the Ledger device needs a bit of time to recover @@ -24,8 +69,14 @@ async function run() { console.info( `Signing on Ledger device with account index ${accountNumber10} (${address10}). Please review and approve on the device now.`, ); - const signature1 = await demo.sign(signer, accountNumber10, address10, address10); + const signature1 = await signMsgSend(signer, accountNumber10, address10, address10); console.info("Signature:", signature1); } -run().catch(console.error); +run().then( + () => process.exit(0), + (err) => { + console.error(err); + process.exit(1); + }, +); diff --git a/packages/ledger-amino/src/demo/node.ts b/packages/ledger-amino/src/demo/node.ts deleted file mode 100644 index 33ab7176..00000000 --- a/packages/ledger-amino/src/demo/node.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { makeCosmoshubPath, makeSignDoc, StdFee, StdSignature } from "@cosmjs/amino"; -import { toBase64 } from "@cosmjs/encoding"; -import TransportNodeHid from "@ledgerhq/hw-transport-node-hid"; - -import { LedgerSigner } from "../ledgersigner"; - -const defaultChainId = "testing"; -const defaultFee: StdFee = { - amount: [{ amount: "100", denom: "ucosm" }], - gas: "250", -}; -const defaultMemo = "Some memo"; -const defaultSequence = "0"; - -export async function createSigner(): Promise { - const interactiveTimeout = 120_000; - const ledgerTransport = await TransportNodeHid.create(interactiveTimeout, interactiveTimeout); - return new LedgerSigner(ledgerTransport, { - testModeAllowed: true, - hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2), makeCosmoshubPath(10)], - }); -} - -export async function getAccounts(signer: LedgerSigner): Promise< - ReadonlyArray<{ - readonly algo: string; - readonly address: string; - readonly pubkey: string; - }> -> { - const accounts = await signer.getAccounts(); - return accounts.map((account) => ({ ...account, pubkey: toBase64(account.pubkey) })); -} - -export async function sign( - signer: LedgerSigner, - accountNumber: number, - fromAddress: string, - toAddress: string, -): Promise { - const msgs = [ - { - type: "cosmos-sdk/MsgSend", - value: { - amount: [ - { - amount: "1234567", - denom: "ucosm", - }, - ], - from_address: fromAddress, - to_address: toAddress, - }, - }, - ]; - const signDoc = makeSignDoc(msgs, defaultFee, defaultChainId, defaultMemo, accountNumber, defaultSequence); - const { signature } = await signer.signAmino(fromAddress, signDoc); - return signature; -} From 9d35df3d91047c5520c9e507457629c01b3e09f8 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 25 Jan 2022 17:15:51 +0100 Subject: [PATCH 7/8] Use bech32 encoder from CosmJS --- packages/ledger-amino/src/launchpadledger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ledger-amino/src/launchpadledger.ts b/packages/ledger-amino/src/launchpadledger.ts index f5c90710..40831ef1 100644 --- a/packages/ledger-amino/src/launchpadledger.ts +++ b/packages/ledger-amino/src/launchpadledger.ts @@ -1,4 +1,4 @@ -import { encodeSecp256k1Pubkey, makeCosmoshubPath, Secp256k1Pubkey } from "@cosmjs/amino"; +import { encodeSecp256k1Pubkey, makeCosmoshubPath, pubkeyToAddress, Secp256k1Pubkey } from "@cosmjs/amino"; import { HdPath, Secp256k1Signature } from "@cosmjs/crypto"; import { fromUtf8 } from "@cosmjs/encoding"; import { assert } from "@cosmjs/utils"; @@ -115,7 +115,7 @@ export class LaunchpadLedger { public async getCosmosAddress(pubkey?: Uint8Array): Promise { const pubkeyToUse = pubkey || (await this.getPubkey()); - return CosmosApp.getBech32FromPK(this.prefix, Buffer.from(pubkeyToUse)); + return pubkeyToAddress(encodeSecp256k1Pubkey(pubkeyToUse), this.prefix); } public async sign(message: Uint8Array, hdPath?: HdPath): Promise { From 5176fad7332842ba1813c1fa145413b210921684 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 25 Jan 2022 17:57:02 +0100 Subject: [PATCH 8/8] Refactor we demo and add show address functionality --- packages/ledger-amino/README.md | 8 +-- packages/ledger-amino/demo/index.css | 2 +- packages/ledger-amino/demo/index.html | 71 +++++++++++++------- packages/ledger-amino/src/demo/web.ts | 44 +++++++++--- packages/ledger-amino/webpack.demo.config.js | 6 ++ 5 files changed, 93 insertions(+), 38 deletions(-) diff --git a/packages/ledger-amino/README.md b/packages/ledger-amino/README.md index 8660351e..21db73ce 100644 --- a/packages/ledger-amino/README.md +++ b/packages/ledger-amino/README.md @@ -27,15 +27,13 @@ yarn demo-node ### Browser -Build the package for web: +Serve the project locally: ```sh +# Build the package for web yarn pack-web -``` -Host the `ledger-amino` package directory, for example using Python 3: - -```sh +# Host the `ledger-amino` package directory, for example using Python 3 python3 -m http.server ``` diff --git a/packages/ledger-amino/demo/index.css b/packages/ledger-amino/demo/index.css index 974bced3..137a9877 100644 --- a/packages/ledger-amino/demo/index.css +++ b/packages/ledger-amino/demo/index.css @@ -1,4 +1,4 @@ -div { +section { display: flex; align-items: center; justify-content: center; diff --git a/packages/ledger-amino/demo/index.html b/packages/ledger-amino/demo/index.html index bb483690..d2ca617a 100644 --- a/packages/ledger-amino/demo/index.html +++ b/packages/ledger-amino/demo/index.html @@ -8,40 +8,63 @@ -
+

Ledger Demo

-
-
+ +
  1. Connect the Ledger device via USB
  2. Open the Cosmos app on the Ledger device
  3. Click the buttons below
+
+
+
-
+
+

Accounts

-
-
-
- - -
-
- - -
-
- - -
-
- -
-
+ +
+

Sign

+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
diff --git a/packages/ledger-amino/src/demo/web.ts b/packages/ledger-amino/src/demo/web.ts index 028b0f11..e029c88d 100644 --- a/packages/ledger-amino/src/demo/web.ts +++ b/packages/ledger-amino/src/demo/web.ts @@ -1,4 +1,5 @@ import { AccountData, makeCosmoshubPath, StdSignDoc } from "@cosmjs/amino"; +import { pathToString, stringToPath } from "@cosmjs/crypto"; import { toBase64 } from "@cosmjs/encoding"; import { Uint53 } from "@cosmjs/math"; import { assert } from "@cosmjs/utils"; @@ -9,6 +10,9 @@ import { LedgerSigner } from "../ledgersigner"; declare const window: any; declare const document: any; +const accountNumbers = [0, 1, 2, 10]; +const paths = accountNumbers.map(makeCosmoshubPath); + let accounts: readonly AccountData[] = []; function createSignDoc(accountNumber: number, address: string): string { @@ -59,12 +63,22 @@ window.updateMessage = (accountNumberInput: unknown) => { signDocTextArea.textContent = createSignDoc(accountNumber, address); }; +window.setPath = (accountNumberInput: unknown) => { + assert(typeof accountNumberInput === "string"); + const accountNumber = Uint53.fromString(accountNumberInput).toNumber(); + + const path = pathToString(paths[accountNumber]); + const pathInput = document.getElementById("path"); + pathInput.value = path; +}; + +// This must be called by the user an cannot be done on load (see "TransportWebUSBGestureRequired"). window.createSigner = async function createSigner(): Promise { const interactiveTimeout = 120_000; const ledgerTransport = await TransportWebUSB.create(interactiveTimeout, interactiveTimeout); return new LedgerSigner(ledgerTransport, { testModeAllowed: true, - hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2)], + hdPaths: paths, }); }; @@ -73,7 +87,8 @@ window.getAccounts = async function getAccounts(signer: LedgerSigner | undefined console.error("Please wait for transport to connect"); return; } - const accountNumberInput = document.getElementById("account-number"); + const accountNumberInput1 = document.getElementById("account-number1"); + const accountNumberInput2 = document.getElementById("account-number2"); const addressInput = document.getElementById("address"); const accountsDiv = document.getElementById("accounts"); const signDocTextArea = document.getElementById("sign-doc"); @@ -87,16 +102,33 @@ window.getAccounts = async function getAccounts(signer: LedgerSigner | undefined })); accountsDiv.textContent = JSON.stringify(prettyAccounts, null, "\n"); const accountNumber = 0; - accountNumberInput.max = accounts.length - 1; - accountNumberInput.value = accountNumber; + + // Show address block + accountNumberInput1.max = accounts.length - 1; + accountNumberInput1.value = accountNumber; + window.setPath(accountNumber.toString()); + + // Sign block + accountNumberInput2.max = accounts.length - 1; + accountNumberInput2.value = accountNumber; const address = accounts[0].address; addressInput.value = address; signDocTextArea.textContent = createSignDoc(accountNumber, address); } catch (error) { + console.error(error); accountsDiv.textContent = error; } }; +window.showAddress = async function showAddress(signer: LedgerSigner | undefined): Promise { + if (signer === undefined) { + console.error("Please wait for transport to connect"); + return; + } + const path = stringToPath(document.getElementById("path").value); + await signer.showAddress(path); +}; + window.sign = async function sign(signer: LedgerSigner | undefined): Promise { if (signer === undefined) { console.error("Please wait for transport to connect"); @@ -115,7 +147,3 @@ window.sign = async function sign(signer: LedgerSigner | undefined): Promise { - window.signer = await window.createSigner(); -}; diff --git a/packages/ledger-amino/webpack.demo.config.js b/packages/ledger-amino/webpack.demo.config.js index aecae6ae..47858f41 100644 --- a/packages/ledger-amino/webpack.demo.config.js +++ b/packages/ledger-amino/webpack.demo.config.js @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ const glob = require("glob"); const path = require("path"); +const webpack = require("webpack"); const target = "web"; const demodir = path.join(__dirname, "dist", "demo"); @@ -14,6 +15,11 @@ module.exports = [ path: demodir, filename: "ledger.js", }, + plugins: [ + new webpack.ProvidePlugin({ + Buffer: ["buffer", "Buffer"], + }), + ], resolve: { fallback: { buffer: false,