From c9df59de03880b73e7b27d628ead34f17e36ed57 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 5 Apr 2024 16:17:48 +0800 Subject: [PATCH 1/8] Update SignalService.sol --- .../protocol/contracts/signal/SignalService.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index eb7415f3e62..9b1f3ae568e 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -22,6 +22,7 @@ contract SignalService is EssentialContract, ISignalService { uint256[48] private __gap; error SS_EMPTY_PROOF(); + error SS_INVALID_HOPS(); error SS_INVALID_SENDER(); error SS_INVALID_LAST_HOP_CHAINID(); error SS_INVALID_MID_HOP_CHAINID(); @@ -92,6 +93,7 @@ contract SignalService is EssentialContract, ISignalService { { HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); + uint64[] memory trace = new uint64[](hopProofs.length - 1); uint64 chainId = _chainId; address app = _app; @@ -103,6 +105,10 @@ contract SignalService is EssentialContract, ISignalService { for (uint256 i; i < hopProofs.length; ++i) { hop = hopProofs[i]; + for (uint256 j; j < i; ++j) { + if (trace[j] == hop.chainId) revert SS_INVALID_HOPS(); + } + bytes32 signalRoot = _verifyHopProof(chainId, app, signal, value, hop, signalService); bool isLastHop = i == hopProofs.length - 1; @@ -110,6 +116,8 @@ contract SignalService is EssentialContract, ISignalService { if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); signalService = address(this); } else { + trace[i] = hop.chainId; + if (hop.chainId == 0 || hop.chainId == block.chainid) { revert SS_INVALID_MID_HOP_CHAINID(); } @@ -147,6 +155,7 @@ contract SignalService is EssentialContract, ISignalService { { HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); + uint64[] memory trace = new uint64[](hopProofs.length - 1); uint64 chainId = _chainId; address app = _app; @@ -155,9 +164,14 @@ contract SignalService is EssentialContract, ISignalService { address signalService = resolve(chainId, "signal_service", false); HopProof memory hop; + for (uint256 i; i < hopProofs.length; ++i) { hop = hopProofs[i]; + for (uint256 j; j < i; ++j) { + if (trace[j] == hop.chainId) revert SS_INVALID_HOPS(); + } + _verifyHopProof(chainId, app, signal, value, hop, signalService); bool isLastHop = i == hopProofs.length - 1; @@ -165,6 +179,8 @@ contract SignalService is EssentialContract, ISignalService { if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); signalService = address(this); } else { + trace[i] = hop.chainId; + if (hop.chainId == 0 || hop.chainId == block.chainid) { revert SS_INVALID_MID_HOP_CHAINID(); } From 0167378d7138c1b77f2148fc50f65eda34397fec Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 5 Apr 2024 16:18:49 +0800 Subject: [PATCH 2/8] Update SignalService.sol --- packages/protocol/contracts/signal/SignalService.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 9b1f3ae568e..9674e886038 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -22,7 +22,7 @@ contract SignalService is EssentialContract, ISignalService { uint256[48] private __gap; error SS_EMPTY_PROOF(); - error SS_INVALID_HOPS(); + error SS_INVALID_HOPS_WITH_LOOP(); error SS_INVALID_SENDER(); error SS_INVALID_LAST_HOP_CHAINID(); error SS_INVALID_MID_HOP_CHAINID(); @@ -106,7 +106,7 @@ contract SignalService is EssentialContract, ISignalService { hop = hopProofs[i]; for (uint256 j; j < i; ++j) { - if (trace[j] == hop.chainId) revert SS_INVALID_HOPS(); + if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); } bytes32 signalRoot = _verifyHopProof(chainId, app, signal, value, hop, signalService); @@ -169,7 +169,7 @@ contract SignalService is EssentialContract, ISignalService { hop = hopProofs[i]; for (uint256 j; j < i; ++j) { - if (trace[j] == hop.chainId) revert SS_INVALID_HOPS(); + if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); } _verifyHopProof(chainId, app, signal, value, hop, signalService); From cf23095b11417b02eb0cd600b0c539a5ef5cf050 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 5 Apr 2024 16:24:41 +0800 Subject: [PATCH 3/8] Update SignalService.t.sol --- .../protocol/test/signal/SignalService.t.sol | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/protocol/test/signal/SignalService.t.sol b/packages/protocol/test/signal/SignalService.t.sol index 49e0419617f..b742ad52a0d 100644 --- a/packages/protocol/test/signal/SignalService.t.sol +++ b/packages/protocol/test/signal/SignalService.t.sol @@ -455,6 +455,55 @@ contract TestSignalService is TaikoTest { }); } + function test_SignalService_proveSignalReceived_revert_with_a_loop() public { + uint64 srcChainId = uint64(block.chainid + 1); + + vm.prank(Alice); + addressManager.setAddress(srcChainId, "signal_service", randAddress()); + + SignalService.HopProof[] memory proofs = new SignalService.HopProof[](3); + + // first hop with full merkle proof + proofs[0].chainId = uint64(block.chainid + 2); + proofs[0].blockId = 1; + proofs[0].rootHash = randBytes32(); + proofs[0].accountProof = new bytes[](1); + proofs[0].storageProof = new bytes[](10); + + // second hop with storage merkle proof + proofs[1].chainId = uint64(block.chainid + 2); + proofs[1].blockId = 2; + proofs[1].rootHash = randBytes32(); + proofs[1].accountProof = new bytes[](0); + proofs[1].storageProof = new bytes[](10); + + // third/last hop with full merkle proof + proofs[2].chainId = uint64(block.chainid); + proofs[2].blockId = 3; + proofs[2].rootHash = randBytes32(); + proofs[2].accountProof = new bytes[](1); + proofs[2].storageProof = new bytes[](10); + + // Add two trusted hop relayers + vm.startPrank(Alice); + addressManager.setAddress(proofs[0].chainId, "signal_service", randAddress() /*relay1*/ ); + addressManager.setAddress(proofs[1].chainId, "signal_service", randAddress() /*relay2*/ ); + vm.stopPrank(); + + vm.prank(taiko); + signalService.syncChainData( + proofs[1].chainId, LibSignals.STATE_ROOT, proofs[2].blockId, proofs[2].rootHash + ); + + vm.expectRevert(SignalService.SS_INVALID_HOPS_WITH_LOOP.selector); + signalService.proveSignalReceived({ + _chainId: srcChainId, + _app: randAddress(), + _signal: randBytes32(), + _proof: abi.encode(proofs) + }); + } + function test_SignalService_proveSignalReceived_multiple_hops_caching() public { uint64 srcChainId = uint64(block.chainid + 1); uint64 nextChainId = srcChainId + 100; From 1cee1f55f4fb5913f2ed8526677e026043723830 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 5 Apr 2024 16:53:21 +0800 Subject: [PATCH 4/8] Update SignalService.sol --- .../contracts/signal/SignalService.sol | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 9674e886038..5fff07f485d 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -93,7 +93,11 @@ contract SignalService is EssentialContract, ISignalService { { HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); - uint64[] memory trace = new uint64[](hopProofs.length - 1); + uint256 lenLessOne; + unchecked { + lenLessOne = hopProofs.length - 1; + } + uint64[] memory trace = new uint64[](lenLessOne); uint64 chainId = _chainId; address app = _app; @@ -110,7 +114,7 @@ contract SignalService is EssentialContract, ISignalService { } bytes32 signalRoot = _verifyHopProof(chainId, app, signal, value, hop, signalService); - bool isLastHop = i == hopProofs.length - 1; + bool isLastHop = i == lenLessOne; if (isLastHop) { if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); @@ -155,7 +159,12 @@ contract SignalService is EssentialContract, ISignalService { { HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); - uint64[] memory trace = new uint64[](hopProofs.length - 1); + + uint256 lenLessOne; + unchecked { + lenLessOne = hopProofs.length - 1; + } + uint64[] memory trace = new uint64[](lenLessOne); uint64 chainId = _chainId; address app = _app; @@ -173,7 +182,8 @@ contract SignalService is EssentialContract, ISignalService { } _verifyHopProof(chainId, app, signal, value, hop, signalService); - bool isLastHop = i == hopProofs.length - 1; + + bool isLastHop = i == lenLessOne; if (isLastHop) { if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); From 5cd016582e00755af98881b788a68ef9703dc6b1 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 5 Apr 2024 16:54:15 +0800 Subject: [PATCH 5/8] Update SignalService.sol --- packages/protocol/contracts/signal/SignalService.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 5fff07f485d..57c98cfe38f 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -183,9 +183,7 @@ contract SignalService is EssentialContract, ISignalService { _verifyHopProof(chainId, app, signal, value, hop, signalService); - bool isLastHop = i == lenLessOne; - - if (isLastHop) { + if (i == lenLessOne) { if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); signalService = address(this); } else { From 8747d6167e893e8f1dccc922623ca0b43f1b845b Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 5 Apr 2024 17:52:50 +0800 Subject: [PATCH 6/8] Update SignalService.t.sol --- packages/protocol/test/signal/SignalService.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/test/signal/SignalService.t.sol b/packages/protocol/test/signal/SignalService.t.sol index b742ad52a0d..10fc85e7ea6 100644 --- a/packages/protocol/test/signal/SignalService.t.sol +++ b/packages/protocol/test/signal/SignalService.t.sol @@ -471,7 +471,7 @@ contract TestSignalService is TaikoTest { proofs[0].storageProof = new bytes[](10); // second hop with storage merkle proof - proofs[1].chainId = uint64(block.chainid + 2); + proofs[1].chainId = proofs[0].chainId; // same proofs[1].blockId = 2; proofs[1].rootHash = randBytes32(); proofs[1].accountProof = new bytes[](0); From 5a148efc628ab85a478f627c0854f66ec771f8e5 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Fri, 5 Apr 2024 21:37:40 +0800 Subject: [PATCH 7/8] refactor(protocol): refactor SignalService to remove duplicate code (#16661) --- .../contracts/signal/SignalService.sol | 219 ++++++++---------- 1 file changed, 102 insertions(+), 117 deletions(-) diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 57c98cfe38f..1b32b41c27b 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -21,6 +21,16 @@ contract SignalService is EssentialContract, ISignalService { uint256[48] private __gap; + struct CacheAction { + bytes32 rootHash; + bytes32 signalRoot; + uint64 chainId; + uint64 blockId; + bool isFullProof; + bool isLastHop; + CacheOption option; + } + error SS_EMPTY_PROOF(); error SS_INVALID_HOPS_WITH_LOOP(); error SS_INVALID_SENDER(); @@ -91,56 +101,10 @@ contract SignalService is EssentialContract, ISignalService { validSender(_app) nonZeroValue(_signal) { - HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); - if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); - uint256 lenLessOne; - unchecked { - lenLessOne = hopProofs.length - 1; - } - uint64[] memory trace = new uint64[](lenLessOne); - - uint64 chainId = _chainId; - address app = _app; - bytes32 signal = _signal; - bytes32 value = _signal; - address signalService = resolve(chainId, "signal_service", false); - - HopProof memory hop; - for (uint256 i; i < hopProofs.length; ++i) { - hop = hopProofs[i]; - - for (uint256 j; j < i; ++j) { - if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); - } - - bytes32 signalRoot = _verifyHopProof(chainId, app, signal, value, hop, signalService); - bool isLastHop = i == lenLessOne; - - if (isLastHop) { - if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); - signalService = address(this); - } else { - trace[i] = hop.chainId; - - if (hop.chainId == 0 || hop.chainId == block.chainid) { - revert SS_INVALID_MID_HOP_CHAINID(); - } - signalService = resolve(hop.chainId, "signal_service", false); - } - - bool isFullProof = hop.accountProof.length != 0; - - _cacheChainData(hop, chainId, hop.blockId, signalRoot, isFullProof, isLastHop); - - bytes32 kind = isFullProof ? LibSignals.STATE_ROOT : LibSignals.SIGNAL_ROOT; - signal = signalForChainData(chainId, kind, hop.blockId); - value = hop.rootHash; - chainId = hop.chainId; - app = signalService; - } + CacheAction[] memory actions = _verifySignalReceived(_chainId, _app, _signal, _proof); - if (value == 0 || value != _loadSignalValue(address(this), signal)) { - revert SS_SIGNAL_NOT_FOUND(); + for (uint256 i; i < actions.length; ++i) { + _cache(actions[i]); } } @@ -157,56 +121,7 @@ contract SignalService is EssentialContract, ISignalService { validSender(_app) nonZeroValue(_signal) { - HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); - if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); - - uint256 lenLessOne; - unchecked { - lenLessOne = hopProofs.length - 1; - } - uint64[] memory trace = new uint64[](lenLessOne); - - uint64 chainId = _chainId; - address app = _app; - bytes32 signal = _signal; - bytes32 value = _signal; - address signalService = resolve(chainId, "signal_service", false); - - HopProof memory hop; - - for (uint256 i; i < hopProofs.length; ++i) { - hop = hopProofs[i]; - - for (uint256 j; j < i; ++j) { - if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); - } - - _verifyHopProof(chainId, app, signal, value, hop, signalService); - - if (i == lenLessOne) { - if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); - signalService = address(this); - } else { - trace[i] = hop.chainId; - - if (hop.chainId == 0 || hop.chainId == block.chainid) { - revert SS_INVALID_MID_HOP_CHAINID(); - } - signalService = resolve(hop.chainId, "signal_service", false); - } - - bool isFullProof = hop.accountProof.length != 0; - - bytes32 kind = isFullProof ? LibSignals.STATE_ROOT : LibSignals.SIGNAL_ROOT; - signal = signalForChainData(chainId, kind, hop.blockId); - value = hop.rootHash; - chainId = hop.chainId; - app = signalService; - } - - if (value == 0 || value != _loadSignalValue(address(this), signal)) { - revert SS_SIGNAL_NOT_FOUND(); - } + _verifySignalReceived(_chainId, _app, _signal, _proof); } /// @inheritdoc ISignalService @@ -345,30 +260,25 @@ contract SignalService is EssentialContract, ISignalService { emit SignalSent(_app, _signal, slot_, _value); } - function _cacheChainData( - HopProof memory _hop, - uint64 _chainId, - uint64 _blockId, - bytes32 _signalRoot, - bool _isFullProof, - bool _isLastHop - ) - private - { + function _cache(CacheAction memory _action) private { // cache state root - bool cacheStateRoot = _hop.cacheOption == CacheOption.CACHE_BOTH - || _hop.cacheOption == CacheOption.CACHE_STATE_ROOT; + bool cacheStateRoot = _action.option == CacheOption.CACHE_BOTH + || _action.option == CacheOption.CACHE_STATE_ROOT; - if (cacheStateRoot && _isFullProof && !_isLastHop) { - _syncChainData(_chainId, LibSignals.STATE_ROOT, _blockId, _hop.rootHash); + if (cacheStateRoot && _action.isFullProof && !_action.isLastHop) { + _syncChainData( + _action.chainId, LibSignals.STATE_ROOT, _action.blockId, _action.rootHash + ); } // cache signal root - bool cacheSignalRoot = _hop.cacheOption == CacheOption.CACHE_BOTH - || _hop.cacheOption == CacheOption.CACHE_SIGNAL_ROOT; + bool cacheSignalRoot = _action.option == CacheOption.CACHE_BOTH + || _action.option == CacheOption.CACHE_SIGNAL_ROOT; - if (cacheSignalRoot && (_isFullProof || !_isLastHop)) { - _syncChainData(_chainId, LibSignals.SIGNAL_ROOT, _blockId, _signalRoot); + if (cacheSignalRoot && (_action.isFullProof || !_action.isLastHop)) { + _syncChainData( + _action.chainId, LibSignals.SIGNAL_ROOT, _action.blockId, _action.signalRoot + ); } } @@ -387,4 +297,79 @@ contract SignalService is EssentialContract, ISignalService { value_ := sload(slot) } } + + function _verifySignalReceived( + uint64 _chainId, + address _app, + bytes32 _signal, + bytes calldata _proof + ) + private + view + validSender(_app) + nonZeroValue(_signal) + returns (CacheAction[] memory actions) + { + HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); + if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); + + uint64[] memory trace = new uint64[](hopProofs.length - 1); + actions = new CacheAction[](hopProofs.length); + + uint64 chainId = _chainId; + address app = _app; + bytes32 signal = _signal; + bytes32 value = _signal; + address signalService = resolve(chainId, "signal_service", false); + + HopProof memory hop; + bytes32 signalRoot; + bool isFullProof; + bool isLastHop; + + for (uint256 i; i < hopProofs.length; ++i) { + hop = hopProofs[i]; + + for (uint256 j; j < i; ++j) { + if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); + } + + signalRoot = _verifyHopProof(chainId, app, signal, value, hop, signalService); + isLastHop = i == trace.length; + if (isLastHop) { + if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); + signalService = address(this); + } else { + trace[i] = hop.chainId; + + if (hop.chainId == 0 || hop.chainId == block.chainid) { + revert SS_INVALID_MID_HOP_CHAINID(); + } + signalService = resolve(hop.chainId, "signal_service", false); + } + + isFullProof = hop.accountProof.length != 0; + + actions[i] = CacheAction( + hop.rootHash, + signalRoot, + chainId, + hop.blockId, + isFullProof, + isLastHop, + hop.cacheOption + ); + + signal = signalForChainData( + chainId, isFullProof ? LibSignals.STATE_ROOT : LibSignals.SIGNAL_ROOT, hop.blockId + ); + value = hop.rootHash; + chainId = hop.chainId; + app = signalService; + } + + if (value == 0 || value != _loadSignalValue(address(this), signal)) { + revert SS_SIGNAL_NOT_FOUND(); + } + } } From f4906ee6e6ea45db40d1c2ee188ebb69efd23887 Mon Sep 17 00:00:00 2001 From: Daniel Wang Date: Fri, 5 Apr 2024 21:39:00 +0800 Subject: [PATCH 8/8] Revert "refactor(protocol): refactor SignalService to remove duplicate code (#16661)" This reverts commit 5a148efc628ab85a478f627c0854f66ec771f8e5. --- .../contracts/signal/SignalService.sol | 219 ++++++++++-------- 1 file changed, 117 insertions(+), 102 deletions(-) diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 1b32b41c27b..57c98cfe38f 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -21,16 +21,6 @@ contract SignalService is EssentialContract, ISignalService { uint256[48] private __gap; - struct CacheAction { - bytes32 rootHash; - bytes32 signalRoot; - uint64 chainId; - uint64 blockId; - bool isFullProof; - bool isLastHop; - CacheOption option; - } - error SS_EMPTY_PROOF(); error SS_INVALID_HOPS_WITH_LOOP(); error SS_INVALID_SENDER(); @@ -101,10 +91,56 @@ contract SignalService is EssentialContract, ISignalService { validSender(_app) nonZeroValue(_signal) { - CacheAction[] memory actions = _verifySignalReceived(_chainId, _app, _signal, _proof); + HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); + if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); + uint256 lenLessOne; + unchecked { + lenLessOne = hopProofs.length - 1; + } + uint64[] memory trace = new uint64[](lenLessOne); + + uint64 chainId = _chainId; + address app = _app; + bytes32 signal = _signal; + bytes32 value = _signal; + address signalService = resolve(chainId, "signal_service", false); + + HopProof memory hop; + for (uint256 i; i < hopProofs.length; ++i) { + hop = hopProofs[i]; + + for (uint256 j; j < i; ++j) { + if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); + } + + bytes32 signalRoot = _verifyHopProof(chainId, app, signal, value, hop, signalService); + bool isLastHop = i == lenLessOne; + + if (isLastHop) { + if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); + signalService = address(this); + } else { + trace[i] = hop.chainId; + + if (hop.chainId == 0 || hop.chainId == block.chainid) { + revert SS_INVALID_MID_HOP_CHAINID(); + } + signalService = resolve(hop.chainId, "signal_service", false); + } + + bool isFullProof = hop.accountProof.length != 0; + + _cacheChainData(hop, chainId, hop.blockId, signalRoot, isFullProof, isLastHop); + + bytes32 kind = isFullProof ? LibSignals.STATE_ROOT : LibSignals.SIGNAL_ROOT; + signal = signalForChainData(chainId, kind, hop.blockId); + value = hop.rootHash; + chainId = hop.chainId; + app = signalService; + } - for (uint256 i; i < actions.length; ++i) { - _cache(actions[i]); + if (value == 0 || value != _loadSignalValue(address(this), signal)) { + revert SS_SIGNAL_NOT_FOUND(); } } @@ -121,7 +157,56 @@ contract SignalService is EssentialContract, ISignalService { validSender(_app) nonZeroValue(_signal) { - _verifySignalReceived(_chainId, _app, _signal, _proof); + HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); + if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); + + uint256 lenLessOne; + unchecked { + lenLessOne = hopProofs.length - 1; + } + uint64[] memory trace = new uint64[](lenLessOne); + + uint64 chainId = _chainId; + address app = _app; + bytes32 signal = _signal; + bytes32 value = _signal; + address signalService = resolve(chainId, "signal_service", false); + + HopProof memory hop; + + for (uint256 i; i < hopProofs.length; ++i) { + hop = hopProofs[i]; + + for (uint256 j; j < i; ++j) { + if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); + } + + _verifyHopProof(chainId, app, signal, value, hop, signalService); + + if (i == lenLessOne) { + if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); + signalService = address(this); + } else { + trace[i] = hop.chainId; + + if (hop.chainId == 0 || hop.chainId == block.chainid) { + revert SS_INVALID_MID_HOP_CHAINID(); + } + signalService = resolve(hop.chainId, "signal_service", false); + } + + bool isFullProof = hop.accountProof.length != 0; + + bytes32 kind = isFullProof ? LibSignals.STATE_ROOT : LibSignals.SIGNAL_ROOT; + signal = signalForChainData(chainId, kind, hop.blockId); + value = hop.rootHash; + chainId = hop.chainId; + app = signalService; + } + + if (value == 0 || value != _loadSignalValue(address(this), signal)) { + revert SS_SIGNAL_NOT_FOUND(); + } } /// @inheritdoc ISignalService @@ -260,25 +345,30 @@ contract SignalService is EssentialContract, ISignalService { emit SignalSent(_app, _signal, slot_, _value); } - function _cache(CacheAction memory _action) private { + function _cacheChainData( + HopProof memory _hop, + uint64 _chainId, + uint64 _blockId, + bytes32 _signalRoot, + bool _isFullProof, + bool _isLastHop + ) + private + { // cache state root - bool cacheStateRoot = _action.option == CacheOption.CACHE_BOTH - || _action.option == CacheOption.CACHE_STATE_ROOT; + bool cacheStateRoot = _hop.cacheOption == CacheOption.CACHE_BOTH + || _hop.cacheOption == CacheOption.CACHE_STATE_ROOT; - if (cacheStateRoot && _action.isFullProof && !_action.isLastHop) { - _syncChainData( - _action.chainId, LibSignals.STATE_ROOT, _action.blockId, _action.rootHash - ); + if (cacheStateRoot && _isFullProof && !_isLastHop) { + _syncChainData(_chainId, LibSignals.STATE_ROOT, _blockId, _hop.rootHash); } // cache signal root - bool cacheSignalRoot = _action.option == CacheOption.CACHE_BOTH - || _action.option == CacheOption.CACHE_SIGNAL_ROOT; + bool cacheSignalRoot = _hop.cacheOption == CacheOption.CACHE_BOTH + || _hop.cacheOption == CacheOption.CACHE_SIGNAL_ROOT; - if (cacheSignalRoot && (_action.isFullProof || !_action.isLastHop)) { - _syncChainData( - _action.chainId, LibSignals.SIGNAL_ROOT, _action.blockId, _action.signalRoot - ); + if (cacheSignalRoot && (_isFullProof || !_isLastHop)) { + _syncChainData(_chainId, LibSignals.SIGNAL_ROOT, _blockId, _signalRoot); } } @@ -297,79 +387,4 @@ contract SignalService is EssentialContract, ISignalService { value_ := sload(slot) } } - - function _verifySignalReceived( - uint64 _chainId, - address _app, - bytes32 _signal, - bytes calldata _proof - ) - private - view - validSender(_app) - nonZeroValue(_signal) - returns (CacheAction[] memory actions) - { - HopProof[] memory hopProofs = abi.decode(_proof, (HopProof[])); - if (hopProofs.length == 0) revert SS_EMPTY_PROOF(); - - uint64[] memory trace = new uint64[](hopProofs.length - 1); - actions = new CacheAction[](hopProofs.length); - - uint64 chainId = _chainId; - address app = _app; - bytes32 signal = _signal; - bytes32 value = _signal; - address signalService = resolve(chainId, "signal_service", false); - - HopProof memory hop; - bytes32 signalRoot; - bool isFullProof; - bool isLastHop; - - for (uint256 i; i < hopProofs.length; ++i) { - hop = hopProofs[i]; - - for (uint256 j; j < i; ++j) { - if (trace[j] == hop.chainId) revert SS_INVALID_HOPS_WITH_LOOP(); - } - - signalRoot = _verifyHopProof(chainId, app, signal, value, hop, signalService); - isLastHop = i == trace.length; - if (isLastHop) { - if (hop.chainId != block.chainid) revert SS_INVALID_LAST_HOP_CHAINID(); - signalService = address(this); - } else { - trace[i] = hop.chainId; - - if (hop.chainId == 0 || hop.chainId == block.chainid) { - revert SS_INVALID_MID_HOP_CHAINID(); - } - signalService = resolve(hop.chainId, "signal_service", false); - } - - isFullProof = hop.accountProof.length != 0; - - actions[i] = CacheAction( - hop.rootHash, - signalRoot, - chainId, - hop.blockId, - isFullProof, - isLastHop, - hop.cacheOption - ); - - signal = signalForChainData( - chainId, isFullProof ? LibSignals.STATE_ROOT : LibSignals.SIGNAL_ROOT, hop.blockId - ); - value = hop.rootHash; - chainId = hop.chainId; - app = signalService; - } - - if (value == 0 || value != _loadSignalValue(address(this), signal)) { - revert SS_SIGNAL_NOT_FOUND(); - } - } }