Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Commit

Permalink
Merge pull request #36 from jzaki/bw-35-update-signer-for-tx-sets
Browse files Browse the repository at this point in the history
Update signer for tx sets
  • Loading branch information
voltrevo authored Nov 5, 2021
2 parents 7ba5ac4 + 03eeef2 commit ca2f3bc
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 98 deletions.
2 changes: 1 addition & 1 deletion signer/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
16 changes: 4 additions & 12 deletions signer/src/aggregate.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
import * as hubbleBls from "../deps/hubble-bls";

import { AggregateTransactionData, TransactionData } from "./types";
import { Transaction } from "./types";

export default (txs: TransactionData[]): AggregateTransactionData => {
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: txs.map(tx => ({
publicKey: tx.publicKey,
nonce: tx.nonce,
ethValue: tx.ethValue,
contractAddress: tx.contractAddress,
encodedFunction: tx.encodedFunction,
})),
signature: aggregateSignature,
subTransactions: txs.map(txSet => txSet.subTransactions).flat(),
signature: hubbleBls.mcl.dumpG1(aggSigG1),
};
}
12 changes: 6 additions & 6 deletions signer/src/encodeMessageForSigning.ts
Original file line number Diff line number Diff line change
@@ -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),
]
);
}
2 changes: 0 additions & 2 deletions signer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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),
};
}
16 changes: 10 additions & 6 deletions signer/src/sign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 { Transaction, 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);
): Transaction => {
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),
subTransactions: [
{
...txTemplate,
publicKey: getPublicKey(signerFactory, domain)(privateKey),
}
],
signature,
}
};
19 changes: 5 additions & 14 deletions signer/src/types.ts
Original file line number Diff line number Diff line change
@@ -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 SubTransaction = TransactionTemplate & { publicKey: string };

export type AggregateTransactionData = {
transactions: {
publicKey: string;
nonce: BigNumber;
ethValue: BigNumber;
contractAddress: string;
encodedFunction: string;
}[],
signature: string,
export type Transaction = {
subTransactions: SubTransaction[];
signature: string;
};
18 changes: 10 additions & 8 deletions signer/src/verify.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import * as hubbleBls from "../deps/hubble-bls";

import encodeMessageForSigning from "./encodeMessageForSigning";
import { TransactionData } from "./types";
import { Transaction } from "./types";

export default (
domain: Uint8Array,
chainId: number,
) => (
txData: TransactionData,
): boolean => {
) => (tx: Transaction): 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(tx.signature),
tx.subTransactions.map(
subTx => hubbleBls.mcl.loadG2(subTx.publicKey),
),
tx.subTransactions.map(
subTx => encodeMessageForSigning(chainId)(subTx),
),
);
};
23 changes: 0 additions & 23 deletions signer/src/verifyAggregate.ts

This file was deleted.

9 changes: 9 additions & 0 deletions signer/test/helpers/Range.ts
Original file line number Diff line number Diff line change
@@ -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;
}
82 changes: 56 additions & 26 deletions signer/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ 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";
import Range from "./helpers/Range";

const domain = arrayify(keccak256("0xfeedbee5"));
const weiPerToken = BigNumber.from(10).pow(18);
Expand All @@ -14,7 +15,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,
Expand All @@ -26,7 +27,7 @@ const samples = (() => {

return {
contractAddress,
rawTx,
txTemplate,
privateKey,
otherPrivateKey,
};
Expand All @@ -39,9 +40,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",
Expand All @@ -51,18 +52,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);
});
Expand All @@ -71,34 +76,59 @@ 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);
});

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);
});
});

0 comments on commit ca2f3bc

Please sign in to comment.