Improve parsing, two types of pubkey decoding
This commit is contained in:
parent
62b5c88c58
commit
f5fbb02ba4
@ -33,8 +33,8 @@ describe("address", () => {
|
||||
expect(
|
||||
decodeCosmosPubkey("cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5"),
|
||||
).toEqual({
|
||||
prefix: "cosmospub",
|
||||
data: fromBase64("A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ"),
|
||||
algo: Algorithm.Secp256k1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Address, Algorithm, PubkeyBundle } from "@iov/bcp";
|
||||
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";
|
||||
@ -8,7 +8,10 @@ export type CosmosPubkeyBech32Prefix = "cosmospub" | "cosmosvalconspub" | "cosmo
|
||||
export type CosmosBech32Prefix = CosmosAddressBech32Prefix | CosmosPubkeyBech32Prefix;
|
||||
|
||||
// As discussed in https://github.com/binance-chain/javascript-sdk/issues/163
|
||||
const pubkeyAminoPrefix = Encoding.fromHex("eb5ae98721");
|
||||
// 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);
|
||||
@ -33,22 +36,27 @@ export function decodeCosmosAddress(
|
||||
|
||||
export function decodeCosmosPubkey(
|
||||
encodedPubkey: string,
|
||||
): { readonly prefix: CosmosPubkeyBech32Prefix; readonly data: Uint8Array } {
|
||||
): { 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.`);
|
||||
}
|
||||
|
||||
if (!equal(data.slice(0, pubkeyAminoPrefix.length), pubkeyAminoPrefix)) {
|
||||
throw new Error("Pubkey does not have the expected amino prefix " + Encoding.toHex(pubkeyAminoPrefix));
|
||||
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));
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@ -151,13 +151,7 @@ export class CosmWasmConnection implements BlockchainConnection {
|
||||
this.tokenInfo.find(token => token.denom === denom),
|
||||
);
|
||||
|
||||
const pubkey = !account.public_key
|
||||
? undefined
|
||||
: {
|
||||
algo: Algorithm.Secp256k1,
|
||||
// amino-js has wrong (outdated) types
|
||||
data: decodeCosmosPubkey(account.public_key as any).data as PubkeyBytes,
|
||||
};
|
||||
const pubkey = !account.public_key ? undefined : decodeCosmosPubkey(account.public_key);
|
||||
return {
|
||||
address: address,
|
||||
balance: supportedCoins.map(coin => decodeAmount(this.tokenInfo, coin)),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { coinToDecimal, TxsResponse, types } from "@cosmwasm/sdk";
|
||||
import { coinToDecimal, isAminoStdTx, TxsResponse, types } from "@cosmwasm/sdk";
|
||||
import {
|
||||
Address,
|
||||
Algorithm,
|
||||
@ -95,7 +95,7 @@ export function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee {
|
||||
|
||||
export function parseTx(tx: types.Tx, chainId: ChainId, nonce: Nonce, tokens: TokenInfos): SignedTransaction {
|
||||
const txValue = tx.value;
|
||||
if (!types.isAminoStdTx(txValue)) {
|
||||
if (!isAminoStdTx(txValue)) {
|
||||
throw new Error("Only Amino StdTx is supported");
|
||||
}
|
||||
if (txValue.msg.length !== 1) {
|
||||
|
||||
6
packages/bcp/types/address.d.ts
vendored
6
packages/bcp/types/address.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import { Address, PubkeyBundle } from "@iov/bcp";
|
||||
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;
|
||||
@ -11,8 +11,8 @@ export declare function decodeCosmosAddress(
|
||||
export declare function decodeCosmosPubkey(
|
||||
encodedPubkey: string,
|
||||
): {
|
||||
readonly prefix: CosmosPubkeyBech32Prefix;
|
||||
readonly data: Uint8Array;
|
||||
readonly algo: Algorithm;
|
||||
readonly data: PubkeyBytes;
|
||||
};
|
||||
export declare function isValidAddress(address: string): boolean;
|
||||
export declare function pubkeyToAddress(pubkey: PubkeyBundle, prefix: CosmosBech32Prefix): Address;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export { coinToDecimal } from "./decoding";
|
||||
export { decimalToCoin } from "./encoding";
|
||||
export { RestClient, TxsResponse } from "./restclient";
|
||||
// export { AminoTx, isAminoStdTx, TokenInfo } from "./types";
|
||||
export { default as types } from "./types";
|
||||
// types.X are all the types we re-export
|
||||
// Note: this doesn't work for functions, just typescript types, so we must explicitly re-export functions
|
||||
export { default as types, isAminoStdTx } from "./types";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import amino, { unmarshalTx } from "@tendermint/amino-js";
|
||||
import { unmarshalTx } from "@tendermint/amino-js";
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
|
||||
import { AminoTx } from "./types";
|
||||
import { AminoTx, BaseAccount } from "./types";
|
||||
|
||||
interface NodeInfo {
|
||||
readonly network: string;
|
||||
@ -35,7 +35,7 @@ interface BlocksResponse {
|
||||
|
||||
interface AuthAccountsResponse {
|
||||
readonly result: {
|
||||
readonly value: amino.BaseAccount;
|
||||
readonly value: BaseAccount;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// We will move all needed *interfaces* from amino-js here
|
||||
// This means bcp can just import them from here (if needed at all)
|
||||
export interface Tx {
|
||||
type: string;
|
||||
value: any;
|
||||
readonly type: string;
|
||||
// TODO
|
||||
readonly value: any;
|
||||
}
|
||||
|
||||
export interface StdTx {
|
||||
@ -13,7 +14,7 @@ export interface StdTx {
|
||||
}
|
||||
|
||||
export interface Msg {
|
||||
type: string;
|
||||
readonly type: string;
|
||||
// TODO: make better union type
|
||||
readonly value: MsgSend;
|
||||
}
|
||||
@ -32,20 +33,30 @@ export interface StdFee {
|
||||
}
|
||||
|
||||
export interface Coin {
|
||||
denom: string;
|
||||
amount: string;
|
||||
readonly denom: string;
|
||||
readonly amount: string;
|
||||
}
|
||||
|
||||
export interface StdSignature {
|
||||
pub_key: PubKey;
|
||||
signature: string;
|
||||
readonly pub_key: PubKey;
|
||||
readonly signature: string;
|
||||
}
|
||||
|
||||
export interface PubKey {
|
||||
/** Amino registered name, e.g. `"tendermint/PubKeySecp256k1"` */
|
||||
type: string;
|
||||
/** Base64-encoded key bytes */
|
||||
value: string;
|
||||
readonly type: string;
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
// AccountPubKey is bech32-encoded amino-binary encoded PubKey interface. oof.
|
||||
export type AccountPubKey = string;
|
||||
|
||||
export interface BaseAccount {
|
||||
/** Bech32 account address */
|
||||
readonly address: string;
|
||||
readonly coins: ReadonlyArray<Coin>;
|
||||
readonly public_key: AccountPubKey;
|
||||
readonly account_number: string;
|
||||
readonly sequence: string;
|
||||
}
|
||||
|
||||
export type AminoTx = Tx & { readonly value: StdTx };
|
||||
|
||||
2
packages/sdk/types/index.d.ts
vendored
2
packages/sdk/types/index.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
export { coinToDecimal } from "./decoding";
|
||||
export { decimalToCoin } from "./encoding";
|
||||
export { RestClient, TxsResponse } from "./restclient";
|
||||
export { default as types } from "./types";
|
||||
export { default as types, isAminoStdTx } from "./types";
|
||||
|
||||
5
packages/sdk/types/restclient.d.ts
vendored
5
packages/sdk/types/restclient.d.ts
vendored
@ -1,5 +1,4 @@
|
||||
import amino from "@tendermint/amino-js";
|
||||
import { AminoTx } from "./types";
|
||||
import { AminoTx, BaseAccount } from "./types";
|
||||
interface NodeInfo {
|
||||
readonly network: string;
|
||||
}
|
||||
@ -27,7 +26,7 @@ interface BlocksResponse {
|
||||
}
|
||||
interface AuthAccountsResponse {
|
||||
readonly result: {
|
||||
readonly value: amino.BaseAccount;
|
||||
readonly value: BaseAccount;
|
||||
};
|
||||
}
|
||||
export interface TxsResponse {
|
||||
|
||||
29
packages/sdk/types/types.d.ts
vendored
29
packages/sdk/types/types.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
export interface Tx {
|
||||
type: string;
|
||||
value: any;
|
||||
readonly type: string;
|
||||
readonly value: any;
|
||||
}
|
||||
export interface StdTx {
|
||||
readonly msg: ReadonlyArray<Msg>;
|
||||
@ -9,7 +9,7 @@ export interface StdTx {
|
||||
readonly memo: string | undefined;
|
||||
}
|
||||
export interface Msg {
|
||||
type: string;
|
||||
readonly type: string;
|
||||
readonly value: MsgSend;
|
||||
}
|
||||
export interface MsgSend {
|
||||
@ -24,18 +24,25 @@ export interface StdFee {
|
||||
readonly gas: string;
|
||||
}
|
||||
export interface Coin {
|
||||
denom: string;
|
||||
amount: string;
|
||||
readonly denom: string;
|
||||
readonly amount: string;
|
||||
}
|
||||
export interface StdSignature {
|
||||
pub_key: PubKey;
|
||||
signature: string;
|
||||
readonly pub_key: PubKey;
|
||||
readonly signature: string;
|
||||
}
|
||||
export interface PubKey {
|
||||
/** Amino registered name, e.g. `"tendermint/PubKeySecp256k1"` */
|
||||
type: string;
|
||||
/** Base64-encoded key bytes */
|
||||
value: string;
|
||||
readonly type: string;
|
||||
readonly value: string;
|
||||
}
|
||||
export declare type AccountPubKey = string;
|
||||
export interface BaseAccount {
|
||||
/** Bech32 account address */
|
||||
readonly address: string;
|
||||
readonly coins: ReadonlyArray<Coin>;
|
||||
readonly public_key: AccountPubKey;
|
||||
readonly account_number: string;
|
||||
readonly sequence: string;
|
||||
}
|
||||
export declare type AminoTx = Tx & {
|
||||
readonly value: StdTx;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user