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

LineaStatebridge #51

Closed
wants to merge 5 commits into from
Closed
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ export MSG_CLAIM_VALUE=0
export MSG_CLAIM_NONCE=0
export MSG_CLAIM_CALLDATA="YOUR_CALL_DATA"
export MSG_CLAIM_FEE_RECIPIENT=0x0
export MAINNET_RPC_URL=https://eth.llamarpc.com

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"dependencies": {
"@openzeppelin/contracts": "^4.9.6",
"@openzeppelin/contracts-upgradeable": "^4.9.6",
"@prb/test": "^0.6.4",
"@worldcoin/world-id-state-bridge": "github:worldcoin/world-id-state-bridge#729d234",
"ethers": "^6.13.2",
"linea-contracts": "github:Consensys/linea-contracts"
Expand Down Expand Up @@ -47,4 +48,4 @@
"test:coverage": "forge coverage",
"test:coverage:report": "forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage"
}
}
}
9 changes: 5 additions & 4 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/
@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable
forge-std/=node_modules/forge-std/src
world-id-state-bridge=node_modules/@worldcoin/world-id-state-bridge/src
linea-contracts/=node_modules/linea-contracts/contracts
@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/
forge-std/=node_modules/forge-std/src/
world-id-state-bridge/=node_modules/@worldcoin/world-id-state-bridge/src/
linea-contracts/=node_modules/linea-contracts/contracts/
@prb/test/=node_modules/@prb/test/src/
54 changes: 54 additions & 0 deletions src/mocks/MocksBrigedWorldID.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import { WorldIDBridge } from "world-id-state-bridge/abstract/WorldIDBridge.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";

/// @title LineaWorldID Mock
/// @author Worldcoin & IkemHood
/// @notice Mock of LineaWorldID in order to test functionality on a local chain
/// @custom:deployment deployed through make local-mock
contract MockBridgedWorldID is WorldIDBridge, Ownable2Step {
///////////////////////////////////////////////////////////////////////////////
/// CONSTRUCTION ///
///////////////////////////////////////////////////////////////////////////////

/// @notice Initializes the contract the depth of the associated merkle tree.
///
/// @param _treeDepth The depth of the WorldID Semaphore merkle tree.
constructor(uint8 _treeDepth) WorldIDBridge(_treeDepth) { }

///////////////////////////////////////////////////////////////////////////////
/// ROOT MIRRORING ///
///////////////////////////////////////////////////////////////////////////////

/// @notice This function is called by the state bridge contract when it forwards a new root to
/// the bridged WorldID.
/// @dev This function can revert if Optimism's CrossDomainMessenger stops processing proofs
/// or if OPLabs stops submitting them. Next iteration of Optimism's cross-domain messaging, will be
/// fully permissionless for message-passing, so this will not be an issue.
/// Sequencer needs to include changes to the CrossDomainMessenger contract on L1,
/// not economically penalized if messages are not included, however the fraud prover (Cannon)
/// can force the sequencer to include it.
///
/// @param newRoot The value of the new root.
///
/// @custom:reverts CannotOverwriteRoot If the root already exists in the root history.
/// @custom:reverts string If the caller is not the owner.
function receiveRoot(uint256 newRoot) public virtual onlyOwner {
_receiveRoot(newRoot);
}

///////////////////////////////////////////////////////////////////////////////
/// DATA MANAGEMENT ///
///////////////////////////////////////////////////////////////////////////////

/// @notice Sets the amount of time it takes for a root in the root history to expire.
///
/// @param expiryTime The new amount of time it takes for a root to expire.
///
/// @custom:reverts string If the caller is not the owner.
function setRootHistoryExpiry(uint256 expiryTime) public virtual override onlyOwner {
_setRootHistoryExpiry(expiryTime);
}
}
114 changes: 114 additions & 0 deletions src/mocks/MocksMessageService.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.19 <=0.8.24;

import { IMessageService } from "linea-contracts/interfaces/IMessageService.sol";

/**
* @title Mock Message Service
* @author Worldcoin & IkemHood
* @notice Mock implementation of the IMessageService interface for testing functionality on a local chain.
* @dev deployed through make mock and make local-mock
*/
contract MockMessageService is IMessageService {
/// @notice Tracks the original sender for mocking purposes.
address private _originalSender;

/// @notice Counter to generate unique nonces for each message.
uint256 private _nonceCounter;

/// @notice Mapping to keep track of claimed messages to prevent double claims.
mapping(bytes32 => bool) private _claimedMessages;

/**
* @notice Sets the original sender address for mocking purposes.
* @param _sender The address to set as the original sender.
*/
function setOriginalSender(address _sender) external {
_originalSender = _sender;
}

/**
* @notice Mocks the process of sending a message.
* @dev This function should be called with a msg.value = _value + _fee. The fee will be paid on the destination
* chain.
* @param _to The destination address on the destination chain.
* @param _fee The message service fee on the origin chain.
* @param _calldata The calldata used by the destination message service to call the destination contract.
*/
function sendMessage(address _to, uint256 _fee, bytes calldata _calldata) external payable override {
if (msg.value < _fee) {
revert FeeTooLow();
}
if (msg.value < _fee + 1) {
// Assuming 1 wei as the minimum value for testing purposes
revert ValueSentTooLow();
}

// Generate a unique message hash based on the message parameters.
bytes32 messageHash = keccak256(abi.encodePacked(msg.sender, _to, _fee, msg.value, _nonceCounter, _calldata));

// Emit the MessageSent event with the calculated hash and other details.
emit MessageSent(msg.sender, _to, _fee, msg.value, _nonceCounter, _calldata, messageHash);

// Increment the nonce counter for the next message.
_nonceCounter++;
}

/**
* @notice Mocks the process of claiming a message on the destination chain.
* @param _from The msg.sender calling the origin message service.
* @param _to The destination address on the destination chain.
* @param _fee The message service fee on the origin chain.
* @param _value The value to be transferred to the destination address.
* @param _feeRecipient Address that will receive the fees.
* @param _calldata The calldata used by the destination message service to call/forward to the destination
* contract.
* @param _nonce Unique message number.
*/
function claimMessage(
address _from,
address _to,
uint256 _fee,
uint256 _value,
address payable _feeRecipient,
bytes calldata _calldata,
uint256 _nonce
)
external
override
{
// Decode the message hash based on the provided parameters.
bytes32 messageHash = keccak256(abi.encodePacked(_from, _to, _fee, _value, _nonce, _calldata));

// Revert if the message has already been claimed to prevent double claims.
if (_claimedMessages[messageHash]) {
revert MessageSendingFailed(_to);
}

// Mark the message as claimed.
_claimedMessages[messageHash] = true;

// Emit the MessageClaimed event with the calculated hash.
emit MessageClaimed(messageHash);

// Simulate sending the fee to the recipient.
(bool success,) = _feeRecipient.call{ value: _fee }("");
if (!success) {
revert FeePaymentFailed(_feeRecipient);
}

// Simulate sending the value to the recipient.
(success,) = _to.call{ value: _value }("");
if (!success) {
revert MessageSendingFailed(_to);
}
}

/**
* @notice Returns the original sender of the message on the origin layer.
* @return The original sender address.
*/
function sender() external view override returns (address) {
return _originalSender;
}
}
41 changes: 41 additions & 0 deletions src/mocks/MocksStateBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import { MockBridgedWorldID } from "./MockBridgedWorldID.sol";
import { IWorldIDIdentityManager } from "world-id-state-bridge/interfaces/IWorldIDIdentityManager.sol";
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";

/// @title Mock State Bridge
/// @author Worldcoin
/// @notice Mock of the StateBridge to test functionality on a local chain
/// @custom:deployment deployed through make local-mock
contract MockStateBridge is Ownable2Step {
/// @notice MockWorldIDIdentityManager contract which will hold a mock root
IWorldIDIdentityManager public worldID;

/// @notice MockBridgedWorldID contract which will receive the root
MockBridgedWorldID public mockBridgedWorldID;

/// @notice Emmited when the root is not a valid root in the canonical WorldID Identity Manager contract
error InvalidRoot();

/// @notice constructor
constructor(address _mockWorldID, address _mockBridgedWorldID) {
worldID = IWorldIDIdentityManager(_mockWorldID);
mockBridgedWorldID = MockBridgedWorldID(_mockBridgedWorldID);
}

/// @notice Sends the latest WorldID Identity Manager root to the Bridged WorldID contract.
/// @dev Calls this method on the L1 Proxy contract to relay roots to WorldID supported chains.
function propagateRoot() public {
uint256 latestRoot = worldID.latestRoot();
_sendRootToMockBridgedWorldID(latestRoot);
}

// @notice Sends the latest WorldID Identity Manager root to all chains.
/// @dev Calls this method on the L1 Proxy contract to relay roots to WorldID supported chains.
/// @param root The latest WorldID Identity Manager root.
function _sendRootToMockBridgedWorldID(uint256 root) internal {
mockBridgedWorldID.receiveRoot(root);
}
}
102 changes: 102 additions & 0 deletions src/mocks/MocksWorldIDIdentityManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import { IWorldIDIdentityManager } from "world-id-state-bridge/interfaces/IWorldIDIdentityManager.sol";

/// @title WorldID Identity Manager Mock
/// @author Worldcoin
/// @notice Mock of the WorldID Identity Manager contract (world-id-contracts) to test functionality on a local chain
/// @dev deployed through make mock and make local-mock
contract MockWorldIDIdentityManager is IWorldIDIdentityManager {
uint256 internal _latestRoot;

/// @notice Represents the kind of change that is made to the root of the tree.
enum TreeChange {
Insertion,
Deletion
}

/// @notice Emitted when the current root of the tree is updated.
///
/// @param preRoot The value of the tree's root before the update.
/// @param kind Either "insertion" or "update", the kind of alteration that was made to the
/// tree.
/// @param postRoot The value of the tree's root after the update.
event TreeChanged(uint256 indexed preRoot, TreeChange indexed kind, uint256 indexed postRoot);

constructor(uint256 initRoot) {
_latestRoot = initRoot;
}

/// @notice Registers identities into the WorldID system.
/// @dev Can only be called by the identity operator.
/// @dev Registration is performed off-chain and verified on-chain via the `insertionProof`.
/// This saves gas and time over inserting identities one at a time.
///
/// @param insertionProof The proof that given the conditions (`preRoot`, `startIndex` and
/// `identityCommitments`), insertion into the tree results in `postRoot`. Elements 0 and
/// 1 are the `x` and `y` coordinates for `ar` respectively. Elements 2 and 3 are the `x`
/// coordinate for `bs`, and elements 4 and 5 are the `y` coordinate for `bs`. Elements 6
/// and 7 are the `x` and `y` coordinates for `krs`.
/// @param preRoot The value for the root of the tree before the `identityCommitments` have been
//// inserted. Must be an element of the field `Kr`. (already in reduced form)
/// @param startIndex The position in the tree at which the insertions were made.
/// @param identityCommitments The identities that were inserted into the tree starting at
/// `startIndex` and `preRoot` to give `postRoot`. All of the commitments must be
/// elements of the field `Kr`.
/// @param postRoot The root obtained after inserting all of `identityCommitments` into the tree
/// described by `preRoot`. Must be an element of the field `Kr`. (alread in reduced form)
///
function registerIdentities(
uint256[8] calldata insertionProof,
uint256 preRoot,
uint32 startIndex,
uint256[] calldata identityCommitments,
uint256 postRoot
)
public
{
_latestRoot = postRoot;
emit TreeChanged(preRoot, TreeChange.Insertion, postRoot);
}

/// @notice Deletes identities from the WorldID system.
/// @dev Can only be called by the identity operator.
/// @dev Deletion is performed off-chain and verified on-chain via the `deletionProof`.
/// This saves gas and time over deleting identities one at a time.
///
/// @param deletionProof The proof that given the conditions (`preRoot` and `packedDeletionIndices`),
/// deletion into the tree results in `postRoot`. Elements 0 and 1 are the `x` and `y`
/// coordinates for `ar` respectively. Elements 2 and 3 are the `x` coordinate for `bs`,
/// and elements 4 and 5 are the `y` coordinate for `bs`. Elements 6 and 7 are the `x`
/// and `y` coordinates for `krs`.
/// @param packedDeletionIndices The indices of the identities that were deleted from the tree. The batch size is
/// inferred from the length of this
//// array: batchSize = packedDeletionIndices / 4
/// @param preRoot The value for the root of the tree before the corresponding identity commitments have
/// been deleted. Must be an element of the field `Kr`.
/// @param postRoot The root obtained after deleting all of `identityCommitments` into the tree
/// described by `preRoot`. Must be an element of the field `Kr`.
function deleteIdentities(
uint256[8] calldata deletionProof,
bytes calldata packedDeletionIndices,
uint256 preRoot,
uint256 postRoot
)
public
{
_latestRoot = postRoot;
emit TreeChanged(preRoot, TreeChange.Deletion, postRoot);
}

function insertRoot(uint256 postRoot) public {
uint256 preRoot = _latestRoot;
_latestRoot = postRoot;

emit TreeChanged(preRoot, TreeChange.Insertion, postRoot);
}

function latestRoot() external view returns (uint256) {
return _latestRoot;
}
}
Loading
Loading