From 45153d92cbcd0e80438c925d5ce5c52df3abd696 Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Sat, 21 Jan 2023 17:41:07 +0800 Subject: [PATCH] feat(protocol): implement `Bridge.isMessageFailed` (#13004) Co-authored-by: Roger <50648015+RogerLamTd@users.noreply.github.com> Co-authored-by: jeff <113397187+cyberhorsey@users.noreply.github.com> --- packages/protocol/contracts/bridge/Bridge.sol | 14 +++++++ .../protocol/contracts/bridge/IBridge.sol | 7 ++++ .../contracts/bridge/libs/LibBridgeData.sol | 6 +++ .../contracts/bridge/libs/LibBridgeStatus.sol | 40 +++++++++++++++++++ packages/protocol/tasks/deploy_L1.ts | 2 + .../protocol/test/libs/LibTrieProof.test.ts | 1 + packages/protocol/test/test_integration.sh | 2 +- packages/protocol/test/utils/bridge.ts | 8 ++++ .../utils/generate_genesis/taikoL2.ts | 3 +- 9 files changed, 81 insertions(+), 2 deletions(-) diff --git a/packages/protocol/contracts/bridge/Bridge.sol b/packages/protocol/contracts/bridge/Bridge.sol index d33362a741..3fa0076540 100644 --- a/packages/protocol/contracts/bridge/Bridge.sol +++ b/packages/protocol/contracts/bridge/Bridge.sol @@ -113,6 +113,20 @@ contract Bridge is EssentialContract, IBridge { }); } + function isMessageFailed( + bytes32 msgHash, + uint256 destChainId, + bytes calldata proof + ) public view virtual override returns (bool) { + return + LibBridgeStatus.isMessageFailed({ + resolver: AddressResolver(this), + msgHash: msgHash, + destChainId: destChainId, + proof: proof + }); + } + function getMessageStatus( bytes32 msgHash ) public view virtual returns (LibBridgeStatus.MessageStatus) { diff --git a/packages/protocol/contracts/bridge/IBridge.sol b/packages/protocol/contracts/bridge/IBridge.sol index c8ece2e3a3..9719cfcf49 100644 --- a/packages/protocol/contracts/bridge/IBridge.sol +++ b/packages/protocol/contracts/bridge/IBridge.sol @@ -56,6 +56,13 @@ interface IBridge { bytes calldata proof ) external view returns (bool); + /// Checks if a msgHash has been failed on the destination chain. + function isMessageFailed( + bytes32 msgHash, + uint256 destChainId, + bytes calldata proof + ) external view returns (bool); + /// Returns the bridge state context. function context() external view returns (Context memory context); } diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeData.sol b/packages/protocol/contracts/bridge/libs/LibBridgeData.sol index 2a0f784632..f71324ec1c 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeData.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeData.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.9; import "../../common/AddressResolver.sol"; import "../../libs/LibAddress.sol"; +import "../../libs/LibBlockHeader.sol"; import "../../libs/LibMath.sol"; import "../IBridge.sol"; @@ -23,6 +24,11 @@ library LibBridgeData { uint256[46] __gap; } + struct StatusProof { + BlockHeader header; + bytes proof; + } + bytes32 internal constant MESSAGE_HASH_PLACEHOLDER = bytes32(uint256(1)); uint256 internal constant CHAINID_PLACEHOLDER = type(uint256).max; address internal constant SRC_CHAIN_SENDER_PLACEHOLDER = diff --git a/packages/protocol/contracts/bridge/libs/LibBridgeStatus.sol b/packages/protocol/contracts/bridge/libs/LibBridgeStatus.sol index 9cfb36216b..a051557d23 100644 --- a/packages/protocol/contracts/bridge/libs/LibBridgeStatus.sol +++ b/packages/protocol/contracts/bridge/libs/LibBridgeStatus.sol @@ -6,10 +6,17 @@ pragma solidity ^0.8.9; +import "../../common/IHeaderSync.sol"; +import "../../libs/LibBlockHeader.sol"; +import "../../libs/LibTrieProof.sol"; +import "./LibBridgeData.sol"; + /** * @author dantaik */ library LibBridgeStatus { + using LibBlockHeader for BlockHeader; + enum MessageStatus { NEW, RETRIABLE, @@ -52,6 +59,39 @@ library LibBridgeStatus { return keccak256(abi.encodePacked("MESSAGE_STATUS", msgHash)); } + function isMessageFailed( + AddressResolver resolver, + bytes32 msgHash, + uint256 destChainId, + bytes calldata proof + ) internal view returns (bool) { + require(destChainId != block.chainid, "B:destChainId"); + require(msgHash != 0, "B:msgHash"); + + LibBridgeData.StatusProof memory sp = abi.decode( + proof, + (LibBridgeData.StatusProof) + ); + bytes32 syncedHeaderHash = IHeaderSync(resolver.resolve("taiko", false)) + .getSyncedHeader(sp.header.height); + + if ( + syncedHeaderHash == 0 || + syncedHeaderHash != sp.header.hashBlockHeader() + ) { + return false; + } + + return + LibTrieProof.verify({ + stateRoot: sp.header.stateRoot, + addr: resolver.resolve(destChainId, "bridge", false), + slot: getMessageStatusSlot(msgHash), + value: bytes32(uint256(LibBridgeStatus.MessageStatus.FAILED)), + mkproof: sp.proof + }); + } + function _setMessageStatus(bytes32 msgHash, MessageStatus status) private { bytes32 slot = getMessageStatusSlot(msgHash); uint256 value = uint256(status); diff --git a/packages/protocol/tasks/deploy_L1.ts b/packages/protocol/tasks/deploy_L1.ts index 19d93f1057..a37769c7a0 100644 --- a/packages/protocol/tasks/deploy_L1.ts +++ b/packages/protocol/tasks/deploy_L1.ts @@ -240,6 +240,7 @@ async function deployBaseLibs(hre: any) { } async function deployBridge(hre: any, addressManager: string): Promise { + const libTrieProof = await utils.deployContract(hre, "LibTrieProof"); const libBridgeRetry = await utils.deployContract(hre, "LibBridgeRetry"); const libBridgeProcess = await utils.deployContract( hre, @@ -247,6 +248,7 @@ async function deployBridge(hre: any, addressManager: string): Promise { ); const Bridge = await utils.deployContract(hre, "Bridge", { + LibTrieProof: libTrieProof.address, LibBridgeRetry: libBridgeRetry.address, LibBridgeProcess: libBridgeProcess.address, }); diff --git a/packages/protocol/test/libs/LibTrieProof.test.ts b/packages/protocol/test/libs/LibTrieProof.test.ts index aed3860e93..710794809e 100644 --- a/packages/protocol/test/libs/LibTrieProof.test.ts +++ b/packages/protocol/test/libs/LibTrieProof.test.ts @@ -62,6 +62,7 @@ describe("integration:LibTrieProof", function () { libraries: { LibBridgeProcess: libBridgeProcess.address, LibBridgeRetry: libBridgeRetry.address, + LibTrieProof: testLibTreProof.address, }, }); diff --git a/packages/protocol/test/test_integration.sh b/packages/protocol/test/test_integration.sh index 40c7fd6098..c545c9c0ab 100755 --- a/packages/protocol/test/test_integration.sh +++ b/packages/protocol/test/test_integration.sh @@ -40,7 +40,7 @@ docker run -d \ function waitTestNode { echo "Waiting for test node: $1" # Wait till the test node fully started - RETRIES=30 + RETRIES=120 i=0 until curl \ --silent \ diff --git a/packages/protocol/test/utils/bridge.ts b/packages/protocol/test/utils/bridge.ts index bbaa168df1..0b302e95b0 100644 --- a/packages/protocol/test/utils/bridge.ts +++ b/packages/protocol/test/utils/bridge.ts @@ -6,6 +6,7 @@ import { SignalService, EtherVault, TestHeaderSync, + LibTrieProof, } from "../../typechain"; import { Message } from "./message"; import { Block, BlockHeader, getBlockHeader } from "./rpc"; @@ -16,6 +17,12 @@ async function deployBridge( addressManager: AddressManager, chainId: number ): Promise<{ bridge: Bridge; etherVault: EtherVault }> { + const libTrieProof: LibTrieProof = await ( + await hardhatEthers.getContractFactory("LibTrieProof") + ) + .connect(signer) + .deploy(); + const libBridgeProcess = await ( await hardhatEthers.getContractFactory("LibBridgeProcess") ) @@ -30,6 +37,7 @@ async function deployBridge( const BridgeFactory = await hardhatEthers.getContractFactory("Bridge", { libraries: { + LibTrieProof: libTrieProof.address, LibBridgeProcess: libBridgeProcess.address, LibBridgeRetry: libBridgeRetry.address, }, diff --git a/packages/protocol/utils/generate_genesis/taikoL2.ts b/packages/protocol/utils/generate_genesis/taikoL2.ts index 1b9df47787..04f88972bf 100644 --- a/packages/protocol/utils/generate_genesis/taikoL2.ts +++ b/packages/protocol/utils/generate_genesis/taikoL2.ts @@ -168,11 +168,12 @@ async function generateContractConfigs( break; case "Bridge": if ( + !addressMap.LibTrieProof || !addressMap.LibBridgeRetry || !addressMap.LibBridgeProcess ) { throw new Error( - "LibBridgeRetry/LibBridgeProcess not initialized" + "LibTrieProof/LibBridgeRetry/LibBridgeProcess not initialized" ); }