Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use correct verfier for AnonAadhaar #1

Merged
merged 12 commits into from
Jul 5, 2024
Merged
18 changes: 10 additions & 8 deletions packages/plugins/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@
"devDependencies": {
"@account-abstraction/contracts": "0.7.0",
"@account-abstraction/utils": "^0.6.0",
"@anon-aadhaar/contracts": "2.2.0",
"@anon-aadhaar/core": "2.2.0",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
"@nomicfoundation/hardhat-ethers": "^3.0.0",
"@nomicfoundation/hardhat-ignition": "^0.15.5",
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.0",
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^3.0.0",
"@nomicfoundation/hardhat-verify": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.8",
"@nomicfoundation/ignition-core": "^0.15.5",
"@thehubbleproject/bls": "^0.5.1",
"@typechain/ethers-v6": "^0.4.0",
"@typechain/hardhat": "^8.0.0",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
"@types/chai": "^4.2.0",
"@types/circomlibjs": "^0.1.6",
"@types/mocha": ">=9.1.0",
Expand All @@ -44,13 +46,13 @@
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-prettier": "^5.0.0",
"ethers": "^6.4.0",
"hardhat": "^2.17.1",
"hardhat-gas-reporter": "^1.0.8",
"hardhat": "^2.22.6",
"hardhat-gas-reporter": "^2.2.0",
"hardhat-preprocessor": "^0.1.5",
"prettier": "^3.0.3",
"solidity-coverage": "^0.8.0",
"ts-node": ">=8.0.0",
"typechain": "^8.1.0",
"typescript": "^5.4.3"
}
}
}
3 changes: 3 additions & 0 deletions packages/plugins/src/paymaster/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Paymaster

These are exemplary paymaster contracts. The SponsorEverythingPaymaster contract and its test can serve as references when developing more complex paymasters for your specific use cases.
35 changes: 35 additions & 0 deletions packages/plugins/src/paymaster/SponsorEverythingPaymaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4 <0.9.0;

import {IEntryPoint} from 'account-abstraction/interfaces/IEntryPoint.sol';

import {BasePaymaster} from 'account-abstraction/core/BasePaymaster.sol';
import {UserOperationLib} from 'account-abstraction/core/UserOperationLib.sol';
import {PackedUserOperation} from 'account-abstraction/interfaces/PackedUserOperation.sol';

/*//////////////////////////////////////////////////////////////////////////
THIS CONTRACT IS STILL IN ACTIVE DEVELOPMENT. NOT FOR PRODUCTION USE
//////////////////////////////////////////////////////////////////////////*/

/// @title This paymaster sponsors everything.
contract SponsorEverythingPaymaster is BasePaymaster {
using UserOperationLib for PackedUserOperation;

constructor(IEntryPoint _entryPoint) BasePaymaster(_entryPoint) {}

/**
* Validate a user operation.
* @param userOp - The user operation.
* @param userOpHash - The hash of the user operation.
* @param maxCost - The maximum cost of the user operation.
*/
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) internal virtual override returns (bytes memory context, uint256 validationData) {
// Validation logic comes here.
// Approve everything.
return ("", 0);
}
}
2 changes: 1 addition & 1 deletion packages/plugins/src/safe/SafeAnonAadhaarPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma abicoder v2;

import {Safe4337Base, SIG_VALIDATION_FAILED} from "./utils/Safe4337Base.sol";
import {IEntryPoint, PackedUserOperation} from "account-abstraction/interfaces/IEntryPoint.sol";
import {IAnonAadhaar} from "@anon-aadhaar/contracts/interfaces/IAnonAadhaar.sol";
import {IAnonAadhaar} from "./utils/anonAadhaar/interfaces/IAnonAadhaar.sol";

interface ISafe {
function enableModule(address module) external;
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/src/safe/utils/anonAadhaar/AnonAadhaar.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.19;

import "@anon-aadhaar/contracts/interfaces/IAnonAadhaarGroth16Verifier.sol";
import "@anon-aadhaar/contracts/interfaces/IAnonAadhaar.sol";
import "./interfaces/IAnonAadhaarGroth16Verifier.sol";
import "./interfaces/IAnonAadhaar.sol";

// Note: This is a AnonAadhaar contract with a modification that made`verifier` state variable immutable
// so that verification doesn't fail due to invalid storage access.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

pragma solidity >=0.7.0 <0.9.0;

contract Verifier {
contract AnonAadhaarVerifier {
// Scalar field size
uint256 constant r =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/src/safe/utils/anonAadhaar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Anon Aadhaar Contracts

These contracts are copied from https://github.com/anon-aadhaar/anon-aadhaar/tree/main/packages/contracts @ `v2.2.0`. This has been done for 2 reasons:
- `AnonAadhaarVerifier.sol` needed to be modified to use gas opcodes that work within the [validation cycle gas opcode limitations for ERC-4337 (OP-012)](https://eips.ethereum.org/EIPS/eip-7562#opcode-rules).
- Using the `@anon-aadhaar/contracts` npm package's contract interfaces causes issues with Typechain generation in this project.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

interface IAnonAadhaar {
function verifyAnonAadhaarProof(
uint nullifierSeed,
uint nullifier,
uint timestamp,
uint signal,
uint[4] memory revealArray,
uint[8] memory groth16Proof
) external view returns (bool);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

interface IAnonAadhaarGroth16Verifier {
function verifyProof(
uint[2] calldata _pA,
uint[2][2] calldata _pB,
uint[2] calldata _pC,
uint[9] calldata publicInputs
) external view returns (bool);
}
5 changes: 5 additions & 0 deletions packages/plugins/src/safe/utils/imports.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ import {Safe} from "safe-contracts/contracts/Safe.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
import {SimpleAccountFactory} from "account-abstraction/samples/SimpleAccountFactory.sol";
import {BLSSignatureAggregator} from "account-abstraction/samples/bls/BLSSignatureAggregator.sol";

// Anon-Aadhaar
import {AnonAadhaar} from "./anonAadhaar/AnonAadhaar.sol";
import {AnonAadhaarVerifier} from "./anonAadhaar/AnonAadhaarVerifier.sol";

20 changes: 13 additions & 7 deletions packages/plugins/test/e2e/SafeAnonAadhaarPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import DeterministicDeployer from "../../lib-ts/deterministic-deployer/Determini
import {
SafeAnonAadhaarFactory__factory,
SafeAnonAadhaarPlugin__factory,
Verifier__factory,
AnonAadhaarVerifier__factory,
AnonAadhaar__factory,
Safe,
AnonAadhaar,
Expand Down Expand Up @@ -74,13 +74,15 @@ describe("SafeAnonAadhaarPlugin", () => {
const signer = await provider.getSigner();

// Deploy AnonAadhaarGroth16Verifier contract
const anonAadhaarVerifier = await new Verifier__factory(signer).deploy();
const anonAadhaarVerifier = await new AnonAadhaarVerifier__factory(signer).deploy();
await anonAadhaarVerifier.waitForDeployment();

// Deploy AnonAadhaar contract
anonAadhaar = await new AnonAadhaar__factory(signer).deploy(
await anonAadhaarVerifier.getAddress(),
BigInt(testPublicKeyHash).toString()
BigInt(testPublicKeyHash).toString(),
);
await anonAadhaar.waitForDeployment();

anonAadhaarAddress = await anonAadhaar.getAddress();

Expand Down Expand Up @@ -173,12 +175,17 @@ describe("SafeAnonAadhaarPlugin", () => {
});

// proving
console.debug("Generating Anon Aadhaar proof. This could take some time...");
const proofTimingKey = "Anon Aadhaar proof generation time";
console.time(proofTimingKey);
const anonAadhaarCore = await prove(args);
console.timeEnd(proofTimingKey);

const anonAadhaarProof = anonAadhaarCore.proof;
const packedGroth16Proof = packGroth16Proof(anonAadhaarProof.groth16Proof);

// view call to AnonAadhaar contract to see if verification returns true
const ret = await anonAadhaar.verifyAnonAadhaarProof(
expect(await anonAadhaar.verifyAnonAadhaarProof(
nullifierSeed,
anonAadhaarProof.nullifier,
anonAadhaarProof.timestamp,
Expand All @@ -190,16 +197,15 @@ describe("SafeAnonAadhaarPlugin", () => {
anonAadhaarProof.state,
],
packedGroth16Proof
);
console.log("ret: ", ret);
)).to.equal(true);

// encode proof data into userOpSignature
const encoder = ethers.AbiCoder.defaultAbiCoder();
const userOpSignature = encoder.encode(
["uint", "uint", "uint", "uint[4]", "uint[8]"],
[
BigInt(nullifierSeed),
Number(anonAadhaarCore?.proof.timestamp),
Number(anonAadhaarProof.timestamp),
BigInt(userOpHash), // insert userOpHash into signature
[
anonAadhaarProof.ageAbove18,
Expand Down
110 changes: 110 additions & 0 deletions packages/plugins/test/e2e/SafeSponsorEverythingPaymaster.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { expect } from "chai";
import { ethers } from "ethers";
import {
SafeECDSAFactory__factory,
SafeECDSAPlugin__factory,
SponsorEverythingPaymaster__factory,
EntryPoint__factory
} from "../../typechain-types";
import receiptOf from "./utils/receiptOf";
import { setupTests } from "./utils/setupTests";
import { createAndSendUserOpWithEcdsaSig } from "./utils/createUserOp";

const oneEther = ethers.parseEther("1");

describe("SafeSponsorEverythingPaymasterPlugin", () => {
it("should pass the ERC4337 validation", async () => {
const {
bundlerProvider,
provider,
admin,
owner,
entryPointAddress,
deployer,
safeSingleton,
} = await setupTests();

// Deploy paymaster.
const paymaster = await new SponsorEverythingPaymaster__factory(admin).deploy(entryPointAddress);
await paymaster.waitForDeployment();
const paymasterAddress = await paymaster.getAddress();

// Paymaster deposits.
await paymaster.connect(admin).deposit({ value: oneEther })

const recipient = ethers.Wallet.createRandom();
const transferAmount = oneEther;
const dummySignature = await owner.signMessage("dummy sig");

// Deploy ecdsa plugin
const safeECDSAFactory = await deployer.connectOrDeploy(
SafeECDSAFactory__factory,
[],
);

const createArgs = [
safeSingleton,
entryPointAddress,
await owner.getAddress(),
0,
] satisfies Parameters<typeof safeECDSAFactory.create.staticCall>;

const accountAddress = await safeECDSAFactory.create.staticCall(
...createArgs,
);

await receiptOf(safeECDSAFactory.create(...createArgs));

const safeEcdsaPlugin = SafeECDSAPlugin__factory.connect(
accountAddress,
owner,
);

// Native tokens for the pre-fund
await receiptOf(
admin.sendTransaction({
to: accountAddress,
value: oneEther,
}),
);

// Construct userOp
const userOpCallData = safeEcdsaPlugin.interface.encodeFunctionData(
"execTransaction",
[recipient.address, transferAmount, "0x00"],
);

// Note: factoryParams is not used because we need to create both the safe
// proxy and the plugin, and 4337 currently only allows one contract
// creation in this step. Since we need an extra step anyway, it's simpler
// to do the whole create outside of 4337.
const factoryParams = {
factory: "0x",
factoryData: "0x",
};

// Check paymaster balances before and after sending UserOp.
const entrypoint = EntryPoint__factory.connect(entryPointAddress, provider)
const paymasterBalanceBefore = await entrypoint.balanceOf(paymasterAddress)

// Send userOp
await createAndSendUserOpWithEcdsaSig(
provider,
bundlerProvider,
owner,
accountAddress,
factoryParams,
userOpCallData,
entryPointAddress,
dummySignature,
paymasterAddress,
3e5,
"0x"
);

const paymasterBalanceAfter = await entrypoint.balanceOf(paymasterAddress)

expect(paymasterBalanceBefore).greaterThan(paymasterBalanceAfter)
expect(await provider.getBalance(recipient.address)).to.equal(oneEther);
});
});
Loading
Loading