Skip to content

Commit

Permalink
wip: checkpoint signature aggregation logic
Browse files Browse the repository at this point in the history
  • Loading branch information
0xOsiris committed Dec 17, 2024
1 parent 40cf4c5 commit 606d00d
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 32 deletions.
11 changes: 11 additions & 0 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
[profile.default]
evm_version = 'cancun'
src = "src"
out = "out"
libs = ["lib"]
via_ir = true
optimizer = true
optimizer_runs = 200

remappings = [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@account-abstraction/contracts/=lib/account-abstraction/contracts/",
"@world-id-contracts/=lib/world-id-contracts/src/",
"@account-abstraction/=lib/account-abstraction/contracts/",
"@BokkyPooBahsDateTimeLibrary/=lib/BokkyPooBahsDateTimeLibrary/contracts/",
"@helpers/=src/helpers/",
"openzeppelin-contracts/=lib/world-id-contracts/lib/openzeppelin-contracts/contracts/",
]

[fuzz]
runs = 5000
max_test_rejects = 150000



66 changes: 66 additions & 0 deletions contracts/src/PBHSignatureAggregator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IAggregator} from "@account-abstraction/contracts/interfaces/IAggregator.sol";
import {IPBHVerifier} from "./interfaces/IPBHVerifier.sol";
import "@account-abstraction/contracts/interfaces/PackedUserOperation.sol";

contract PBHSignatureAggregator is IAggregator {
error InvalidUserOperations();

/// @notice The PBHVerifier contract.
IPBHVerifier internal immutable pbhVerifier;

constructor(address _pbhVerifier) {
pbhVerifier = IPBHVerifier(_pbhVerifier);
}

/**
* Validate aggregated signature.
* Revert if the aggregated signature does not match the given list of operations.
* @param userOps - Array of UserOperations to validate the signature for.
* @param signature - The aggregated signature.
*/
function validateSignatures(PackedUserOperation[] calldata userOps, bytes calldata) external view {
bytes memory encoded = abi.encode(userOps);
try pbhVerifier.validateSignaturesCallback(keccak256(encoded)) {}
catch {
revert InvalidUserOperations();
}
}

/**
* Validate signature of a single userOp.
* This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
* the aggregator this account uses.
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp - The userOperation received from the user.
* @return sigForUserOp - The value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig".
*/
function validateUserOpSignature(PackedUserOperation calldata userOp)
external
view
returns (bytes memory sigForUserOp)
{}

/**
* Aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code perform this aggregation.
* @param userOps - Array of UserOperations to collect the signatures from.
* @return aggregatedSignature - The aggregated signature.
*/
function aggregateSignatures(PackedUserOperation[] calldata userOps)
external
view
returns (bytes memory aggregatedSignature)
{
// Aggregates all of the proofs on
for (uint256 i = 0; i < userOps.length; ++i) {
// (0:65) - UserOp Signature
// (65:65 + 320) - Packed Proof Data
bytes memory signature = userOps[i].signature;
}
}
}
51 changes: 30 additions & 21 deletions contracts/src/PBHVerifierImplV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import {ByteHasher} from "./helpers/ByteHasher.sol";
import {PBHExternalNullifier} from "./helpers/PBHExternalNullifier.sol";
import {IWorldIDGroups} from "@world-id-contracts/interfaces/IWorldIDGroups.sol";
import {WorldIDImpl} from "@world-id-contracts/abstract/WorldIDImpl.sol";
import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {IPBHVerifier} from "./interfaces/IPBHVerifier.sol";
import "@BokkyPooBahsDateTimeLibrary/BokkyPooBahsDateTimeLibrary.sol";

/// @title PBH Verifier Implementation Version 1
/// @author Worldcoin
/// @notice An implementation of a priority blockspace for humans (PBH) transaction verifier.
/// @dev This is the implementation delegated to by a proxy.
contract PBHVerifierImplV1 is WorldIDImpl {
contract PBHVerifierImplV1 is IPBHVerifier, WorldIDImpl {
///////////////////////////////////////////////////////////////////////////////
/// A NOTE ON IMPLEMENTATION CONTRACTS ///
///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -74,7 +76,7 @@ contract PBHVerifierImplV1 is WorldIDImpl {
event PBH(
uint256 indexed root,
address indexed sender,
uint256 nonce,
uint256 nonce,
bytes callData,
uint256 indexed pbhExternalNullifier,
uint256 nullifierHash,
Expand All @@ -93,10 +95,18 @@ contract PBHVerifierImplV1 is WorldIDImpl {
/// @dev The World ID instance that will be used for verifying proofs
IWorldIDGroups internal _worldId;

/// @dev The EntryPoint where Aggregated PBH Bundles will be proxied to.
IEntryPoint internal immutable _entryPoint;

/// @notice The number of PBH transactions that may be used by a single
/// World ID in a given month.
uint8 public numPbhPerMonth;

/// @notice Transient Storage key used to store Hashed UserOps.
/// @dev The PBHSignatureAggregator will cross reference this slot to ensure
/// The PBHVerifier is always the proxy to the EntryPoint for PBH Bundles.
bytes32 internal constant HASHED_OPS_SLOT = bytes32("0x4");

///////////////////////////////////////////////////////////////////////////////
/// Mappings ///
//////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -125,22 +135,23 @@ contract PBHVerifierImplV1 is WorldIDImpl {
/// @dev This function is explicitly not virtual as it does not make sense to override even when
/// upgrading. Create a separate initializer function instead.
///
/// @param worldId The World ID instance that will be used for verifying proofs. If set to the
/// @param __worldId The World ID instance that will be used for verifying proofs. If set to the
/// 0 addess, then it will be assumed that verification will take place off chain.
/// @param _numPbhPerMonth The number of allowed PBH transactions per month.
///
/// @custom:reverts string If called more than once at the same initialisation number.
function initialize(IWorldIDGroups worldId, uint8 _numPbhPerMonth) public reinitializer(1) {
function initialize(address __worldId, address __entryPoint, uint8 _numPbhPerMonth) public reinitializer(1) {
// First, ensure that all of the parent contracts are initialised.
__delegateInit();

_worldId = worldId;
_worldId = IWorldIDGroups(__worldId);
_entryPoint = IEntryPoint(__entryPoint);
numPbhPerMonth = _numPbhPerMonth;

// Say that the contract is initialized.
__setInitialized();

emit PBHVerifierImplInitialized(worldId, _numPbhPerMonth);
emit PBHVerifierImplInitialized(__worldId, _numPbhPerMonth);
}

/// @notice Responsible for initialising all of the supertypes of this contract.
Expand Down Expand Up @@ -178,7 +189,7 @@ contract PBHVerifierImplV1 is WorldIDImpl {

// Verify the external nullifier
PBHExternalNullifier.verify(pbhExternalNullifier, numPbhPerMonth);

// If worldId address is set, proceed with on chain verification,
// otherwise assume verification has been done off chain by the builder.
if (address(_worldId) != address(0)) {
Expand All @@ -192,20 +203,20 @@ contract PBHVerifierImplV1 is WorldIDImpl {
// We now record the user has done this, so they can't do it again (proof of uniqueness)
nullifierHashes[nullifierHash] = true;

emit PBH(
root,
sender,
nonce,
callData,
pbhExternalNullifier,
nullifierHash,
proof
);
emit PBH(root, sender, nonce, callData, pbhExternalNullifier, nullifierHash, proof);
}

/// @notice Validates the hashed operations is the same as the hash transiently stored.
/// @param hashedOps The hashed operations to validate.
function validateSignaturesCallback(bytes32 hashedOps) external view virtual onlyProxy onlyInitialized {
assembly ("memory-safe") {
if iszero(eq(tload(HASHED_OPS_SLOT), hashedOps)) { revert(0, 0) }
}
}

/// @notice Sets the number of PBH transactions allowed per month.
/// @param _numPbhPerMonth The number of allowed PBH transactions per month.
function setNumPbhPerMonth(uint8 _numPbhPerMonth) external onlyOwner {
function setNumPbhPerMonth(uint8 _numPbhPerMonth) external virtual onlyOwner onlyProxy onlyInitialized {
assembly ("memory-safe") {
sstore(numPbhPerMonth.slot, _numPbhPerMonth)
}
Expand All @@ -214,9 +225,7 @@ contract PBHVerifierImplV1 is WorldIDImpl {
/// @dev If the World ID address is set to 0, then it is assumed that verification will take place off chain.
/// @notice Sets the World ID instance that will be used for verifying proofs.
/// @param worldId The World ID instance that will be used for verifying proofs.
function setWorldId(address worldId) external onlyOwner {
assembly ("memory-safe") {
sstore(_worldId.slot, worldId)
}
function setWorldId(address __worldId) external virtual onlyOwner onlyProxy onlyInitialized {
_worldId = IWorldIDGroups(__worldId);
}
}
24 changes: 24 additions & 0 deletions contracts/src/interfaces/IPBHVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IWorldIDGroups} from "@world-id-contracts/interfaces/IWorldIDGroups.sol";

interface IPBHVerifier {
function initialize(address __worldId, address __entryPoint, uint8 _numPbhPerMonth) external;

function verifyPbhProof(
uint256 root,
address sender,
uint256 nonce,
bytes memory callData,
uint256 pbhExternalNullifier,
uint256 nullifierHash,
uint256[8] memory proof
) external;

function validateSignaturesCallback() external view;

function setNumPbhPerMonth(uint8 _numPbhPerMonth) external;

function setWorldId(address worldId) external;
}
10 changes: 2 additions & 8 deletions contracts/test/MockWorldIDGroups.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "@world-id-contracts/interfaces/IWorldIDGroups.sol";

contract MockWorldIDGroups is IWorldIDGroups {
bool public verifyProofSuccess = true;

event VerifyProofCalled(
uint256 root,
uint256 groupId,
Expand All @@ -26,14 +27,7 @@ contract MockWorldIDGroups is IWorldIDGroups {
uint256 externalNullifierHash,
uint256[8] memory proof
) external override {
emit VerifyProofCalled(
root,
groupId,
signalHash,
nullifierHash,
externalNullifierHash,
proof
);
emit VerifyProofCalled(root, groupId, signalHash, nullifierHash, externalNullifierHash, proof);
if (!verifyProofSuccess) {
revert("Proof verification failed");
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/PBHVerifierConstruction.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ contract PBHVerifierConstruction is PBHVerifierTest {
// Test
pbhVerifier = new PBHVerifier(address(pbhVerifierImpl), callData);
}
}
}
3 changes: 1 addition & 2 deletions contracts/test/PBHVerifierTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ contract PBHVerifierTest is WorldIDTest {

IWorldIDGroups internal nullManager = IWorldIDGroups(address(0));
IWorldIDGroups internal thisWorldID;

///////////////////////////////////////////////////////////////////////////////
/// TEST ORCHESTRATION ///
///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -75,4 +75,3 @@ contract PBHVerifierTest is WorldIDTest {
pbhVerifierAddress = address(pbhVerifier);
}
}

0 comments on commit 606d00d

Please sign in to comment.