diff --git a/packages/protocol/contracts/libs/LibTrieProof.sol b/packages/protocol/contracts/libs/LibTrieProof.sol index 96dab809e5..b32f233768 100644 --- a/packages/protocol/contracts/libs/LibTrieProof.sol +++ b/packages/protocol/contracts/libs/LibTrieProof.sol @@ -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. @@ -35,7 +34,7 @@ library LibTrieProof { bytes32 rootHash, address addr, bytes32 slot, - bytes memory value, + bytes32 value, bytes[] memory accountProof, bytes[] memory storageProof ) @@ -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(); diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 1d3bd1c56b..2199cace77 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -239,7 +239,7 @@ contract SignalService is EssentialContract, ISignalService { hop.rootHash, signalService, getSignalSlot(chainId, app, signal), - bytes.concat(value), + value, hop.accountProof, hop.storageProof ); diff --git a/packages/protocol/contracts/thirdparty/optimism/rlp/RLPWriter.sol b/packages/protocol/contracts/thirdparty/optimism/rlp/RLPWriter.sol new file mode 100644 index 0000000000..85a80ba4aa --- /dev/null +++ b/packages/protocol/contracts/thirdparty/optimism/rlp/RLPWriter.sol @@ -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; + } + } +} diff --git a/packages/protocol/test/libs/LibTrieProof.t.sol b/packages/protocol/test/libs/LibTrieProof.t.sol index 298e57d20b..693a9896bd 100644 --- a/packages/protocol/test/libs/LibTrieProof.t.sol +++ b/packages/protocol/test/libs/LibTrieProof.t.sol @@ -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 + ); + } }