Create pubkeyToRawAddress and make it work for multisig accounts

This commit is contained in:
Simon Warta 2021-03-22 16:19:22 +01:00
parent 09ce133331
commit 4f5d919c2c
5 changed files with 80 additions and 25 deletions

View File

@ -40,6 +40,7 @@
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
},
"dependencies": {
"@cosmjs/crypto": "^0.25.0-alpha.0",
"@cosmjs/encoding": "^0.25.0-alpha.0",
"@cosmjs/utils": "^0.25.0-alpha.0"
},

View File

@ -1,4 +1,4 @@
import { Bech32, fromBase64 } from "@cosmjs/encoding";
import { Bech32, fromBase64, fromHex, toBase64 } from "@cosmjs/encoding";
import {
decodeAminoPubkey,
@ -6,6 +6,7 @@ import {
encodeAminoPubkey,
encodeBech32Pubkey,
encodeSecp256k1Pubkey,
pubkeyToRawAddress,
} from "./encoding";
import { MultisigThresholdPubkey, Pubkey } from "./pubkeys";
@ -218,4 +219,51 @@ describe("encoding", () => {
expect(encodeAminoPubkey(testgroup4)).toEqual(expected4);
});
});
describe("pubkeyToRawAddress", () => {
it("works for Secp256k1", () => {
const pubkey = {
type: "tendermint/PubKeySecp256k1",
value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP",
};
expect(pubkeyToRawAddress(pubkey)).toEqual(
Bech32.decode("cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r").data,
);
});
it("works for Ed25519", () => {
const pubkey = {
type: "tendermint/PubKeyEd25519",
value: toBase64(fromHex("12ee6f581fe55673a1e9e1382a0829e32075a0aa4763c968bc526e1852e78c95")),
};
expect(pubkeyToRawAddress(pubkey)).toEqual(
Bech32.decode("cosmos1pfq05em6sfkls66ut4m2257p7qwlk448h8mysz").data,
);
});
it("works for multisig", () => {
const test1 = decodeBech32Pubkey(
"wasmpub1addwnpepqwxttx8w2sfs6d8cuzqcuau84grp8xsw95qzdjkmvc44tnckskdxw3zw2km",
// pubkey data: eb5ae98721038cb598ee54130d34f8e0818e7787aa06139a0e2d0026cadb662b55cf16859a67
// address: wasm1jq59w7y34msq69g4w3zvq6d5h3stcajd8g62xm
// address data: 9028577891aee00d15157444c069b4bc60bc764d
);
const test2 = decodeBech32Pubkey(
"wasmpub1addwnpepq2gx7x7e29kge5a4ycunytyqr0u8ynql5h583s8r9wdads9m3v8ks6y0nhc",
// pubkey data: eb5ae9872102906f1bd9516c8cd3b52639322c801bf8724c1fa5e878c0e32b9bd6c0bb8b0f68
// address: wasm146e52j6zphxw8m67cz8860ad5uju892cqmawsg
// address data: aeb3454b420dcce3ef5ec08e7d3fada725c39558
);
const test3 = decodeBech32Pubkey(
"wasmpub1addwnpepq0xfx5vavxmgdkn0p6x0l9p3udttghu3qcldd7ql08wa3xy93qq0xuzvtxc",
// pubkey data: eb5ae9872103cc93519d61b686da6f0e8cff9431e356b45f91063ed6f81f79ddd898858800f3
// address: wasm1a6uxr25mw8qg8zz3l2avsdjsveh4yg9sw7h5np
// address data: eeb861aa9b71c0838851fabac83650666f5220b0
);
expect(pubkeyToRawAddress(test1)).toEqual(fromHex("9028577891aee00d15157444c069b4bc60bc764d"));
expect(pubkeyToRawAddress(test2)).toEqual(fromHex("aeb3454b420dcce3ef5ec08e7d3fada725c39558"));
expect(pubkeyToRawAddress(test3)).toEqual(fromHex("eeb861aa9b71c0838851fabac83650666f5220b0"));
});
});
});

View File

@ -1,3 +1,4 @@
import { ripemd160, sha256 } from "@cosmjs/crypto";
import { Bech32, fromBase64, fromHex, toBase64, toHex } from "@cosmjs/encoding";
import { Uint53 } from "@cosmjs/math";
import { arrayContentStartsWith } from "@cosmjs/utils";
@ -116,6 +117,30 @@ export function encodeAminoPubkey(pubkey: Pubkey): Uint8Array {
}
}
// See https://github.com/tendermint/tendermint/blob/f2ada0a604b4c0763bda2f64fac53d506d3beca7/docs/spec/blockchain/encoding.md#public-key-cryptography
// For secp256k1 this assumes we already have a compressed pubkey.
export function pubkeyToRawAddress(pubkey: Pubkey): Uint8Array {
if (isSecp256k1Pubkey(pubkey)) {
const pubkeyData = fromBase64(pubkey.value);
if (pubkeyData.length !== 33) {
throw new Error(`Invalid Secp256k1 pubkey length (compressed): ${pubkeyData.length}`);
}
return ripemd160(sha256(pubkeyData)).slice(0, 20);
} else if (isEd25519Pubkey(pubkey)) {
const pubkeyData = fromBase64(pubkey.value);
if (pubkeyData.length !== 32) {
throw new Error(`Invalid Ed25519 pubkey length: ${pubkeyData.length}`);
}
return sha256(pubkeyData).slice(0, 20);
} else if (isMultisigThresholdPubkey(pubkey)) {
// https://github.com/tendermint/tendermint/blob/38b401657e4ad7a7eeb3c30a3cbf512037df3740/crypto/multisig/threshold_pubkey.go#L71-L74
const pubkeyData = encodeAminoPubkey(pubkey);
return sha256(pubkeyData).slice(0, 20);
} else {
throw new Error("Unsupported public key type");
}
}
/**
* Encodes a public key to binary Amino and then to bech32.
*

View File

@ -4,6 +4,7 @@ export {
encodeAminoPubkey,
encodeBech32Pubkey,
encodeSecp256k1Pubkey,
pubkeyToRawAddress,
} from "./encoding";
export {
MultisigThresholdPubkey,

View File

@ -1,6 +1,7 @@
import { pubkeyType, SinglePubkey } from "@cosmjs/amino";
import { SinglePubkey } from "@cosmjs/amino";
import { pubkeyToRawAddress } from "@cosmjs/amino/build/encoding";
import { ripemd160, sha256 } from "@cosmjs/crypto";
import { Bech32, fromBase64 } from "@cosmjs/encoding";
import { Bech32 } from "@cosmjs/encoding";
export function rawSecp256k1PubkeyToAddress(pubkeyRaw: Uint8Array, prefix: string): string {
if (pubkeyRaw.length !== 33) {
@ -14,26 +15,5 @@ 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: SinglePubkey, prefix: string): string {
const pubkeyBytes = fromBase64(pubkey.value);
switch (pubkey.type) {
case pubkeyType.secp256k1: {
return rawSecp256k1PubkeyToAddress(pubkeyBytes, prefix);
}
case pubkeyType.ed25519: {
if (pubkeyBytes.length !== 32) {
throw new Error(`Invalid Ed25519 pubkey length: ${pubkeyBytes.length}`);
}
const hash = sha256(pubkeyBytes);
return Bech32.encode(prefix, hash.slice(0, 20));
}
case pubkeyType.sr25519: {
if (pubkeyBytes.length !== 32) {
throw new Error(`Invalid Sr25519 pubkey length: ${pubkeyBytes.length}`);
}
const hash = sha256(pubkeyBytes);
return Bech32.encode(prefix, hash.slice(0, 20));
}
default:
throw new Error("Unrecognized public key algorithm");
}
return Bech32.encode(prefix, pubkeyToRawAddress(pubkey));
}