From 8c3ef4883c8d20080121769e774d36c0b444b406 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Thu, 4 Nov 2021 06:02:20 +0000 Subject: [PATCH 1/4] Wip updates to signer for TxSets --- signer/src/aggregate.ts | 14 ++++---------- signer/src/encodeMessageForSigning.ts | 12 ++++++------ signer/src/index.ts | 2 -- signer/src/sign.ts | 16 ++++++++++------ signer/src/types.ts | 17 ++++------------- signer/src/verify.ts | 16 ++++++++++------ signer/src/verifyAggregate.ts | 23 ----------------------- 7 files changed, 34 insertions(+), 66 deletions(-) delete mode 100644 signer/src/verifyAggregate.ts diff --git a/signer/src/aggregate.ts b/signer/src/aggregate.ts index b5f45e77..c7121dcb 100644 --- a/signer/src/aggregate.ts +++ b/signer/src/aggregate.ts @@ -1,21 +1,15 @@ import * as hubbleBls from "../deps/hubble-bls"; -import { AggregateTransactionData, TransactionData } from "./types"; +import { TransactionSet } from "./types"; -export default (txs: TransactionData[]): AggregateTransactionData => { - const sigsG1 = txs.map(tx => hubbleBls.mcl.loadG1(tx.signature)); +export default (txSets: TransactionSet[]): TransactionSet => { + const sigsG1 = txSets.map(txSet => hubbleBls.mcl.loadG1(txSet.signature)); const aggSigG1 = hubbleBls.signer.aggregate(sigsG1); const aggregateSignature = hubbleBls.mcl.dumpG1(aggSigG1); return { - transactions: txs.map(tx => ({ - publicKey: tx.publicKey, - nonce: tx.nonce, - ethValue: tx.ethValue, - contractAddress: tx.contractAddress, - encodedFunction: tx.encodedFunction, - })), + transactions: txSets.map(txSet => txSet.transactions).flat(), signature: aggregateSignature, }; } diff --git a/signer/src/encodeMessageForSigning.ts b/signer/src/encodeMessageForSigning.ts index cfbcd90a..b05c9755 100644 --- a/signer/src/encodeMessageForSigning.ts +++ b/signer/src/encodeMessageForSigning.ts @@ -1,20 +1,20 @@ import { keccak256 } from "@ethersproject/keccak256"; import { pack as solidityPack } from "@ethersproject/solidity"; -import { RawTransactionData } from "./types"; +import { TransactionTemplate } from "./types"; export default ( chainId: number, ) => ( - rawTxData: RawTransactionData, + txTemplate: TransactionTemplate, ): string => { return solidityPack( ["uint256", "uint256", "uint256", "address", "bytes32"], [ chainId, - rawTxData.nonce, - rawTxData.ethValue, - rawTxData.contractAddress, - keccak256(rawTxData.encodedFunction), + txTemplate.nonce, + txTemplate.ethValue, + txTemplate.contractAddress, + keccak256(txTemplate.encodedFunction), ] ); } diff --git a/signer/src/index.ts b/signer/src/index.ts index 2a01b353..b475794a 100644 --- a/signer/src/index.ts +++ b/signer/src/index.ts @@ -7,7 +7,6 @@ import getPublicKeyHash from "./getPublicKeyHash"; import AsyncReturnType from "./helpers/AsyncReturnType"; import sign from "./sign"; import verify from "./verify"; -import verifyAggregate from "./verifyAggregate"; export * from "./types"; @@ -34,6 +33,5 @@ export async function initBlsWalletSigner({ getPublicKeyHash: getPublicKeyHash(signerFactory, domain), sign: sign(signerFactory, domain, chainId), verify: verify(domain, chainId), - verifyAggregate: verifyAggregate(domain, chainId), }; } diff --git a/signer/src/sign.ts b/signer/src/sign.ts index 2f62d931..7069c3dc 100644 --- a/signer/src/sign.ts +++ b/signer/src/sign.ts @@ -2,24 +2,28 @@ import * as hubbleBls from "../deps/hubble-bls"; import encodeMessageForSigning from "./encodeMessageForSigning"; import getPublicKey from "./getPublicKey"; -import { RawTransactionData, TransactionData } from "./types"; +import { TransactionSet, TransactionTemplate } from "./types"; export default ( signerFactory: hubbleBls.signer.BlsSignerFactory, domain: Uint8Array, chainId: number, ) => ( - rawTransactionData: RawTransactionData, + txTemplate: TransactionTemplate, privateKey: string, -): TransactionData => { - const message = encodeMessageForSigning(chainId)(rawTransactionData); +): TransactionSet => { + const message = encodeMessageForSigning(chainId)(txTemplate); const signer = signerFactory.getSigner(domain, privateKey); const signature = hubbleBls.mcl.dumpG1(signer.sign(message)); return { - ...rawTransactionData, - publicKey: getPublicKey(signerFactory, domain)(privateKey), + transactions: [ + { + ...txTemplate, + publicKey: getPublicKey(signerFactory, domain)(privateKey), + } + ], signature, } }; diff --git a/signer/src/types.ts b/signer/src/types.ts index 1dbd6574..24f7e4be 100644 --- a/signer/src/types.ts +++ b/signer/src/types.ts @@ -1,24 +1,15 @@ import { BigNumber } from "@ethersproject/bignumber"; -export type RawTransactionData = { +export type TransactionTemplate = { nonce: BigNumber; ethValue: BigNumber; contractAddress: string; encodedFunction: string; }; -export type TransactionData = RawTransactionData & { - publicKey: string; - signature: string; -}; +export type UnsignedTransaction = TransactionTemplate & { publicKey: string }; -export type AggregateTransactionData = { - transactions: { - publicKey: string; - nonce: BigNumber; - ethValue: BigNumber; - contractAddress: string; - encodedFunction: string; - }[], +export type TransactionSet = { + transactions: UnsignedTransaction[], signature: string, }; diff --git a/signer/src/verify.ts b/signer/src/verify.ts index 896c378c..a5d8b8d7 100644 --- a/signer/src/verify.ts +++ b/signer/src/verify.ts @@ -1,19 +1,23 @@ import * as hubbleBls from "../deps/hubble-bls"; import encodeMessageForSigning from "./encodeMessageForSigning"; -import { TransactionData } from "./types"; +import { TransactionSet } from "./types"; export default ( domain: Uint8Array, chainId: number, ) => ( - txData: TransactionData, + txSet: TransactionSet, ): boolean => { const verifier = new hubbleBls.signer.BlsVerifier(domain); - return verifier.verify( - hubbleBls.mcl.loadG1(txData.signature), - hubbleBls.mcl.loadG2(txData.publicKey), - encodeMessageForSigning(chainId)(txData), + return verifier.verifyMultiple( + hubbleBls.mcl.loadG1(txSet.signature), + txSet.transactions.map( + tx => hubbleBls.mcl.loadG2(tx.publicKey), + ), + txSet.transactions.map( + tx => encodeMessageForSigning(chainId)(tx), + ), ); }; diff --git a/signer/src/verifyAggregate.ts b/signer/src/verifyAggregate.ts deleted file mode 100644 index af58dfc7..00000000 --- a/signer/src/verifyAggregate.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as hubbleBls from "../deps/hubble-bls"; - -import encodeMessageForSigning from "./encodeMessageForSigning"; -import { AggregateTransactionData } from "./types"; - -export default ( - domain: Uint8Array, - chainId: number, -) => ( - aggregateTxData: AggregateTransactionData, -): boolean => { - const verifier = new hubbleBls.signer.BlsVerifier(domain); - - return verifier.verifyMultiple( - hubbleBls.mcl.loadG1(aggregateTxData.signature), - aggregateTxData.transactions.map( - tx => hubbleBls.mcl.loadG2(tx.publicKey), - ), - aggregateTxData.transactions.map( - tx => encodeMessageForSigning(chainId)(tx), - ), - ); -}; From 7e92686a6ecb559d007f33eceef9272b13aaf66e Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Fri, 5 Nov 2021 02:30:19 +0000 Subject: [PATCH 2/4] Renaming, fix tests --- signer/src/aggregate.ts | 12 ++++----- signer/src/sign.ts | 6 ++--- signer/src/types.ts | 6 ++--- signer/src/verify.ts | 16 +++++------ signer/test/index.test.ts | 56 +++++++++++++++++++++------------------ 5 files changed, 48 insertions(+), 48 deletions(-) diff --git a/signer/src/aggregate.ts b/signer/src/aggregate.ts index c7121dcb..15857097 100644 --- a/signer/src/aggregate.ts +++ b/signer/src/aggregate.ts @@ -1,15 +1,13 @@ import * as hubbleBls from "../deps/hubble-bls"; -import { TransactionSet } from "./types"; +import { Transaction } from "./types"; -export default (txSets: TransactionSet[]): TransactionSet => { - const sigsG1 = txSets.map(txSet => hubbleBls.mcl.loadG1(txSet.signature)); +export default (txs: Transaction[]): Transaction => { + const sigsG1 = txs.map(tx => hubbleBls.mcl.loadG1(tx.signature)); const aggSigG1 = hubbleBls.signer.aggregate(sigsG1); - const aggregateSignature = hubbleBls.mcl.dumpG1(aggSigG1); - return { - transactions: txSets.map(txSet => txSet.transactions).flat(), - signature: aggregateSignature, + subTransactions: txs.map(txSet => txSet.subTransactions).flat(), + signature: hubbleBls.mcl.dumpG1(aggSigG1), }; } diff --git a/signer/src/sign.ts b/signer/src/sign.ts index 7069c3dc..0d42012f 100644 --- a/signer/src/sign.ts +++ b/signer/src/sign.ts @@ -2,7 +2,7 @@ import * as hubbleBls from "../deps/hubble-bls"; import encodeMessageForSigning from "./encodeMessageForSigning"; import getPublicKey from "./getPublicKey"; -import { TransactionSet, TransactionTemplate } from "./types"; +import { Transaction, TransactionTemplate } from "./types"; export default ( signerFactory: hubbleBls.signer.BlsSignerFactory, @@ -11,14 +11,14 @@ export default ( ) => ( txTemplate: TransactionTemplate, privateKey: string, -): TransactionSet => { +): Transaction => { const message = encodeMessageForSigning(chainId)(txTemplate); const signer = signerFactory.getSigner(domain, privateKey); const signature = hubbleBls.mcl.dumpG1(signer.sign(message)); return { - transactions: [ + subTransactions: [ { ...txTemplate, publicKey: getPublicKey(signerFactory, domain)(privateKey), diff --git a/signer/src/types.ts b/signer/src/types.ts index 24f7e4be..afa43f03 100644 --- a/signer/src/types.ts +++ b/signer/src/types.ts @@ -7,9 +7,9 @@ export type TransactionTemplate = { encodedFunction: string; }; -export type UnsignedTransaction = TransactionTemplate & { publicKey: string }; +export type SubTransaction = TransactionTemplate & { publicKey: string }; -export type TransactionSet = { - transactions: UnsignedTransaction[], +export type Transaction = { + subTransactions: SubTransaction[], signature: string, }; diff --git a/signer/src/verify.ts b/signer/src/verify.ts index a5d8b8d7..8bcbf414 100644 --- a/signer/src/verify.ts +++ b/signer/src/verify.ts @@ -1,23 +1,21 @@ import * as hubbleBls from "../deps/hubble-bls"; import encodeMessageForSigning from "./encodeMessageForSigning"; -import { TransactionSet } from "./types"; +import { Transaction } from "./types"; export default ( domain: Uint8Array, chainId: number, -) => ( - txSet: TransactionSet, -): boolean => { +) => (tx: Transaction): boolean => { const verifier = new hubbleBls.signer.BlsVerifier(domain); return verifier.verifyMultiple( - hubbleBls.mcl.loadG1(txSet.signature), - txSet.transactions.map( - tx => hubbleBls.mcl.loadG2(tx.publicKey), + hubbleBls.mcl.loadG1(tx.signature), + tx.subTransactions.map( + subTx => hubbleBls.mcl.loadG2(subTx.publicKey), ), - txSet.transactions.map( - tx => encodeMessageForSigning(chainId)(tx), + tx.subTransactions.map( + subTx => encodeMessageForSigning(chainId)(subTx), ), ); }; diff --git a/signer/test/index.test.ts b/signer/test/index.test.ts index c6f2457c..b7a72d85 100644 --- a/signer/test/index.test.ts +++ b/signer/test/index.test.ts @@ -5,7 +5,7 @@ import { arrayify } from "@ethersproject/bytes"; import { keccak256 } from "@ethersproject/keccak256"; import { expect } from "chai"; -import { initBlsWalletSigner, RawTransactionData } from "../src"; +import { initBlsWalletSigner, Transaction, TransactionTemplate } from "../src"; const domain = arrayify(keccak256("0xfeedbee5")); const weiPerToken = BigNumber.from(10).pow(18); @@ -14,7 +14,7 @@ const samples = (() => { const dummy256HexString = "0x" + "0123456789".repeat(10).slice(0, 64); const contractAddress = dummy256HexString; - const rawTx: RawTransactionData = { + const txTemplate: TransactionTemplate = { nonce: BigNumber.from(123), ethValue: BigNumber.from(0), contractAddress, @@ -26,7 +26,7 @@ const samples = (() => { return { contractAddress, - rawTx, + txTemplate, privateKey, otherPrivateKey, }; @@ -39,9 +39,9 @@ describe("index", () => { domain, }); - const { rawTx, privateKey, otherPrivateKey } = samples; + const { txTemplate, privateKey, otherPrivateKey } = samples; - const tx = sign(rawTx, privateKey); + const tx = sign(txTemplate, privateKey); expect(tx.signature).to.equal([ "0x177500780b42f245e98229245126c9042e1cdaadc7ada72021ddd43492963a7b26f7a", @@ -51,18 +51,22 @@ describe("index", () => { expect(verify(tx)).to.equal(true); const txBadSig = { - ...sign(rawTx, otherPrivateKey), - publicKey: tx.publicKey, // Pretend this is the public key + ...tx, + signature: sign(txTemplate, otherPrivateKey).signature, }; expect(verify(txBadSig)).to.equal(false); - const txBadMessage = { - ...tx, - - // Pretend the client signed to pay a million tokens - ethValue: weiPerToken.mul(1000000), - } + const txBadMessage: Transaction = { + subTransactions: [ + { + ...tx.subTransactions[0], + // Pretend the client signed to pay a million tokens + ethValue: weiPerToken.mul(1000000), + }, + ], + signature: tx.signature, + }; expect(verify(txBadMessage)).to.equal(false); }); @@ -71,34 +75,34 @@ describe("index", () => { const { sign, aggregate, - verifyAggregate, + verify, } = await initBlsWalletSigner({ chainId: 123, domain }); - const { rawTx, privateKey } = samples; + const { txTemplate, privateKey } = samples; - const tx = sign(rawTx, privateKey); - const aggregateTx = aggregate([tx, tx]); + const tx1 = sign(txTemplate, privateKey); + const tx2 = aggregate([tx1, tx1]); - expect(aggregateTx.signature).to.equal([ + expect(tx2.signature).to.equal([ "0x2cc0b05e8200cf564042735d15e2cc98181e730203530300022aafdd1ceb905830430", "28617145dca56a00bf0693710e24683616ff4a42bc3cca7d587b36ff91f", ].join("")); - expect(verifyAggregate(aggregateTx)).to.equal(true); + expect(verify(tx2)).to.equal(true); - const aggregateTxBadMessage = { - ...aggregateTx, - transactions: [ - aggregateTx.transactions[0], + const tx2BadMessage: Transaction = { + ...tx2, + subTransactions: [ + tx2.subTransactions[0], { - ...aggregateTx.transactions[1], + ...tx2.subTransactions[1], // Pretend this client signed to pay a million tokens ethValue: weiPerToken.mul(1000000), - } + }, ], } - expect(verifyAggregate(aggregateTxBadMessage)).to.equal(false); + expect(verify(tx2BadMessage)).to.equal(false); }); }); From a35344f0b8c7bd2979686ee4b3e97ac0781abe7d Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Fri, 5 Nov 2021 03:36:23 +0000 Subject: [PATCH 3/4] Add double aggregate test --- signer/test/helpers/Range.ts | 9 +++++++++ signer/test/index.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 signer/test/helpers/Range.ts diff --git a/signer/test/helpers/Range.ts b/signer/test/helpers/Range.ts new file mode 100644 index 00000000..34a41808 --- /dev/null +++ b/signer/test/helpers/Range.ts @@ -0,0 +1,9 @@ +export default function Range(limit: number): number[] { + const result: number[] = []; + + for (let i = 0; i < limit; i += 1) { + result.push(i); + } + + return result; +} diff --git a/signer/test/index.test.ts b/signer/test/index.test.ts index b7a72d85..2d64f777 100644 --- a/signer/test/index.test.ts +++ b/signer/test/index.test.ts @@ -6,6 +6,7 @@ import { keccak256 } from "@ethersproject/keccak256"; import { expect } from "chai"; import { initBlsWalletSigner, Transaction, TransactionTemplate } from "../src"; +import Range from "./helpers/Range"; const domain = arrayify(keccak256("0xfeedbee5")); const weiPerToken = BigNumber.from(10).pow(18); @@ -105,4 +106,29 @@ describe("index", () => { expect(verify(tx2BadMessage)).to.equal(false); }); + + it("can aggregate transactions which already have multiple subTransactions", async () => { + const { + sign, + aggregate, + verify, + } = await initBlsWalletSigner({ chainId: 123, domain }); + + const { txTemplate, privateKey } = samples; + + const txs = Range(4).map(i => sign( + { + ...txTemplate, + ethValue: BigNumber.from(i), + }, + privateKey, + )); + + const aggTx1 = aggregate(txs.slice(0, 2)); + const aggTx2 = aggregate(txs.slice(2, 4)); + + let aggAggTx = aggregate([aggTx1, aggTx2]); + + expect(verify(aggAggTx)).to.equal(true); + }); }); From 03eeef25c8b469de6462efb59d79168f1468fe46 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Fri, 5 Nov 2021 03:46:52 +0000 Subject: [PATCH 4/4] 0.7.0 --- signer/package.json | 2 +- signer/src/types.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/signer/package.json b/signer/package.json index fc43333d..a22fa330 100644 --- a/signer/package.json +++ b/signer/package.json @@ -1,6 +1,6 @@ { "name": "bls-wallet-signer", - "version": "0.6.1", + "version": "0.7.0", "description": "Client-side tool for signing bls transaction data", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", diff --git a/signer/src/types.ts b/signer/src/types.ts index afa43f03..7596eed6 100644 --- a/signer/src/types.ts +++ b/signer/src/types.ts @@ -10,6 +10,6 @@ export type TransactionTemplate = { export type SubTransaction = TransactionTemplate & { publicKey: string }; export type Transaction = { - subTransactions: SubTransaction[], - signature: string, + subTransactions: SubTransaction[]; + signature: string; };