Skip to content

Commit

Permalink
fix(protocol): fix LibTrieProof.verifyMerkleProof by RLP-encoding the…
Browse files Browse the repository at this point in the history
… byte32 value first (#16018)
  • Loading branch information
dantaik committed Feb 23, 2024
1 parent 0496ff4 commit e1f21c1
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 8 deletions.
16 changes: 9 additions & 7 deletions packages/protocol/contracts/libs/LibTrieProof.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@

pragma solidity ^0.8.24;

import { RLPReader } from "../thirdparty/optimism/rlp/RLPReader.sol";
import { SecureMerkleTrie } from "../thirdparty/optimism/trie/SecureMerkleTrie.sol";
import "../thirdparty/optimism/rlp/RLPReader.sol";
import "../thirdparty/optimism/rlp/RLPWriter.sol";
import "../thirdparty/optimism/trie/SecureMerkleTrie.sol";

/**
* @title LibTrieProof
*/
/// @title LibTrieProof
library LibTrieProof {
// The consensus format representing account is RLP encoded in the
// following order: nonce, balance, storageHash, codeHash.
Expand All @@ -35,7 +34,7 @@ library LibTrieProof {
bytes32 rootHash,
address addr,
bytes32 slot,
bytes memory value,
bytes32 value,
bytes[] memory accountProof,
bytes[] memory storageProof
)
Expand All @@ -58,7 +57,10 @@ library LibTrieProof {
}

bool verified = SecureMerkleTrie.verifyInclusionProof(
bytes.concat(slot), value, storageProof, bytes32(storageRoot)
bytes.concat(slot),
RLPWriter.writeUint(uint256(value)),
storageProof,
bytes32(storageRoot)
);

if (!verified) revert LTP_INVALID_INCLUSION_PROOF();
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/signal/SignalService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ contract SignalService is EssentialContract, ISignalService {
hop.rootHash,
signalService,
getSignalSlot(chainId, app, signal),
bytes.concat(value),
value,
hop.accountProof,
hop.storageProof
);
Expand Down
163 changes: 163 additions & 0 deletions packages/protocol/contracts/thirdparty/optimism/rlp/RLPWriter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @custom:attribution https://github.com/bakaoh/solidity-rlp-encode
/// @title RLPWriter
/// @author RLPWriter is a library for encoding Solidity types to RLP bytes. Adapted from Bakaoh's
/// RLPEncode library (https://github.com/bakaoh/solidity-rlp-encode) with minor
/// modifications to improve legibility.
library RLPWriter {
/// @notice RLP encodes a byte string.
/// @param _in The byte string to encode.
/// @return out_ The RLP encoded string in bytes.
function writeBytes(bytes memory _in) internal pure returns (bytes memory out_) {
if (_in.length == 1 && uint8(_in[0]) < 128) {
out_ = _in;
} else {
out_ = abi.encodePacked(_writeLength(_in.length, 128), _in);
}
}

/// @notice RLP encodes a list of RLP encoded byte byte strings.
/// @param _in The list of RLP encoded byte strings.
/// @return list_ The RLP encoded list of items in bytes.
function writeList(bytes[] memory _in) internal pure returns (bytes memory list_) {
list_ = _flatten(_in);
list_ = abi.encodePacked(_writeLength(list_.length, 192), list_);
}

/// @notice RLP encodes a string.
/// @param _in The string to encode.
/// @return out_ The RLP encoded string in bytes.
function writeString(string memory _in) internal pure returns (bytes memory out_) {
out_ = writeBytes(bytes(_in));
}

/// @notice RLP encodes an address.
/// @param _in The address to encode.
/// @return out_ The RLP encoded address in bytes.
function writeAddress(address _in) internal pure returns (bytes memory out_) {
out_ = writeBytes(abi.encodePacked(_in));
}

/// @notice RLP encodes a uint.
/// @param _in The uint256 to encode.
/// @return out_ The RLP encoded uint256 in bytes.
function writeUint(uint256 _in) internal pure returns (bytes memory out_) {
out_ = writeBytes(_toBinary(_in));
}

/// @notice RLP encodes a bool.
/// @param _in The bool to encode.
/// @return out_ The RLP encoded bool in bytes.
function writeBool(bool _in) internal pure returns (bytes memory out_) {
out_ = new bytes(1);
out_[0] = (_in ? bytes1(0x01) : bytes1(0x80));
}

/// @notice Encode the first byte and then the `len` in binary form if `length` is more than 55.
/// @param _len The length of the string or the payload.
/// @param _offset 128 if item is string, 192 if item is list.
/// @return out_ RLP encoded bytes.
function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory out_) {
if (_len < 56) {
out_ = new bytes(1);
out_[0] = bytes1(uint8(_len) + uint8(_offset));
} else {
uint256 lenLen;
uint256 i = 1;
while (_len / i != 0) {
lenLen++;
i *= 256;
}

out_ = new bytes(lenLen + 1);
out_[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55);
for (i = 1; i <= lenLen; i++) {
out_[i] = bytes1(uint8((_len / (256 ** (lenLen - i))) % 256));
}
}
}

/// @notice Encode integer in big endian binary form with no leading zeroes.
/// @param _x The integer to encode.
/// @return out_ RLP encoded bytes.
function _toBinary(uint256 _x) private pure returns (bytes memory out_) {
bytes memory b = abi.encodePacked(_x);

uint256 i = 0;
for (; i < 32; i++) {
if (b[i] != 0) {
break;
}
}

out_ = new bytes(32 - i);
for (uint256 j = 0; j < out_.length; j++) {
out_[j] = b[i++];
}
}

/// @custom:attribution https://github.com/Arachnid/solidity-stringutils
/// @notice Copies a piece of memory to another location.
/// @param _dest Destination location.
/// @param _src Source location.
/// @param _len Length of memory to copy.
function _memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure {
uint256 dest = _dest;
uint256 src = _src;
uint256 len = _len;

for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}

uint256 mask;
unchecked {
mask = 256 ** (32 - len) - 1;
}
assembly {
let srcpart := and(mload(src), not(mask))
let destpart := and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}

/// @custom:attribution https://github.com/sammayo/solidity-rlp-encoder
/// @notice Flattens a list of byte strings into one byte string.
/// @param _list List of byte strings to flatten.
/// @return out_ The flattened byte string.
function _flatten(bytes[] memory _list) private pure returns (bytes memory out_) {
if (_list.length == 0) {
return new bytes(0);
}

uint256 len;
uint256 i = 0;
for (; i < _list.length; i++) {
len += _list[i].length;
}

out_ = new bytes(len);
uint256 flattenedPtr;
assembly {
flattenedPtr := add(out_, 0x20)
}

for (i = 0; i < _list.length; i++) {
bytes memory item = _list[i];

uint256 listPtr;
assembly {
listPtr := add(item, 0x20)
}

_memcpy(flattenedPtr, listPtr, item.length);
flattenedPtr += _list[i].length;
}
}
}
40 changes: 40 additions & 0 deletions packages/protocol/test/libs/LibTrieProof.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,44 @@ contract TestLibTrieProof is TaikoTest {

assertEq(storageRoot2, storageRoot);
}

function test_jeff_slot_value_not_1() public {
// signal service address on internal testnet l1
address addr = 0x83e383dec6E3C2CD167E3bF6aA8c36F0e55Ad910;
// signalService.getStorageSlot(event.srcchainid, event.Raw.Address [the bridge address on
// L1], event.MsgHash)
bytes32 slot = 0x07bfd8daaa640fc5f384ee9b5c1f8984e3075701525ff86aa1c6fe62b09fc2bc;
// block.Root() of the block used to get the proof
bytes32 stateRoot = 0x0883e3ee355161a08c1696409375ea33f4bb53a9121bc90bc4d69c4f285382aa;

bytes[] memory accountProof = new bytes[](3);
// eth_getProof responds some bytes
accountProof[0] =
hex"f90211a0f776494ecffe03ad2af2426ee4fb5ae66c012c03ea3955d5b40a52fe7c6b8df6a0ef4a67411e4accae9bbb05bbcc17d036432de4e3ee0b0d02a04ee6a67db9c1a5a06c4c8f2b3206553ec83f1081a6742edff2f7b7830d3ba4166872c4f3f32bae0ba0aa2e8d96490616498fa142ba7c6a628e2839b9b182c9995992ecf1c1d346784fa0f8d7262d0abdf5d084338a640322c4dd45b1484a630331922afa60256917058aa0e49099e467972084cd70025874c6ae0a6841159aec02f39384de2d9ae7460986a02ad2a355fe840ae1bfe7843616d6bb027c1fd478918cc956e0ca7b61ec0044d0a061ec8e714d951772d74765ae997bd25f3982ea8a0c4dda8baa1e47adbd5a3975a039e5f7e8298126da1e7d6f42b1f20846d68ff40dc857b0dd251d2791c0345589a065f4d771a35d862a10b2a2da6d7d6c1e082cd59280cbab1c8e6aaf89069e72dca07851c1d2a801228a1adb24b61a5b4e1d4e12c5043a0af1b1790cd79b281c43a0a031acf4e7bd8cfb52c19cb512fb2578f416cbcf28cd3b4387d7b6107a17d4ff31a09e8def26ad9cf07bfbba2cf4a3fad0fe4075030b9a67acbecbfa5b7bdc8620cca07174a59e7f5b71a1a20f8c4a232100c1303378a7eb5dd523904d5305e20ee7dca0199ae8cd6d2b09717d59b5106ef31b451b30d777296b4afb43db9327117471eca09193465aefe93e209d05f892f77002dd5c2e74d4eed502ebae3acb7db1e33acc80";
accountProof[1] =
hex"f8f1a0b4df476cf7a24306eb6b96d0a8fe974b88e035d93e6716cf56e3f6bacee15c88808080a09265834cbd374b79cc4f97cdab16770f4434bce18ff9724970bff1c91d54f2f1a0e13b8847e8cfc7fd465a61a64b732d940d8f87ed65828e1004e46fb4256aba48a05a7b35dc9c135fc4df95c4b02179719a3505d201fb590212cf41c31eae1ceb6980a07c46a7878ac08b149792973e8a17982311f3c997624aed0fde76ba45d9a1ba41a0ebfe5b284e549d79c976f878d93266d238d9ea3254d0ef9c199d2c5a12067de48080a05d5e60bc5b531718ba65199b06b68087c640472ef4b3afb7e4b857280edbf2e580808080";
accountProof[2] =
hex"f869a02024e130452851ed161bc592f5949e62d0e9b45d447bdfeef098148a98e92454b846f8440180a09a86a68eec0b73092dc0028cb4afb05332e85df8af2c9b0f67bbb750a4ada35da0dc679fd48cf611aa38e906adc93928c5f8f6fae534978ea2fa4f5935f5ed1b2c";

bytes[] memory storageProof = new bytes[](5);
storageProof[0] =
hex"f90211a062880ba06fb396ad4e16f01d22ca7a1ae677e3fe428817928db30e2cae96b97ca0aa57a805600be2304ffdd527bd3dc9e91161233dc59afb71c1aab542eafe70caa03bc6c86a4c6b49305b95b3812a162f7e6bec891b287ef291e9c468071ef0c4ada08ac85ec9872d9e6f5b4c6e72154158df88d44163457cf0bbf2134e385c871a4ea0f35f3c83fbd9da738bbfea1bc87b073d3b64abdecb6294b61cf3eb535eabefdea0905c9b0e1755d389f306f026ccb71f8f7e54cd68720cc1c76682504eeb7bceaea06867477d77649657f698380e66844a7ed14818e8aad9f4ac5748963ede640e0aa0caa272deb3227cb8b0765a718ac85bbc7ee93a04bc0a2cb9c5509c9394470eb3a01689508cc26d870b0183c13bee39ed5342bf32464813f872d7ea4e5bc5f79845a0b578886ee673adcdf7b219cd13d9d641f8e15dd9ec6c9345435e7968bc6bcc82a0fbd86d32d6c60089268373be401211c3b606efeb550659b9e43458007dce2eb6a035d73d30ad77c85ef247ab8473f71314a9d175c1e9a0ce73a78a103a3766f54ca0c08386bed5af43c7cadb42d9df7ba3b94886f483e8a2e66aaef7391a15ab51cba002ce1e689b6193a6d3a8c822b0b0076dfdf732fd045a4dc122ec3879fe3de70ea0db27c27a802c40acbde50e5c357e5327a91242e6550fe461eec10ac136ddddcea0ad6d871b4c62042c68f0ecfdb986a24ea7d850563bbd3d27f6916bc3ddd170a480";
storageProof[1] =
hex"f90211a05c9b8f83e3c03e07271225e2ebce1cbe9e7db3b14d2724ec6efe9cf8fce6fc06a0dbd4cd41e027eefe208271111ea3e99cb39b4645e7e166d084d62f427a9313ada0cc65078735257beecceb9c74985901fa16e8e9fb228ce6aaa62aedb282a1795fa012f4c2ae88c8f0396048da6a095d0fa2c8b86398651cd37a72d68d88d25ff19ea037cda349771733bba3681eda450fee72f5e3dcbb6b8f2acf4a2bd145d0bfad6da0ef1359be1a9f658e580c968b92029dbf62ce7a56932c10acce28b25bf7206665a037d9790673a2be78a1555bee7d37ab10d1b8d94d1f12bb011b7cc7257bf13004a0dd9b4774c203afaaeb098ab623ce32f1df6f8ff0ac1bbcb78e358b7a242cd19aa0dde51d1f37baae98d02b2e35c81030f17407fc31304ab72cf999bb2c7e8abff3a0f8672c12a366e074d6f42c2c7b0c5cc010bc4ec703c65e3b58c4fbfee18e89c2a057ba424e40bd1c6a8e7d494703f392e834d8ca7696759e2c0216ebd18bcf662fa01eafd299e8a772c056e6919eeb67bf7e1098129855234e942cfc18aaf364d39ea0df6b60bdf553e1511f445fdcf1fb7aadc23bf390eeb11145c9e2742552c2ed6da02e79f5afb8c177c40737cea4aed39fe3c0269f5a8989e02c07a0135594b83bb1a035535dac85afa0e4848c0186cc8687bc7d2de0215b97ea43e65c8e4da0a52517a08ce682327123eb41b4d49ef283ffe11d1da1b9d7163e892b775a63dd31072ec080";
storageProof[2] =
hex"f90211a0f40a5ee1ce9d240682f7f7af3c5950bba267b4b78e46c954d2b0f8d77da43a23a058e05fe5ccced350c75f8d4c495cd3db51c2d6215573df35178e32ddd7458ff9a06ce4c63e3b3d650248fe6b8d4efb02f5e460406a006ce732318cd5907a54625ba0b0c5eb8747f10eba341df8f0be99a8433654a4621cd0fcbd6ea24f6a5c3d3a82a0e98d81a5b3bc5f25ea24671e6b947bf2aa68c093d7c3bc946b6e71beb9f46df3a00b51ebdd2337cce7ae87e81412c668d305aefcfe25bbb46cc80b48520844adafa0312ebc38d28bbbcf414ed6c48cfb94d26be82bebf06795ca1ed8f088e340210fa066babdf1ddd352c84662fc3ba586a8dc1048bb4d99eea14b62cb389bff71e6fda08e9bde33f5da83fd3fbdc2c1358c67e321fccce1633039c44a689231a38c3a5ca0cc1d1601726ab0b999cf08fe2a87c29e480c97cb501601591c9a9cf0a0ab63dea0a6547a4b3a8647df43e5b81c5224284d4bf19bab142ed58fb28f5a7ff768ee2ca0b804617fff71909c150ae899e64c3cc07c73ea0b92e32a6033ce84a94a78cea9a0a207d46501ca64b8e3514b071d220f38eb545347cb1837f7acbf801d4e3f20fca0083b56370fe68526c7a36714ddb3e98c0ffc8c3d653e254de50e81a6548ddf03a057777667c151137326519774b769a478fa2f1a95df75e70f8972de7a50e25613a061aa71f8b8ea84ea22ed462f83cda58656815a941b458247f14f26b9f7104e1980";
storageProof[3] =
hex"f9013180a0bc1d276fab8bdca46e499a62df0598844a81dd822e5873a57cda01191549b1fca02e7323a53a6103bb95f23070328af116852a340bb8ce85780414f0ee46930f52a0632f43d1fd3bdd8d2e603fca74d2af51c60c1d23fe64cddb02a983779a4bacf1a011d0b3e5a1f1a3dd42b6f6ac95bd34a8044a25a5e99e33cebf730333438047438080a09937213f98b982f56a05cf65bf9113618da12308da8bf5a95c715456d0b91a0ca013844416bff2fdea9d9419e88f58a78728c14bd4e7ba678545ad33224bbea4f080a0de8762bd1bb07b389404f9d925f3bff2f6ea8675b8e89d089cfad9e21f2384b180a0e2be312f35cc32b95912918aa41fbd324a21b7a4acb39fc349b1e4768ca56e9080a052b7d13546e19995f0a3a422a980b8ff101be9494a68562c1e5266933ed25f7b8080";
storageProof[4] =
hex"f8429f20228b7509ed753c777c7f96e7105bab2dc7c2ceacb0ff8611edbd2189f583a1a00a793aea9233fc987a8f710991f16b576e23b67a15874faab4e18192aad8fd17";

LibTrieProof.verifyMerkleProof(
stateRoot,
addr,
slot,
hex"0a793aea9233fc987a8f710991f16b576e23b67a15874faab4e18192aad8fd17",
accountProof,
storageProof
);
}
}

0 comments on commit e1f21c1

Please sign in to comment.