Skip to content

Commit

Permalink
test: add sha256 merkle tests, additional sha256 hashing and more has…
Browse files Browse the repository at this point in the history
…hing assertions
  • Loading branch information
QEDK committed Jan 27, 2024
1 parent da0b6f9 commit 1e942ec
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 4 deletions.
47 changes: 43 additions & 4 deletions test/AvailBridgeTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ contract AvailBridgeTest is Test, MurkyBase {
WrappedAvail public avail;
VectorxMock public vectorx;
ProxyAdmin public admin;
Sha2Merkle public sha2merkle;
address public owner;
address public pauser;
bytes public constant revertCode = "5F5FFD";
Expand All @@ -27,6 +28,7 @@ contract AvailBridgeTest is Test, MurkyBase {
vectorx = new VectorxMock();
admin = new ProxyAdmin(msg.sender);
pauser = makeAddr("pauser");
sha2merkle = new Sha2Merkle();
address impl = address(new AvailBridge());
bridge = AvailBridge(address(new TransparentUpgradeableProxy(impl, address(admin), "")));
avail = new WrappedAvail(address(bridge));
Expand Down Expand Up @@ -408,6 +410,36 @@ contract AvailBridgeTest is Test, MurkyBase {
assertEq(avail.totalSupply(), amount);
}

function test_receiveAVAIL_2(bytes32 rangeHash, uint64 messageId, bytes32[16] calldata c_leaves, bytes32[16] calldata c_dataRoots, uint256 rand, bytes32 blobRoot) external {
// this function is a bit unreadable because forge coverage does not support IR compilation which results
// in stack too deep errors
bytes32[] memory dataRoots = new bytes32[](c_dataRoots.length);
bytes32[] memory leaves = new bytes32[](c_leaves.length);
for (uint256 i = 0; i < c_leaves.length;) {
dataRoots[i] = c_dataRoots[i];
leaves[i] = c_leaves[i];
unchecked {
++i;
}
}
address to = makeAddr("to");
leaves[rand % leaves.length] = keccak256(abi.encode(IAvailBridge.Message(0x02, bytes32("1"), bytes32(bytes20(to)), 1, 2, abi.encode(bytes32(0), 1), messageId)));
// set dataRoot at this point in the array
dataRoots[rand % dataRoots.length] = hashLeafPairs(blobRoot, getRoot(leaves));
vectorx.set(rangeHash, sha2merkle.getRoot(dataRoots));

vm.expectCall(address(avail), abi.encodeCall(avail.mint, (to, 1)));
{
bridge.receiveAVAIL(IAvailBridge.Message(0x02, bytes32("1"), bytes32(bytes20(to)), 1, 2, abi.encode(bytes32(0), 1), messageId), IAvailBridge.MerkleProofInput(sha2merkle.getProof(dataRoots, rand % dataRoots.length), getProof(leaves, rand % leaves.length), rangeHash, rand % dataRoots.length, blobRoot, getRoot(leaves), keccak256(abi.encode(IAvailBridge.Message(0x02, bytes32("1"), bytes32(bytes20(to)), 1, 2, abi.encode(bytes32(0), 1), messageId))), rand % leaves.length));
}
{
assertTrue(bridge.isBridged(keccak256(abi.encode(IAvailBridge.Message(0x02, bytes32("1"), bytes32(bytes20(to)), 1, 2, abi.encode(bytes32(0), 1), messageId)))));
}
{
assertEq(avail.totalSupply(), 1);
}
}

function testRevertInvalidAssetId_receiveETH(bytes32 assetId) external {
vm.assume(assetId != 0x4554480000000000000000000000000000000000000000000000000000000000);
IAvailBridge.Message memory message =
Expand Down Expand Up @@ -637,6 +669,7 @@ contract AvailBridgeTest is Test, MurkyBase {
assertEq(address(bridge).balance, amount);
assertEq(bridge.isSent(0), keccak256(abi.encode(message)));
assertEq(from.balance, balance - amount);
assertEq(bridge.messageId(), 1);
}

function testRevertInvalidAssetId_sendERC20(bytes32 assetId, bytes32 dest, uint256 amount) external {
Expand Down Expand Up @@ -694,8 +727,8 @@ contract AvailBridgeTest is Test, MurkyBase {
bytes32 dataRoot = hashLeafPairs(blobRoot, bridgeRoot);
// set dataRoot at this point in the array
dataRoots[rand % dataRoots.length] = dataRoot;
bytes32 dataRootCommitment = getRoot(dataRoots);
bytes32[] memory dataRootProof = getProof(dataRoots, rand % dataRoots.length);
bytes32 dataRootCommitment = sha2merkle.getRoot(dataRoots);
bytes32[] memory dataRootProof = sha2merkle.getProof(dataRoots, rand % dataRoots.length);
vectorx.set(rangeHash, dataRootCommitment);
for (uint256 i = 0; i < leaves.length;) {
bytes32[] memory leafProof = getProof(leaves, i);
Expand Down Expand Up @@ -736,8 +769,8 @@ contract AvailBridgeTest is Test, MurkyBase {
bytes32 dataRoot = hashLeafPairs(blobRoot, bridgeRoot);
// set dataRoot at this point in the array
dataRoots[rand % dataRoots.length] = dataRoot;
bytes32 dataRootCommitment = getRoot(dataRoots);
bytes32[] memory dataRootProof = getProof(dataRoots, rand % dataRoots.length);
bytes32 dataRootCommitment = sha2merkle.getRoot(dataRoots);
bytes32[] memory dataRootProof = sha2merkle.getProof(dataRoots, rand % dataRoots.length);
vectorx.set(rangeHash, dataRootCommitment);
for (uint256 i = 0; i < leaves.length;) {
bytes32[] memory leafProof = getProof(leaves, i);
Expand All @@ -755,3 +788,9 @@ contract AvailBridgeTest is Test, MurkyBase {
return keccak256(abi.encode(left, right));
}
}

contract Sha2Merkle is MurkyBase {
function hashLeafPairs(bytes32 left, bytes32 right) public pure override returns (bytes32) {
return sha256(abi.encode(left, right));
}
}
180 changes: 180 additions & 0 deletions test/MerkleSha2Test.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.23;

import {MurkyBase} from "lib/murky/src/common/MurkyBase.sol";
import {Merkle} from "src/lib/Merkle.sol";
import {Vm, Test} from "forge-std/Test.sol";

contract MerkleTest is Test, MurkyBase {
MerkleUser merkleUser;

function setUp() public {
merkleUser = new MerkleUser();
}

/// @notice Hashing function for Murky
function hashLeafPairs(bytes32 left, bytes32 right) public pure override returns (bytes32) {
return sha256(abi.encode(left, right));
}

function test_checkMembershipSingleLeaf(bytes32 leaf, bytes32 wrongRoot, uint256 index) external {
vm.assume(index != 0 && wrongRoot != leaf);
bytes32 randomDataHash = sha256(abi.encode(leaf));
bytes32[] memory proof = new bytes32[](0);

// should return true for leaf and false for random hash
assertTrue(merkleUser.checkMembership(leaf, 0, leaf, proof));
// check with wrong leaf
assertFalse(merkleUser.checkMembership(randomDataHash, 0, leaf, proof));
// check with fixed wrong index
assertFalse(merkleUser.checkMembership(leaf, 1, leaf, proof));
// check with wrong index
assertFalse(merkleUser.checkMembership(leaf, index, leaf, proof));
// check with wrong leaf and wrong index
assertFalse(merkleUser.checkMembership(randomDataHash, index, leaf, proof));
// check with wrong index, wrong leaf and wrong root
assertFalse(merkleUser.checkMembership(randomDataHash, index, wrongRoot, proof));
}

function test_checkMembership(bytes32[] memory leaves, uint256 index, uint256 wrongIndex, bytes32 wrongRoot)
external
{
vm.assume(leaves.length > 1 && index < leaves.length && wrongIndex != index);
bytes32 root = getRoot(leaves);
vm.assume(wrongRoot != root);
bytes32[] memory proof = getProof(leaves, index);
bytes32 leaf = leaves[index];
bytes32 randomDataHash = sha256(abi.encode(leaf));

// should return true for leaf and false for random hash
assertTrue(merkleUser.checkMembership(leaf, index, root, proof));
// check with wrong leaf
assertFalse(merkleUser.checkMembership(randomDataHash, index, root, proof));
// check with fixed wrong index
assertFalse(merkleUser.checkMembership(leaf, leaves.length, root, proof));
// check with wrong index
assertFalse(merkleUser.checkMembership(leaf, wrongIndex, root, proof));
// check with wrong index and wrong leaf
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, root, proof));
// check with wrong index, wrong leaf and wrong root
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, wrongRoot, proof));
// check with wrong proof
proof[wrongIndex % proof.length] = keccak256(abi.encode(proof[wrongIndex % proof.length]));
assertFalse(merkleUser.checkMembership(leaf, index, root, proof));
}

function test_checkMembershipLargeTree(
bytes32[] memory leaves,
uint256 index,
uint256 wrongIndex,
bytes32 wrongRoot
) external {
vm.assume(leaves.length >= 128 && index < leaves.length && wrongIndex != index);
bytes32 root = getRoot(leaves);
vm.assume(wrongRoot != root);
bytes32[] memory proof = getProof(leaves, index);
bytes32 leaf = leaves[index];
bytes32 randomDataHash = sha256(abi.encode(leaf));

// should return true for leaf and false for random hash
assertTrue(merkleUser.checkMembership(leaf, index, root, proof));
// check with wrong leaf
assertFalse(merkleUser.checkMembership(randomDataHash, index, root, proof));
// check with fixed wrong index
assertFalse(merkleUser.checkMembership(leaf, leaves.length, root, proof));
// check with wrong index
assertFalse(merkleUser.checkMembership(leaf, wrongIndex, root, proof));
// check with wrong index and wrong leaf
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, root, proof));
// check with wrong index, wrong leaf and wrong root
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, wrongRoot, proof));
// check with wrong proof
proof[wrongIndex % proof.length] = keccak256(abi.encode(proof[wrongIndex % proof.length]));
assertFalse(merkleUser.checkMembership(leaf, index, root, proof));
}

function test_checkMembershipLargeTree2(
bytes32[256] memory c_leaves,
uint256 index,
uint256 wrongIndex,
bytes32 wrongRoot
) external {
vm.assume(index < c_leaves.length && wrongIndex != index);
bytes32[] memory leaves = new bytes32[](c_leaves.length);
for (uint256 i = 0; i < c_leaves.length;) {
leaves[i] = c_leaves[i];
unchecked {
++i;
}
}
bytes32 root = getRoot(leaves);
vm.assume(wrongRoot != root);
bytes32[] memory proof = getProof(leaves, index);
bytes32 leaf = leaves[index];
bytes32 randomDataHash = sha256(abi.encode(leaf));

// should return true for leaf and false for random hash
assertTrue(merkleUser.checkMembership(leaf, index, root, proof));
// check with wrong leaf
assertFalse(merkleUser.checkMembership(randomDataHash, index, root, proof));
// check with fixed wrong index
assertFalse(merkleUser.checkMembership(leaf, leaves.length, root, proof));
// check with wrong index
assertFalse(merkleUser.checkMembership(leaf, wrongIndex, root, proof));
// check with wrong index and wrong leaf
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, root, proof));
// check with wrong index, wrong leaf and wrong root
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, wrongRoot, proof));
// check with wrong proof
proof[wrongIndex % proof.length] = keccak256(abi.encode(proof[wrongIndex % proof.length]));
assertFalse(merkleUser.checkMembership(leaf, index, root, proof));
}

function test_checkMembershipHardcoded(uint256 wrongIndex, bytes32 wrongRoot) external {
vm.assume(wrongIndex != 65 && wrongRoot != 0x8eebcc756e5fd418501eff745f180ff16f151b82f823623b1b656bde0599fa15);
bytes32[8] memory c_proof = [bytes32(0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5),0x51f84e7279cdf6acb81af77aec64f618f71029b7d9c6d37c035c37134e517af2,0x69c8458dd62d27ea9abd40586ce53e5220d43b626c27f76468a57e94347f0d6b,0x5a021e65ea5c6b76469b68db28c7a390836e22c21c6f95cdef4d3408eb6b8050,0x676a0d0fab94c57be20667b57cd0800d7e5afc9b1c039a3c89995d527fbcf6c4,0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30,0xe51e1602448430542788cabb952ab87348561d146fe366b2525e581c0530c77e,0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c];
bytes32[] memory proof = new bytes32[](8);
for (uint256 i = 0; i < 8;) {
proof[i] = c_proof[i];
unchecked {
++i;
}
}
bytes32 randomDataHash = sha256(abi.encode(bytes32(0x2bd651601ffb95b9346c4867848e9621b53236baa08bfb29c9da28e7be7aeb23)));
assertTrue(merkleUser.checkMembershipMemory(bytes32(0x2bd651601ffb95b9346c4867848e9621b53236baa08bfb29c9da28e7be7aeb23), 65, bytes32(0x8eebcc756e5fd418501eff745f180ff16f151b82f823623b1b656bde0599fa15), proof));
// checked with fixed wrong index
assertFalse(merkleUser.checkMembershipMemory(bytes32(0x2bd651601ffb95b9346c4867848e9621b53236baa08bfb29c9da28e7be7aeb23), 66, bytes32(0x8eebcc756e5fd418501eff745f180ff16f151b82f823623b1b656bde0599fa15), proof));
// check with fuzzed wrong index
assertFalse(merkleUser.checkMembershipMemory(bytes32(0x2bd651601ffb95b9346c4867848e9621b53236baa08bfb29c9da28e7be7aeb23), wrongIndex, bytes32(0x8eebcc756e5fd418501eff745f180ff16f151b82f823623b1b656bde0599fa15), proof));
// check with fuzzed leaf
assertFalse(merkleUser.checkMembershipMemory(randomDataHash, 65, bytes32(0x8eebcc756e5fd418501eff745f180ff16f151b82f823623b1b656bde0599fa15), proof));
// check with fuzzed root
assertFalse(merkleUser.checkMembershipMemory(bytes32(0x2bd651601ffb95b9346c4867848e9621b53236baa08bfb29c9da28e7be7aeb23), 65, wrongRoot, proof));
// check with fuzzed root and leaf
assertFalse(merkleUser.checkMembershipMemory(randomDataHash, 65, wrongRoot, proof));
proof[wrongIndex % proof.length] = keccak256(abi.encode(proof[wrongIndex % proof.length]));
assertFalse(merkleUser.checkMembershipMemory(bytes32(0x2bd651601ffb95b9346c4867848e9621b53236baa08bfb29c9da28e7be7aeb23), 65, bytes32(0x8eebcc756e5fd418501eff745f180ff16f151b82f823623b1b656bde0599fa15), proof));
}
}

/*//////////////////////////////////////////////////////////////////////////
MOCKS
//////////////////////////////////////////////////////////////////////////*/

contract MerkleUser {
function checkMembership(bytes32 leaf, uint256 index, bytes32 rootHash, bytes32[] calldata proof)
external
view
returns (bool)
{
return Merkle.verifySha2(proof, rootHash, index, leaf);
}

function checkMembershipMemory(bytes32 leaf, uint256 index, bytes32 rootHash, bytes32[] memory proof)
external
view
returns (bool)
{
return Merkle.verifySha2Memory(proof, rootHash, index, leaf);
}
}
9 changes: 9 additions & 0 deletions test/MerkleTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ contract MerkleTest is Test, MurkyBase {
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, root, proof));
// check with wrong index, wrong leaf and wrong root
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, wrongRoot, proof));
// check with wrong proof
proof[wrongIndex % proof.length] = keccak256(abi.encode(proof[wrongIndex % proof.length]));
assertFalse(merkleUser.checkMembership(leaf, index, root, proof));
}

function test_checkMembershipLargeTree(
Expand Down Expand Up @@ -85,6 +88,9 @@ contract MerkleTest is Test, MurkyBase {
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, root, proof));
// check with wrong index, wrong leaf and wrong root
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, wrongRoot, proof));
// check with wrong proof
proof[wrongIndex % proof.length] = keccak256(abi.encode(proof[wrongIndex % proof.length]));
assertFalse(merkleUser.checkMembership(leaf, index, root, proof));
}

function test_checkMembershipLargeTree2(
Expand Down Expand Up @@ -119,6 +125,9 @@ contract MerkleTest is Test, MurkyBase {
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, root, proof));
// check with wrong index, wrong leaf and wrong root
assertFalse(merkleUser.checkMembership(randomDataHash, wrongIndex, wrongRoot, proof));
// check with wrong proof
proof[wrongIndex % proof.length] = keccak256(abi.encode(proof[wrongIndex % proof.length]));
assertFalse(merkleUser.checkMembership(leaf, index, root, proof));
}
}

Expand Down

0 comments on commit 1e942ec

Please sign in to comment.