Merge pull request #600 from web3well/392-blsWalletSigner-verify-cannot-verify-bundles-with-multiple-addresses

Add ability to blsWalletSigner to verify multiple addresses
This commit is contained in:
John Guilding
2023-05-09 11:26:35 +01:00
committed by GitHub
11 changed files with 126 additions and 50 deletions

View File

@@ -21,7 +21,7 @@
"@types/koa__cors": "^3.3.0",
"@types/koa__router": "^8.0.11",
"@types/node-fetch": "^2.6.1",
"bls-wallet-clients": "0.9.0",
"bls-wallet-clients": "0.9.0-1620721",
"fp-ts": "^2.12.1",
"io-ts": "^2.2.16",
"io-ts-reporters": "^2.0.1",

View File

@@ -887,10 +887,10 @@ bech32@1.1.4:
resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9"
integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==
bls-wallet-clients@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/bls-wallet-clients/-/bls-wallet-clients-0.9.0.tgz#edfbdb24011856b52d9b438af174b6acbeda27ec"
integrity sha512-ebEifAPkGfTft6xdVVgQfC6HEXzgw+wX2d76w2K1OUsB4FeKiAYRLMXtnKtl7tdQoMknHElD6xrLChKaCACYLQ==
bls-wallet-clients@0.9.0-1620721:
version "0.9.0-1620721"
resolved "https://registry.yarnpkg.com/bls-wallet-clients/-/bls-wallet-clients-0.9.0-1620721.tgz#572798d10fa6246ab44bbd0e11b97df11abd6d15"
integrity sha512-ekSrK2bCiWoTuhnqdKTp0kXDuIUmE3lc9pqtonOAZC/6ReU2cVbW+F6U1/echtagdWL6GBArJgB2JnVQVznRpg==
dependencies:
"@thehubbleproject/bls" "^0.5.1"
ethers "^5.7.2"

View File

@@ -54,7 +54,7 @@ export type {
PublicKey,
Signature,
VerificationGateway,
} from "https://esm.sh/bls-wallet-clients@0.9.0";
} from "https://esm.sh/bls-wallet-clients@0.9.0-1620721";
export {
Aggregator as AggregatorClient,
@@ -70,10 +70,10 @@ export {
getConfig,
MockERC20Factory,
VerificationGatewayFactory,
} from "https://esm.sh/bls-wallet-clients@0.9.0";
} from "https://esm.sh/bls-wallet-clients@0.9.0-1620721";
// Workaround for esbuild's export-star bug
import blsWalletClients from "https://esm.sh/bls-wallet-clients@0.9.0";
import blsWalletClients from "https://esm.sh/bls-wallet-clients@0.9.0-1620721";
const { bundleFromDto, bundleToDto, initBlsWalletSigner } = blsWalletClients;
export { bundleFromDto, bundleToDto, initBlsWalletSigner };

View File

@@ -156,14 +156,15 @@ export default class BundleService {
const failures: TransactionFailure[] = [];
for (const walletAddr of walletAddresses) {
const signedCorrectly = this.blsWalletSigner.verify(bundle, walletAddr);
if (!signedCorrectly) {
failures.push({
type: "invalid-signature",
description: `invalid signature for wallet address ${walletAddr}`,
});
}
const signedCorrectly = this.blsWalletSigner.verify(
bundle,
walletAddresses,
);
if (!signedCorrectly) {
failures.push({
type: "invalid-signature",
description: `invalid bundle signature for signature ${bundle.signature}`,
});
}
failures.push(...await this.ethereumService.checkNonces(bundle));

View File

@@ -54,7 +54,7 @@ Fixture.test("rejects bundle with invalid signature", async (fx) => {
// sig test)
tx.signature = otherTx.signature;
assertEquals(await bundleService.bundleTable.count(), 0);
assertEquals(bundleService.bundleTable.count(), 0);
const res = await bundleService.add(tx);
if ("hash" in res) {
@@ -63,7 +63,47 @@ Fixture.test("rejects bundle with invalid signature", async (fx) => {
assertEquals(res.failures.map((f) => f.type), ["invalid-signature"]);
// Bundle table remains empty
assertEquals(await bundleService.bundleTable.count(), 0);
assertEquals(bundleService.bundleTable.count(), 0);
});
Fixture.test("rejects bundle with valid signature but invalid public key", async (fx) => {
const bundleService = fx.createBundleService();
const [wallet, otherWallet] = await fx.setupWallets(2);
const operation: Operation = {
nonce: await wallet.Nonce(),
gas: 0,
actions: [
{
ethValue: 0,
contractAddress: fx.testErc20.address,
encodedFunction: fx.testErc20.interface.encodeFunctionData(
"mint",
[wallet.address, "3"],
),
},
],
};
const tx = wallet.sign(operation);
const otherTx = otherWallet.sign(operation);
// Make the signature invalid
// Note: Bug in bls prevents just corrupting the signature (see other invalid
// sig test)
tx.senderPublicKeys[0] = otherTx.senderPublicKeys[0];
assertEquals(bundleService.bundleTable.count(), 0);
const res = await bundleService.add(tx);
if ("hash" in res) {
throw new Error("expected bundle to fail");
}
assertEquals(res.failures.map((f) => f.type), ["invalid-signature"]);
assertEquals(res.failures.map((f) => f.description), [`invalid bundle signature for signature ${tx.signature}`]);
// Bundle table remains empty
assertEquals(bundleService.bundleTable.count(), 0);
});
Fixture.test("rejects bundle with nonce from the past", async (fx) => {
@@ -85,7 +125,7 @@ Fixture.test("rejects bundle with nonce from the past", async (fx) => {
],
});
assertEquals(await bundleService.bundleTable.count(), 0);
assertEquals(bundleService.bundleTable.count(), 0);
const res = await bundleService.add(tx);
if ("hash" in res) {
@@ -94,7 +134,7 @@ Fixture.test("rejects bundle with nonce from the past", async (fx) => {
assertEquals(res.failures.map((f) => f.type), ["duplicate-nonce"]);
// Bundle table remains empty
assertEquals(await bundleService.bundleTable.count(), 0);
assertEquals(bundleService.bundleTable.count(), 0);
});
Fixture.test(
@@ -128,7 +168,7 @@ Fixture.test(
// https://github.com/thehubbleproject/hubble-bls/pull/20
tx.signature = otherTx.signature;
assertEquals(await bundleService.bundleTable.count(), 0);
assertEquals(bundleService.bundleTable.count(), 0);
const res = await bundleService.add(tx);
if ("hash" in res) {
@@ -141,7 +181,7 @@ Fixture.test(
);
// Bundle table remains empty
assertEquals(await bundleService.bundleTable.count(), 0);
assertEquals(bundleService.bundleTable.count(), 0);
},
);
@@ -164,11 +204,11 @@ Fixture.test("adds bundle with future nonce", async (fx) => {
],
});
assertEquals(await bundleService.bundleTable.count(), 0);
assertEquals(bundleService.bundleTable.count(), 0);
assertBundleSucceeds(await bundleService.add(tx));
assertEquals(await bundleService.bundleTable.count(), 1);
assertEquals(bundleService.bundleTable.count(), 1);
});
// TODO (merge-ok): Add a mechanism for limiting the number of stored

View File

@@ -1,6 +1,6 @@
{
"name": "bls-wallet-clients",
"version": "0.9.0",
"version": "0.9.0-1620721",
"description": "Client libraries for interacting with BLS Wallet components",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",

View File

@@ -6,7 +6,7 @@ import type { Bundle } from "./types";
import isValidEmptyBundle from "./isValidEmptyBundle";
export default (domain: Uint8Array) =>
(bundle: Bundle, walletAddress: string): boolean => {
(bundle: Bundle, walletAddresses: Array<string>): boolean => {
// hubbleBls verifier incorrectly rejects empty bundles
if (isValidEmptyBundle(bundle)) {
return true;
@@ -25,8 +25,8 @@ export default (domain: Uint8Array) =>
BigNumber.from(n2).toHexString(),
BigNumber.from(n3).toHexString(),
]),
bundle.operations.map((op) =>
encodeMessageForSigning()(op, walletAddress),
bundle.operations.map((op, i) =>
encodeMessageForSigning()(op, walletAddresses[i]),
),
);
};

View File

@@ -65,7 +65,7 @@ describe("index", () => {
"0x2f90b24bbc03de665816b3a632e0c7b5fb837c87541d9337480671613cf1359c",
]);
expect(verify(bundle, walletAddress)).to.equal(true);
expect(verify(bundle, [walletAddress])).to.equal(true);
const { sign: signWithOtherPrivateKey } = await initBlsWalletSigner({
chainId: 123,
@@ -79,7 +79,7 @@ describe("index", () => {
.signature,
};
expect(verify(bundleBadSig, walletAddress)).to.equal(false);
expect(verify(bundleBadSig, [walletAddress])).to.equal(false);
const bundleBadMessage: Bundle = {
senderPublicKeys: bundle.senderPublicKeys,
@@ -99,7 +99,7 @@ describe("index", () => {
signature: bundle.signature,
};
expect(verify(bundleBadMessage, walletAddress)).to.equal(false);
expect(verify(bundleBadMessage, [walletAddress])).to.equal(false);
});
it("aggregates transactions", async () => {
@@ -131,11 +131,15 @@ describe("index", () => {
"0x0235a99bcd1f0793efb7f3307cd349f211a433f60cfab795f5f976298f17a768",
]);
expect(verify(bundle1, walletAddress)).to.equal(true);
expect(verify(bundle2, otherWalletAddress)).to.equal(true);
expect(verify(bundle1, [walletAddress])).to.equal(true);
expect(verify(bundle2, [otherWalletAddress])).to.equal(true);
expect(verify(bundle1, otherWalletAddress)).to.equal(false);
expect(verify(bundle2, walletAddress)).to.equal(false);
expect(verify(bundle1, [otherWalletAddress])).to.equal(false);
expect(verify(bundle2, [walletAddress])).to.equal(false);
expect(verify(aggBundle, [walletAddress, otherWalletAddress])).to.equal(
true,
);
const aggBundleBadMessage: Bundle = {
...aggBundle,
@@ -156,8 +160,12 @@ describe("index", () => {
],
};
expect(verify(aggBundleBadMessage, walletAddress)).to.equal(false);
expect(verify(aggBundleBadMessage, otherWalletAddress)).to.equal(false);
expect(
verify(aggBundleBadMessage, [walletAddress, otherWalletAddress]),
).to.equal(false);
expect(
verify(aggBundleBadMessage, [otherWalletAddress, walletAddress]),
).to.equal(false);
});
it("can aggregate transactions which already have multiple subTransactions", async () => {
@@ -188,8 +196,39 @@ describe("index", () => {
const aggBundle2 = aggregate(bundles.slice(2, 4));
const aggAggBundle = aggregate([aggBundle1, aggBundle2]);
const walletAddresses = new Array(4).fill(walletAddress);
expect(verify(aggAggBundle, walletAddress)).to.equal(true);
expect(verify(aggAggBundle, walletAddresses)).to.equal(true);
});
it("should fail to verify bundle with wallet address mismatches", async () => {
const {
bundleTemplate,
privateKey,
otherPrivateKey,
walletAddress,
otherWalletAddress,
verificationGatewayAddress,
} = samples;
const { sign, aggregate, verify } = await initBlsWalletSigner({
chainId: 123,
verificationGatewayAddress,
privateKey,
});
const { sign: signWithOtherPrivateKey } = await initBlsWalletSigner({
chainId: 123,
verificationGatewayAddress,
privateKey: otherPrivateKey,
});
const bundle1 = sign(bundleTemplate, walletAddress);
const bundle2 = signWithOtherPrivateKey(bundleTemplate, otherWalletAddress);
const aggBundle = aggregate([bundle1, bundle2]);
expect(verify(aggBundle, [otherWalletAddress, walletAddress])).to.equal(
false,
);
});
it("generates expected publicKeyStr", async () => {
@@ -238,6 +277,6 @@ describe("index", () => {
const emptyBundle = aggregate([]);
expect(verify(emptyBundle, samples.walletAddress)).to.equal(true);
expect(verify(emptyBundle, [samples.walletAddress])).to.equal(true);
});
});

View File

@@ -116,8 +116,6 @@ describe("BlsProvider", () => {
it("should throw an error when sending a modified signed transaction", async () => {
// Arrange
const address = await blsSigner.getAddress();
const signedTransaction = await blsSigner.signTransaction({
value: parseEther("1"),
to: ethers.Wallet.createRandom().address,
@@ -134,7 +132,7 @@ describe("BlsProvider", () => {
// Assert
await expect(result()).to.be.rejectedWith(
Error,
`[{"type":"invalid-signature","description":"invalid signature for wallet address ${address}"}]`,
`[{"type":"invalid-signature","description":"invalid bundle signature for signature ${userBundle.signature}"}]`,
);
});
@@ -286,8 +284,6 @@ describe("BlsProvider", () => {
it("should throw an error when sending a modified signed transaction", async () => {
// Arrange
const address = await blsSigner.getAddress();
const signedTransaction = await blsSigner.signTransactionBatch({
transactions: [
{
@@ -309,7 +305,7 @@ describe("BlsProvider", () => {
// Assert
await expect(result()).to.be.rejectedWith(
Error,
`[{"type":"invalid-signature","description":"invalid signature for wallet address ${address}"}]`,
`[{"type":"invalid-signature","description":"invalid bundle signature for signature ${userBundle.signature}"}]`,
);
});

View File

@@ -37,7 +37,7 @@
"assert-browserify": "^2.0.0",
"async-mutex": "^0.3.2",
"axios": "^0.27.2",
"bls-wallet-clients": "0.9.0",
"bls-wallet-clients": "0.9.0-1620721",
"browser-passworder": "^2.0.3",
"bs58check": "^2.1.2",
"crypto-browserify": "^3.12.0",

View File

@@ -2898,10 +2898,10 @@ blakejs@^1.1.0:
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==
bls-wallet-clients@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/bls-wallet-clients/-/bls-wallet-clients-0.9.0.tgz#edfbdb24011856b52d9b438af174b6acbeda27ec"
integrity sha512-ebEifAPkGfTft6xdVVgQfC6HEXzgw+wX2d76w2K1OUsB4FeKiAYRLMXtnKtl7tdQoMknHElD6xrLChKaCACYLQ==
bls-wallet-clients@0.9.0-1620721:
version "0.9.0-1620721"
resolved "https://registry.yarnpkg.com/bls-wallet-clients/-/bls-wallet-clients-0.9.0-1620721.tgz#572798d10fa6246ab44bbd0e11b97df11abd6d15"
integrity sha512-ekSrK2bCiWoTuhnqdKTp0kXDuIUmE3lc9pqtonOAZC/6ReU2cVbW+F6U1/echtagdWL6GBArJgB2JnVQVznRpg==
dependencies:
"@thehubbleproject/bls" "^0.5.1"
ethers "^5.7.2"