-
Notifications
You must be signed in to change notification settings - Fork 1
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
PBHVerifier #74
Draft
0xForerunner
wants to merge
23
commits into
main
Choose a base branch
from
forerunner/pbh-verifier
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+564
−0
Draft
PBHVerifier #74
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
8d96b05
chore: forge init
0xForerunner 57e0963
forge install: forge-std
0xForerunner 0d2fd70
wip forge init
0xForerunner 42a210d
forge install: world-id-contracts
0xForerunner a4df740
forge install: semaphore-v3
0xForerunner a51e13d
WIP
0xForerunner ee0126f
wip
0xForerunner bf756b0
wip
0xForerunner c4c3203
forge install: account-abstraction
0xForerunner 938bac0
wip
0xForerunner 8d4469c
wip
0xForerunner 2da172d
forge install: BokkyPooBahsDateTimeLibrary
0xForerunner 990ae18
cleanup
0xForerunner 28aa3ce
cleanup
0xForerunner 35d1a28
wip
0xForerunner 6ddd845
wip
0xForerunner 7af6d9f
fix tests
0xForerunner cc3d03a
cleanup
0xForerunner 3ec365a
add proxy
0xForerunner 010c3cd
rename
0xForerunner cf16121
fix remappings
0xForerunner 495b1f9
proxy work
0xForerunner 248091f
onlyProxy
0xForerunner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[submodule "pbh-verifier/lib/forge-std"] | ||
path = pbh-verifier/lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std | ||
[submodule "pbh-verifier/lib/world-id-contracts"] | ||
path = pbh-verifier/lib/world-id-contracts | ||
url = https://github.com/worldcoin/world-id-contracts | ||
[submodule "pbh-verifier/lib/BokkyPooBahsDateTimeLibrary"] | ||
path = pbh-verifier/lib/BokkyPooBahsDateTimeLibrary | ||
url = https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
pull_request: | ||
workflow_dispatch: | ||
|
||
env: | ||
FOUNDRY_PROFILE: ci | ||
|
||
jobs: | ||
check: | ||
strategy: | ||
fail-fast: true | ||
|
||
name: Foundry project | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
submodules: recursive | ||
|
||
- name: Install Foundry | ||
uses: foundry-rs/foundry-toolchain@v1 | ||
with: | ||
version: nightly | ||
|
||
- name: Show Forge version | ||
run: | | ||
forge --version | ||
|
||
- name: Run Forge fmt | ||
run: | | ||
forge fmt --check | ||
id: fmt | ||
|
||
- name: Run Forge build | ||
run: | | ||
forge build --sizes | ||
id: build | ||
|
||
- name: Run Forge tests | ||
run: | | ||
forge test -vvv | ||
id: test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Compiler files | ||
cache/ | ||
out/ | ||
|
||
# Ignores development broadcast logs | ||
!/broadcast | ||
/broadcast/*/31337/ | ||
/broadcast/**/dry-run/ | ||
|
||
# Docs | ||
docs/ | ||
|
||
# Dotenv file | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
FROM ghcr.io/foundry-rs/foundry:latest | ||
|
||
WORKDIR /world-id | ||
|
||
COPY . . | ||
|
||
# Fetch libs | ||
RUN forge install | ||
|
||
# Build the project | ||
RUN forge build | ||
|
||
# RUN ls script; exit 1 | ||
RUN ./script/generate_anvil_state.sh | ||
|
||
ENTRYPOINT ["anvil", "--host", "0.0.0.0", "--load-state", "state.json"] | ||
0xForerunner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
CMD [] | ||
0xForerunner marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
MIT License | ||
|
||
Copyright 2023 Worldcoin Foundation | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and | ||
associated documentation files (the "Software"), to deal in the Software without restriction, | ||
including without limitation the rights to use, copy, modify, merge, publish, distribute, | ||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial | ||
portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT | ||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES | ||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# PBH Validator | ||
|
||
As mentioned previously, Stage 1 of 4337 PBH features a PBHSignatureAggregator and PBHValidator contract, allowing a user to include a World ID proof encoded in the UserOp signature. The signature aggregator will call the PBHValidator for each UserOp included in the bundle, verifying the associated proof. | ||
|
||
The `PBHValidator` contract will extract the proof data from the signature, validate proof inputs and verify the proof. The `signal` for the proof will consist of the `userOpHash`. Upon successful verification of the proof, the `PBHValidator` will bump the PBH nonce for the `nullifierHash` associated with the proof. The PBH nonce is used to ensure that a given World ID user does not use more than `n` transactions a month. | ||
|
||
If the UserOp successfully clears all of these checks, a `PBH` event will be emitted indicating to the builder that this UserOp is a valid PBH user operation. The builder will only consider a “PBH” bundle for priority inclusion if all UserOps in the bundle emit a PBH event, and `aggregator` is specified as the `PBHSignatureAggregator`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[profile.default] | ||
src = "src" | ||
out = "out" | ||
libs = ["lib"] | ||
via_ir = true |
Submodule BokkyPooBahsDateTimeLibrary
added at
1dc26f
Submodule world-id-contracts
added at
583534
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@world-id-contracts/=lib/world-id-contracts/src/ | ||
@account-abstraction/=lib/account-abstraction/contracts/ | ||
@BokkyPooBahsDateTimeLibrary/=lib/BokkyPooBahsDateTimeLibrary/contracts/ | ||
@helpers/=src/helpers/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
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/WorldIDProxy.sol"; | ||
import "@BokkyPooBahsDateTimeLibrary/BokkyPooBahsDateTimeLibrary.sol"; | ||
|
||
/// @title PBH Verifier Implementation Version 1 | ||
/// @author Worldcoin | ||
/// @notice An implementation of a batch-based identity manager for the WorldID protocol. | ||
/// @dev This is the implementation delegated to by a proxy. | ||
contract PBHVerifierImplV1 is WorldIDImpl { | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// A NOTE ON IMPLEMENTATION CONTRACTS /// | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
// This contract is designed explicitly to operate from behind a proxy contract. As a result, | ||
// there are a few important implementation considerations: | ||
// | ||
// - All updates made after deploying a given version of the implementation should inherit from | ||
// the latest version of the implementation. This prevents storage clashes. | ||
// - All functions that are less access-restricted than `private` should be marked `virtual` in | ||
// order to enable the fixing of bugs in the existing interface. | ||
// - Any function that reads from or modifies state (i.e. is not marked `pure`) must be | ||
// annotated with the `onlyProxy` and `onlyInitialized` modifiers. This ensures that it can | ||
// only be called when it has access to the data in the proxy, otherwise results are likely to | ||
// be nonsensical. | ||
// - This contract deals with important data for the PBH system. Ensure that all newly-added | ||
// functionality is carefully access controlled using `onlyOwner`, or a more granular access | ||
// mechanism. | ||
// - Do not assign any contract-level variables at the definition site unless they are | ||
// `constant`. | ||
// | ||
// Additionally, the following notes apply: | ||
// | ||
// - Initialisation and ownership management are not protected behind `onlyProxy` intentionally. | ||
// This ensures that the contract can safely be disposed of after it is no longer used. | ||
// - Carefully consider what data recovery options are presented as new functionality is added. | ||
// Care must be taken to ensure that a migration plan can exist for cases where upgrades | ||
// cannot recover from an issue or vulnerability. | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// !!!!! DATA: DO NOT REORDER !!!!! /// | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
// To ensure compatibility between upgrades, it is exceedingly important that no reordering of | ||
// these variables takes place. If reordering happens, a storage clash will occur (effectively a | ||
// memory safety error). | ||
|
||
using ByteHasher for bytes; | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// ERRORS /// | ||
////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Thrown when attempting to reuse a nullifier | ||
error InvalidNullifier(); | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// Events /// | ||
////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Emitted when a verifier is updated in the lookup table. | ||
/// | ||
/// @param nullifierHash The nullifier hash that was used. | ||
event PBH( | ||
uint256 indexed nullifierHash | ||
); | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// Vars /// | ||
////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @dev The World ID group ID (always 1) | ||
uint256 internal immutable GROUP_ID = 1; | ||
|
||
/// @dev The World ID instance that will be used for verifying proofs | ||
IWorldIDGroups internal immutable worldId; | ||
|
||
/// @dev Make this configurable | ||
uint8 internal immutable numPbhPerMonth; | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// Mappings /// | ||
////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @dev Whether a nullifier hash has been used already. Used to guarantee an action is only performed once by a single person | ||
mapping(uint256 => bool) internal nullifierHashes; | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// Functions /// | ||
////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @param _worldId The WorldID instance that will verify the proofs | ||
constructor( | ||
IWorldIDGroups _worldId, | ||
uint8 _numPbhPerMonth | ||
) { | ||
worldId = _worldId; | ||
numPbhPerMonth = _numPbhPerMonth; | ||
} | ||
|
||
/// @param root The root of the Merkle tree (returned by the JS widget). | ||
/// @param sender The root of the Merkle tree (returned by the JS widget). | ||
/// @param nonce The root of the Merkle tree (returned by the JS widget). | ||
/// @param callData The root of the Merkle tree (returned by the JS widget). | ||
/// @param nullifierHash The nullifier hash for this proof, preventing double signaling (returned by the JS widget). | ||
/// @param proof The zero-knowledge proof that demonstrates the claimer is registered with World ID (returned by the JS widget). | ||
function verifyPbhProof( | ||
uint256 root, | ||
address sender, | ||
uint256 nonce, | ||
bytes memory callData, | ||
uint256 pbhExternalNullifier, | ||
uint256 nullifierHash, | ||
uint256[8] memory proof | ||
) external { | ||
// First, we make sure this person hasn't done this before | ||
if (nullifierHashes[nullifierHash]) revert InvalidNullifier(); | ||
|
||
// We now generate the signal hash from the sender, nonce, and calldata | ||
uint256 signalHash = abi.encodePacked( | ||
sender, | ||
nonce, | ||
callData | ||
).hashToField(); | ||
|
||
// Verify the external nullifier | ||
PBHExternalNullifier.verify(pbhExternalNullifier, numPbhPerMonth); | ||
|
||
// We now verify the provided proof is valid and the user is verified by World ID | ||
worldId.verifyProof( | ||
root, | ||
GROUP_ID, | ||
signalHash, | ||
nullifierHash, | ||
pbhExternalNullifier, | ||
proof | ||
); | ||
|
||
// We now record the user has done this, so they can't do it again (proof of uniqueness) | ||
nullifierHashes[nullifierHash] = true; | ||
|
||
emit PBH(nullifierHash); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import {WorldIDProxy} from "@world-id-contracts/abstract/WorldIDProxy.sol"; | ||
|
||
/// @title PBH Verifier | ||
/// @author Worldcoin | ||
/// @notice An implementation of an on chain PBH verifier. | ||
contract PBHVerifier is WorldIDProxy { | ||
/////////////////////////////////////////////////////////////////////////////// | ||
/// !!!! DO NOT ADD MEMBERS HERE !!!! /// | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
/// CONSTRUCTION /// | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Constructs a new instance of the PBH Verifier. | ||
/// @dev This constructor is only called once, and can be called with the encoded call necessary | ||
/// to initialize the logic contract. | ||
/// | ||
/// @param _logic The initial implementation (delegate) of the contract that this acts as a proxy | ||
/// for. | ||
/// @param _data If this is non-empty, it is used as the data for a `delegatecall` to `_logic`. | ||
/// This is usually an encoded function call, and allows for initialising the storage of | ||
/// the proxy in a way similar to a traditional solidity constructor. | ||
constructor(address _logic, bytes memory _data) payable WorldIDProxy(_logic, _data) { | ||
// !!!! DO NOT PUT PROGRAM LOGIC HERE !!!! | ||
// It should go in the `initialize` function of the delegate instead. | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
library ByteHasher { | ||
/// @dev Creates a keccak256 hash of a bytestring. | ||
/// @param value The bytestring to hash | ||
/// @return The hash of the specified value | ||
/// @dev `>> 8` makes sure that the result is included in our field | ||
function hashToField(bytes memory value) internal pure returns (uint256) { | ||
return uint256(keccak256(abi.encodePacked(value))) >> 8; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import "@BokkyPooBahsDateTimeLibrary/BokkyPooBahsDateTimeLibrary.sol"; | ||
|
||
/// @title PBHExternalNullifierLib | ||
/// @notice Library for encoding, decoding, and verifying PBH external nullifiers. | ||
/// External nullifiers are used to uniquely identify actions or events | ||
/// within a specific year and month using a nonce. | ||
/// @dev The encoding format is as follows: | ||
/// - Bits 32-255: Empty | ||
/// - Bits 16-31: Year | ||
/// - Bits 8-15: Month | ||
/// - Bits 0-7: Nonce | ||
library PBHExternalNullifier { | ||
/// @notice Thrown when the provided external nullifier year doesn't | ||
/// match the current year | ||
error InvalidExternalNullifierYear(); | ||
|
||
/// @notice Thrown when the provided external nullifier month doesn't | ||
/// match the current month | ||
error InvalidExternalNullifierMonth(); | ||
|
||
/// @notice Thrown when the provided external | ||
/// nullifier pbhNonce >= numPbhPerMonth | ||
error InvalidPbhNonce(); | ||
|
||
/// @notice Encodes a PBH external nullifier using the provided year, month, and nonce. | ||
/// @param pbhNonce An 8-bit nonce value (0-255) used to uniquely identify the nullifier within a month. | ||
/// @param month An 8-bit 1-indexed value representing the month (1-12). | ||
/// @param year A 16-bit value representing the year (e.g., 2024). | ||
/// @return The encoded PBHExternalNullifier. | ||
function encode(uint8 pbhNonce, uint8 month, uint16 year) internal pure returns (uint256) { | ||
require(month > 0 && month < 13, InvalidExternalNullifierMonth()); | ||
require(year < 10000, InvalidExternalNullifierYear()); | ||
return (uint32(year) << 16) | (uint32(month) << 8) | uint32(pbhNonce); | ||
} | ||
|
||
/// @notice Decodes an encoded PBHExternalNullifier into its constituent components. | ||
/// @param externalNullifier The encoded external nullifier to decode. | ||
/// @return pbhNonce The 8-bit nonce extracted from the external nullifier. | ||
/// @return month The 8-bit month extracted from the external nullifier. | ||
/// @return year The 16-bit year extracted from the external nullifier. | ||
function decode(uint256 externalNullifier) internal pure returns (uint8 pbhNonce, uint8 month, uint16 year) { | ||
year = uint16(externalNullifier >> 16); | ||
month = uint8((externalNullifier >> 8) & 0xFF); | ||
pbhNonce = uint8(externalNullifier & 0xFF); | ||
} | ||
|
||
/// @notice Verifies the validity of a PBHExternalNullifier by checking its components. | ||
/// @param externalNullifier The external nullifier to verify. | ||
/// @param numPbhPerMonth The maximum allowed value for the `pbhNonce` in the nullifier. | ||
/// @dev This function ensures the external nullifier matches the current year and month, | ||
/// and that the nonce does not exceed `numPbhPerMonth`. | ||
function verify(uint256 externalNullifier, uint8 numPbhPerMonth) public view { | ||
(uint8 pbhNonce, uint8 month, uint16 year) = PBHExternalNullifier.decode(externalNullifier); | ||
require(year == BokkyPooBahsDateTimeLibrary.getYear(block.timestamp), InvalidExternalNullifierYear()); | ||
require(month == BokkyPooBahsDateTimeLibrary.getMonth(block.timestamp), InvalidExternalNullifierMonth()); | ||
require(pbhNonce <= numPbhPerMonth, InvalidPbhNonce()); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also move this workflow to the root
.github/workflows
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@0xOsiris could you explain your thoughts here?