-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
85 additions
and
0 deletions.
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,85 @@ | ||
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; | ||
} | ||
} | ||
|
||
interface IWorldID { | ||
/// @notice Reverts if the zero-knowledge proof is invalid. | ||
/// @param root The of the Merkle tree | ||
/// @param groupId The id of the Semaphore group | ||
/// @param signalHash A keccak256 hash of the Semaphore signal | ||
/// @param nullifierHash The nullifier hash | ||
/// @param externalNullifierHash A keccak256 hash of the external nullifier | ||
/// @param proof The zero-knowledge proof | ||
/// @dev Note that a double-signaling check is not included here, and should be carried by the caller. | ||
function verifyProof( | ||
uint256 root, | ||
uint256 groupId, | ||
uint256 signalHash, | ||
uint256 nullifierHash, | ||
uint256 externalNullifierHash, | ||
uint256[8] calldata proof | ||
) external view; | ||
} | ||
|
||
contract WorldcoinVerifier { | ||
using ByteHasher for bytes; | ||
|
||
/// @notice Thrown when attempting to reuse a nullifier | ||
error InvalidNullifier(); | ||
|
||
/// @dev The address of the World ID Router contract that will be used for verifying proofs | ||
IWorldID internal immutable worldId; | ||
|
||
/// @dev The keccak256 hash of the externalNullifier (unique identifier of the action performed), combination of appId and action | ||
uint256 internal immutable externalNullifierHash; | ||
|
||
/// @dev The World ID group ID (1 for Orb-verified) | ||
uint256 internal immutable groupId = 1; | ||
|
||
/// @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; | ||
|
||
/// @param _worldId The address of the WorldIDRouter that will verify the proofs | ||
/// @param _appId The World ID App ID (from Developer Portal) | ||
/// @param _action The World ID Action (from Developer Portal) | ||
constructor( | ||
IWorldID _worldId, | ||
string memory _appId, | ||
string memory _action | ||
) { | ||
worldId = _worldId; | ||
externalNullifierHash = abi | ||
.encodePacked(abi.encodePacked(_appId).hashToField(), _action) | ||
.hashToField(); | ||
} | ||
|
||
/// @param signal An arbitrary input from the user that cannot be tampered with. In this case, it is the user's wallet address. | ||
/// @param root The root (returned by the IDKit widget). | ||
/// @param nullifierHash The nullifier hash for this proof, preventing double signaling (returned by the IDKit widget). | ||
/// @param proof The zero-knowledge proof that demonstrates the claimer is registered with World ID (returned by the IDKit widget). | ||
function verifyAndExecute( | ||
address signal, | ||
uint256 root, | ||
uint256 nullifierHash, | ||
uint256[8] calldata proof | ||
) public { | ||
if (nullifierHashes[nullifierHash]) revert InvalidNullifier(); | ||
|
||
nullifierHashes[nullifierHash] = true; | ||
|
||
worldId.verifyProof( | ||
root, | ||
groupId, // set to "1" in the constructor | ||
abi.encodePacked(signal).hashToField(), | ||
nullifierHash, | ||
externalNullifierHash, | ||
proof | ||
); | ||
} | ||
} |