Create createMultisigThresholdPubkey

This commit is contained in:
Simon Warta 2021-03-22 18:40:19 +01:00
parent 1a533fc24d
commit 7255af4186
5 changed files with 190 additions and 73 deletions

View File

@ -7,7 +7,17 @@ import {
encodeBech32Pubkey,
encodeSecp256k1Pubkey,
} from "./encoding";
import { MultisigThresholdPubkey, Pubkey } from "./pubkeys";
import { Pubkey } from "./pubkeys";
import {
testgroup1,
testgroup1Address,
testgroup2,
testgroup2Address,
testgroup3,
testgroup3Address,
testgroup4,
testgroup4Address,
} from "./testutils.spec";
describe("encoding", () => {
describe("encodeSecp256k1Pubkey", () => {
@ -137,84 +147,16 @@ describe("encoding", () => {
});
it("works for multisig", () => {
// ./build/wasmd keys add test1
// ./build/wasmd keys add test2
// ./build/wasmd keys add test3
// ./build/wasmd keys add testgroup1 --multisig=test1,test2,test3 --multisig-threshold 2
// ./build/wasmd keys add testgroup2 --multisig=test1,test2,test3 --multisig-threshold 1
// # By default pubkeys are sorted by its address data (https://github.com/cosmos/cosmos-sdk/blob/v0.42.2/client/keys/add.go#L172-L174)
// ./build/wasmd keys add testgroup3 --multisig=test3,test1 --multisig-threshold 2
// ./build/wasmd keys add testgroup4 --multisig=test3,test1 --nosort --multisig-threshold 2
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
);
// 2/3 multisig
const testgroup1: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "2",
pubkeys: [test1, test2, test3],
},
};
const expected1 = Bech32.decode(
"wasmpub1ytql0csgqgfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vufzd666axrjzq5sdudaj5tv3nfm2f3exgkgqxlcwfxplf0g0rqwx2um6mqthzc0dqfzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7v7aysdd",
).data;
const expected1 = Bech32.decode(testgroup1Address).data;
expect(encodeAminoPubkey(testgroup1)).toEqual(expected1);
// 1/3 multisig
const testgroup2: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "1",
pubkeys: [test1, test2, test3],
},
};
const expected2 = Bech32.decode(
"wasmpub1ytql0csgqyfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vufzd666axrjzq5sdudaj5tv3nfm2f3exgkgqxlcwfxplf0g0rqwx2um6mqthzc0dqfzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7vc4ejke",
).data;
const expected2 = Bech32.decode(testgroup2Address).data;
expect(encodeAminoPubkey(testgroup2)).toEqual(expected2);
// 2/2 multisig
const testgroup3: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "2",
pubkeys: [test1, test3],
},
};
const expected3 = Bech32.decode(
"wasmpub1ytql0csgqgfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vufzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7vzjhugu",
).data;
const expected3 = Bech32.decode(testgroup3Address).data;
expect(encodeAminoPubkey(testgroup3)).toEqual(expected3);
// 2/2 multisig with custom sorting
const testgroup4: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "2",
pubkeys: [test3, test1],
},
};
const expected4 = Bech32.decode(
"wasmpub1ytql0csgqgfzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7vfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vujvg56k",
).data;
const expected4 = Bech32.decode(testgroup4Address).data;
expect(encodeAminoPubkey(testgroup4)).toEqual(expected4);
});
});

View File

@ -18,3 +18,4 @@ export {
isSinglePubkey,
pubkeyType,
} from "./pubkeys";
export { createMultisigThresholdPubkey } from "./multisig";

View File

@ -0,0 +1,63 @@
import { compareArrays, createMultisigThresholdPubkey } from "./multisig";
import { test1, test2, test3, testgroup1, testgroup2, testgroup3, testgroup4 } from "./testutils.spec";
describe("multisig", () => {
describe("compareArrays", () => {
it("return 0 for equal arrays", () => {
expect(compareArrays(new Uint8Array([]), new Uint8Array([]))).toEqual(0);
expect(compareArrays(new Uint8Array([1]), new Uint8Array([1]))).toEqual(0);
expect(compareArrays(new Uint8Array([3, 2, 1]), new Uint8Array([3, 2, 1]))).toEqual(0);
});
it("return > 0 for left > right", () => {
expect(compareArrays(new Uint8Array([5, 5, 5]), new Uint8Array([5, 5, 4]))).toBeGreaterThan(0);
expect(compareArrays(new Uint8Array([5, 5, 5]), new Uint8Array([5, 4, 5]))).toBeGreaterThan(0);
expect(compareArrays(new Uint8Array([5, 5, 5]), new Uint8Array([4, 5, 5]))).toBeGreaterThan(0);
expect(compareArrays(new Uint8Array([5, 5, 5]), new Uint8Array([5, 5]))).toBeGreaterThan(0);
expect(compareArrays(new Uint8Array([5, 5, 5]), new Uint8Array([5]))).toBeGreaterThan(0);
expect(compareArrays(new Uint8Array([5, 5, 5]), new Uint8Array([]))).toBeGreaterThan(0);
});
it("return < 0 for left < right", () => {
expect(compareArrays(new Uint8Array([5, 5, 4]), new Uint8Array([5, 5, 5]))).toBeLessThan(0);
expect(compareArrays(new Uint8Array([5, 4, 5]), new Uint8Array([5, 5, 5]))).toBeLessThan(0);
expect(compareArrays(new Uint8Array([4, 5, 5]), new Uint8Array([5, 5, 5]))).toBeLessThan(0);
expect(compareArrays(new Uint8Array([5, 5]), new Uint8Array([5, 5, 5]))).toBeLessThan(0);
expect(compareArrays(new Uint8Array([5]), new Uint8Array([5, 5, 5]))).toBeLessThan(0);
expect(compareArrays(new Uint8Array([]), new Uint8Array([5, 5, 5]))).toBeLessThan(0);
});
it("can be used with sort", () => {
const values = [
new Uint8Array([2]),
new Uint8Array([1]),
new Uint8Array([2, 5]),
new Uint8Array([3]),
new Uint8Array([]),
].sort(compareArrays);
expect(values).toEqual([
new Uint8Array([]),
new Uint8Array([1]),
new Uint8Array([2]),
new Uint8Array([2, 5]),
new Uint8Array([3]),
]);
});
});
describe("MultisigThresholdPubkey", () => {
it("works with sorting", () => {
expect(createMultisigThresholdPubkey([test1, test2, test3], 2)).toEqual(testgroup1);
expect(createMultisigThresholdPubkey([test1, test2, test3], 1)).toEqual(testgroup2);
expect(createMultisigThresholdPubkey([test3, test1], 2)).toEqual(testgroup3);
expect(createMultisigThresholdPubkey([test1, test2, test3], 2, false)).toEqual(testgroup1);
expect(createMultisigThresholdPubkey([test1, test2, test3], 1, false)).toEqual(testgroup2);
expect(createMultisigThresholdPubkey([test3, test1], 2, false)).toEqual(testgroup3);
});
it("works with nosort", () => {
expect(createMultisigThresholdPubkey([test3, test1], 2, true)).toEqual(testgroup4);
});
});
});

View File

@ -0,0 +1,38 @@
import { toHex } from "@cosmjs/encoding";
import { Uint53 } from "@cosmjs/math";
import { pubkeyToRawAddress } from "./addresses";
import { MultisigThresholdPubkey, SinglePubkey } from "./pubkeys";
/**
* Compare arrays lexicographically.
*
* Returns value < 0 if `a < b`.
* Returns value > 0 if `a > b`.
* Returns 0 if `a === b`.
*/
export function compareArrays(a: Uint8Array, b: Uint8Array): number {
return toHex(a).localeCompare(toHex(b));
}
export function createMultisigThresholdPubkey(
pubkeys: readonly SinglePubkey[],
threshold: number,
nosort = false,
): MultisigThresholdPubkey {
const outPubkeys = nosort
? pubkeys
: Array.from(pubkeys).sort((lhs, rhs) => {
// https://github.com/cosmos/cosmos-sdk/blob/v0.42.2/client/keys/add.go#L172-L174
const addressLhs = pubkeyToRawAddress(lhs);
const addressRhs = pubkeyToRawAddress(rhs);
return compareArrays(addressLhs, addressRhs);
});
return {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: new Uint53(threshold).toString(),
pubkeys: outPubkeys,
},
};
}

View File

@ -0,0 +1,73 @@
import { decodeBech32Pubkey } from "./encoding";
import { MultisigThresholdPubkey } from "./pubkeys";
// ./build/wasmd keys add test1
// ./build/wasmd keys add test2
// ./build/wasmd keys add test3
// ./build/wasmd keys add testgroup1 --multisig=test1,test2,test3 --multisig-threshold 2
// ./build/wasmd keys add testgroup2 --multisig=test1,test2,test3 --multisig-threshold 1
// # By default pubkeys are sorted by its address data (https://github.com/cosmos/cosmos-sdk/blob/v0.42.2/client/keys/add.go#L172-L174)
// ./build/wasmd keys add testgroup3 --multisig=test3,test1 --multisig-threshold 2
// ./build/wasmd keys add testgroup4 --multisig=test3,test1 --nosort --multisig-threshold 2
export const test1 = decodeBech32Pubkey(
"wasmpub1addwnpepqwxttx8w2sfs6d8cuzqcuau84grp8xsw95qzdjkmvc44tnckskdxw3zw2km",
// pubkey data: eb5ae98721038cb598ee54130d34f8e0818e7787aa06139a0e2d0026cadb662b55cf16859a67
// address: wasm1jq59w7y34msq69g4w3zvq6d5h3stcajd8g62xm
// address data: 9028577891aee00d15157444c069b4bc60bc764d
);
export const test2 = decodeBech32Pubkey(
"wasmpub1addwnpepq2gx7x7e29kge5a4ycunytyqr0u8ynql5h583s8r9wdads9m3v8ks6y0nhc",
// pubkey data: eb5ae9872102906f1bd9516c8cd3b52639322c801bf8724c1fa5e878c0e32b9bd6c0bb8b0f68
// address: wasm146e52j6zphxw8m67cz8860ad5uju892cqmawsg
// address data: aeb3454b420dcce3ef5ec08e7d3fada725c39558
);
export const test3 = decodeBech32Pubkey(
"wasmpub1addwnpepq0xfx5vavxmgdkn0p6x0l9p3udttghu3qcldd7ql08wa3xy93qq0xuzvtxc",
// pubkey data: eb5ae9872103cc93519d61b686da6f0e8cff9431e356b45f91063ed6f81f79ddd898858800f3
// address: wasm1a6uxr25mw8qg8zz3l2avsdjsveh4yg9sw7h5np
// address data: eeb861aa9b71c0838851fabac83650666f5220b0
);
// 2/3 multisig
export const testgroup1: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "2",
pubkeys: [test1, test2, test3],
},
};
export const testgroup1Address =
"wasmpub1ytql0csgqgfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vufzd666axrjzq5sdudaj5tv3nfm2f3exgkgqxlcwfxplf0g0rqwx2um6mqthzc0dqfzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7v7aysdd";
export const testgroup2: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "1",
pubkeys: [test1, test2, test3],
},
};
export const testgroup2Address =
"wasmpub1ytql0csgqyfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vufzd666axrjzq5sdudaj5tv3nfm2f3exgkgqxlcwfxplf0g0rqwx2um6mqthzc0dqfzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7vc4ejke";
// 2/2 multisig
export const testgroup3: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "2",
pubkeys: [test1, test3],
},
};
export const testgroup3Address =
"wasmpub1ytql0csgqgfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vufzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7vzjhugu";
// 2/2 multisig with custom sorting
export const testgroup4: MultisigThresholdPubkey = {
type: "tendermint/PubKeyMultisigThreshold",
value: {
threshold: "2",
pubkeys: [test3, test1],
},
};
export const testgroup4Address =
"wasmpub1ytql0csgqgfzd666axrjzq7vjdge6cdksmdx7r5vl72rrc6kk30ezp376mup77wamzvgtzqq7vfzd666axrjzquvkkvwu4qnp5603cyp3emc02sxzwdqutgqym9dke3t2h83dpv6vujvg56k";