Skip to content

Commit

Permalink
create WorldIDIdentityManagerImplV2 to add support for deleteIdentiti…
Browse files Browse the repository at this point in the history
…es (#126)

* init

* add logic and fix breaking tests

missing new tests and removing all state bridge logic

* make ci ignore draft PRs, use latest foundry version

* remove state bridge logic

* update solidity version

* intermediary push

* fix tests

tests compile, however missing ImplV2 tests for init, uninit and deletionVerifier logic

* fix init tests

* fix uninit tests

* add deletion tests

missing two tests which require the real gnark circuit, pending

* add deletion circuit tests

failing atm

* add getters setters tests

* add asm keccak

* compilation fix

tests are still broken

* fix test

* fix deletion tests

* trigger CI

* add scoping to avoid breaking test into multiple parts

* fix to use bytes calldata instead of uint256[]

* update nastpec docs

* update nastpec docs

---------

Co-authored-by: Marcin Kostrzewa <[email protected]>
  • Loading branch information
dcbuild3r and kustosz authored Sep 7, 2023
1 parent 2f409a5 commit 68c9569
Show file tree
Hide file tree
Showing 56 changed files with 1,838 additions and 707 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ name: Unit Tests

jobs:
tests:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest

steps:
Expand All @@ -17,8 +18,6 @@ jobs:

- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly-e15e33a07c0920189fc336391f538c3dad53da73

- uses: actions/setup-node@v2
with:
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ any personal data**. Stop bots, stop abuse.
World ID uses a device called the [Orb](https://worldcoin.org/how-the-launch-works) which takes a
picture of a person's iris to verify they are a unique and alive human. The protocol only requires a
hash-equivalent (i.e. irreversible) of the iris to be stored (which happens on a blockchain). The
protocol uses [Zero-knowledge proofs](https://docs.worldcoin.org/further-reading/zero-knowledge-proofs) so no traceable information is
ever public.
protocol uses
[Zero-knowledge proofs](https://docs.worldcoin.org/further-reading/zero-knowledge-proofs) so no
traceable information is ever public.

World ID is meant for on-chain web3 apps, traditional Cloud applications, and even IRL
verifications. Go to the [World ID app](https://worldcoin.org/download-app) to get started.
Expand Down
2 changes: 1 addition & 1 deletion src/WorldIDIdentityManager.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
pragma solidity ^0.8.21;

import {WorldIDProxy} from "./abstract/WorldIDProxy.sol";

Expand Down
146 changes: 24 additions & 122 deletions src/WorldIDIdentityManagerImplV1.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
pragma solidity ^0.8.21;

import {WorldIDImpl} from "./abstract/WorldIDImpl.sol";

Expand Down Expand Up @@ -81,7 +81,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
/// @notice The table of verifiers for verifying batch identity insertions.
VerifierLookupTable internal batchInsertionVerifiers;

/// @notice The table of verifiers for verifying batch identity insertions.
/// @notice The table of verifiers for verifying identity updates.
VerifierLookupTable internal identityUpdateVerifiers;

/// @notice The verifier instance needed for operating within the semaphore protocol.
Expand Down Expand Up @@ -123,13 +123,15 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
/// @notice Represents the kind of change that is made to the root of the tree.
enum TreeChange {
Insertion,
Deletion,
Update
}

/// @notice Represents the kinds of dependencies that can be updated.
enum Dependency {
StateBridge,
InsertionVerifierLookupTable,
DeletionVerifierLookupTable,
UpdateVerifierLookupTable,
SemaphoreVerifier
}
Expand Down Expand Up @@ -239,12 +241,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
/// @param _treeDepth The depth of the MerkeTree
/// @param initialRoot The initial value for the `latestRoot` in the contract. When deploying
/// this should be set to the root of the empty tree.
/// @param _enableStateBridge Whether or not the state bridge should be enabled when
/// initialising the identity manager.
/// @param __stateBridge The initial state bridge contract to use.
event WorldIDIdentityManagerImplInitialized(
uint8 _treeDepth, uint256 initialRoot, bool _enableStateBridge, IBridge __stateBridge
);
event WorldIDIdentityManagerImplInitialized(uint8 _treeDepth, uint256 initialRoot);

///////////////////////////////////////////////////////////////////////////////
/// INITIALIZATION ///
Expand Down Expand Up @@ -273,9 +270,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
/// @param _batchInsertionVerifiers The verifier lookup table for batch insertions.
/// @param _batchUpdateVerifiers The verifier lookup table for batch updates.
/// @param _semaphoreVerifier The verifier to use for semaphore protocol proofs.
/// @param _enableStateBridge Whether or not the state bridge should be enabled when
/// initialising the identity manager.
/// @param __stateBridge The initial state bridge contract to use.
///
/// @custom:reverts string If called more than once at the same initialisation number.
/// @custom:reverts UnsupportedTreeDepth If passed tree depth is not among defined values.
Expand All @@ -284,9 +278,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
uint256 initialRoot,
VerifierLookupTable _batchInsertionVerifiers,
VerifierLookupTable _batchUpdateVerifiers,
ISemaphoreVerifier _semaphoreVerifier,
bool _enableStateBridge,
IBridge __stateBridge
ISemaphoreVerifier _semaphoreVerifier
) public reinitializer(1) {
// First, ensure that all of the parent contracts are initialised.
__delegateInit();
Expand All @@ -302,16 +294,12 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
batchInsertionVerifiers = _batchInsertionVerifiers;
identityUpdateVerifiers = _batchUpdateVerifiers;
semaphoreVerifier = _semaphoreVerifier;
_stateBridge = __stateBridge;
_isStateBridgeEnabled = _enableStateBridge;
_identityOperator = owner();

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

emit WorldIDIdentityManagerImplInitialized(
_treeDepth, initialRoot, _enableStateBridge, __stateBridge
);
emit WorldIDIdentityManagerImplInitialized(_treeDepth, initialRoot);
}

/// @notice Responsible for initialising all of the supertypes of this contract.
Expand Down Expand Up @@ -421,9 +409,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
// which it was expired.
rootHistory[preRoot] = uint128(block.timestamp);

// With the update confirmed, we send the root across multiple chains to ensure sync.
sendRootToStateBridge();

emit TreeChanged(preRoot, TreeChange.Insertion, postRoot);
} catch Error(string memory errString) {
/// This is not the revert we're looking for.
Expand Down Expand Up @@ -571,9 +556,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
// it was expired.
rootHistory[preRoot] = uint128(block.timestamp);

// With the update confirmed, we send the root across multiple chains to ensure sync.
sendRootToStateBridge();

emit TreeChanged(preRoot, TreeChange.Update, postRoot);
} catch Error(string memory errString) {
/// This is not the revert we're looking for.
Expand Down Expand Up @@ -663,78 +645,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
return _latestRoot;
}

/// @notice Sends the latest root to the state bridge.
/// @dev Only sends if the state bridge address is not the zero address.
///
function sendRootToStateBridge() internal virtual onlyProxy onlyInitialized {
if (_isStateBridgeEnabled && address(_stateBridge) != address(0)) {
_stateBridge.sendRootMultichain(_latestRoot);
}
}

/// @notice Allows a caller to query the address of the current stateBridge.
///
/// @return stateBridgeContract The address of the currently used stateBridge
function stateBridge()
public
view
virtual
onlyProxy
onlyInitialized
returns (IBridge stateBridgeContract)
{
return _stateBridge;
}

/// @notice Allows a caller to upgrade the stateBridge.
/// @dev Only the owner of the contract can call this function.
///
/// @param newStateBridge The new stateBridge contract
function setStateBridge(IBridge newStateBridge)
public
virtual
onlyProxy
onlyInitialized
onlyOwner
{
if (address(newStateBridge) == address(0)) {
revert InvalidStateBridgeAddress();
}

if (!_isStateBridgeEnabled) {
enableStateBridge();
}

IBridge oldStateBridge = _stateBridge;
_stateBridge = newStateBridge;

emit DependencyUpdated(
Dependency.StateBridge, address(oldStateBridge), address(newStateBridge)
);
}

/// @notice Enables the state bridge.
/// @dev Only the owner of the contract can call this function.
function enableStateBridge() public virtual onlyProxy onlyInitialized onlyOwner {
if (!_isStateBridgeEnabled) {
_isStateBridgeEnabled = true;
emit StateBridgeStateChange(true);
} else {
revert StateBridgeAlreadyEnabled();
}
}

/// @notice Disables the state bridge.
/// @dev Only the owner of the contract can call this function.
function disableStateBridge() public virtual onlyProxy onlyInitialized onlyOwner {
if (_isStateBridgeEnabled) {
_isStateBridgeEnabled = false;
emit StateBridgeStateChange(false);
} else {
revert StateBridgeAlreadyDisabled();
}
}

/// @notice Allows a caller to query the root history for information about a given root.
/// @dev Should be used sparingly as the query can be quite expensive.
///
Expand Down Expand Up @@ -797,11 +707,9 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
// if `element` is zero, it will underflow and be greater than SNARK_SCALAR_FIELD - 1.
// If `element` is non-zero, we can cancel the -1 terms and the comparison becomes what
// we want: `element < SNARK_SCALAR_FIELD`.
for
{ }
and(lt(offset, max), lt(sub(calldataload(offset), 1), SNARK_SCALAR_FIELD_MIN_ONE))
{ offset := add(offset, 32) }
{ }
for {} and(
lt(offset, max), lt(sub(calldataload(offset), 1), SNARK_SCALAR_FIELD_MIN_ONE)
) { offset := add(offset, 32) } {}
}

// check if the loop terminated before end of array and handle the remaining elements
Expand All @@ -813,17 +721,15 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
// this means we need to finish looping and make sure all remaining elements are zero.
if (element == 0) {
assembly ("memory-safe") {
// we just confirmed this element is zero
offset := add(offset, 32)

// increment offset until either of the following happens:
// - offset is equal to max, meaning we've reached the end of the array
// - the element at offset is non-zero
for
{ }
and(lt(offset, max), iszero(calldataload(offset)))
{ offset := add(offset, 32) }
{ }
// we just confirmed this element is zero
offset := add(offset, 32)

// increment offset until either of the following happens:
// - offset is equal to max, meaning we've reached the end of the array
// - the element at offset is non-zero
for {} and(lt(offset, max), iszero(calldataload(offset))) {
offset := add(offset, 32)
} {}
}

// check if the loop terminated because it found a non-zero element
Expand All @@ -836,7 +742,7 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
// otherwise check if the loop terminated because it found an unreduced element
// if so, revert
else if (element >= SNARK_SCALAR_FIELD) {
revert UnreducedElement(UnreducedElementType.IdentityCommitment, element);
revert UnreducedElement(UnreducedElementType.IdentityCommitment, element);
}
}
}
Expand All @@ -859,13 +765,11 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
// increment offset until either of the following happens:
// - offset is equal to max, meaning we've reached the end of the array
// - the element at offset is greater than or equal to SNARK_SCALAR_FIELD
for
{ }
and(lt(offset, max), lt(calldataload(offset), SNARK_SCALAR_FIELD))
{ offset := add(offset, 32) }
{ }
for {} and(lt(offset, max), lt(calldataload(offset), SNARK_SCALAR_FIELD)) {
offset := add(offset, 32)
} {}
}
// check if the loop terminated before end of array and revert if so
// check if the loop terminated before end of array and revert if so
if (offset < max) {
uint256 index = identityCommitments.length - ((max - offset) >> 5);
uint256 element = identityCommitments[index];
Expand Down Expand Up @@ -1053,8 +957,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
uint256 oldExpiry = rootHistoryExpiry;
rootHistoryExpiry = newExpiryTime;

_stateBridge.setRootHistoryExpiry(newExpiryTime);

emit RootHistoryExpirySet(oldExpiry, newExpiryTime);
}

Expand Down
Loading

0 comments on commit 68c9569

Please sign in to comment.