From 8807e7532ddb30b59cf97175202f2265cbce750c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 4 Feb 2020 19:39:49 +0100 Subject: [PATCH 1/4] Move bcp-free address en/de-coding into sdk --- packages/sdk/src/address.spec.ts | 56 ++++++++++++++ packages/sdk/src/address.ts | 109 ++++++++++++++++++++++++++++ packages/sdk/src/restclient.spec.ts | 3 +- packages/sdk/src/types.ts | 24 ++++-- packages/sdk/types/address.d.ts | 7 ++ packages/sdk/types/types.d.ts | 17 ++++- 6 files changed, 206 insertions(+), 10 deletions(-) create mode 100644 packages/sdk/src/address.spec.ts create mode 100644 packages/sdk/src/address.ts create mode 100644 packages/sdk/types/address.d.ts diff --git a/packages/sdk/src/address.spec.ts b/packages/sdk/src/address.spec.ts new file mode 100644 index 00000000..5f9b2cc6 --- /dev/null +++ b/packages/sdk/src/address.spec.ts @@ -0,0 +1,56 @@ +import { Encoding } from "@iov/encoding"; + +import { decodeBech32Pubkey, isValidAddress, pubkeyToAddress } from "./address"; +import { PubKeyEd25519, PubKeySecp256k1 } from "./types"; + +const { toBase64, fromHex } = Encoding; + +describe("address", () => { + describe("decodeBech32Pubkey", () => { + it("works", () => { + expect( + decodeBech32Pubkey("cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5"), + ).toEqual({ + type: "tendermint/PubKeySecp256k1", + value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ", + }); + }); + }); + + describe("isValidAddress", () => { + it("accepts valid addresses", () => { + expect(isValidAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6")).toEqual(true); + expect(isValidAddress("cosmosvalcons10q82zkzzmaku5lazhsvxv7hsg4ntpuhdwadmss")).toEqual(true); + expect(isValidAddress("cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq")).toEqual(true); + }); + + it("rejects invalid addresses", () => { + // Bad size + expect(isValidAddress("cosmos10q82zkzzmaku5lazhsvxv7hsg4ntpuhh8289f")).toEqual(false); + // Bad checksum + expect(isValidAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs7")).toEqual(false); + // Bad prefix + expect(isValidAddress("cosmot10q82zkzzmaku5lazhsvxv7hsg4ntpuhd8j5266")).toEqual(false); + }); + }); + + describe("pubkeyToAddress", () => { + it("works for Secp256k1 compressed", () => { + const prefix = "cosmos"; + const pubkey: PubKeySecp256k1 = { + type: "tendermint/PubKeySecp256k1", + value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", + }; + expect(pubkeyToAddress(pubkey, prefix)).toEqual("cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r"); + }); + + it("works for Ed25519", () => { + const prefix = "cosmos"; + const pubkey: PubKeyEd25519 = { + type: "tendermint/PubKeyEd25519", + value: toBase64(fromHex("12ee6f581fe55673a1e9e1382a0829e32075a0aa4763c968bc526e1852e78c95")), + }; + expect(pubkeyToAddress(pubkey, prefix)).toEqual("cosmos1pfq05em6sfkls66ut4m2257p7qwlk448h8mysz"); + }); + }); +}); diff --git a/packages/sdk/src/address.ts b/packages/sdk/src/address.ts new file mode 100644 index 00000000..13f13b7f --- /dev/null +++ b/packages/sdk/src/address.ts @@ -0,0 +1,109 @@ +import { Ripemd160, Sha256 } from "@iov/crypto"; +import { Bech32, Encoding } from "@iov/encoding"; +import equal from "fast-deep-equal"; + +import { Bech32PubKey, PubKey } from "./types"; + +const { fromBase64, toBase64 } = Encoding; + +// TODO: make this much more configurable +export type CosmosAddressBech32Prefix = "cosmos" | "cosmosvalcons" | "cosmosvaloper"; +export type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmosvaloperpub"; +export type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; + +function isCosmosAddressBech32Prefix(prefix: string): prefix is CosmosAddressBech32Prefix { + return ["cosmos", "cosmosvalcons", "cosmosvaloper"].includes(prefix); +} + +function isCosmosPubkeyBech32Prefix(prefix: string): prefix is CosmosPubkeyBech32Prefix { + return ["cosmospub", "cosmosvalconspub", "cosmosvaloperpub"].includes(prefix); +} + +// As discussed in https://github.com/binance-chain/javascript-sdk/issues/163 +// Prefixes listed here: https://github.com/tendermint/tendermint/blob/d419fffe18531317c28c29a292ad7d253f6cafdf/docs/spec/blockchain/encoding.md#public-key-cryptography +// Last bytes is varint-encoded length prefix +const pubkeyAminoPrefixSecp256k1 = Encoding.fromHex("eb5ae98721"); +const pubkeyAminoPrefixEd25519 = Encoding.fromHex("1624de6420"); +const pubkeyAminoPrefixSr25519 = Encoding.fromHex("0dfb1005"); +const pubkeyAminoPrefixLength = pubkeyAminoPrefixSecp256k1.length; + +export function decodeBech32Pubkey(bech: Bech32PubKey): PubKey { + const { prefix, data } = Bech32.decode(bech); + if (!isCosmosPubkeyBech32Prefix(prefix)) { + throw new Error(`Invalid bech32 prefix. Must be one of cosmos, cosmosvalcons, or cosmosvaloper.`); + } + + const aminoPrefix = data.slice(0, pubkeyAminoPrefixLength); + const rest = data.slice(pubkeyAminoPrefixLength); + if (equal(aminoPrefix, pubkeyAminoPrefixSecp256k1)) { + if (rest.length !== 33) { + throw new Error("Invalid rest data length. Expected 33 bytes (compressed secp256k1 pubkey)."); + } + return { + type: "tendermint/PubKeySecp256k1", + value: toBase64(rest), + }; + } else if (equal(aminoPrefix, pubkeyAminoPrefixEd25519)) { + if (rest.length !== 32) { + throw new Error("Invalid rest data length. Expected 32 bytes (Ed25519 pubkey)."); + } + return { + type: "tendermint/PubKeyEd25519", + value: toBase64(rest), + }; + } else if (equal(aminoPrefix, pubkeyAminoPrefixSr25519)) { + if (rest.length !== 32) { + throw new Error("Invalid rest data length. Expected 32 bytes (Sr25519 pubkey)."); + } + return { + type: "tendermint/PubKeySr25519", + value: toBase64(rest), + }; + } else { + throw new Error("Unsupported Pubkey type. Amino prefix: " + Encoding.toHex(aminoPrefix)); + } +} + +export function isValidAddress(address: string): boolean { + try { + const { prefix, data } = Bech32.decode(address); + if (!isCosmosAddressBech32Prefix(prefix)) { + return false; + } + return data.length === 20; + } catch { + return false; + } +} + +// See https://github.com/tendermint/tendermint/blob/f2ada0a604b4c0763bda2f64fac53d506d3beca7/docs/spec/blockchain/encoding.md#public-key-cryptography +// This assumes we already have a cosmos-compressed pubkey +export function pubkeyToAddress(pubkey: PubKey, prefix: string): string { + const pubkeyBytes = fromBase64(pubkey.value); + switch (pubkey.type) { + case "tendermint/PubKeySecp256k1": { + if (pubkeyBytes.length !== 33) { + throw new Error(`Invalid Secp256k1 pubkey length (compressed): ${pubkeyBytes.length}`); + } + const hash1 = new Sha256(pubkeyBytes).digest(); + const hash2 = new Ripemd160(hash1).digest(); + return Bech32.encode(prefix, hash2); + } + case "tendermint/PubKeyEd25519": { + if (pubkeyBytes.length !== 32) { + throw new Error(`Invalid Ed25519 pubkey length: ${pubkeyBytes.length}`); + } + const hash = new Sha256(pubkeyBytes).digest(); + return Bech32.encode(prefix, hash.slice(0, 20)); + } + case "tendermint/PubKeySr25519": { + if (pubkeyBytes.length !== 32) { + throw new Error(`Invalid Sr25519 pubkey length: ${pubkeyBytes.length}`); + } + const hash = new Sha256(pubkeyBytes).digest(); + return Bech32.encode(prefix, hash.slice(0, 20)); + } + default: + throw new Error("Unrecognized public key algorithm"); + } +} diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index 41e9db67..a490139a 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -78,7 +78,8 @@ describe("RestClient", () => { describe("encodeTx", () => { it("works for cosmoshub example", async () => { pendingWithoutCosmos(); - const tx: StdTx = data.tx.value; + // need to convince the compiler we have a valid string for pubkey type + const tx: StdTx = data.tx.value as StdTx; const client = new RestClient(httpUrl); expect(await client.encodeTx(tx)).toEqual(fromBase64(data.tx_data)); }); diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 1404e90d..2aa16d64 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -100,19 +100,33 @@ export interface StdSignature { readonly signature: string; } -export interface PubKey { - readonly type: string; +// value field is base64-encoded in all cases +export type PubKey = PubKeyEd25519 | PubKeySecp256k1 | PubKeySr25519; + +export interface PubKeySecp256k1 { + readonly type: "tendermint/PubKeySecp256k1"; + // Note: this contains a Secp256k1 COMPRESSED pubkey - to encode from bcp/keycontrol land, you must compress it first readonly value: string; } -// AccountPubKey is bech32-encoded amino-binary encoded PubKey interface. oof. -export type AccountPubKey = string; +export interface PubKeyEd25519 { + readonly type: "tendermint/PubKeyEd25519"; + readonly value: string; +} + +export interface PubKeySr25519 { + readonly type: "tendermint/PubKeySr25519"; + readonly value: string; +} + +// Bech32PubKey is bech32-encoded amino-binary encoded PubKey interface. oof. +export type Bech32PubKey = string; export interface BaseAccount { /** Bech32 account address */ readonly address: string; readonly coins: ReadonlyArray; - readonly public_key: AccountPubKey; + readonly public_key: Bech32PubKey; readonly account_number: number; readonly sequence: number; } diff --git a/packages/sdk/types/address.d.ts b/packages/sdk/types/address.d.ts new file mode 100644 index 00000000..fbb34ec2 --- /dev/null +++ b/packages/sdk/types/address.d.ts @@ -0,0 +1,7 @@ +import { Bech32PubKey, PubKey } from "./types"; +export declare type CosmosAddressBech32Prefix = "cosmos" | "cosmosvalcons" | "cosmosvaloper"; +export declare type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmosvaloperpub"; +export declare type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; +export declare function decodeBech32Pubkey(bech: Bech32PubKey): PubKey; +export declare function isValidAddress(address: string): boolean; +export declare function pubkeyToAddress(pubkey: PubKey, prefix: string): string; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 962c80c0..4723c474 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -70,16 +70,25 @@ export interface StdSignature { readonly pub_key: PubKey; readonly signature: string; } -export interface PubKey { - readonly type: string; +export declare type PubKey = PubKeyEd25519 | PubKeySecp256k1 | PubKeySr25519; +export interface PubKeySecp256k1 { + readonly type: "tendermint/PubKeySecp256k1"; readonly value: string; } -export declare type AccountPubKey = string; +export interface PubKeyEd25519 { + readonly type: "tendermint/PubKeyEd25519"; + readonly value: string; +} +export interface PubKeySr25519 { + readonly type: "tendermint/PubKeySr25519"; + readonly value: string; +} +export declare type Bech32PubKey = string; export interface BaseAccount { /** Bech32 account address */ readonly address: string; readonly coins: ReadonlyArray; - readonly public_key: AccountPubKey; + readonly public_key: Bech32PubKey; readonly account_number: number; readonly sequence: number; } From a2c14bb80b9085aa57a524d5646e6cc09b8a5ead Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 4 Feb 2020 20:31:33 +0100 Subject: [PATCH 2/4] Update bcp address to reduce code dup --- packages/bcp/src/address.spec.ts | 24 +------- packages/bcp/src/address.ts | 102 +++++++++---------------------- packages/bcp/src/decode.spec.ts | 13 ++-- packages/bcp/types/address.d.ts | 12 +--- packages/sdk/src/address.spec.ts | 8 +-- packages/sdk/src/address.ts | 2 +- packages/sdk/src/index.ts | 1 + packages/sdk/types/address.d.ts | 2 +- packages/sdk/types/index.d.ts | 1 + 9 files changed, 47 insertions(+), 118 deletions(-) diff --git a/packages/bcp/src/address.spec.ts b/packages/bcp/src/address.spec.ts index 21fcb192..4fed23b2 100644 --- a/packages/bcp/src/address.spec.ts +++ b/packages/bcp/src/address.spec.ts @@ -1,33 +1,11 @@ import { Address, Algorithm, PubkeyBytes } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; -import { decodeCosmosAddress, decodeCosmosPubkey, isValidAddress, pubkeyToAddress } from "./address"; +import { decodeCosmosPubkey, isValidAddress, pubkeyToAddress } from "./address"; const { fromBase64, fromHex } = Encoding; describe("address", () => { - // Bech32 encoding/decoding data generated using https://github.com/bitcoinjs/bech32 - describe("decodeCosmosAddress", () => { - it("throws for invalid prefix", () => { - expect(() => - decodeCosmosAddress("cosmot10q82zkzzmaku5lazhsvxv7hsg4ntpuhd8j5266" as Address), - ).toThrowError(/invalid bech32 prefix/i); - }); - - it("throws for invalid length", () => { - expect(() => - decodeCosmosAddress("cosmos1alcmj76e030g83fedrnx8lvsythhg70zlct4cwx3" as Address), - ).toThrowError(/invalid data length/i); - }); - - it("decodes valid addresses", () => { - expect(decodeCosmosAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" as Address)).toEqual({ - prefix: "cosmos", - data: fromHex("0d82b1e7c96dbfa42462fe612932e6bff111d51b"), - }); - }); - }); - describe("decodeCosmosPubkey", () => { it("works", () => { expect( diff --git a/packages/bcp/src/address.ts b/packages/bcp/src/address.ts index fa5e7ff2..1fc4ddb2 100644 --- a/packages/bcp/src/address.ts +++ b/packages/bcp/src/address.ts @@ -1,88 +1,44 @@ +import { CosmosBech32Prefix, decodeBech32Pubkey, encodeAddress, isValidAddress } from "@cosmwasm/sdk"; import { Address, Algorithm, PubkeyBundle, PubkeyBytes } from "@iov/bcp"; -import { Ripemd160, Secp256k1, Sha256 } from "@iov/crypto"; -import { Bech32, Encoding } from "@iov/encoding"; -import equal from "fast-deep-equal"; +import { Secp256k1 } from "@iov/crypto"; +import { Encoding } from "@iov/encoding"; -export type CosmosAddressBech32Prefix = "cosmos" | "cosmosvalcons" | "cosmosvaloper"; -export type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmosvaloperpub"; -export type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; +export { CosmosBech32Prefix, isValidAddress }; -// As discussed in https://github.com/binance-chain/javascript-sdk/issues/163 -// Prefixes listed here: https://github.com/tendermint/tendermint/blob/d419fffe18531317c28c29a292ad7d253f6cafdf/docs/spec/blockchain/encoding.md#public-key-cryptography -const pubkeyAminoPrefixSecp256k1 = Encoding.fromHex("eb5ae98721"); -const pubkeyAminoPrefixEd25519 = Encoding.fromHex("1624de64"); -const pubkeyAminoPrefixLength = pubkeyAminoPrefixSecp256k1.length; - -function isCosmosAddressBech32Prefix(prefix: string): prefix is CosmosAddressBech32Prefix { - return ["cosmos", "cosmosvalcons", "cosmosvaloper"].includes(prefix); -} - -function isCosmosPubkeyBech32Prefix(prefix: string): prefix is CosmosPubkeyBech32Prefix { - return ["cosmospub", "cosmosvalconspub", "cosmosvaloperpub"].includes(prefix); -} - -export function decodeCosmosAddress( - address: Address, -): { readonly prefix: CosmosAddressBech32Prefix; readonly data: Uint8Array } { - const { prefix, data } = Bech32.decode(address); - if (!isCosmosAddressBech32Prefix(prefix)) { - throw new Error(`Invalid bech32 prefix. Must be one of cosmos, cosmosvalcons, or cosmosvaloper.`); - } - if (data.length !== 20) { - throw new Error("Invalid data length. Expected 20 bytes."); - } - return { prefix: prefix, data: data }; -} +const { fromBase64, toBase64 } = Encoding; export function decodeCosmosPubkey( encodedPubkey: string, ): { readonly algo: Algorithm; readonly data: PubkeyBytes } { - const { prefix, data } = Bech32.decode(encodedPubkey); - if (!isCosmosPubkeyBech32Prefix(prefix)) { - throw new Error(`Invalid bech32 prefix. Must be one of cosmos, cosmosvalcons, or cosmosvaloper.`); - } - - const aminoPrefix = data.slice(0, pubkeyAminoPrefixLength); - const rest = data.slice(pubkeyAminoPrefixLength); - if (equal(aminoPrefix, pubkeyAminoPrefixSecp256k1)) { - if (rest.length !== 33) { - throw new Error("Invalid rest data length. Expected 33 bytes (compressed secp256k1 pubkey)."); - } - return { algo: Algorithm.Secp256k1, data: rest as PubkeyBytes }; - } else if (equal(aminoPrefix, pubkeyAminoPrefixEd25519)) { - if (rest.length !== 32) { - throw new Error("Invalid rest data length. Expected 32 bytes (ed25519 pubkey)."); - } - return { algo: Algorithm.Ed25519, data: rest as PubkeyBytes }; - } else { - throw new Error("Unsupported Pubkey type. Amino prefix: " + Encoding.toHex(aminoPrefix)); - } -} - -export function isValidAddress(address: string): boolean { - try { - decodeCosmosAddress(address as Address); - return true; - } catch { - return false; + const sdkPubKey = decodeBech32Pubkey(encodedPubkey); + switch (sdkPubKey.type) { + case "tendermint/PubKeySecp256k1": + return { algo: Algorithm.Secp256k1, data: fromBase64(sdkPubKey.value) as PubkeyBytes }; + case "tendermint/PubKeyEd25519": + return { algo: Algorithm.Ed25519, data: fromBase64(sdkPubKey.value) as PubkeyBytes }; + default: + throw new Error("Unsupported Pubkey type: " + sdkPubKey.type); } } // See https://github.com/tendermint/tendermint/blob/f2ada0a604b4c0763bda2f64fac53d506d3beca7/docs/spec/blockchain/encoding.md#public-key-cryptography export function pubkeyToAddress(pubkey: PubkeyBundle, prefix: CosmosBech32Prefix): Address { - const pubkeyData = - pubkey.algo === Algorithm.Secp256k1 ? Secp256k1.compressPubkey(pubkey.data) : pubkey.data; - switch (pubkey.algo) { - case Algorithm.Secp256k1: { - const hash1 = new Sha256(pubkeyData).digest(); - const hash2 = new Ripemd160(hash1).digest(); - return Bech32.encode(prefix, hash2) as Address; + let pubkeyType: "tendermint/PubKeySecp256k1" | "tendermint/PubKeyEd25519"; + let pubkeyData: Uint8Array = pubkey.data; + + if (pubkey.algo === Algorithm.Secp256k1) { + pubkeyType = "tendermint/PubKeySecp256k1"; + if (pubkeyData.length > 33) { + pubkeyData = Secp256k1.compressPubkey(pubkey.data); } - case Algorithm.Ed25519: { - const hash = new Sha256(pubkeyData).digest(); - return Bech32.encode(prefix, hash.slice(0, 20)) as Address; - } - default: - throw new Error("Unrecognized public key algorithm"); + } else if (pubkey.algo === Algorithm.Ed25519) { + pubkeyType = "tendermint/PubKeyEd25519"; + } else { + throw new Error(`Unsupported algorithm: ${pubkey.algo}`); } + const sdkKey = { + type: pubkeyType, + value: toBase64(pubkeyData), + }; + return encodeAddress(sdkKey, prefix) as Address; } diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index c783b5a4..9e9cf0d8 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ import { types } from "@cosmwasm/sdk"; +import { StdTx } from "@cosmwasm/sdk/types/types"; import { Address, Algorithm, TokenTicker } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; @@ -62,7 +63,7 @@ describe("decode", () => { describe("decodePubkey", () => { it("works for secp256k1", () => { - const pubkey = { + const pubkey: types.PubKey = { type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", }; @@ -70,7 +71,7 @@ describe("decode", () => { }); it("works for ed25519", () => { - const pubkey = { + const pubkey: types.PubKey = { type: "tendermint/PubKeyEd25519", value: "s69CnMgLTpuRyEfecjws3mWssBrOICUx8C2O1DkKSto=", }; @@ -82,7 +83,7 @@ describe("decode", () => { it("throws for unsupported types", () => { // https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/sr25519/codec.go#L12 - const pubkey = { + const pubkey: types.PubKey = { type: "tendermint/PubKeySr25519", value: "N4FJNPE5r/Twz55kO1QEIxyaGF5/HTXH6WgLQJWsy1o=", }; @@ -100,7 +101,7 @@ describe("decode", () => { describe("decodeFullSignature", () => { it("works", () => { - const fullSignature = { + const fullSignature: types.StdSignature = { pub_key: { type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", @@ -157,7 +158,7 @@ describe("decode", () => { describe("parseTx", () => { it("works", () => { - expect(parseTx(data.tx.value, chainId, nonce, defaultTokens)).toEqual(signedTxJson); + expect(parseTx(data.tx.value as StdTx, chainId, nonce, defaultTokens)).toEqual(signedTxJson); }); }); @@ -168,7 +169,7 @@ describe("decode", () => { height: "2823", txhash: txId, raw_log: '[{"msg_index":0,"success":true,"log":""}]', - tx: data.tx, + tx: data.tx as types.AminoTx, }; const expected = { ...signedTxJson, diff --git a/packages/bcp/types/address.d.ts b/packages/bcp/types/address.d.ts index fd3a99c3..158addbc 100644 --- a/packages/bcp/types/address.d.ts +++ b/packages/bcp/types/address.d.ts @@ -1,18 +1,10 @@ +import { CosmosBech32Prefix, isValidAddress } from "@cosmwasm/sdk"; import { Address, Algorithm, PubkeyBundle, PubkeyBytes } from "@iov/bcp"; -export declare type CosmosAddressBech32Prefix = "cosmos" | "cosmosvalcons" | "cosmosvaloper"; -export declare type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmosvaloperpub"; -export declare type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; -export declare function decodeCosmosAddress( - address: Address, -): { - readonly prefix: CosmosAddressBech32Prefix; - readonly data: Uint8Array; -}; +export { CosmosBech32Prefix, isValidAddress }; export declare function decodeCosmosPubkey( encodedPubkey: string, ): { readonly algo: Algorithm; readonly data: PubkeyBytes; }; -export declare function isValidAddress(address: string): boolean; export declare function pubkeyToAddress(pubkey: PubkeyBundle, prefix: CosmosBech32Prefix): Address; diff --git a/packages/sdk/src/address.spec.ts b/packages/sdk/src/address.spec.ts index 5f9b2cc6..c3160ce2 100644 --- a/packages/sdk/src/address.spec.ts +++ b/packages/sdk/src/address.spec.ts @@ -1,6 +1,6 @@ import { Encoding } from "@iov/encoding"; -import { decodeBech32Pubkey, isValidAddress, pubkeyToAddress } from "./address"; +import { decodeBech32Pubkey, encodeAddress, isValidAddress } from "./address"; import { PubKeyEd25519, PubKeySecp256k1 } from "./types"; const { toBase64, fromHex } = Encoding; @@ -34,14 +34,14 @@ describe("address", () => { }); }); - describe("pubkeyToAddress", () => { + describe("encodeAddress", () => { it("works for Secp256k1 compressed", () => { const prefix = "cosmos"; const pubkey: PubKeySecp256k1 = { type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", }; - expect(pubkeyToAddress(pubkey, prefix)).toEqual("cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r"); + expect(encodeAddress(pubkey, prefix)).toEqual("cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r"); }); it("works for Ed25519", () => { @@ -50,7 +50,7 @@ describe("address", () => { type: "tendermint/PubKeyEd25519", value: toBase64(fromHex("12ee6f581fe55673a1e9e1382a0829e32075a0aa4763c968bc526e1852e78c95")), }; - expect(pubkeyToAddress(pubkey, prefix)).toEqual("cosmos1pfq05em6sfkls66ut4m2257p7qwlk448h8mysz"); + expect(encodeAddress(pubkey, prefix)).toEqual("cosmos1pfq05em6sfkls66ut4m2257p7qwlk448h8mysz"); }); }); }); diff --git a/packages/sdk/src/address.ts b/packages/sdk/src/address.ts index 13f13b7f..5819dca3 100644 --- a/packages/sdk/src/address.ts +++ b/packages/sdk/src/address.ts @@ -78,7 +78,7 @@ export function isValidAddress(address: string): boolean { // See https://github.com/tendermint/tendermint/blob/f2ada0a604b4c0763bda2f64fac53d506d3beca7/docs/spec/blockchain/encoding.md#public-key-cryptography // This assumes we already have a cosmos-compressed pubkey -export function pubkeyToAddress(pubkey: PubKey, prefix: string): string { +export function encodeAddress(pubkey: PubKey, prefix: string): string { const pubkeyBytes = fromBase64(pubkey.value); switch (pubkey.type) { case "tendermint/PubKeySecp256k1": { diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 9016d432..2b01f294 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,5 +1,6 @@ import * as types from "./types"; +export { CosmosBech32Prefix, decodeBech32Pubkey, encodeAddress, isValidAddress } from "./address"; export { unmarshalTx } from "./decoding"; export { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; diff --git a/packages/sdk/types/address.d.ts b/packages/sdk/types/address.d.ts index fbb34ec2..4fddadd5 100644 --- a/packages/sdk/types/address.d.ts +++ b/packages/sdk/types/address.d.ts @@ -4,4 +4,4 @@ export declare type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" export declare type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; export declare function decodeBech32Pubkey(bech: Bech32PubKey): PubKey; export declare function isValidAddress(address: string): boolean; -export declare function pubkeyToAddress(pubkey: PubKey, prefix: string): string; +export declare function encodeAddress(pubkey: PubKey, prefix: string): string; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index 8c8e4b4b..18324a0a 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,4 +1,5 @@ import * as types from "./types"; +export { CosmosBech32Prefix, decodeBech32Pubkey, encodeAddress, isValidAddress } from "./address"; export { unmarshalTx } from "./decoding"; export { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; From 0f6721e5f4fabba81cef22c0c35a954654c1d2e4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 5 Feb 2020 10:23:08 +0100 Subject: [PATCH 3/4] Fix lint error --- packages/bcp/src/address.spec.ts | 2 +- packages/bcp/src/decode.spec.ts | 3 +-- packages/sdk/src/types.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/bcp/src/address.spec.ts b/packages/bcp/src/address.spec.ts index 4fed23b2..cdfd3e31 100644 --- a/packages/bcp/src/address.spec.ts +++ b/packages/bcp/src/address.spec.ts @@ -1,4 +1,4 @@ -import { Address, Algorithm, PubkeyBytes } from "@iov/bcp"; +import { Algorithm, PubkeyBytes } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; import { decodeCosmosPubkey, isValidAddress, pubkeyToAddress } from "./address"; diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index 9e9cf0d8..ac9ddd40 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ import { types } from "@cosmwasm/sdk"; -import { StdTx } from "@cosmwasm/sdk/types/types"; import { Address, Algorithm, TokenTicker } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; @@ -158,7 +157,7 @@ describe("decode", () => { describe("parseTx", () => { it("works", () => { - expect(parseTx(data.tx.value as StdTx, chainId, nonce, defaultTokens)).toEqual(signedTxJson); + expect(parseTx(data.tx.value as types.StdTx, chainId, nonce, defaultTokens)).toEqual(signedTxJson); }); }); diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 2aa16d64..b7db4963 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -119,7 +119,7 @@ export interface PubKeySr25519 { readonly value: string; } -// Bech32PubKey is bech32-encoded amino-binary encoded PubKey interface. oof. +// bech32-encoded amino-binary encoded PubKey interface. oof. export type Bech32PubKey = string; export interface BaseAccount { From dc93bfc1e7a88e61470cc708145ac94729620da9 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 5 Feb 2020 10:30:21 +0100 Subject: [PATCH 4/4] Simplify sdk pubkey types --- packages/bcp/src/decode.spec.ts | 4 ++-- packages/sdk/src/address.spec.ts | 5 ++--- packages/sdk/src/types.ts | 27 ++++++++++++--------------- packages/sdk/types/types.d.ts | 14 +++----------- 4 files changed, 19 insertions(+), 31 deletions(-) diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index ac9ddd40..fe8feb51 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -157,7 +157,7 @@ describe("decode", () => { describe("parseTx", () => { it("works", () => { - expect(parseTx(data.tx.value as types.StdTx, chainId, nonce, defaultTokens)).toEqual(signedTxJson); + expect(parseTx(data.tx.value, chainId, nonce, defaultTokens)).toEqual(signedTxJson); }); }); @@ -168,7 +168,7 @@ describe("decode", () => { height: "2823", txhash: txId, raw_log: '[{"msg_index":0,"success":true,"log":""}]', - tx: data.tx as types.AminoTx, + tx: data.tx, }; const expected = { ...signedTxJson, diff --git a/packages/sdk/src/address.spec.ts b/packages/sdk/src/address.spec.ts index c3160ce2..7c73ebaa 100644 --- a/packages/sdk/src/address.spec.ts +++ b/packages/sdk/src/address.spec.ts @@ -1,7 +1,6 @@ import { Encoding } from "@iov/encoding"; import { decodeBech32Pubkey, encodeAddress, isValidAddress } from "./address"; -import { PubKeyEd25519, PubKeySecp256k1 } from "./types"; const { toBase64, fromHex } = Encoding; @@ -37,7 +36,7 @@ describe("address", () => { describe("encodeAddress", () => { it("works for Secp256k1 compressed", () => { const prefix = "cosmos"; - const pubkey: PubKeySecp256k1 = { + const pubkey = { type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", }; @@ -46,7 +45,7 @@ describe("address", () => { it("works for Ed25519", () => { const prefix = "cosmos"; - const pubkey: PubKeyEd25519 = { + const pubkey = { type: "tendermint/PubKeyEd25519", value: toBase64(fromHex("12ee6f581fe55673a1e9e1382a0829e32075a0aa4763c968bc526e1852e78c95")), }; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index b7db4963..5c3f1d60 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -100,24 +100,21 @@ export interface StdSignature { readonly signature: string; } -// value field is base64-encoded in all cases -export type PubKey = PubKeyEd25519 | PubKeySecp256k1 | PubKeySr25519; - -export interface PubKeySecp256k1 { - readonly type: "tendermint/PubKeySecp256k1"; - // Note: this contains a Secp256k1 COMPRESSED pubkey - to encode from bcp/keycontrol land, you must compress it first +export interface PubKey { + // type is one of the strings defined in pubkeyTypes + // I don't use a string literal union here as that makes trouble with json test data: + // https://github.com/confio/cosm-js/pull/44#pullrequestreview-353280504 + readonly type: string; + // Value field is base64-encoded in all cases + // Note: if type is Secp256k1, this must contain a COMPRESSED pubkey - to encode from bcp/keycontrol land, you must compress it first readonly value: string; } -export interface PubKeyEd25519 { - readonly type: "tendermint/PubKeyEd25519"; - readonly value: string; -} - -export interface PubKeySr25519 { - readonly type: "tendermint/PubKeySr25519"; - readonly value: string; -} +export const pubkeyTypes: string[] = [ + "tendermint/PubKeySecp256k1", + "tendermint/PubKeyEd25519", + "tendermint/PubKeySr25519", +]; // bech32-encoded amino-binary encoded PubKey interface. oof. export type Bech32PubKey = string; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 4723c474..81880b03 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -70,19 +70,11 @@ export interface StdSignature { readonly pub_key: PubKey; readonly signature: string; } -export declare type PubKey = PubKeyEd25519 | PubKeySecp256k1 | PubKeySr25519; -export interface PubKeySecp256k1 { - readonly type: "tendermint/PubKeySecp256k1"; - readonly value: string; -} -export interface PubKeyEd25519 { - readonly type: "tendermint/PubKeyEd25519"; - readonly value: string; -} -export interface PubKeySr25519 { - readonly type: "tendermint/PubKeySr25519"; +export interface PubKey { + readonly type: string; readonly value: string; } +export declare const pubkeyTypes: string[]; export declare type Bech32PubKey = string; export interface BaseAccount { /** Bech32 account address */