Skip to content

Commit

Permalink
feat: add worldid verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
dawksh committed Aug 3, 2024
1 parent 78c4558 commit 033b495
Showing 1 changed file with 85 additions and 0 deletions.
85 changes: 85 additions & 0 deletions src/WorldcoinVerifier.sol
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
);
}
}

0 comments on commit 033b495

Please sign in to comment.