Generalize pubkey types
This commit is contained in:
parent
f8cf23766c
commit
810389d784
@ -24,6 +24,11 @@ and this project adheres to
|
||||
`SigningStargateClient`.
|
||||
- @cosmjs/amino: New package created that contains the shared amino signing
|
||||
functionality for @cosmjs/launchpad and @cosmjs/stargate.
|
||||
- @cosmjs/amino: Split public key interfaces into `Pubkey`, `SinglePubkey` and
|
||||
`Secp256k1Pubkey` where `Pubkey` is a generalization of the old `PubKey` that
|
||||
supported nested pubkeys for multisig. `SinglePubkey` is the old `PubKey` in
|
||||
which the `value` is a base64 encoded string. And `Secp256k1Pubkey` is a
|
||||
single secp256k1 pubkey.
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@ -7,9 +7,9 @@ import {
|
||||
encodeBech32Pubkey,
|
||||
encodeSecp256k1Pubkey,
|
||||
} from "./encoding";
|
||||
import { PubKey } from "./pubkeys";
|
||||
import { Pubkey } from "./pubkeys";
|
||||
|
||||
describe("pubkey", () => {
|
||||
describe("encoding", () => {
|
||||
describe("encodeSecp256k1Pubkey", () => {
|
||||
it("encodes a compresed pubkey", () => {
|
||||
const pubkey = fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP");
|
||||
@ -85,7 +85,7 @@ describe("pubkey", () => {
|
||||
|
||||
describe("encodeAminoPubkey", () => {
|
||||
it("works for secp256k1", () => {
|
||||
const pubkey: PubKey = {
|
||||
const pubkey: Pubkey = {
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
|
||||
};
|
||||
@ -98,7 +98,7 @@ describe("pubkey", () => {
|
||||
it("works for ed25519", () => {
|
||||
// Decoded from http://localhost:26657/validators
|
||||
// Encoded from `corald tendermint show-validator`
|
||||
const pubkey: PubKey = {
|
||||
const pubkey: Pubkey = {
|
||||
type: "tendermint/PubKeyEd25519",
|
||||
value: "YZHlYxP5R6olj3Tj3f7VgkQE5VaOvv9G0jKATqdQsqI=",
|
||||
};
|
||||
@ -111,7 +111,7 @@ describe("pubkey", () => {
|
||||
|
||||
describe("encodeBech32Pubkey", () => {
|
||||
it("works for secp256k1", () => {
|
||||
const pubkey: PubKey = {
|
||||
const pubkey: Pubkey = {
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
|
||||
};
|
||||
@ -123,7 +123,7 @@ describe("pubkey", () => {
|
||||
it("works for ed25519", () => {
|
||||
// Decoded from http://localhost:26657/validators
|
||||
// Encoded from `corald tendermint show-validator`
|
||||
const pubkey: PubKey = {
|
||||
const pubkey: Pubkey = {
|
||||
type: "tendermint/PubKeyEd25519",
|
||||
value: "YZHlYxP5R6olj3Tj3f7VgkQE5VaOvv9G0jKATqdQsqI=",
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Bech32, fromBase64, fromHex, toBase64, toHex } from "@cosmjs/encoding";
|
||||
import { arrayContentEquals } from "@cosmjs/utils";
|
||||
|
||||
import { PubKey, pubkeyType } from "./pubkeys";
|
||||
import { Pubkey, pubkeyType, Secp256k1Pubkey } from "./pubkeys";
|
||||
|
||||
export function encodeSecp256k1Pubkey(pubkey: Uint8Array): PubKey {
|
||||
export function encodeSecp256k1Pubkey(pubkey: Uint8Array): Secp256k1Pubkey {
|
||||
if (pubkey.length !== 33 || (pubkey[0] !== 0x02 && pubkey[0] !== 0x03)) {
|
||||
throw new Error("Public key must be compressed secp256k1, i.e. 33 bytes starting with 0x02 or 0x03");
|
||||
}
|
||||
@ -24,7 +24,7 @@ const pubkeyAminoPrefixLength = pubkeyAminoPrefixSecp256k1.length;
|
||||
/**
|
||||
* Decodes a pubkey in the Amino binary format to a type/value object.
|
||||
*/
|
||||
export function decodeAminoPubkey(data: Uint8Array): PubKey {
|
||||
export function decodeAminoPubkey(data: Uint8Array): Pubkey {
|
||||
const aminoPrefix = data.slice(0, pubkeyAminoPrefixLength);
|
||||
const rest = data.slice(pubkeyAminoPrefixLength);
|
||||
if (arrayContentEquals(aminoPrefix, pubkeyAminoPrefixSecp256k1)) {
|
||||
@ -62,7 +62,7 @@ export function decodeAminoPubkey(data: Uint8Array): PubKey {
|
||||
*
|
||||
* @param bechEncoded the bech32 encoded pubkey
|
||||
*/
|
||||
export function decodeBech32Pubkey(bechEncoded: string): PubKey {
|
||||
export function decodeBech32Pubkey(bechEncoded: string): Pubkey {
|
||||
const { data } = Bech32.decode(bechEncoded);
|
||||
return decodeAminoPubkey(data);
|
||||
}
|
||||
@ -70,7 +70,7 @@ export function decodeBech32Pubkey(bechEncoded: string): PubKey {
|
||||
/**
|
||||
* Encodes a public key to binary Amino.
|
||||
*/
|
||||
export function encodeAminoPubkey(pubkey: PubKey): Uint8Array {
|
||||
export function encodeAminoPubkey(pubkey: Pubkey): Uint8Array {
|
||||
let aminoPrefix: Uint8Array;
|
||||
switch (pubkey.type) {
|
||||
// Note: please don't add cases here without writing additional unit tests
|
||||
@ -92,6 +92,6 @@ export function encodeAminoPubkey(pubkey: PubKey): Uint8Array {
|
||||
* @param pubkey the public key to encode
|
||||
* @param prefix the bech32 prefix (human readable part)
|
||||
*/
|
||||
export function encodeBech32Pubkey(pubkey: PubKey, prefix: string): string {
|
||||
export function encodeBech32Pubkey(pubkey: Pubkey, prefix: string): string {
|
||||
return Bech32.encode(prefix, encodeAminoPubkey(pubkey));
|
||||
}
|
||||
|
||||
@ -5,4 +5,4 @@ export {
|
||||
encodeBech32Pubkey,
|
||||
encodeSecp256k1Pubkey,
|
||||
} from "./encoding";
|
||||
export { PubKey, pubkeyType } from "./pubkeys";
|
||||
export { Pubkey, Secp256k1Pubkey, SinglePubkey, isSinglePubkey, pubkeyType } from "./pubkeys";
|
||||
|
||||
44
packages/amino/src/pubkeys.spec.ts
Normal file
44
packages/amino/src/pubkeys.spec.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { isSinglePubkey } from "./pubkeys";
|
||||
|
||||
describe("pubkeys", () => {
|
||||
const pubkeyEd25519 = {
|
||||
type: "tendermint/PubKeyEd25519",
|
||||
value: "YZHlYxP5R6olj3Tj3f7VgkQE5VaOvv9G0jKATqdQsqI=",
|
||||
};
|
||||
const pubkeySecp256k1 = {
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP",
|
||||
};
|
||||
const pubkeyMultisigThreshold = {
|
||||
type: "tendermint/PubKeyMultisigThreshold",
|
||||
value: {
|
||||
threshold: "3",
|
||||
pubkeys: [
|
||||
{
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "A4KZH7VSRwW/6RTExROivRYKsQP63LnGcBlXFo+eKGpQ",
|
||||
},
|
||||
{
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "A8/Cq4VigOnDgl6RSdcx97fjrdCo/qwAX6C34n7ZDZLs",
|
||||
},
|
||||
{
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "ApKgZuwy03xgdRnXqG6yEHATomsWDOPacy7nbpsuUCSS",
|
||||
},
|
||||
{
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "Aptm8E3WSSFS0RTAIUW+bLi/slYnTEE+h4qPTG28CHfq",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
describe("isSinglePubkey", () => {
|
||||
it("works", () => {
|
||||
expect(isSinglePubkey(pubkeyEd25519)).toEqual(true);
|
||||
expect(isSinglePubkey(pubkeySecp256k1)).toEqual(true);
|
||||
expect(isSinglePubkey(pubkeyMultisigThreshold)).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,10 +1,13 @@
|
||||
export interface PubKey {
|
||||
export interface Pubkey {
|
||||
// type is one of the strings defined in pubkeyType
|
||||
// I don't use a string literal union here as that makes trouble with json test data:
|
||||
// https://github.com/cosmos/cosmjs/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: any;
|
||||
}
|
||||
|
||||
export interface Secp256k1Pubkey extends SinglePubkey {
|
||||
readonly type: "tendermint/PubKeySecp256k1";
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
@ -16,3 +19,26 @@ export const pubkeyType = {
|
||||
/** @see https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/sr25519/codec.go#L12 */
|
||||
sr25519: "tendermint/PubKeySr25519" as const,
|
||||
};
|
||||
|
||||
/**
|
||||
* A pubkey which contains the data directly without further nesting.
|
||||
*
|
||||
* You can think of this as a non-multisig pubkey.
|
||||
*/
|
||||
export interface SinglePubkey extends Pubkey {
|
||||
// type is one of the strings defined in pubkeyType
|
||||
// I don't use a string literal union here as that makes trouble with json test data:
|
||||
// https://github.com/cosmos/cosmjs/pull/44#pullrequestreview-353280504
|
||||
readonly type: string;
|
||||
/**
|
||||
* The base64 encoding of the Amino binary encoded pubkey.
|
||||
*
|
||||
* Note: if type is Secp256k1, this must contain a 33 bytes compressed pubkey.
|
||||
*/
|
||||
readonly value: string;
|
||||
}
|
||||
|
||||
export function isSinglePubkey(pubkey: Pubkey): pubkey is SinglePubkey {
|
||||
const singPubkeyTypes: string[] = [pubkeyType.ed25519, pubkeyType.secp256k1, pubkeyType.sr25519];
|
||||
return singPubkeyTypes.includes(pubkey.type);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { PubKey, pubkeyType } from "@cosmjs/amino";
|
||||
import { pubkeyType, SinglePubkey } from "@cosmjs/amino";
|
||||
import { ripemd160, sha256 } from "@cosmjs/crypto";
|
||||
import { Bech32, fromBase64 } from "@cosmjs/encoding";
|
||||
|
||||
@ -13,7 +13,7 @@ export function rawSecp256k1PubkeyToAddress(pubkeyRaw: Uint8Array, prefix: strin
|
||||
|
||||
// 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 pubkeyToAddress(pubkey: SinglePubkey, prefix: string): string {
|
||||
const pubkeyBytes = fromBase64(pubkey.value);
|
||||
switch (pubkey.type) {
|
||||
case pubkeyType.secp256k1: {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { PubKey } from "@cosmjs/amino";
|
||||
import { Pubkey } from "@cosmjs/amino";
|
||||
import { sha256 } from "@cosmjs/crypto";
|
||||
import { fromBase64, fromHex, toHex } from "@cosmjs/encoding";
|
||||
import { Uint53 } from "@cosmjs/math";
|
||||
@ -24,7 +24,7 @@ export interface Account {
|
||||
/** Bech32 account address */
|
||||
readonly address: string;
|
||||
readonly balance: readonly Coin[];
|
||||
readonly pubkey: PubKey | undefined;
|
||||
readonly pubkey: Pubkey | undefined;
|
||||
readonly accountNumber: number;
|
||||
readonly sequence: number;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ export {
|
||||
encodeBech32Pubkey,
|
||||
encodeSecp256k1Pubkey,
|
||||
pubkeyType,
|
||||
PubKey,
|
||||
SinglePubkey as PubKey,
|
||||
} from "@cosmjs/amino";
|
||||
|
||||
import * as logs from "./logs";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { PubKey } from "@cosmjs/amino";
|
||||
import { Pubkey } from "@cosmjs/amino";
|
||||
|
||||
import { Coin } from "../coins";
|
||||
import { LcdClient } from "./lcdclient";
|
||||
@ -26,7 +26,7 @@ export interface BaseAccount {
|
||||
* [1]: https://github.com/cosmos/cosmos-sdk/pull/5280
|
||||
* [2]: https://github.com/cosmos/cosmos-sdk/pull/6749
|
||||
*/
|
||||
readonly public_key: string | PubKey | null;
|
||||
readonly public_key: string | Pubkey | null;
|
||||
/**
|
||||
* The account number assigned by the blockchain.
|
||||
*
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { PubKey } from "@cosmjs/amino";
|
||||
import { Pubkey } from "@cosmjs/amino";
|
||||
|
||||
import { normalizePubkey, uint64ToNumber, uint64ToString } from "./utils";
|
||||
|
||||
@ -85,7 +85,7 @@ describe("utils", () => {
|
||||
});
|
||||
|
||||
it("passes PubKey unchanged", () => {
|
||||
const original: PubKey = {
|
||||
const original: Pubkey = {
|
||||
type: "tendermint/PubKeySecp256k1",
|
||||
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { decodeBech32Pubkey, PubKey } from "@cosmjs/amino";
|
||||
import { decodeBech32Pubkey, Pubkey } from "@cosmjs/amino";
|
||||
import { Uint64 } from "@cosmjs/math";
|
||||
|
||||
/**
|
||||
@ -29,7 +29,7 @@ export function uint64ToString(input: number | string): string {
|
||||
*
|
||||
* Returns null when unset.
|
||||
*/
|
||||
export function normalizePubkey(input: string | PubKey | null): PubKey | null {
|
||||
export function normalizePubkey(input: string | Pubkey | null): Pubkey | null {
|
||||
if (!input) return null;
|
||||
if (typeof input === "string") return decodeBech32Pubkey(input);
|
||||
return input;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { PubKey } from "@cosmjs/amino";
|
||||
import { Pubkey } from "@cosmjs/amino";
|
||||
|
||||
import { Coin } from "./coins";
|
||||
|
||||
@ -9,6 +9,6 @@ export interface StdFee {
|
||||
}
|
||||
|
||||
export interface StdSignature {
|
||||
readonly pub_key: PubKey;
|
||||
readonly pub_key: Pubkey;
|
||||
readonly signature: string;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { encodeSecp256k1Pubkey, PubKey as AminoPubKey } from "@cosmjs/amino";
|
||||
import { encodeSecp256k1Pubkey, SinglePubkey as AminoPubKey } from "@cosmjs/amino";
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
|
||||
import { PubKey } from "./codec/cosmos/crypto/secp256k1/keys";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { PubKey } from "@cosmjs/amino";
|
||||
import { Pubkey } from "@cosmjs/amino";
|
||||
import { Uint64 } from "@cosmjs/math";
|
||||
import { decodePubkey } from "@cosmjs/proto-signing";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
@ -16,7 +16,7 @@ import { Any } from "./codec/google/protobuf/any";
|
||||
export interface Account {
|
||||
/** Bech32 account address */
|
||||
readonly address: string;
|
||||
readonly pubkey: PubKey | null;
|
||||
readonly pubkey: Pubkey | null;
|
||||
readonly accountNumber: number;
|
||||
readonly sequence: number;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user