diff --git a/examples/validium/abi/ValidiumContract.json b/examples/validium/abi/ValidiumContract.json index ad5fb9ae4..b8e409453 100644 --- a/examples/validium/abi/ValidiumContract.json +++ b/examples/validium/abi/ValidiumContract.json @@ -1,4 +1,23 @@ [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, { "inputs": [ { @@ -18,7 +37,7 @@ }, { "internalType": "uint256", - "name": "leafIndex", + "name": "index", "type": "uint256" }, { @@ -31,7 +50,7 @@ "outputs": [ { "internalType": "bool", - "name": "", + "name": "isMember", "type": "bool" } ], @@ -57,6 +76,26 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -69,5 +108,18 @@ "outputs": [], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" } -] \ No newline at end of file +] diff --git a/examples/validium/contracts/ValidiumContract.sol b/examples/validium/contracts/ValidiumContract.sol index 008214d26..6f891000a 100644 --- a/examples/validium/contracts/ValidiumContract.sol +++ b/examples/validium/contracts/ValidiumContract.sol @@ -1,16 +1,18 @@ -pragma solidity 0.8.15; +//SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/access/Ownable.sol"; contract DataAvailabilityRouter { mapping(uint32 => bytes32) public roots; } -contract ValidiumContract { - - DataAvailabilityRouter router; +contract ValidiumContract is Ownable { + DataAvailabilityRouter private router; function setRouter( address _router - ) public { + ) public onlyOwner { router = DataAvailabilityRouter(_router); } @@ -22,33 +24,44 @@ contract ValidiumContract { function checkDataRootMembership( uint32 blockNumber, - bytes32[] memory proof, + bytes32[] calldata proof, uint256 numberOfLeaves, - uint256 leafIndex, + uint256 index, bytes32 leaf - ) public view returns (bool) { - if (leafIndex >= numberOfLeaves) { - return false; - } - - uint256 position = leafIndex; - uint256 width = numberOfLeaves; + ) public view returns (bool isMember) { + // if the proof is of size n, the tree height will be n+1 + // in a tree of height n+1, max possible leaves are 2^n + require(index < numberOfLeaves, "INVALID_LEAF_INDEX"); + // refuse to accept padded leaves as proof + require(leaf != bytes32(0), "INVALID_LEAF"); - bytes32 computedHash = leaf; + bytes32 rootHash = getDataRoot(blockNumber); + assembly ("memory-safe") { + if proof.length { + let end := add(proof.offset, shl(5, proof.length)) + let i := proof.offset + let width := numberOfLeaves - for (uint256 i = 0; i < proof.length; i++) { - bytes32 proofElement = proof[i]; - - if (position % 2 == 1 || position + 1 == width) { - computedHash = sha256(abi.encodePacked(proofElement, computedHash)); - } else { - computedHash = sha256(abi.encodePacked(computedHash, proofElement)); + for {} 1 {} { + let leafSlot := shl(5, and(0x1, index)) + if eq(add(index, 1), width) { + leafSlot := 0x20 + } + mstore(leafSlot, leaf) + mstore(xor(leafSlot, 32), calldataload(i)) + leaf := keccak256(0, 64) + index := shr(1, index) + i := add(i, 32) + width := add(shr(1, sub(width, 1)), 1) + if iszero(lt(i, end)) { + break + } + } } + isMember := eq(leaf, rootHash) - position /= 2; - width = (width - 1) / 2 + 1; } - - return computedHash == getDataRoot(blockNumber); + return isMember; } } +