Merge pull request #884 from hleb-albau/882_decode_multisig

#882 Decode multisig pubkey
This commit is contained in:
Simon Warta 2021-09-30 21:57:31 +02:00 committed by GitHub
commit c2f1f4cd5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 0 deletions

View File

@ -95,6 +95,11 @@ describe("encoding", () => {
value: "YZHlYxP5R6olj3Tj3f7VgkQE5VaOvv9G0jKATqdQsqI=",
});
});
it("works for multisig", () => {
const decoded = decodeBech32Pubkey(testgroup1PubkeyBech32);
expect(decoded).toEqual(testgroup1);
});
});
describe("encodeAminoPubkey", () => {

View File

@ -6,6 +6,7 @@ import {
isEd25519Pubkey,
isMultisigThresholdPubkey,
isSecp256k1Pubkey,
MultisigThresholdPubkey,
Pubkey,
pubkeyType,
Secp256k1Pubkey,
@ -61,6 +62,9 @@ export function decodeAminoPubkey(data: Uint8Array): Pubkey {
type: pubkeyType.sr25519,
value: toBase64(rest),
};
} else if (arrayContentStartsWith(data, pubkeyAminoPrefixMultisigThreshold)) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return decodeMultisigPubkey(data);
} else {
throw new Error("Unsupported public key type. Amino data starts with: " + toHex(data.slice(0, 5)));
}
@ -77,6 +81,79 @@ export function decodeBech32Pubkey(bechEncoded: string): Pubkey {
return decodeAminoPubkey(data);
}
/**
* Uvarint decoder for Amino.
* @see https://github.com/tendermint/go-amino/blob/8e779b71f40d175/decoder.go#L64-76
* @returns varint as number, and bytes count occupied by varaint
*/
function decodeUvarint(reader: number[]): [number, number] {
if (reader.length < 1) {
throw new Error("Can't decode varint. EOF");
}
if (reader[0] > 127) {
throw new Error(
"Decoding numbers > 127 is not supported here. Please tell those lazy CosmJS maintainers to port the binary.Varint implementation from the Go standard library and write some tests.",
);
}
return [reader[0], 1];
}
/**
* Decodes a multisig pubkey to type object.
* Pubkey structure [ prefix + const + threshold + loop:(const + pubkeyLength + pubkey ) ]
* [ 4b + 1b + varint + loop:(1b + varint + pubkeyLength bytes) ]
* @param data encoded pubkey
*/
function decodeMultisigPubkey(data: Uint8Array): MultisigThresholdPubkey {
const reader = Array.from(data);
// remove multisig amino prefix;
const prefixFromReader = reader.splice(0, pubkeyAminoPrefixMultisigThreshold.length);
if (!arrayContentStartsWith(prefixFromReader, pubkeyAminoPrefixMultisigThreshold)) {
throw new Error("Invalid multisig prefix.");
}
// remove 0x08 threshold prefix;
if (reader.shift() != 0x08) {
throw new Error("Invalid multisig data. Expecting 0x08 prefix before threshold.");
}
// read threshold
const [threshold, thresholdBytesLength] = decodeUvarint(reader);
reader.splice(0, thresholdBytesLength);
// read participants pubkeys
const pubkeys = [];
while (reader.length > 0) {
// remove 0x12 threshold prefix;
if (reader.shift() != 0x12) {
throw new Error("Invalid multisig data. Expecting 0x12 prefix before participant pubkey length.");
}
// read pubkey length
const [pubkeyLength, pubkeyLengthBytesSize] = decodeUvarint(reader);
reader.splice(0, pubkeyLengthBytesSize);
// verify that we can read pubkey
if (reader.length < pubkeyLength) {
throw new Error("Invalid multisig data length.");
}
// read and decode participant pubkey
const encodedPubkey = reader.splice(0, pubkeyLength);
const pubkey = decodeAminoPubkey(Uint8Array.from(encodedPubkey));
pubkeys.push(pubkey);
}
return {
type: pubkeyType.multisigThreshold,
value: {
threshold: threshold.toString(),
pubkeys: pubkeys,
},
};
}
/**
* Uvarint encoder for Amino. This is the same encoding as `binary.PutUvarint` from the Go
* standard library.