From cae35522ce1a546d3eb95628a29ded98619160c3 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 23 Jan 2020 17:20:34 +0100 Subject: [PATCH] Add decodeCosmosPubkey --- src/address.spec.ts | 13 ++++++++++++- src/address.ts | 30 +++++++++++++++++++++++++++++- types/address.d.ts | 6 ++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/address.spec.ts b/src/address.spec.ts index f0a97792..1d138206 100644 --- a/src/address.spec.ts +++ b/src/address.spec.ts @@ -1,7 +1,7 @@ import { Address, Algorithm, PubkeyBytes } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; -import { decodeCosmosAddress, isValidAddress, pubkeyToAddress } from "./address"; +import { decodeCosmosAddress, decodeCosmosPubkey, isValidAddress, pubkeyToAddress } from "./address"; const { fromBase64, fromHex } = Encoding; @@ -28,6 +28,17 @@ describe("address", () => { }); }); + describe("decodeCosmosPubkey", () => { + it("works", () => { + expect( + decodeCosmosPubkey("cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5"), + ).toEqual({ + prefix: "cosmospub", + data: fromBase64("A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"), + }); + }); + }); + describe("isValidAddress", () => { it("accepts valid addresses", () => { expect(isValidAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6")).toEqual(true); diff --git a/src/address.ts b/src/address.ts index 853e24af..d36a50d4 100644 --- a/src/address.ts +++ b/src/address.ts @@ -1,15 +1,23 @@ import { Address, Algorithm, PubkeyBundle } from "@iov/bcp"; import { Ripemd160, Secp256k1, Sha256 } from "@iov/crypto"; -import { Bech32 } from "@iov/encoding"; +import { Bech32, Encoding } from "@iov/encoding"; +import equal from "fast-deep-equal"; export type CosmosAddressBech32Prefix = "cosmos" | "cosmosvalcons" | "cosmosvaloper"; export type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmosvaloperpub"; export type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix; +// As discussed in https://github.com/binance-chain/javascript-sdk/issues/163 +const pubkeyAminoPrefix = Encoding.fromHex("eb5ae98721"); + 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 } { @@ -23,6 +31,26 @@ export function decodeCosmosAddress( return { prefix: prefix, data: data }; } +export function decodeCosmosPubkey( + encodedPubkey: string, +): { readonly prefix: CosmosPubkeyBech32Prefix; readonly data: Uint8Array } { + const { prefix, data } = Bech32.decode(encodedPubkey); + if (!isCosmosPubkeyBech32Prefix(prefix)) { + throw new Error(`Invalid bech32 prefix. Must be one of cosmos, cosmosvalcons, or cosmosvaloper.`); + } + + if (!equal(data.slice(0, pubkeyAminoPrefix.length), pubkeyAminoPrefix)) { + throw new Error("Pubkey does not have the expected amino prefix " + Encoding.toHex(pubkeyAminoPrefix)); + } + + const rest = data.slice(pubkeyAminoPrefix.length); + if (rest.length !== 33) { + throw new Error("Invalid rest data length. Expected 33 bytes (compressed secp256k1 pubkey)."); + } + + return { prefix: prefix, data: rest }; +} + export function isValidAddress(address: string): boolean { try { decodeCosmosAddress(address as Address); diff --git a/types/address.d.ts b/types/address.d.ts index 776cb61d..252add6e 100644 --- a/types/address.d.ts +++ b/types/address.d.ts @@ -8,5 +8,11 @@ export declare function decodeCosmosAddress( readonly prefix: CosmosAddressBech32Prefix; readonly data: Uint8Array; }; +export declare function decodeCosmosPubkey( + encodedPubkey: string, +): { + readonly prefix: CosmosPubkeyBech32Prefix; + readonly data: Uint8Array; +}; export declare function isValidAddress(address: string): boolean; export declare function pubkeyToAddress(pubkey: PubkeyBundle, prefix: CosmosBech32Prefix): Address;