From 27f34273f01aa44a32a2399b4308a1721c451d43 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 24 Mar 2021 11:05:28 +0100 Subject: [PATCH] Pull out and test makeCompactBitArray --- packages/stargate/src/multisignature.spec.ts | 154 ++++++++++++++++++- packages/stargate/src/multisignature.ts | 25 ++- 2 files changed, 171 insertions(+), 8 deletions(-) diff --git a/packages/stargate/src/multisignature.spec.ts b/packages/stargate/src/multisignature.spec.ts index 5805a95b..1d4a090f 100644 --- a/packages/stargate/src/multisignature.spec.ts +++ b/packages/stargate/src/multisignature.spec.ts @@ -4,12 +4,164 @@ import { assert } from "@cosmjs/utils"; import { MsgSend } from "./codec/cosmos/bank/v1beta1/tx"; import { TxRaw } from "./codec/cosmos/tx/v1beta1/tx"; -import { makeMultisignedTx } from "./multisignature"; +import { makeCompactBitArray, makeMultisignedTx } from "./multisignature"; import { SignerData, SigningStargateClient } from "./signingstargateclient"; import { assertIsBroadcastTxSuccess } from "./stargateclient"; import { faucet, pendingWithoutSimapp, simapp } from "./testutils.spec"; describe("multisignature", () => { + describe("makeCompactBitArray", () => { + it("works for 0 bits of different lengths", () => { + expect(makeCompactBitArray([])).toEqual({ elems: new Uint8Array([]), extraBitsStored: 0 }); + expect(makeCompactBitArray([false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 1, + }); + expect(makeCompactBitArray([false, false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 2, + }); + expect(makeCompactBitArray([false, false, false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 3, + }); + expect(makeCompactBitArray([false, false, false, false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 4, + }); + expect(makeCompactBitArray([false, false, false, false, false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 5, + }); + expect(makeCompactBitArray([false, false, false, false, false, false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 6, + }); + expect(makeCompactBitArray([false, false, false, false, false, false, false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 7, + }); + expect(makeCompactBitArray([false, false, false, false, false, false, false, false])).toEqual({ + elems: new Uint8Array([0b00000000]), + extraBitsStored: 0, + }); + expect(makeCompactBitArray([false, false, false, false, false, false, false, false, false])).toEqual({ + elems: new Uint8Array([0b00000000, 0b00000000]), + extraBitsStored: 1, + }); + expect( + makeCompactBitArray([false, false, false, false, false, false, false, false, false, false]), + ).toEqual({ elems: new Uint8Array([0b00000000, 0b00000000]), extraBitsStored: 2 }); + }); + + it("works for 1 bits of different lengths", () => { + expect(makeCompactBitArray([])).toEqual({ elems: new Uint8Array([]), extraBitsStored: 0 }); + expect(makeCompactBitArray([true])).toEqual({ + elems: new Uint8Array([0b10000000]), + extraBitsStored: 1, + }); + expect(makeCompactBitArray([true, true])).toEqual({ + elems: new Uint8Array([0b11000000]), + extraBitsStored: 2, + }); + expect(makeCompactBitArray([true, true, true])).toEqual({ + elems: new Uint8Array([0b11100000]), + extraBitsStored: 3, + }); + expect(makeCompactBitArray([true, true, true, true])).toEqual({ + elems: new Uint8Array([0b11110000]), + extraBitsStored: 4, + }); + expect(makeCompactBitArray([true, true, true, true, true])).toEqual({ + elems: new Uint8Array([0b11111000]), + extraBitsStored: 5, + }); + expect(makeCompactBitArray([true, true, true, true, true, true])).toEqual({ + elems: new Uint8Array([0b11111100]), + extraBitsStored: 6, + }); + expect(makeCompactBitArray([true, true, true, true, true, true, true])).toEqual({ + elems: new Uint8Array([0b11111110]), + extraBitsStored: 7, + }); + expect(makeCompactBitArray([true, true, true, true, true, true, true, true])).toEqual({ + elems: new Uint8Array([0b11111111]), + extraBitsStored: 0, + }); + expect(makeCompactBitArray([true, true, true, true, true, true, true, true, true])).toEqual({ + elems: new Uint8Array([0b11111111, 0b10000000]), + extraBitsStored: 1, + }); + expect(makeCompactBitArray([true, true, true, true, true, true, true, true, true, true])).toEqual({ + elems: new Uint8Array([0b11111111, 0b11000000]), + extraBitsStored: 2, + }); + }); + + it("works for 1 bit in different places", () => { + expect( + makeCompactBitArray([true, false, false, false, false, false, false, false, false, false]), + ).toEqual({ + elems: new Uint8Array([0b10000000, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, true, false, false, false, false, false, false, false, false]), + ).toEqual({ + elems: new Uint8Array([0b01000000, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, true, false, false, false, false, false, false, false]), + ).toEqual({ + elems: new Uint8Array([0b00100000, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, false, true, false, false, false, false, false, false]), + ).toEqual({ + elems: new Uint8Array([0b00010000, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, false, false, true, false, false, false, false, false]), + ).toEqual({ + elems: new Uint8Array([0b00001000, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, false, false, false, true, false, false, false, false]), + ).toEqual({ + elems: new Uint8Array([0b00000100, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, false, false, false, false, true, false, false, false]), + ).toEqual({ + elems: new Uint8Array([0b00000010, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, false, false, false, false, false, true, false, false]), + ).toEqual({ + elems: new Uint8Array([0b00000001, 0b00000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, false, false, false, false, false, false, true, false]), + ).toEqual({ + elems: new Uint8Array([0b00000000, 0b10000000]), + extraBitsStored: 2, + }); + expect( + makeCompactBitArray([false, false, false, false, false, false, false, false, false, true]), + ).toEqual({ + elems: new Uint8Array([0b00000000, 0b01000000]), + extraBitsStored: 2, + }); + }); + }); + describe("makeMultisignedTx", () => { it("works", async () => { pendingWithoutSimapp(); diff --git a/packages/stargate/src/multisignature.ts b/packages/stargate/src/multisignature.ts index de98e563..08b579c8 100644 --- a/packages/stargate/src/multisignature.ts +++ b/packages/stargate/src/multisignature.ts @@ -9,6 +9,21 @@ import { AuthInfo, SignerInfo } from "./codec/cosmos/tx/v1beta1/tx"; import { TxRaw } from "./codec/cosmos/tx/v1beta1/tx"; import { StdFee } from "./fee"; +export function makeCompactBitArray(bits: readonly boolean[]): CompactBitArray { + const byteCount = Math.ceil(bits.length / 8); + const extraBits = bits.length - Math.floor(bits.length / 8) * 8; + const bytes = new Uint8Array(byteCount); // zero-filled + + bits.forEach((value, index) => { + const bytePos = Math.floor(index / 8); + const bitPos = index % 8; + // eslint-disable-next-line no-bitwise + if (value) bytes[bytePos] |= 0b1 << (8 - 1 - bitPos); + }); + + return CompactBitArray.fromPartial({ elems: bytes, extraBitsStored: extraBits }); +} + export function makeMultisignedTx( multisigPubkey: MultisigThresholdPubkey, sequence: number, @@ -19,14 +34,13 @@ export function makeMultisignedTx( const addresses = Array.from(signatures.keys()); const prefix = Bech32.decode(addresses[0]).prefix; - let bits = 0; + const signers: boolean[] = Array(multisigPubkey.value.pubkeys.length).fill(false); const signaturesList = new Array(); for (let i = 0; i < multisigPubkey.value.pubkeys.length; i++) { const signerAddress = pubkeyToAddress(multisigPubkey.value.pubkeys[i], prefix); const signature = signatures.get(signerAddress); if (signature) { - // eslint-disable-next-line no-bitwise - bits |= 0b1 << (8 - 1 - i); + signers[i] = true; signaturesList.push(signature); } } @@ -35,10 +49,7 @@ export function makeMultisignedTx( publicKey: encodePubkey(multisigPubkey), modeInfo: { multi: { - bitarray: CompactBitArray.fromPartial({ - elems: new Uint8Array([bits]), - extraBitsStored: multisigPubkey.value.pubkeys.length, - }), + bitarray: makeCompactBitArray(signers), modeInfos: signaturesList.map((_) => ({ single: { mode: SignMode.SIGN_MODE_LEGACY_AMINO_JSON } })), }, },