Skip to content
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

create WorldIDIdentityManagerImplV2 to add support for deleteIdentities #126

Merged
merged 21 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -85,7 +85,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 @@ -127,13 +127,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 @@ -243,12 +245,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 @@ -277,9 +274,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 @@ -288,9 +282,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 @@ -306,16 +298,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 @@ -425,9 +413,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 @@ -575,9 +560,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 @@ -667,78 +649,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 @@ -801,11 +711,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 @@ -817,17 +725,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 @@ -840,7 +746,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 @@ -861,13 +767,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 @@ -1055,8 +959,6 @@ contract WorldIDIdentityManagerImplV1 is WorldIDImpl, IWorldID {
uint256 oldExpiry = rootHistoryExpiry;
rootHistoryExpiry = newExpiryTime;

_stateBridge.setRootHistoryExpiry(newExpiryTime);

emit RootHistoryExpirySet(oldExpiry, newExpiryTime);
}

Expand Down
Loading
Loading