Skip to content

Commit

Permalink
fix(protocol): prove signal with full merkle proofs against block sta…
Browse files Browse the repository at this point in the history
…te roots (#15683)

Co-authored-by: Daniel Wang <dan@taiko.xyz>
Co-authored-by: Daniel Wang <99078276+dantaik@users.noreply.github.com>
Co-authored-by: Keszey Dániel <keszeyd@MacBook-Pro.local>
Co-authored-by: Brechtpd <Brechtp.devos@gmail.com>
  • Loading branch information
5 people committed Feb 11, 2024
1 parent 603f24b commit e2f4bc2
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 5 deletions.
60 changes: 60 additions & 0 deletions packages/protocol/contracts/libs/LibTrieProof.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
// _____ _ _ _ _
// |_ _|_ _(_) |_____ | | __ _| |__ ___
// | |/ _` | | / / _ \ | |__/ _` | '_ (_-<
// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/

pragma solidity ^0.8.24;

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

/**
* @title LibTrieProof
*/
library LibTrieProof {
// The consensus format representing account is RLP encoded in the
// following order: nonce, balance, storageHash, codeHash.
uint256 private constant ACCOUNT_FIELD_INDEX_STORAGE_HASH = 2;

error LTP_INVALID_ACCOUNT_PROOF();

/**
* Verifies that the value of a slot in the storage of an account is value.
*
* @param stateRoot The merkle root of state tree.
* @param addr The address of contract.
* @param slot The slot in the contract.
* @param value The value to be verified.
* @param mkproof The proof obtained by encoding storage proof.
* @return verified The verification result.
*/
function verifyFullMerkleProof(
bytes32 stateRoot,
address addr,
bytes32 slot,
bytes32 value,
bytes memory mkproof
)
internal
pure
returns (bool verified)
{
(bytes[] memory accountProof, bytes[] memory storageProof) =
abi.decode(mkproof, (bytes[], bytes[]));

bytes memory rlpAccount =
SecureMerkleTrie.get(abi.encodePacked(addr), accountProof, stateRoot);

if (rlpAccount.length == 0) revert LTP_INVALID_ACCOUNT_PROOF();

RLPReader.RLPItem[] memory accountState = RLPReader.readList(rlpAccount);

bytes memory storageRoot =
RLPReader.readBytes(accountState[ACCOUNT_FIELD_INDEX_STORAGE_HASH]);

verified = SecureMerkleTrie.verifyInclusionProof(
bytes.concat(slot), bytes.concat(value), storageProof, bytes32(storageRoot)
);
}
}
15 changes: 10 additions & 5 deletions packages/protocol/contracts/signal/SignalService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pragma solidity 0.8.24;
import "lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol";
import "../common/EssentialContract.sol";
import "../common/ICrossChainSync.sol";
import "../libs/LibTrieProof.sol";
import "../thirdparty/optimism/trie/SecureMerkleTrie.sol";
import "../thirdparty/optimism/rlp/RLPReader.sol";
import "./IHopRelayRegistry.sol";
Expand Down Expand Up @@ -57,6 +58,7 @@ contract SignalService is EssentialContract, ISignalService {
error SS_INVALID_PARAMS();
error SS_INVALID_PROOF();
error SS_INVALID_APP();
error SS_INVALID_HOP_PROOF();
error SS_INVALID_RELAY();
error SS_INVALID_SIGNAL();
error SS_INVALID_STATE_ROOT();
Expand Down Expand Up @@ -121,6 +123,9 @@ contract SignalService is EssentialContract, ISignalService {
hrr = IHopRelayRegistry(resolve("hop_relay_registry", false));
}

ICrossChainSync ccs = ICrossChainSync(resolve("taiko", false));
bytes32 stateRoot = ccs.getSyncedSnippet(p.height).stateRoot;

// If a signal is sent from chainA -> chainB -> chainC (this chain), we verify the proofs in
// the following order:
// 1. using chainC's latest parent's stateRoot to verify that chainB's TaikoL1/TaikoL2
Expand All @@ -130,6 +135,7 @@ contract SignalService is EssentialContract, ISignalService {
// We always verify the proofs in the reversed order (top to bottom).
for (uint256 i; i < p.hops.length; ++i) {
Hop memory hop = p.hops[i];
if (hop.stateRoot == stateRoot) revert SS_INVALID_HOP_PROOF();

if (!hrr.isRelayRegistered(_srcChainId, hop.chainId, hop.relay)) {
revert SS_INVALID_RELAY();
Expand All @@ -142,9 +148,6 @@ contract SignalService is EssentialContract, ISignalService {
_srcSignal = hop.stateRoot;
}

ICrossChainSync ccs = ICrossChainSync(resolve("taiko", false));
bytes32 stateRoot = ccs.getSyncedSnippet(p.height).stateRoot;

verifyMerkleProof(stateRoot, _srcChainId, _srcApp, _srcSignal, p.merkleProof);
return true;
}
Expand All @@ -163,9 +166,11 @@ contract SignalService is EssentialContract, ISignalService {
if (stateRoot == 0) revert SS_INVALID_STATE_ROOT();
if (merkleProof.length == 0) revert SS_INVALID_PROOF();

bool verified;
address signalService = resolve(srcChainId, "signal_service", false);

// TODO(dani): implement this please
bytes32 slot = getSignalSlot(srcChainId, srcApp, srcSignal);
bool verified =
LibTrieProof.verifyFullMerkleProof(stateRoot, signalService, slot, hex"01", merkleProof);

if (!verified) revert SS_INVALID_PROOF();
}
Expand Down
63 changes: 63 additions & 0 deletions packages/protocol/test/libs/LibTrieProof.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../TaikoTest.sol";
import "../../contracts/libs/LibTrieProof.sol";

contract TestVerifyFullMerkleProof is TaikoTest {
function test_verifyFullMerkleProof() public {
// Not needed for now, but leave it as is.
//uint64 chainId = 11_155_111; // Created the proofs on a deployed Sepolia
// contract, this is why this chainId.
// Actually this is the messageHash i sent (obviously just a made up bytes32!)
// Not needed for now, but leave it as is.
//bytes32 signalSent = 0x21761f7cd1af3972774272b39a0f4602dbcd418325cddb14e156b4bb073d52a8;
// //Actually a messageHash
// This one is the "sender app" aka the source bridge but i mocked it for now to be an EOA
// (for slot calculation)
address contractWhichStoresValue1AtSlot = 0x17DF3c450D1dC61558ecA7B10e4bBC8ddcdB1f28;
// This is the slot i queried the eth_getProof on Sepolia for blockheight: 0x5000B5
bytes32 slotStoredAtTheApp =
0xfa2ef1bab164a0522c2c110bbea1a54ac6399d3ba24437480c29947143a5402e;
// This is the worldStateRoot at blockheight: 0x5000B5 (Sepolia!)
bytes32 worldStateRoot = 0x90c5f343ed98545ad5ad4e840492e1008218c0ea92f8fd74a826aaf4c477a3fe;
// This is the worldStateRoot just RLP encoded with
// https://toolkit.abdk.consulting/ethereum#key-to-address,rlp
// Not needed for now but leave it as is
//bytes memory worldStateRootRLPEncoded =
// hex"e1a090c5f343ed98545ad5ad4e840492e1008218c0ea92f8fd74a826aaf4c477a3fe";

bytes[] memory accountProof = new bytes[](8);
// eth_getProof responds some bytes
accountProof[0] =
hex"f90211a01564179a33d2ff80c3618ab74314aad556cb7652c2d51f7a3aff3a787ecdb29ea03bd6509773c4530091e420ef4921beef2be63ad2429b6e12ba5d7a7f9a6da270a090be43dc028d9eb13a63b5799782f30928e6882b328ab845f320e1eb138d7962a026633ae06c23cd30c5e1cc91cfd7be03652cb9c853fa42538803c8eb631e920fa0711616cf812d7ed5f308b2822bffc5038edac8abedb0f7339c9933387472b495a09266ec8cec3b85e1ea194e79c73a4b2ecc122abffa356b575c31af7e9d3b6921a0a16f884fe19eeaefb8eeb2f017f6ecb24ee5822d5b3bc4be98bcfe374a910761a07973653d008e00cebcc3794f8221fa530d9a18ce77331a3e473e8dc3735943d9a0bc31a4cd1ab5c4ff0bd12f6ffc12f5ef19897df9addc478aa06b223d3087ef9da059aec5d8753109a5893d67ec742b2b51703ecf5920e4947007a6c77265ac41f9a09c0af28e7444278ab0b478870fa98c9f37e9d19681023f8e039f3ce67492e707a008395a6c64c1c76a1e655a28419504519e9957251e83501eef6093971f937c19a04f2a4e14fe7bbf05ea2b75de2bfe95fd33d2c86ff554293ebdf09da7da579cd5a0816ed8d0b69fce577f3c70eceebe8634387716d91a1edc4b3cc9d5feea31ba94a0a270d82ecded0d0712336be6e078794e533721b40698c9b5165b6c420a8a8545a0bcfbc71cc6ee796c58ef28f0d0e453fc08f6326c1891b51512e19af5f4842ffe80";
accountProof[1] =
hex"f90211a0c441ffc1fe3662f328ca995e9cad90cecf26ad98028846b7a1bdb6fe60aa8419a058336ba27c2e0feb5bd8bdeea62ebe54fc4cc05e2376f69e1c721b1cd9098498a07b847e55c107bd976b5984ea7d97d39c68573ae74afd82d4ab9d857cb44e6f82a05f5c1810a0178191798266d171f2f9f9711f5116c5dc4ecccbc29642a24b1873a01da99f321b183e1a2068ac0fd57dccbd637b3fa91b060a80c836ce1ea79fcae0a0f8cf0f8299146bf9d1ec0b8932ddc23ec29cd61daf5cdbe704a5c90f31f0d60ca0407441445132919ff986f169f24e82ce53fe15a444a674ab1604f0924efdd51da0313be5d2f630a30f414aed4012bbe081e55853a55209a3a83045b62f10d74ad2a0ab4a1665cc9ac1dc2f87d98c55e9b893acf84e592635d359a83d0e14181a933da0e4ed4ac12b93b12016ae1c0fd62e6a15b01e617779e8611c27ce22b0b5e78d68a0852127e581d41ec5c58d6a5ccb1b0e7ae90553575b0d9ffb2ac847515671c80fa01ec12bc1938419bebfdc17ed6d10d456fe31202f2dd1534e9bc6b78c95e2b358a000e5a67fdc3cc307a3244b77ddb504867a2cfb0cc9a614a47452d8cfad011af4a0a222a375f0b35461137d84e1beb8cc0e842ea7194784286b0426d80e04ce0ee6a04dc890b4343e041888128a5185860e039b82636d328c52f630007339d445fc0aa00619a2c3c30245bef0821c9f85bc3b79ee2b7f895a4de79ad57f42add8f8cc8d80";
accountProof[2] =
hex"f90211a02b8838e8f55f133d202a53a8b8d9243d936f43b636e8edf4cead5028150ba4bda0199e1c6b8025bdb25824acf47c79f2cb46e9912eea4c7951b1841e6831361cf7a03f9cf217c04256b4b8d32218e998e34a2bb16c17aec758811d9cf2b2510a50a0a06a04facdefe440e0d78718bee9ac31d6693d197ea44bb324156d3bcbdc601763a0d30849e23529944c1763e9de2d3ae6d229b4310bcb2415dec58e5375d04d23efa0a4013ee6afb6af932f3e2d126fbe53f656be59f27df7c700099c91e2a65e2bd3a0233ef3fbc0234cbcb9f76ad4ece6c2f50e480e873226f8f8cef2851d10a7ee33a0b5d1dc30ea1bad8ac548ffd9ac408eabb033f2fcf8d3c1780da97180d89f5997a0a4192803a6131538e307b331f9f842b2302d17492f37537a56a63c36eb01b408a06087dc2726b6a21a4d06d7d73b988925cf3ba2c898d442024468a52f8a4010a8a0bfa291361ed2810c2f8ab0eee7fd894e5915e8ad2d12739d4f380466dc3c7f55a08becd8c73f80d6073df6cceb89e796e669744aa852ce13c7b367fe60c2b34d04a012c2d1a15f1bf98ad00cd0eb061a42eaaf0015a5f4c41a894f249c9a3afc3560a0fef566b4ad9fa37ee9ae59f880f4bb38958c8a62f26ae3668f1dbda3e9fd3b05a0702301629db7bdddcc00dbd348adbcffc890bb1c80fe6ef7a996363adc89770da068b975b9b36fc59378c54d5dd868e53ea8a59248da1dc6c4576d9f781913cd7880";
accountProof[3] =
hex"f90211a0d98672296e62b8aba7e20671ca0476e58efc4ad0c40480e9ced84696bbfa56a8a0d2c53384b6e3b1b3088fcd412af98f85a52b882914a35adb2aa56cd3484997ada09015daa671fe301f656b0ed4f4f2172d683dae40113979a3f710145106031767a00d055b96c4f5804b7d3e4b8159d72bd84ca9e8b4547b504fe2a9933b4359a2d4a0d2560c909a3b2cc465480d1815b06c613d7a2ae7f6c1f00027cf4a57cdc282d7a0c29b7764bdd0df90b1fe350b488ffe69c3c4202f1712c77aab4fa2e0103ae673a06ca16b3f2b111bb14b8b52d36b85b7df2dada9796c2dd0b13f293149cb3e38fba0219ab02566d6474fa0621669616f97f7be79e1cb5886bc75be99be4984bd978ca0d103757173ada974a0f4996e3191ebce8f3ddc61c9db536ff7389c010b03e536a0f65268a6de255bde8ea97282e9f7a858b15b5bde5b9cde5b654c19cd909231f8a06c3ee37ad1762f6fb9da2b3efec779a61036f76b4c97b3ba8ea1339cfc76393ea09b28c2f98e0eedafa97d733d468c1d7a505f8d6b544bbbaf990cdd4781ee765ea0eeda6a2e3dd383ab103d9c14656df77a6b05a566338745325edb03ab344c7404a0ca35c2b85039e6a1fce323649d015876c13702faa510cee4f4a7efaac5456185a00a0e83158f3c174485b6b6ff64f793d0bd578215a685acc86b002bcc99c99f9ba0fd0d0c3d0f27d3e7bc63462df509157e56d03e310ee26a60a4f7dfa87a9e0c2080";
accountProof[4] =
hex"f90211a0f69c09b524b3e7a97fe238809583b7b68f41443a76c0f5d689b371c1df513fc1a0d0f0bcbbd6dae7bd8dd9ba196a34e81c7cc9fea6ac4894a69ec2b5b7bdae2f01a081975fbe995c9a7a55b8e846a70be0d6695c8529b7f564448bf64f2ef25d8c5ca02cd43f9e6fafbe13cc8e845d8c19b51ddc62610119948c635c7e1c9c9998dc22a03b4e6dfe0edcdfbb0b8252f97cc6d2a11f25c571b093cc53aca88d38a94bf827a0e9b70cbde13f142578dba52a5a862122a2332e15c148edda2b9b419528ee9231a014ff7fc46869c016d4ac3b0487a59bf269a4d80fd1afce355f5343d8135bcadaa0d65fafd2a1e02154fcf48c51a354ab294799c6c0444bfadc792ea195e9b9fdefa0edfde20b547f393f4c05f1e6e32b10da328df10d0b231b5c5af4f0d02bdd3492a095fd7a02b5be79308c127233cec06c203ae54ed2c85b69878eeb13abfc9d6a9aa0af6751551d96ea25ae9afb6113a2c13bef56254c3ea24b1a643562fdbdb4100da0c3b3d7526009039e7cf06279d8226f536ccedbed96da7d3f82aa878063bbeb9aa03dcfed22cae1cee772be285fbba92960fe887eb0997eb9a221849e690b2a6917a05c86e58c113f61d4c44bbe0d6c45ee4d4cdf1ecf308d1f0d43f2a284e0bd1300a03eea9d225bd25dc3dc2d48bf156abc17e6d20686624fa5e93ab8b05f42058e34a0794f702cf0ca4e67ad88a9e9ee7d51adf3b3b3aa47621aa5bf0b8cab433176ec80";
accountProof[5] =
hex"f901f1a0605770c9b68c1b4e33579a35850c1c44731852d55172ec379b2377e54c359d9ca0d704783a3e1d333129d0305964be5f1af602b860253deddb80c0b36bb71f697ca0b4c43f62e620af02dbeaf9d0e79b62d30041ef97ceb70190e3a1bfa0711ddd21a0ffc993b4e33b8a39b84563804f7439299ca8cc0ce747625fb160baccaac0ae56a073146beddea840ed57473fa066835ba3f5b4a7a950965aa8b97b55604ad328e280a0461d8ad7f395cfca01b2b5173ac2cbc81f0343061a4cad2d93593b29d91ba9baa067a777686549c4b2408aa78e935a777bc447b5055a778faf62a5dda63f5c274ea0903c36605f0616a74dcbb4730437ffefea698cbef452e19a08072830f25c581fa06ff86e3168eb6b77472acb077d925fd36000139a924531125a1cd469152712d2a048fd7f01074966c50c0a3cc68a53a0ea29410c0f9a37595657493ec9dffcd4e9a02358f5f4c8b91b0b865a8f70f22f9f082aefeccd53776ba835ead842ff9d74a0a03f7d16b8024d3f8cd006b4e6cc69ce07c520178254ebafe68ada722871342257a08e1bf934b22c0a817fb470fb0e8c792fd1987f062a1af318de783df5a9c71081a063d0b5247f4a977839a12223db8c2e6f120ea6f0db673f0fb056415745e8015ba0cca532f0172104741733687ad1b87337f554def14726f29fabc556b070d077cf80";
accountProof[6] =
hex"f8518080808080808080808080a04fc082a60fe2f12e75c8d6dddca59133c0e855e7d412ffbaba8c2117d37aa35780a06bd1536769280b725a364161ed10f1737f8b70938227ae1cdda228f45ac1e168808080";
accountProof[7] =
hex"f8669d3a74f1d0cadee872fd4b113921a69b6f137711ffc6bc55289ddac192dcb846f8440180a0f7916f389ccda56e3831e115238b7389b30750886785a3c21265601572698f0fa040ed175bbf1e21348615151831ea7a1164fb6d1bd4e1fa03290f0d97bd122021";

bytes[] memory storageProof = new bytes[](1);
storageProof[0] =
hex"e3a1209749684f52b5c0717a7ca78127fb56043d637d81763c04e9d30ba4d4746d56e901";
bytes memory merkleProof = abi.encode(accountProof, storageProof);

vm.startPrank(Alice);
LibTrieProof.verifyFullMerkleProof(
worldStateRoot,
contractWhichStoresValue1AtSlot,
slotStoredAtTheApp,
hex"01",
merkleProof
);
}
}

0 comments on commit e2f4bc2

Please sign in to comment.