From 3b19488f46a740b1edb2952493fd6d0d1c9631b3 Mon Sep 17 00:00:00 2001 From: Qi Zhou Date: Wed, 29 May 2024 13:31:32 +0800 Subject: [PATCH] add LibDA.sol --- .../src/dispute/lib/LibDA.sol | 74 +++++++++++++++++++ .../test/dispute/lib/LibDA.t.sol | 65 ++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 packages/contracts-bedrock/src/dispute/lib/LibDA.sol create mode 100644 packages/contracts-bedrock/test/dispute/lib/LibDA.t.sol diff --git a/packages/contracts-bedrock/src/dispute/lib/LibDA.sol b/packages/contracts-bedrock/src/dispute/lib/LibDA.sol new file mode 100644 index 0000000000000..e5318df13d7c3 --- /dev/null +++ b/packages/contracts-bedrock/src/dispute/lib/LibDA.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +library LibDA { + uint256 constant DA_TYPE_CALLDATA = 0; + uint256 constant DA_TYPE_EIP4844 = 1; + + function getClaimsHash(uint256 daType, uint256 nelemebts, bytes memory data) internal view returns (bytes32 root) { + if (daType == DA_TYPE_EIP4844) { + // TODO: may specify which blob? + root = blobhash(0); + require(root != bytes32(0), "root must not zero"); + return root; + } + + require(daType == DA_TYPE_CALLDATA, "unsupported DA type"); + require(nelemebts * 32 == data.length, "data must 32 * n"); + require(nelemebts > 0, "data must not empty"); + + while (nelemebts != 1) { + for (uint256 i = 0 ; i < nelemebts / 2; i++) { + bytes32 hash; + uint256 roff = i * 32 * 2; + uint256 woff = i * 32; + assembly { + hash := keccak256(add(add(data, 0x20), roff), 64) + mstore(add(add(data, 0x20), woff), hash) + } + } + + // directly copy the last item + if (nelemebts % 2 == 1) { + uint256 roff = (nelemebts - 1) * 32; + uint256 woff = (nelemebts / 2) * 32; + bytes32 hash; + assembly { + hash := mload(add(add(data, 0x20), roff)) + mstore(add(add(data, 0x20), woff), hash) + } + } + + nelemebts = (nelemebts + 1) / 2; + } + + assembly { + root := mload(add(data, 0x20)) + } + } + + function verifyClaimHash(uint256 daType, bytes32 root, uint256 nelements, uint256 idx, bytes32 claimHash, bytes memory proof) internal pure { + require(daType == 0, "unsupported DA type"); + bytes32 hash = claimHash; + uint256 proofOff = 0; + while (nelements != 1) { + if (idx != nelements - 1 || nelements % 2 == 0) { + bytes32 pHash; + require(proofOff < proof.length, "no enough proof"); + assembly { + pHash := mload(add(add(proof, 0x20), proofOff)) + } + proofOff += 32; + if (idx % 2 == 0) { + hash = keccak256(abi.encode(hash, pHash)); + } else { + hash = keccak256(abi.encode(pHash, hash)); + } + } + nelements = (nelements + 1) / 2; + idx = idx / 2; + } + require(root == hash, "proof failed"); + } +} + diff --git a/packages/contracts-bedrock/test/dispute/lib/LibDA.t.sol b/packages/contracts-bedrock/test/dispute/lib/LibDA.t.sol new file mode 100644 index 0000000000000..09b4e61ec6389 --- /dev/null +++ b/packages/contracts-bedrock/test/dispute/lib/LibDA.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import { Test } from "forge-std/Test.sol"; +import { LibDA } from "src/dispute/lib/LibDA.sol"; + +/// @notice Tests for `LibDA` +contract LibDA_Test is Test { + function test_calldata_one() public view { + bytes32 root; + bytes memory input = "00000000000000000000000000000000"; + root = LibDA.getClaimsHash(LibDA.DA_TYPE_CALLDATA, 1, input); + assertEq(root, bytes32("00000000000000000000000000000000")); + input = "10000000000000000000000000000001"; + root = LibDA.getClaimsHash(LibDA.DA_TYPE_CALLDATA, 1, input); + assertEq(root, bytes32("10000000000000000000000000000001")); + } + + function test_calldata_two() public view { + bytes32 root; + bytes memory input = "0000000000000000000000000000000010000000000000000000000000000001"; + root = LibDA.getClaimsHash(LibDA.DA_TYPE_CALLDATA, 2, input); + assertEq(root, keccak256(abi.encode(bytes32("00000000000000000000000000000000"), bytes32("10000000000000000000000000000001")))); + } + + function test_calldata_three() public view { + bytes32 root; + bytes memory input = "000000000000000000000000000000001000000000000000000000000000000120000000000000000000000000000002"; + root = LibDA.getClaimsHash(LibDA.DA_TYPE_CALLDATA, 3, input); + assertEq(root, keccak256(abi.encode( + keccak256(abi.encode(bytes32("00000000000000000000000000000000"), bytes32("10000000000000000000000000000001"))), + bytes32("20000000000000000000000000000002") + ))); + } + + function test_calldata_seven() public view { + bytes32 root; + bytes memory input = "00000000000000000000000000000000100000000000000000000000000000012000000000000000000000000000000230000000000000000000000000000003400000000000000000000000000000045000000000000000000000000000000560000000000000000000000000000006"; + root = LibDA.getClaimsHash(LibDA.DA_TYPE_CALLDATA, 7, input); + assertEq(root, + keccak256(abi.encode( + keccak256(abi.encode( + keccak256(abi.encode( + bytes32("00000000000000000000000000000000"), + bytes32("10000000000000000000000000000001"))), + keccak256(abi.encode( + bytes32("20000000000000000000000000000002"), + bytes32("30000000000000000000000000000003"))))), + keccak256(abi.encode( + keccak256(abi.encode( + bytes32("40000000000000000000000000000004"), + bytes32("50000000000000000000000000000005"))), + bytes32("60000000000000000000000000000006"))) + ))); + } + + function test_calldata_prove_three() public view { + bytes32 root; + bytes memory input = "000000000000000000000000000000001000000000000000000000000000000120000000000000000000000000000002"; + root = LibDA.getClaimsHash(LibDA.DA_TYPE_CALLDATA, 3, input); + LibDA.verifyClaimHash(LibDA.DA_TYPE_CALLDATA, root, 3, 0, "00000000000000000000000000000000", "1000000000000000000000000000000120000000000000000000000000000002"); + LibDA.verifyClaimHash(LibDA.DA_TYPE_CALLDATA, root, 3, 1, "10000000000000000000000000000001", "0000000000000000000000000000000020000000000000000000000000000002"); + LibDA.verifyClaimHash(LibDA.DA_TYPE_CALLDATA, root, 3, 2, "20000000000000000000000000000002", bytes.concat(keccak256(abi.encode(bytes32("00000000000000000000000000000000"), bytes32("10000000000000000000000000000001"))))); + } +}