From c964c68cb57b25a67c1129bf671d806199f03fe7 Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Fri, 10 Dec 2021 12:04:48 +0000 Subject: [PATCH 01/11] feat(arbitrum): add gateway contracts --- contracts/src/bridge/arbitrum/L1Bridge.sol | 7 +- contracts/src/bridge/arbitrum/L2Bridge.sol | 2 +- .../src/gateway/arbitrum/ForeignGateway.sol | 76 +++++++++++++++++++ .../src/gateway/arbitrum/HomeGateway.sol | 43 +++++++++++ 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 contracts/src/gateway/arbitrum/ForeignGateway.sol create mode 100644 contracts/src/gateway/arbitrum/HomeGateway.sol diff --git a/contracts/src/bridge/arbitrum/L1Bridge.sol b/contracts/src/bridge/arbitrum/L1Bridge.sol index 7108a2bb8..c131dc349 100644 --- a/contracts/src/bridge/arbitrum/L1Bridge.sol +++ b/contracts/src/bridge/arbitrum/L1Bridge.sol @@ -39,7 +39,7 @@ contract L1Bridge { uint256 _maxGas, uint256 _gasPriceBid ) external payable returns (uint256) { - (uint256 baseSubmissionCost, ) = arbRetryableTx.getSubmissionPrice(_calldata.length); + uint256 baseSubmissionCost = getSubmissionPrice(_calldata.length); uint256 ticketID = inbox.createRetryableTicket{value: msg.value}( l2Target, @@ -55,4 +55,9 @@ contract L1Bridge { emit RetryableTicketCreated(ticketID); return ticketID; } + + function getSubmissionPrice(uint256 _calldatasize) public view returns (uint256) { + (uint256 submissionCost, ) = arbRetryableTx.getSubmissionPrice(_calldatasize); + return submissionCost; + } } diff --git a/contracts/src/bridge/arbitrum/L2Bridge.sol b/contracts/src/bridge/arbitrum/L2Bridge.sol index d4938e932..e19de7163 100644 --- a/contracts/src/bridge/arbitrum/L2Bridge.sol +++ b/contracts/src/bridge/arbitrum/L2Bridge.sol @@ -20,7 +20,7 @@ contract L2Bridge { * @param _calldata The L1 encoded message data. * @return Unique id to track the message request/transaction. */ - function sendCrossDomainMessage(bytes memory _calldata) external payable returns (uint256) { + function sendCrossDomainMessage(bytes memory _calldata) external returns (uint256) { uint256 withdrawalId = arbsys.sendTxToL1(l1Target, _calldata); emit L2ToL1TxCreated(withdrawalId); diff --git a/contracts/src/gateway/arbitrum/ForeignGateway.sol b/contracts/src/gateway/arbitrum/ForeignGateway.sol new file mode 100644 index 000000000..2261f03b8 --- /dev/null +++ b/contracts/src/gateway/arbitrum/ForeignGateway.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../arbitration/IArbitrable.sol"; +import "../../arbitration/IArbitrator.sol"; +import "../../bridge/arbitrum/L1Bridge.sol"; + +import "./HomeGateway.sol"; + +contract ForeignGateway is IArbitrator { + // L1 bridge with the HomeGateway as the l2target + L1Bridge internal l1bridge; + + // For now this is just a constant, but we'd probably need to + // implement the same arbitrationCost calculation code we'll have + // in the V2 court. + uint256 internal internalArbitrationCost; + + constructor(uint256 _arbitrationCost, L1Bridge _l1bridge) { + internalArbitrationCost = _arbitrationCost; + l1bridge = _l1bridge; + } + + function createDispute(uint256 _choices, bytes calldata _extraData) external payable returns (uint256 disputeID) { + require(msg.value >= arbitrationCost(_extraData), "Not paid enough for arbitration"); + + bytes4 methodSelector = HomeGateway.relayCreateDispute.selector; + bytes memory data = abi.encodeWithSelector(methodSelector, msg.data); + + uint256 bridgeCost = l1bridge.getSubmissionPrice(data.length); + // We only pay for the submissionPrice gas cost + // which is minimum gas cost required for submitting a + // arbitrum retryable ticket to the retry buffer for + // bridge to L2. + // For immediate inclusion a user/bot needs to pay (GasPrice x MaxGas) + // with the associated ticketId that is emitted by this function + // after the ticket is successfully submitted. + // For more details, see: + // https://developer.offchainlabs.com/docs/l1_l2_messages#retryable-tickets-contract-api + // + // We do NOT forward the arbitrationCost to the HomeGateway yet, + // only the calldata. + l1bridge.sendCrossDomainMessage{value: bridgeCost}(data, 0, 0); + + disputeId = 0; // TODO: map to the actual disputeID we get from the V2 court + emit DisputeCreation(disputeId, IArbitrable(msg.sender)); + return disputeId; + } + + function arbitrationCost(bytes calldata _extraData) public view returns (uint256 cost) { + // Calculate the size of calldata that will be passed to the L2 bridge + // as that is a factor for the bridging cost. + // Calldata size of relayCreateDispute: + // relayCreateDispute methodId + + // (createDispute methodId + uint256 _choices + bytes _extraData) + // 4 + 4 + 32 + dynamic + uint256 calldatasize = 40 + _extraData.length; + + uint256 bridgeCost = l1bridge.getSubmissionPrice(calldatasize); + return bridgeCost + internalArbitrationCost; + } + + /** + * Relay the rule call from the home gateway to the arbitrable. + * + * @param _data The calldata to relay + */ + function relayRule(bytes memory _data) external { + address arbitrable = address(0); // see the TODO above about the disputeId + + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = arbitrable.call(_data); + require(success, "Failed to call contract"); + } +} diff --git a/contracts/src/gateway/arbitrum/HomeGateway.sol b/contracts/src/gateway/arbitrum/HomeGateway.sol new file mode 100644 index 000000000..495017363 --- /dev/null +++ b/contracts/src/gateway/arbitrum/HomeGateway.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../arbitration/IArbitrable.sol"; +import "../../arbitration/IArbitrator.sol"; +import "../../bridge/arbitrum/L2Bridge.sol"; + +import "./ForeignGateway.sol"; + +contract HomeGateway is IArbitrable { + // L2 bridge with the ForeignGateway as the l1target + L2Bridge internal l2bridge; + address public arbitrator; + + constructor(address _arbitrator, L2Bridge _l2bridge) { + arbitrator = _arbitrator; + l2bridge = _l2bridge; + } + + function rule(uint256, uint256) external { + require(msg.sender == arbitrator, "Only Arbitrator"); + + bytes4 methodSelector = ForeignGateway.relayRule.selector; + bytes memory data = abi.encodeWithSelector(methodSelector, msg.data); + + l2bridge.sendCrossDomainMessage(data); + } + + /** + * Relay the createDispute call from the foreign gateway to the arbitrator. + * + * // TODO: Implement the evidence redirection from the Evidence v2 standard. + * // TODO: Return the actual disputeId from the court to the ForeignGateway + * for it to maintain it's internal mapping. + * @param _data The calldata to relay + */ + function relayCreateDispute(bytes memory _data) external { + // solhint-disable-next-line avoid-low-level-calls + (bool success, ) = arbitrator.call(_data); + require(success, "Failed to call contract"); + } +} From 2f0542ba3f22bcddfb357f7a2e2d531496dfac9d Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Mon, 27 Dec 2021 13:08:31 +0000 Subject: [PATCH 02/11] refactor(gateway): add {Home,Foreign}Gateway interfaces --- contracts/src/gateway/IForeignGateway.sol | 14 ++++++++++++++ contracts/src/gateway/IHomeGateway.sol | 14 ++++++++++++++ .../{HomeGateway.sol => ArbitrumGateway.sol} | 8 ++++---- .../{ForeignGateway.sol => EthereumGateway.sol} | 14 +++++++------- 4 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 contracts/src/gateway/IForeignGateway.sol create mode 100644 contracts/src/gateway/IHomeGateway.sol rename contracts/src/gateway/arbitrum/{HomeGateway.sol => ArbitrumGateway.sol} (87%) rename contracts/src/gateway/arbitrum/{ForeignGateway.sol => EthereumGateway.sol} (89%) diff --git a/contracts/src/gateway/IForeignGateway.sol b/contracts/src/gateway/IForeignGateway.sol new file mode 100644 index 000000000..7818893c6 --- /dev/null +++ b/contracts/src/gateway/IForeignGateway.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../arbitration/IArbitrator.sol"; + +interface IForeignGateway is IArbitrator { + /** + * Relay the rule call from the home gateway to the arbitrable. + * + * @param _data The calldata to relay + */ + function relayRule(bytes memory _data) external; +} diff --git a/contracts/src/gateway/IHomeGateway.sol b/contracts/src/gateway/IHomeGateway.sol new file mode 100644 index 000000000..0fd2e7fdf --- /dev/null +++ b/contracts/src/gateway/IHomeGateway.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../arbitration/IArbitrable.sol"; + +interface IHomeGateway is IArbitrable { + /** + * Relay the createDispute call from the foreign gateway to the arbitrator. + * + * @param _data The calldata to relay + */ + function relayCreateDispute(bytes memory _data) external; +} diff --git a/contracts/src/gateway/arbitrum/HomeGateway.sol b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol similarity index 87% rename from contracts/src/gateway/arbitrum/HomeGateway.sol rename to contracts/src/gateway/arbitrum/ArbitrumGateway.sol index 495017363..72d10ee78 100644 --- a/contracts/src/gateway/arbitrum/HomeGateway.sol +++ b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.0; -import "../../arbitration/IArbitrable.sol"; import "../../arbitration/IArbitrator.sol"; import "../../bridge/arbitrum/L2Bridge.sol"; -import "./ForeignGateway.sol"; +import "../IHomeGateway.sol"; +import "../IForeignGateway.sol"; -contract HomeGateway is IArbitrable { +contract ArbitrumGateway is IHomeGateway { // L2 bridge with the ForeignGateway as the l1target L2Bridge internal l2bridge; address public arbitrator; @@ -21,7 +21,7 @@ contract HomeGateway is IArbitrable { function rule(uint256, uint256) external { require(msg.sender == arbitrator, "Only Arbitrator"); - bytes4 methodSelector = ForeignGateway.relayRule.selector; + bytes4 methodSelector = IForeignGateway.relayRule.selector; bytes memory data = abi.encodeWithSelector(methodSelector, msg.data); l2bridge.sendCrossDomainMessage(data); diff --git a/contracts/src/gateway/arbitrum/ForeignGateway.sol b/contracts/src/gateway/arbitrum/EthereumGateway.sol similarity index 89% rename from contracts/src/gateway/arbitrum/ForeignGateway.sol rename to contracts/src/gateway/arbitrum/EthereumGateway.sol index 2261f03b8..063373e89 100644 --- a/contracts/src/gateway/arbitrum/ForeignGateway.sol +++ b/contracts/src/gateway/arbitrum/EthereumGateway.sol @@ -3,12 +3,12 @@ pragma solidity ^0.8.0; import "../../arbitration/IArbitrable.sol"; -import "../../arbitration/IArbitrator.sol"; import "../../bridge/arbitrum/L1Bridge.sol"; -import "./HomeGateway.sol"; +import "../IHomeGateway.sol"; +import "../IForeignGateway.sol"; -contract ForeignGateway is IArbitrator { +contract EthereumGateway is IForeignGateway { // L1 bridge with the HomeGateway as the l2target L1Bridge internal l1bridge; @@ -25,7 +25,7 @@ contract ForeignGateway is IArbitrator { function createDispute(uint256 _choices, bytes calldata _extraData) external payable returns (uint256 disputeID) { require(msg.value >= arbitrationCost(_extraData), "Not paid enough for arbitration"); - bytes4 methodSelector = HomeGateway.relayCreateDispute.selector; + bytes4 methodSelector = IHomeGateway.relayCreateDispute.selector; bytes memory data = abi.encodeWithSelector(methodSelector, msg.data); uint256 bridgeCost = l1bridge.getSubmissionPrice(data.length); @@ -43,9 +43,9 @@ contract ForeignGateway is IArbitrator { // only the calldata. l1bridge.sendCrossDomainMessage{value: bridgeCost}(data, 0, 0); - disputeId = 0; // TODO: map to the actual disputeID we get from the V2 court - emit DisputeCreation(disputeId, IArbitrable(msg.sender)); - return disputeId; + disputeID = 0; // TODO: map to the actual disputeID we get from the V2 court + emit DisputeCreation(disputeID, IArbitrable(msg.sender)); + return disputeID; } function arbitrationCost(bytes calldata _extraData) public view returns (uint256 cost) { From 8d95e33b423f43f09a9a743dd105ec29017bd3dc Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Wed, 29 Dec 2021 12:41:26 +0000 Subject: [PATCH 03/11] feat(gateway): add access control functions Note they will always revert right now as the actual sender of the cross domain message will be the wrapper bridge contracts. We need to either have the bridge contracts be inherited or pass their address to the constructor of the other bridge in addition to the address of the gateway. --- .../bridge/arbitrum/AddressAliasHelper.sol | 39 +++++++++++++++++++ contracts/src/bridge/arbitrum/L1Bridge.sol | 7 ++++ contracts/src/bridge/arbitrum/L2Bridge.sol | 5 +++ .../src/gateway/arbitrum/ArbitrumGateway.sol | 8 +++- .../src/gateway/arbitrum/EthereumGateway.sol | 7 +++- 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 contracts/src/bridge/arbitrum/AddressAliasHelper.sol diff --git a/contracts/src/bridge/arbitrum/AddressAliasHelper.sol b/contracts/src/bridge/arbitrum/AddressAliasHelper.sol new file mode 100644 index 000000000..5f79c1042 --- /dev/null +++ b/contracts/src/bridge/arbitrum/AddressAliasHelper.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2019-2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pragma solidity >=0.7.0; + +library AddressAliasHelper { + uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + + /// @notice Utility function that converts the address in the L1 that submitted a tx to + /// the inbox to the msg.sender viewed in the L2 + /// @param l1Address the address in the L1 that triggered the tx to L2 + /// @return l2Address L2 address as viewed in msg.sender + function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { + l2Address = address(uint160(l1Address) + offset); + } + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l2Address L2 address as viewed in msg.sender + /// @return l1Address the address in the L1 that triggered the tx to L2 + function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { + l1Address = address(uint160(l2Address) - offset); + } +} diff --git a/contracts/src/bridge/arbitrum/L1Bridge.sol b/contracts/src/bridge/arbitrum/L1Bridge.sol index c131dc349..ddc738cfc 100644 --- a/contracts/src/bridge/arbitrum/L1Bridge.sol +++ b/contracts/src/bridge/arbitrum/L1Bridge.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import "./interfaces/IInbox.sol"; +import "./interfaces/IOutbox.sol"; import "./interfaces/IArbRetryableTx.sol"; contract L1Bridge { @@ -60,4 +61,10 @@ contract L1Bridge { (uint256 submissionCost, ) = arbRetryableTx.getSubmissionPrice(_calldatasize); return submissionCost; } + + function onlyAuthorized(address _sender) external { + IOutbox outbox = IOutbox(inbox.bridge().activeOutbox()); + address l2Sender = outbox.l2ToL1Sender(); + require(l2Sender == l2Target, "Only L2 target"); + } } diff --git a/contracts/src/bridge/arbitrum/L2Bridge.sol b/contracts/src/bridge/arbitrum/L2Bridge.sol index e19de7163..0d7848820 100644 --- a/contracts/src/bridge/arbitrum/L2Bridge.sol +++ b/contracts/src/bridge/arbitrum/L2Bridge.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import "./interfaces/IArbSys.sol"; +import "./AddressAliasHelper.sol"; contract L2Bridge { address public l1Target; @@ -26,4 +27,8 @@ contract L2Bridge { emit L2ToL1TxCreated(withdrawalId); return withdrawalId; } + + function onlyAuthorized(address _sender) external { + require(_sender == AddressAliasHelper.applyL1ToL2Alias(l1Target), "Only L1 target"); + } } diff --git a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol index 72d10ee78..ed6f9feed 100644 --- a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol +++ b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol @@ -10,9 +10,15 @@ import "../IForeignGateway.sol"; contract ArbitrumGateway is IHomeGateway { // L2 bridge with the ForeignGateway as the l1target + L2Bridge internal l2bridge; address public arbitrator; + modifier onlyFromL1() { + l2bridge.onlyAuthorized(msg.sender); + _; + } + constructor(address _arbitrator, L2Bridge _l2bridge) { arbitrator = _arbitrator; l2bridge = _l2bridge; @@ -35,7 +41,7 @@ contract ArbitrumGateway is IHomeGateway { * for it to maintain it's internal mapping. * @param _data The calldata to relay */ - function relayCreateDispute(bytes memory _data) external { + function relayCreateDispute(bytes memory _data) external onlyFromL1 { // solhint-disable-next-line avoid-low-level-calls (bool success, ) = arbitrator.call(_data); require(success, "Failed to call contract"); diff --git a/contracts/src/gateway/arbitrum/EthereumGateway.sol b/contracts/src/gateway/arbitrum/EthereumGateway.sol index 063373e89..2a7cf35f3 100644 --- a/contracts/src/gateway/arbitrum/EthereumGateway.sol +++ b/contracts/src/gateway/arbitrum/EthereumGateway.sol @@ -17,6 +17,11 @@ contract EthereumGateway is IForeignGateway { // in the V2 court. uint256 internal internalArbitrationCost; + modifier onlyFromL2() { + l1bridge.onlyAuthorized(msg.sender); + _; + } + constructor(uint256 _arbitrationCost, L1Bridge _l1bridge) { internalArbitrationCost = _arbitrationCost; l1bridge = _l1bridge; @@ -66,7 +71,7 @@ contract EthereumGateway is IForeignGateway { * * @param _data The calldata to relay */ - function relayRule(bytes memory _data) external { + function relayRule(bytes memory _data) external onlyFromL2 { address arbitrable = address(0); // see the TODO above about the disputeId // solhint-disable-next-line avoid-low-level-calls From 7c7616f0b991a31ba229c58eebc3a8d1235351f9 Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Thu, 30 Dec 2021 11:57:11 +0000 Subject: [PATCH 04/11] feat(gateway): implement disputeID mapping --- contracts/src/gateway/IForeignGateway.sol | 4 +- contracts/src/gateway/IHomeGateway.sol | 8 ++-- .../src/gateway/arbitrum/ArbitrumGateway.sol | 25 +++++------ .../src/gateway/arbitrum/EthereumGateway.sol | 43 ++++++++++++++----- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/contracts/src/gateway/IForeignGateway.sol b/contracts/src/gateway/IForeignGateway.sol index 7818893c6..3ab92a26b 100644 --- a/contracts/src/gateway/IForeignGateway.sol +++ b/contracts/src/gateway/IForeignGateway.sol @@ -7,8 +7,6 @@ import "../arbitration/IArbitrator.sol"; interface IForeignGateway is IArbitrator { /** * Relay the rule call from the home gateway to the arbitrable. - * - * @param _data The calldata to relay */ - function relayRule(bytes memory _data) external; + function relayRule(bytes32 _disputeHash, uint256 _ruling) external; } diff --git a/contracts/src/gateway/IHomeGateway.sol b/contracts/src/gateway/IHomeGateway.sol index 0fd2e7fdf..af3143abc 100644 --- a/contracts/src/gateway/IHomeGateway.sol +++ b/contracts/src/gateway/IHomeGateway.sol @@ -7,8 +7,10 @@ import "../arbitration/IArbitrable.sol"; interface IHomeGateway is IArbitrable { /** * Relay the createDispute call from the foreign gateway to the arbitrator. - * - * @param _data The calldata to relay */ - function relayCreateDispute(bytes memory _data) external; + function relayCreateDispute( + bytes32 _disputeHash, + uint256 _choices, + bytes calldata _extraData + ) external; } diff --git a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol index ed6f9feed..f0474a52f 100644 --- a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol +++ b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol @@ -12,23 +12,24 @@ contract ArbitrumGateway is IHomeGateway { // L2 bridge with the ForeignGateway as the l1target L2Bridge internal l2bridge; - address public arbitrator; + IArbitrator public arbitrator; + mapping(uint256 => bytes32) disputeIDtoHash; modifier onlyFromL1() { l2bridge.onlyAuthorized(msg.sender); _; } - constructor(address _arbitrator, L2Bridge _l2bridge) { + constructor(IArbitrator _arbitrator, L2Bridge _l2bridge) { arbitrator = _arbitrator; l2bridge = _l2bridge; } - function rule(uint256, uint256) external { - require(msg.sender == arbitrator, "Only Arbitrator"); + function rule(uint256 _disputeID, uint256 _ruling) external { + require(msg.sender == address(arbitrator), "Only Arbitrator"); bytes4 methodSelector = IForeignGateway.relayRule.selector; - bytes memory data = abi.encodeWithSelector(methodSelector, msg.data); + bytes memory data = abi.encodeWithSelector(methodSelector, disputeIDtoHash[_disputeID], _ruling); l2bridge.sendCrossDomainMessage(data); } @@ -37,13 +38,13 @@ contract ArbitrumGateway is IHomeGateway { * Relay the createDispute call from the foreign gateway to the arbitrator. * * // TODO: Implement the evidence redirection from the Evidence v2 standard. - * // TODO: Return the actual disputeId from the court to the ForeignGateway - * for it to maintain it's internal mapping. - * @param _data The calldata to relay */ - function relayCreateDispute(bytes memory _data) external onlyFromL1 { - // solhint-disable-next-line avoid-low-level-calls - (bool success, ) = arbitrator.call(_data); - require(success, "Failed to call contract"); + function relayCreateDispute( + bytes32 _disputeHash, + uint256 _choices, + bytes calldata _extraData + ) external onlyFromL1 { + uint256 disputeID = arbitrator.createDispute(_choices, _extraData); + disputeIDtoHash[disputeID] = _disputeHash; } } diff --git a/contracts/src/gateway/arbitrum/EthereumGateway.sol b/contracts/src/gateway/arbitrum/EthereumGateway.sol index 2a7cf35f3..490908cec 100644 --- a/contracts/src/gateway/arbitrum/EthereumGateway.sol +++ b/contracts/src/gateway/arbitrum/EthereumGateway.sol @@ -11,12 +11,20 @@ import "../IForeignGateway.sol"; contract EthereumGateway is IForeignGateway { // L1 bridge with the HomeGateway as the l2target L1Bridge internal l1bridge; + uint256 localDisputeID; + uint256 chainID; // For now this is just a constant, but we'd probably need to // implement the same arbitrationCost calculation code we'll have // in the V2 court. uint256 internal internalArbitrationCost; + struct Dispute { + uint256 id; + address arbitrable; + } + mapping(bytes32 => Dispute) disputeHashtoDisputeData; + modifier onlyFromL2() { l1bridge.onlyAuthorized(msg.sender); _; @@ -25,13 +33,31 @@ contract EthereumGateway is IForeignGateway { constructor(uint256 _arbitrationCost, L1Bridge _l1bridge) { internalArbitrationCost = _arbitrationCost; l1bridge = _l1bridge; + uint256 id; + assembly { + id := chainid() + } + chainID = id; } function createDispute(uint256 _choices, bytes calldata _extraData) external payable returns (uint256 disputeID) { require(msg.value >= arbitrationCost(_extraData), "Not paid enough for arbitration"); + disputeID = localDisputeID++; + bytes32 disputeHash = keccak256( + abi.encodePacked( + chainID, + blockhash(block.number - 1), + "createDispute", + disputeID, + arbitrationCost(_extraData), + _extraData + ) + ); + disputeHashtoDisputeData[disputeHash] = Dispute({id: disputeID, arbitrable: msg.sender}); + bytes4 methodSelector = IHomeGateway.relayCreateDispute.selector; - bytes memory data = abi.encodeWithSelector(methodSelector, msg.data); + bytes memory data = abi.encodeWithSelector(methodSelector, disputeHash, _choices, _extraData); uint256 bridgeCost = l1bridge.getSubmissionPrice(data.length); // We only pay for the submissionPrice gas cost @@ -44,13 +70,11 @@ contract EthereumGateway is IForeignGateway { // For more details, see: // https://developer.offchainlabs.com/docs/l1_l2_messages#retryable-tickets-contract-api // - // We do NOT forward the arbitrationCost to the HomeGateway yet, + // We do NOT forward the arbitrationCost ETH funds to the HomeGateway yet, // only the calldata. l1bridge.sendCrossDomainMessage{value: bridgeCost}(data, 0, 0); - disputeID = 0; // TODO: map to the actual disputeID we get from the V2 court emit DisputeCreation(disputeID, IArbitrable(msg.sender)); - return disputeID; } function arbitrationCost(bytes calldata _extraData) public view returns (uint256 cost) { @@ -68,14 +92,11 @@ contract EthereumGateway is IForeignGateway { /** * Relay the rule call from the home gateway to the arbitrable. - * - * @param _data The calldata to relay */ - function relayRule(bytes memory _data) external onlyFromL2 { - address arbitrable = address(0); // see the TODO above about the disputeId + function relayRule(bytes32 _disputeHash, uint256 _ruling) external onlyFromL2 { + Dispute memory dispute = disputeHashtoDisputeData[_disputeHash]; - // solhint-disable-next-line avoid-low-level-calls - (bool success, ) = arbitrable.call(_data); - require(success, "Failed to call contract"); + IArbitrable arbitrable = IArbitrable(dispute.arbitrable); + arbitrable.rule(dispute.id, _ruling); } } From 352a08c7f21cefd3c6143058724878dd8524bbe1 Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Mon, 3 Jan 2022 12:16:07 +0000 Subject: [PATCH 05/11] feat(gateway): implement cross-chain evidence standard --- contracts/src/gateway/IForeignEvidence.sol | 11 ++++ contracts/src/gateway/IForeignGateway.sol | 4 ++ contracts/src/gateway/IHomeEvidence.sol | 13 +++++ contracts/src/gateway/IHomeGateway.sol | 4 ++ .../src/gateway/arbitrum/ArbitrumGateway.sol | 51 ++++++++++++++++--- .../src/gateway/arbitrum/EthereumGateway.sol | 47 ++++++++++++++--- 6 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 contracts/src/gateway/IForeignEvidence.sol create mode 100644 contracts/src/gateway/IHomeEvidence.sol diff --git a/contracts/src/gateway/IForeignEvidence.sol b/contracts/src/gateway/IForeignEvidence.sol new file mode 100644 index 000000000..88b4d0538 --- /dev/null +++ b/contracts/src/gateway/IForeignEvidence.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IForeignEvidence { + function disputeID(uint256 _foreignDisputeID) external view returns (uint256); + + function homeChainID(uint256 _disputeID) external view returns (uint256); + + function homeBridge(uint256 _disputeID) external view returns (address); +} diff --git a/contracts/src/gateway/IForeignGateway.sol b/contracts/src/gateway/IForeignGateway.sol index 3ab92a26b..b110eb260 100644 --- a/contracts/src/gateway/IForeignGateway.sol +++ b/contracts/src/gateway/IForeignGateway.sol @@ -5,6 +5,10 @@ pragma solidity ^0.8.0; import "../arbitration/IArbitrator.sol"; interface IForeignGateway is IArbitrator { + function chainID() external view returns (uint256); + + function foreignDisputeHashToID(bytes32 _disputeHash) external view returns (uint256); + /** * Relay the rule call from the home gateway to the arbitrable. */ diff --git a/contracts/src/gateway/IHomeEvidence.sol b/contracts/src/gateway/IHomeEvidence.sol new file mode 100644 index 000000000..4352db591 --- /dev/null +++ b/contracts/src/gateway/IHomeEvidence.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../evidence/IEvidence.sol"; + +interface IHomeEvidence is IEvidence { + function homeToForeignDisputeID(uint256 _homeDisputeID) external view returns (uint256); + + function foreignChainID(uint256 _homeDisputeID) external view returns (uint256); + + function foreignBridge(uint256 _homeDisputeID) external view returns (address); +} diff --git a/contracts/src/gateway/IHomeGateway.sol b/contracts/src/gateway/IHomeGateway.sol index af3143abc..90dbef292 100644 --- a/contracts/src/gateway/IHomeGateway.sol +++ b/contracts/src/gateway/IHomeGateway.sol @@ -5,6 +5,10 @@ pragma solidity ^0.8.0; import "../arbitration/IArbitrable.sol"; interface IHomeGateway is IArbitrable { + function chainID() external view returns (uint256); + + function homeDisputeHashToID(bytes32 _disputeHash) external view returns (uint256); + /** * Relay the createDispute call from the foreign gateway to the arbitrator. */ diff --git a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol index f0474a52f..db6cc82e4 100644 --- a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol +++ b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol @@ -8,21 +8,40 @@ import "../../bridge/arbitrum/L2Bridge.sol"; import "../IHomeGateway.sol"; import "../IForeignGateway.sol"; -contract ArbitrumGateway is IHomeGateway { - // L2 bridge with the ForeignGateway as the l1target +import "../IHomeEvidence.sol"; +contract ArbitrumGateway is IHomeGateway, IHomeEvidence { + // L2 bridge with the ForeignGateway as the l1target L2Bridge internal l2bridge; + + mapping(uint256 => bytes32) public disputeIDtoHash; + mapping(bytes32 => uint256) public disputeHashtoID; + + IForeignGateway public foreignGateway; IArbitrator public arbitrator; - mapping(uint256 => bytes32) disputeIDtoHash; + uint256 public chainID; modifier onlyFromL1() { l2bridge.onlyAuthorized(msg.sender); _; } - constructor(IArbitrator _arbitrator, L2Bridge _l2bridge) { + constructor( + IArbitrator _arbitrator, + L2Bridge _l2bridge, + IForeignGateway _foreignGateway + ) { arbitrator = _arbitrator; l2bridge = _l2bridge; + foreignGateway = _foreignGateway; + + uint256 id; + assembly { + id := chainid() + } + chainID = id; + + emit MetaEvidence(0, "BRIDGE"); } function rule(uint256 _disputeID, uint256 _ruling) external { @@ -36,8 +55,6 @@ contract ArbitrumGateway is IHomeGateway { /** * Relay the createDispute call from the foreign gateway to the arbitrator. - * - * // TODO: Implement the evidence redirection from the Evidence v2 standard. */ function relayCreateDispute( bytes32 _disputeHash, @@ -46,5 +63,27 @@ contract ArbitrumGateway is IHomeGateway { ) external onlyFromL1 { uint256 disputeID = arbitrator.createDispute(_choices, _extraData); disputeIDtoHash[disputeID] = _disputeHash; + disputeHashtoID[_disputeHash] = disputeID; + + emit Dispute(arbitrator, disputeID, 0, 0); + } + + function homeDisputeHashToID(bytes32 _disputeHash) external view returns (uint256) { + return disputeHashtoID[_disputeHash]; + } + + function homeToForeignDisputeID(uint256 _homeDisputeID) external view returns (uint256) { + bytes32 disputeHash = disputeIDtoHash[_homeDisputeID]; + require(disputeHash != 0, "Dispute does not exist"); + + return foreignGateway.foreignDisputeHashToID(disputeHash); + } + + function foreignChainID(uint256 _homeDisputeID) external view returns (uint256) { + return foreignGateway.chainID(); + } + + function foreignBridge(uint256 _homeDisputeID) external view returns (address) { + return address(foreignGateway); } } diff --git a/contracts/src/gateway/arbitrum/EthereumGateway.sol b/contracts/src/gateway/arbitrum/EthereumGateway.sol index 490908cec..5b0320027 100644 --- a/contracts/src/gateway/arbitrum/EthereumGateway.sol +++ b/contracts/src/gateway/arbitrum/EthereumGateway.sol @@ -8,31 +8,42 @@ import "../../bridge/arbitrum/L1Bridge.sol"; import "../IHomeGateway.sol"; import "../IForeignGateway.sol"; -contract EthereumGateway is IForeignGateway { +import "../IForeignEvidence.sol"; + +contract EthereumGateway is IForeignGateway, IForeignEvidence { // L1 bridge with the HomeGateway as the l2target L1Bridge internal l1bridge; - uint256 localDisputeID; - uint256 chainID; + uint256 internal localDisputeID; // For now this is just a constant, but we'd probably need to // implement the same arbitrationCost calculation code we'll have // in the V2 court. uint256 internal internalArbitrationCost; - struct Dispute { + struct DisputeData { uint256 id; address arbitrable; } - mapping(bytes32 => Dispute) disputeHashtoDisputeData; + mapping(uint256 => bytes32) public disputeIDtoHash; + mapping(bytes32 => DisputeData) public disputeHashtoDisputeData; + + IHomeGateway public homeGateway; + uint256 public chainID; modifier onlyFromL2() { l1bridge.onlyAuthorized(msg.sender); _; } - constructor(uint256 _arbitrationCost, L1Bridge _l1bridge) { + constructor( + uint256 _arbitrationCost, + L1Bridge _l1bridge, + IHomeGateway _homeGateway + ) { internalArbitrationCost = _arbitrationCost; l1bridge = _l1bridge; + homeGateway = _homeGateway; + uint256 id; assembly { id := chainid() @@ -54,7 +65,8 @@ contract EthereumGateway is IForeignGateway { _extraData ) ); - disputeHashtoDisputeData[disputeHash] = Dispute({id: disputeID, arbitrable: msg.sender}); + disputeIDtoHash[disputeID] = disputeHash; + disputeHashtoDisputeData[disputeHash] = DisputeData({id: disputeID, arbitrable: msg.sender}); bytes4 methodSelector = IHomeGateway.relayCreateDispute.selector; bytes memory data = abi.encodeWithSelector(methodSelector, disputeHash, _choices, _extraData); @@ -94,9 +106,28 @@ contract EthereumGateway is IForeignGateway { * Relay the rule call from the home gateway to the arbitrable. */ function relayRule(bytes32 _disputeHash, uint256 _ruling) external onlyFromL2 { - Dispute memory dispute = disputeHashtoDisputeData[_disputeHash]; + DisputeData memory dispute = disputeHashtoDisputeData[_disputeHash]; IArbitrable arbitrable = IArbitrable(dispute.arbitrable); arbitrable.rule(dispute.id, _ruling); } + + function foreignDisputeHashToID(bytes32 _disputeHash) external view returns (uint256) { + return disputeHashtoDisputeData[_disputeHash].id; + } + + function disputeID(uint256 _foreignDisputeID) external view returns (uint256) { + bytes32 disputeHash = disputeIDtoHash[_foreignDisputeID]; + require(disputeHash != 0, "Dispute does not exist"); + + return homeGateway.homeDisputeHashToID(disputeHash); + } + + function homeChainID(uint256 _disputeID) external view returns (uint256) { + return homeGateway.chainID(); + } + + function homeBridge(uint256 _disputeID) external view returns (address) { + return address(homeGateway); + } } From c92d64329eb51a2a1cf029f811e142990c8415f4 Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Tue, 4 Jan 2022 13:00:39 +0000 Subject: [PATCH 06/11] feat(bridge): add xdai bridges --- contracts/src/bridge/IL1Bridge.sol | 32 ++++++++++++++ contracts/src/bridge/IL2Bridge.sol | 15 +++++++ contracts/src/bridge/arbitrum/L1Bridge.sol | 5 ++- contracts/src/bridge/arbitrum/L2Bridge.sol | 4 +- contracts/src/bridge/xdai/L1Bridge.sol | 42 +++++++++++++++++++ contracts/src/bridge/xdai/L2Bridge.sol | 28 +++++++++++++ contracts/src/bridge/xdai/interfaces/IAMB.sol | 18 ++++++++ .../src/gateway/arbitrum/ArbitrumGateway.sol | 6 +-- .../src/gateway/arbitrum/EthereumGateway.sol | 6 +-- 9 files changed, 148 insertions(+), 8 deletions(-) create mode 100644 contracts/src/bridge/IL1Bridge.sol create mode 100644 contracts/src/bridge/IL2Bridge.sol create mode 100644 contracts/src/bridge/xdai/L1Bridge.sol create mode 100644 contracts/src/bridge/xdai/L2Bridge.sol create mode 100644 contracts/src/bridge/xdai/interfaces/IAMB.sol diff --git a/contracts/src/bridge/IL1Bridge.sol b/contracts/src/bridge/IL1Bridge.sol new file mode 100644 index 000000000..17c1465cc --- /dev/null +++ b/contracts/src/bridge/IL1Bridge.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IL1Bridge { + /** + * Sends an arbitrary message from one domain to another. + * + * @dev The caller needs to pay some ETH to cover the gas costs + * of the call on L2. Excess ETH that is not used by gas costs will + * be refunded to the `msg.sender` address on L2. + * + * @notice if a user does not desire immediate redemption, they should + * provide a DepositValue of at least CallValue + MaxSubmissionCost. + * If they do desire immediate execution, they should provide a DepositValue + * of at least CallValue + MaxSubmissionCost + (GasPrice x MaxGas). + * + * @param _calldata The L2 encoded message data. + * @param _maxGas Gas limit for immediate L2 execution attempt. + * @param _gasPriceBid L2 Gas price bid for immediate L2 execution attempt. + * @return Unique id to track the message request/transaction. + */ + function sendCrossDomainMessage( + bytes memory _calldata, + uint256 _maxGas, + uint256 _gasPriceBid + ) external payable returns (uint256); + + function getSubmissionPrice(uint256 _calldatasize) external view returns (uint256); + + function onlyAuthorized(address _sender) external; +} diff --git a/contracts/src/bridge/IL2Bridge.sol b/contracts/src/bridge/IL2Bridge.sol new file mode 100644 index 000000000..2b830696f --- /dev/null +++ b/contracts/src/bridge/IL2Bridge.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IL2Bridge { + /** + * Sends an arbitrary message from one domain to another. + * + * @param _calldata The L1 encoded message data. + * @return Unique id to track the message request/transaction. + */ + function sendCrossDomainMessage(bytes memory _calldata) external returns (uint256); + + function onlyAuthorized(address _sender) external; +} diff --git a/contracts/src/bridge/arbitrum/L1Bridge.sol b/contracts/src/bridge/arbitrum/L1Bridge.sol index ddc738cfc..56e149e95 100644 --- a/contracts/src/bridge/arbitrum/L1Bridge.sol +++ b/contracts/src/bridge/arbitrum/L1Bridge.sol @@ -6,7 +6,9 @@ import "./interfaces/IInbox.sol"; import "./interfaces/IOutbox.sol"; import "./interfaces/IArbRetryableTx.sol"; -contract L1Bridge { +import "../IL1Bridge.sol"; + +contract L1Bridge is IL1Bridge { address public l2Target; IInbox public inbox; IArbRetryableTx constant arbRetryableTx = IArbRetryableTx(address(110)); @@ -41,6 +43,7 @@ contract L1Bridge { uint256 _gasPriceBid ) external payable returns (uint256) { uint256 baseSubmissionCost = getSubmissionPrice(_calldata.length); + require(msg.value >= baseSubmissionCost); uint256 ticketID = inbox.createRetryableTicket{value: msg.value}( l2Target, diff --git a/contracts/src/bridge/arbitrum/L2Bridge.sol b/contracts/src/bridge/arbitrum/L2Bridge.sol index 0d7848820..a118a2e0d 100644 --- a/contracts/src/bridge/arbitrum/L2Bridge.sol +++ b/contracts/src/bridge/arbitrum/L2Bridge.sol @@ -5,7 +5,9 @@ pragma solidity ^0.8.0; import "./interfaces/IArbSys.sol"; import "./AddressAliasHelper.sol"; -contract L2Bridge { +import "../IL2Bridge.sol"; + +contract L2Bridge is IL2Bridge { address public l1Target; IArbSys constant arbsys = IArbSys(address(100)); diff --git a/contracts/src/bridge/xdai/L1Bridge.sol b/contracts/src/bridge/xdai/L1Bridge.sol new file mode 100644 index 000000000..70edd8bb6 --- /dev/null +++ b/contracts/src/bridge/xdai/L1Bridge.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./interfaces/IAMB.sol"; + +import "../IL1Bridge.sol"; + +contract L1Bridge is IL1Bridge { + address public l2Target; + IAMB amb; + + constructor(address _l2Target, IAMB _amb) { + l2Target = _l2Target; + amb = _amb; + } + + function sendCrossDomainMessage( + bytes memory _calldata, + uint256 _maxGas, + uint256 _gasPriceBid + ) external payable returns (uint256) { + bytes32 id = amb.requireToPassMessage(l2Target, _calldata, amb.maxGasPerTx()); + return uint256(id); + } + + /** + * @dev The xDai bridge gas cost doesn't depend on the calldata size + * + */ + function getSubmissionPrice( + uint256 /* _calldatasize */ + ) public view returns (uint256) { + return 0; + } + + function onlyAuthorized(address _sender) external { + require(_sender == address(amb), "Only AMB allowed"); + //require(amb.messageSourceChainId() == homeChainId, "Only home chain allowed"); + require(amb.messageSender() == l2Target, "Only home proxy allowed"); + } +} diff --git a/contracts/src/bridge/xdai/L2Bridge.sol b/contracts/src/bridge/xdai/L2Bridge.sol new file mode 100644 index 000000000..97e278f23 --- /dev/null +++ b/contracts/src/bridge/xdai/L2Bridge.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./interfaces/IAMB.sol"; + +import "../IL2Bridge.sol"; + +contract L2Bridge is IL2Bridge { + address public l1Target; + IAMB amb; + + constructor(address _l1Target, IAMB _amb) { + l1Target = _l1Target; + amb = _amb; + } + + function sendCrossDomainMessage(bytes memory _calldata) external returns (uint256) { + bytes32 id = amb.requireToPassMessage(l1Target, _calldata, amb.maxGasPerTx()); + return uint256(id); + } + + function onlyAuthorized(address _sender) external { + require(_sender == address(amb), "Only AMB allowed"); + //require(amb.messageSourceChainId() == homeChainId, "Only home chain allowed"); + require(amb.messageSender() == l1Target, "Only home proxy allowed"); + } +} diff --git a/contracts/src/bridge/xdai/interfaces/IAMB.sol b/contracts/src/bridge/xdai/interfaces/IAMB.sol new file mode 100644 index 000000000..3f5ca3d5e --- /dev/null +++ b/contracts/src/bridge/xdai/interfaces/IAMB.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IAMB { + function requireToPassMessage( + address _contract, + bytes memory _data, + uint256 _gas + ) external returns (bytes32); + + function maxGasPerTx() external view returns (uint256); + + function messageSender() external view returns (address); + + function messageSourceChainId() external view returns (bytes32); + + function messageId() external view returns (bytes32); +} diff --git a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol index db6cc82e4..8ccb46ad8 100644 --- a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol +++ b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../../arbitration/IArbitrator.sol"; -import "../../bridge/arbitrum/L2Bridge.sol"; +import "../../bridge/IL2Bridge.sol"; import "../IHomeGateway.sol"; import "../IForeignGateway.sol"; @@ -12,7 +12,7 @@ import "../IHomeEvidence.sol"; contract ArbitrumGateway is IHomeGateway, IHomeEvidence { // L2 bridge with the ForeignGateway as the l1target - L2Bridge internal l2bridge; + IL2Bridge internal l2bridge; mapping(uint256 => bytes32) public disputeIDtoHash; mapping(bytes32 => uint256) public disputeHashtoID; @@ -28,7 +28,7 @@ contract ArbitrumGateway is IHomeGateway, IHomeEvidence { constructor( IArbitrator _arbitrator, - L2Bridge _l2bridge, + IL2Bridge _l2bridge, IForeignGateway _foreignGateway ) { arbitrator = _arbitrator; diff --git a/contracts/src/gateway/arbitrum/EthereumGateway.sol b/contracts/src/gateway/arbitrum/EthereumGateway.sol index 5b0320027..13e5baf33 100644 --- a/contracts/src/gateway/arbitrum/EthereumGateway.sol +++ b/contracts/src/gateway/arbitrum/EthereumGateway.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../../arbitration/IArbitrable.sol"; -import "../../bridge/arbitrum/L1Bridge.sol"; +import "../../bridge/IL1Bridge.sol"; import "../IHomeGateway.sol"; import "../IForeignGateway.sol"; @@ -12,7 +12,7 @@ import "../IForeignEvidence.sol"; contract EthereumGateway is IForeignGateway, IForeignEvidence { // L1 bridge with the HomeGateway as the l2target - L1Bridge internal l1bridge; + IL1Bridge internal l1bridge; uint256 internal localDisputeID; // For now this is just a constant, but we'd probably need to @@ -37,7 +37,7 @@ contract EthereumGateway is IForeignGateway, IForeignEvidence { constructor( uint256 _arbitrationCost, - L1Bridge _l1bridge, + IL1Bridge _l1bridge, IHomeGateway _homeGateway ) { internalArbitrationCost = _arbitrationCost; From 000b3ef3fc55ad1ec1654166498f9690e9550d8f Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Wed, 5 Jan 2022 03:46:02 +0000 Subject: [PATCH 07/11] refactor(gateway): inherit bridge contracts --- contracts/src/bridge/IL1Bridge.sol | 2 +- contracts/src/bridge/IL2Bridge.sol | 2 +- .../{L1Bridge.sol => ArbL1Bridge.sol} | 6 +- .../{L2Bridge.sol => ArbL2Bridge.sol} | 6 +- .../xdai/{L1Bridge.sol => xDaiL1Bridge.sol} | 10 +- .../xdai/{L2Bridge.sol => xDaiL2Bridge.sol} | 10 +- contracts/src/gateway/BaseForeignGateway.sol | 124 ++++++++++++++++ contracts/src/gateway/BaseHomeGateway.sol | 79 +++++++++++ .../src/gateway/arbitrum/ArbitrumGateway.sol | 86 +----------- .../src/gateway/arbitrum/EthereumGateway.sol | 132 ++---------------- .../{ => interfaces}/IForeignEvidence.sol | 0 .../{ => interfaces}/IForeignGateway.sol | 5 +- .../{ => interfaces}/IHomeEvidence.sol | 2 +- .../gateway/{ => interfaces}/IHomeGateway.sol | 5 +- 14 files changed, 243 insertions(+), 226 deletions(-) rename contracts/src/bridge/arbitrum/{L1Bridge.sol => ArbL1Bridge.sol} (93%) rename contracts/src/bridge/arbitrum/{L2Bridge.sol => ArbL2Bridge.sol} (81%) rename contracts/src/bridge/xdai/{L1Bridge.sol => xDaiL1Bridge.sol} (71%) rename contracts/src/bridge/xdai/{L2Bridge.sol => xDaiL2Bridge.sol} (60%) create mode 100644 contracts/src/gateway/BaseForeignGateway.sol create mode 100644 contracts/src/gateway/BaseHomeGateway.sol rename contracts/src/gateway/{ => interfaces}/IForeignEvidence.sol (100%) rename contracts/src/gateway/{ => interfaces}/IForeignGateway.sol (72%) rename contracts/src/gateway/{ => interfaces}/IHomeEvidence.sol (90%) rename contracts/src/gateway/{ => interfaces}/IHomeGateway.sol (77%) diff --git a/contracts/src/bridge/IL1Bridge.sol b/contracts/src/bridge/IL1Bridge.sol index 17c1465cc..817208036 100644 --- a/contracts/src/bridge/IL1Bridge.sol +++ b/contracts/src/bridge/IL1Bridge.sol @@ -28,5 +28,5 @@ interface IL1Bridge { function getSubmissionPrice(uint256 _calldatasize) external view returns (uint256); - function onlyAuthorized(address _sender) external; + function onlyAuthorized() external; } diff --git a/contracts/src/bridge/IL2Bridge.sol b/contracts/src/bridge/IL2Bridge.sol index 2b830696f..7eab0cf79 100644 --- a/contracts/src/bridge/IL2Bridge.sol +++ b/contracts/src/bridge/IL2Bridge.sol @@ -11,5 +11,5 @@ interface IL2Bridge { */ function sendCrossDomainMessage(bytes memory _calldata) external returns (uint256); - function onlyAuthorized(address _sender) external; + function onlyAuthorized() external; } diff --git a/contracts/src/bridge/arbitrum/L1Bridge.sol b/contracts/src/bridge/arbitrum/ArbL1Bridge.sol similarity index 93% rename from contracts/src/bridge/arbitrum/L1Bridge.sol rename to contracts/src/bridge/arbitrum/ArbL1Bridge.sol index 56e149e95..7dd394bf9 100644 --- a/contracts/src/bridge/arbitrum/L1Bridge.sol +++ b/contracts/src/bridge/arbitrum/ArbL1Bridge.sol @@ -8,7 +8,7 @@ import "./interfaces/IArbRetryableTx.sol"; import "../IL1Bridge.sol"; -contract L1Bridge is IL1Bridge { +contract ArbL1Bridge is IL1Bridge { address public l2Target; IInbox public inbox; IArbRetryableTx constant arbRetryableTx = IArbRetryableTx(address(110)); @@ -43,7 +43,7 @@ contract L1Bridge is IL1Bridge { uint256 _gasPriceBid ) external payable returns (uint256) { uint256 baseSubmissionCost = getSubmissionPrice(_calldata.length); - require(msg.value >= baseSubmissionCost); + require(msg.value >= baseSubmissionCost + (_maxGas * _gasPriceBid)); uint256 ticketID = inbox.createRetryableTicket{value: msg.value}( l2Target, @@ -65,7 +65,7 @@ contract L1Bridge is IL1Bridge { return submissionCost; } - function onlyAuthorized(address _sender) external { + function onlyAuthorized() external { IOutbox outbox = IOutbox(inbox.bridge().activeOutbox()); address l2Sender = outbox.l2ToL1Sender(); require(l2Sender == l2Target, "Only L2 target"); diff --git a/contracts/src/bridge/arbitrum/L2Bridge.sol b/contracts/src/bridge/arbitrum/ArbL2Bridge.sol similarity index 81% rename from contracts/src/bridge/arbitrum/L2Bridge.sol rename to contracts/src/bridge/arbitrum/ArbL2Bridge.sol index a118a2e0d..a0a216447 100644 --- a/contracts/src/bridge/arbitrum/L2Bridge.sol +++ b/contracts/src/bridge/arbitrum/ArbL2Bridge.sol @@ -7,7 +7,7 @@ import "./AddressAliasHelper.sol"; import "../IL2Bridge.sol"; -contract L2Bridge is IL2Bridge { +contract ArbL2Bridge is IL2Bridge { address public l1Target; IArbSys constant arbsys = IArbSys(address(100)); @@ -30,7 +30,7 @@ contract L2Bridge is IL2Bridge { return withdrawalId; } - function onlyAuthorized(address _sender) external { - require(_sender == AddressAliasHelper.applyL1ToL2Alias(l1Target), "Only L1 target"); + function onlyAuthorized() external { + require(msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Target), "Only L1 target"); } } diff --git a/contracts/src/bridge/xdai/L1Bridge.sol b/contracts/src/bridge/xdai/xDaiL1Bridge.sol similarity index 71% rename from contracts/src/bridge/xdai/L1Bridge.sol rename to contracts/src/bridge/xdai/xDaiL1Bridge.sol index 70edd8bb6..99ac7f941 100644 --- a/contracts/src/bridge/xdai/L1Bridge.sol +++ b/contracts/src/bridge/xdai/xDaiL1Bridge.sol @@ -6,7 +6,7 @@ import "./interfaces/IAMB.sol"; import "../IL1Bridge.sol"; -contract L1Bridge is IL1Bridge { +contract xDaiL1Bridge is IL1Bridge { address public l2Target; IAMB amb; @@ -34,9 +34,9 @@ contract L1Bridge is IL1Bridge { return 0; } - function onlyAuthorized(address _sender) external { - require(_sender == address(amb), "Only AMB allowed"); - //require(amb.messageSourceChainId() == homeChainId, "Only home chain allowed"); - require(amb.messageSender() == l2Target, "Only home proxy allowed"); + function onlyAuthorized() external { + require(msg.sender == address(amb), "Only AMB allowed"); + // require(amb.messageSourceChainId() == foreignChainId, "Only foreign chain allowed"); + require(amb.messageSender() == l2Target, "Only foreign gateway allowed"); } } diff --git a/contracts/src/bridge/xdai/L2Bridge.sol b/contracts/src/bridge/xdai/xDaiL2Bridge.sol similarity index 60% rename from contracts/src/bridge/xdai/L2Bridge.sol rename to contracts/src/bridge/xdai/xDaiL2Bridge.sol index 97e278f23..ce30834c4 100644 --- a/contracts/src/bridge/xdai/L2Bridge.sol +++ b/contracts/src/bridge/xdai/xDaiL2Bridge.sol @@ -6,7 +6,7 @@ import "./interfaces/IAMB.sol"; import "../IL2Bridge.sol"; -contract L2Bridge is IL2Bridge { +contract xDaiL2Bridge is IL2Bridge { address public l1Target; IAMB amb; @@ -20,9 +20,9 @@ contract L2Bridge is IL2Bridge { return uint256(id); } - function onlyAuthorized(address _sender) external { - require(_sender == address(amb), "Only AMB allowed"); - //require(amb.messageSourceChainId() == homeChainId, "Only home chain allowed"); - require(amb.messageSender() == l1Target, "Only home proxy allowed"); + function onlyAuthorized() external { + require(msg.sender == address(amb), "Only AMB allowed"); + // require(amb.messageSourceChainId() == homeChainId, "Only home chain allowed"); + require(amb.messageSender() == l1Target, "Only home gateway allowed"); } } diff --git a/contracts/src/gateway/BaseForeignGateway.sol b/contracts/src/gateway/BaseForeignGateway.sol new file mode 100644 index 000000000..51252cbe0 --- /dev/null +++ b/contracts/src/gateway/BaseForeignGateway.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../arbitration/IArbitrable.sol"; +import "../bridge/IL1Bridge.sol"; + +import "./interfaces/IHomeGateway.sol"; +import "./interfaces/IForeignGateway.sol"; + +abstract contract BaseForeignGateway is IL1Bridge, IForeignGateway { + uint256 internal localDisputeID; + + // For now this is just a constant, but we'd probably need to + // implement the same arbitrationCost calculation code we'll have + // in the V2 court. + uint256 internal internalArbitrationCost; + + struct DisputeData { + uint256 id; + address arbitrable; + } + mapping(uint256 => bytes32) public disputeIDtoHash; + mapping(bytes32 => DisputeData) public disputeHashtoDisputeData; + + IHomeGateway public homeGateway; + uint256 public chainID; + + modifier onlyFromL2() { + this.onlyAuthorized(); + _; + } + + constructor(uint256 _arbitrationCost, IHomeGateway _homeGateway) { + internalArbitrationCost = _arbitrationCost; + homeGateway = _homeGateway; + + uint256 id; + assembly { + id := chainid() + } + chainID = id; + } + + function createDispute(uint256 _choices, bytes calldata _extraData) external payable returns (uint256 disputeID) { + require(msg.value >= arbitrationCost(_extraData), "Not paid enough for arbitration"); + + disputeID = localDisputeID++; + bytes32 disputeHash = keccak256( + abi.encodePacked( + chainID, + blockhash(block.number - 1), + "createDispute", + disputeID, + arbitrationCost(_extraData), + _extraData + ) + ); + disputeIDtoHash[disputeID] = disputeHash; + disputeHashtoDisputeData[disputeHash] = DisputeData({id: disputeID, arbitrable: msg.sender}); + + bytes4 methodSelector = IHomeGateway.relayCreateDispute.selector; + bytes memory data = abi.encodeWithSelector(methodSelector, disputeHash, _choices, _extraData); + + uint256 bridgeCost = this.getSubmissionPrice(data.length); + // We only pay for the submissionPrice gas cost + // which is minimum gas cost required for submitting a + // arbitrum retryable ticket to the retry buffer for + // bridge to L2. + // For immediate inclusion a user/bot needs to pay (GasPrice x MaxGas) + // with the associated ticketId that is emitted by this function + // after the ticket is successfully submitted. + // For more details, see: + // https://developer.offchainlabs.com/docs/l1_l2_messages#retryable-tickets-contract-api + // + // We do NOT forward the arbitrationCost ETH funds to the HomeGateway yet, + // only the calldata. + this.sendCrossDomainMessage{value: bridgeCost}(data, 0, 0); + + emit DisputeCreation(disputeID, IArbitrable(msg.sender)); + } + + function arbitrationCost(bytes calldata _extraData) public view returns (uint256 cost) { + // Calculate the size of calldata that will be passed to the L2 bridge + // as that is a factor for the bridging cost. + // Calldata size of relayCreateDispute: + // relayCreateDispute methodId + + // (createDispute methodId + uint256 _choices + bytes _extraData) + // 4 + 4 + 32 + dynamic + uint256 calldatasize = 40 + _extraData.length; + + uint256 bridgeCost = this.getSubmissionPrice(calldatasize); + return bridgeCost + internalArbitrationCost; + } + + /** + * Relay the rule call from the home gateway to the arbitrable. + */ + function relayRule(bytes32 _disputeHash, uint256 _ruling) external onlyFromL2 { + DisputeData memory dispute = disputeHashtoDisputeData[_disputeHash]; + + IArbitrable arbitrable = IArbitrable(dispute.arbitrable); + arbitrable.rule(dispute.id, _ruling); + } + + function foreignDisputeHashToID(bytes32 _disputeHash) external view returns (uint256) { + return disputeHashtoDisputeData[_disputeHash].id; + } + + function disputeID(uint256 _foreignDisputeID) external view returns (uint256) { + bytes32 disputeHash = disputeIDtoHash[_foreignDisputeID]; + require(disputeHash != 0, "Dispute does not exist"); + + return homeGateway.homeDisputeHashToID(disputeHash); + } + + function homeChainID(uint256 _disputeID) external view returns (uint256) { + return homeGateway.chainID(); + } + + function homeBridge(uint256 _disputeID) external view returns (address) { + return address(homeGateway); + } +} diff --git a/contracts/src/gateway/BaseHomeGateway.sol b/contracts/src/gateway/BaseHomeGateway.sol new file mode 100644 index 000000000..e9f9edd42 --- /dev/null +++ b/contracts/src/gateway/BaseHomeGateway.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../arbitration/IArbitrator.sol"; +import "../bridge/IL2Bridge.sol"; + +import "./interfaces/IHomeGateway.sol"; +import "./interfaces/IForeignGateway.sol"; + +abstract contract BaseHomeGateway is IL2Bridge, IHomeGateway { + mapping(uint256 => bytes32) public disputeIDtoHash; + mapping(bytes32 => uint256) public disputeHashtoID; + + IForeignGateway public foreignGateway; + IArbitrator public arbitrator; + uint256 public chainID; + + modifier onlyFromL1() { + this.onlyAuthorized(); + _; + } + + constructor(IArbitrator _arbitrator, IForeignGateway _foreignGateway) { + arbitrator = _arbitrator; + foreignGateway = _foreignGateway; + + uint256 id; + assembly { + id := chainid() + } + chainID = id; + + emit MetaEvidence(0, "BRIDGE"); + } + + function rule(uint256 _disputeID, uint256 _ruling) external { + require(msg.sender == address(arbitrator), "Only Arbitrator"); + + bytes4 methodSelector = IForeignGateway.relayRule.selector; + bytes memory data = abi.encodeWithSelector(methodSelector, disputeIDtoHash[_disputeID], _ruling); + + this.sendCrossDomainMessage(data); + } + + /** + * Relay the createDispute call from the foreign gateway to the arbitrator. + */ + function relayCreateDispute( + bytes32 _disputeHash, + uint256 _choices, + bytes calldata _extraData + ) external onlyFromL1 { + uint256 disputeID = arbitrator.createDispute(_choices, _extraData); + disputeIDtoHash[disputeID] = _disputeHash; + disputeHashtoID[_disputeHash] = disputeID; + + emit Dispute(arbitrator, disputeID, 0, 0); + } + + function homeDisputeHashToID(bytes32 _disputeHash) external view returns (uint256) { + return disputeHashtoID[_disputeHash]; + } + + function homeToForeignDisputeID(uint256 _homeDisputeID) external view returns (uint256) { + bytes32 disputeHash = disputeIDtoHash[_homeDisputeID]; + require(disputeHash != 0, "Dispute does not exist"); + + return foreignGateway.foreignDisputeHashToID(disputeHash); + } + + function foreignChainID(uint256 _homeDisputeID) external view returns (uint256) { + return foreignGateway.chainID(); + } + + function foreignBridge(uint256 _homeDisputeID) external view returns (address) { + return address(foreignGateway); + } +} diff --git a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol index 8ccb46ad8..c30bee17f 100644 --- a/contracts/src/gateway/arbitrum/ArbitrumGateway.sol +++ b/contracts/src/gateway/arbitrum/ArbitrumGateway.sol @@ -3,87 +3,15 @@ pragma solidity ^0.8.0; import "../../arbitration/IArbitrator.sol"; -import "../../bridge/IL2Bridge.sol"; +import "../../bridge/arbitrum/ArbL2Bridge.sol"; -import "../IHomeGateway.sol"; -import "../IForeignGateway.sol"; - -import "../IHomeEvidence.sol"; - -contract ArbitrumGateway is IHomeGateway, IHomeEvidence { - // L2 bridge with the ForeignGateway as the l1target - IL2Bridge internal l2bridge; - - mapping(uint256 => bytes32) public disputeIDtoHash; - mapping(bytes32 => uint256) public disputeHashtoID; - - IForeignGateway public foreignGateway; - IArbitrator public arbitrator; - uint256 public chainID; - - modifier onlyFromL1() { - l2bridge.onlyAuthorized(msg.sender); - _; - } +import "../interfaces/IHomeGateway.sol"; +import "../BaseHomeGateway.sol"; +contract ArbitrumGateway is BaseHomeGateway, ArbL2Bridge { constructor( IArbitrator _arbitrator, - IL2Bridge _l2bridge, - IForeignGateway _foreignGateway - ) { - arbitrator = _arbitrator; - l2bridge = _l2bridge; - foreignGateway = _foreignGateway; - - uint256 id; - assembly { - id := chainid() - } - chainID = id; - - emit MetaEvidence(0, "BRIDGE"); - } - - function rule(uint256 _disputeID, uint256 _ruling) external { - require(msg.sender == address(arbitrator), "Only Arbitrator"); - - bytes4 methodSelector = IForeignGateway.relayRule.selector; - bytes memory data = abi.encodeWithSelector(methodSelector, disputeIDtoHash[_disputeID], _ruling); - - l2bridge.sendCrossDomainMessage(data); - } - - /** - * Relay the createDispute call from the foreign gateway to the arbitrator. - */ - function relayCreateDispute( - bytes32 _disputeHash, - uint256 _choices, - bytes calldata _extraData - ) external onlyFromL1 { - uint256 disputeID = arbitrator.createDispute(_choices, _extraData); - disputeIDtoHash[disputeID] = _disputeHash; - disputeHashtoID[_disputeHash] = disputeID; - - emit Dispute(arbitrator, disputeID, 0, 0); - } - - function homeDisputeHashToID(bytes32 _disputeHash) external view returns (uint256) { - return disputeHashtoID[_disputeHash]; - } - - function homeToForeignDisputeID(uint256 _homeDisputeID) external view returns (uint256) { - bytes32 disputeHash = disputeIDtoHash[_homeDisputeID]; - require(disputeHash != 0, "Dispute does not exist"); - - return foreignGateway.foreignDisputeHashToID(disputeHash); - } - - function foreignChainID(uint256 _homeDisputeID) external view returns (uint256) { - return foreignGateway.chainID(); - } - - function foreignBridge(uint256 _homeDisputeID) external view returns (address) { - return address(foreignGateway); - } + IForeignGateway _foreignGateway, + address _l1Target + ) BaseHomeGateway(_arbitrator, _foreignGateway) ArbL2Bridge(_l1Target) {} } diff --git a/contracts/src/gateway/arbitrum/EthereumGateway.sol b/contracts/src/gateway/arbitrum/EthereumGateway.sol index 13e5baf33..e90bde916 100644 --- a/contracts/src/gateway/arbitrum/EthereumGateway.sol +++ b/contracts/src/gateway/arbitrum/EthereumGateway.sol @@ -2,132 +2,16 @@ pragma solidity ^0.8.0; -import "../../arbitration/IArbitrable.sol"; -import "../../bridge/IL1Bridge.sol"; +import "../../bridge/arbitrum/ArbL1Bridge.sol"; -import "../IHomeGateway.sol"; -import "../IForeignGateway.sol"; - -import "../IForeignEvidence.sol"; - -contract EthereumGateway is IForeignGateway, IForeignEvidence { - // L1 bridge with the HomeGateway as the l2target - IL1Bridge internal l1bridge; - uint256 internal localDisputeID; - - // For now this is just a constant, but we'd probably need to - // implement the same arbitrationCost calculation code we'll have - // in the V2 court. - uint256 internal internalArbitrationCost; - - struct DisputeData { - uint256 id; - address arbitrable; - } - mapping(uint256 => bytes32) public disputeIDtoHash; - mapping(bytes32 => DisputeData) public disputeHashtoDisputeData; - - IHomeGateway public homeGateway; - uint256 public chainID; - - modifier onlyFromL2() { - l1bridge.onlyAuthorized(msg.sender); - _; - } +import "../interfaces/IForeignGateway.sol"; +import "../BaseForeignGateway.sol"; +contract EthereumGateway is BaseForeignGateway, ArbL1Bridge { constructor( uint256 _arbitrationCost, - IL1Bridge _l1bridge, - IHomeGateway _homeGateway - ) { - internalArbitrationCost = _arbitrationCost; - l1bridge = _l1bridge; - homeGateway = _homeGateway; - - uint256 id; - assembly { - id := chainid() - } - chainID = id; - } - - function createDispute(uint256 _choices, bytes calldata _extraData) external payable returns (uint256 disputeID) { - require(msg.value >= arbitrationCost(_extraData), "Not paid enough for arbitration"); - - disputeID = localDisputeID++; - bytes32 disputeHash = keccak256( - abi.encodePacked( - chainID, - blockhash(block.number - 1), - "createDispute", - disputeID, - arbitrationCost(_extraData), - _extraData - ) - ); - disputeIDtoHash[disputeID] = disputeHash; - disputeHashtoDisputeData[disputeHash] = DisputeData({id: disputeID, arbitrable: msg.sender}); - - bytes4 methodSelector = IHomeGateway.relayCreateDispute.selector; - bytes memory data = abi.encodeWithSelector(methodSelector, disputeHash, _choices, _extraData); - - uint256 bridgeCost = l1bridge.getSubmissionPrice(data.length); - // We only pay for the submissionPrice gas cost - // which is minimum gas cost required for submitting a - // arbitrum retryable ticket to the retry buffer for - // bridge to L2. - // For immediate inclusion a user/bot needs to pay (GasPrice x MaxGas) - // with the associated ticketId that is emitted by this function - // after the ticket is successfully submitted. - // For more details, see: - // https://developer.offchainlabs.com/docs/l1_l2_messages#retryable-tickets-contract-api - // - // We do NOT forward the arbitrationCost ETH funds to the HomeGateway yet, - // only the calldata. - l1bridge.sendCrossDomainMessage{value: bridgeCost}(data, 0, 0); - - emit DisputeCreation(disputeID, IArbitrable(msg.sender)); - } - - function arbitrationCost(bytes calldata _extraData) public view returns (uint256 cost) { - // Calculate the size of calldata that will be passed to the L2 bridge - // as that is a factor for the bridging cost. - // Calldata size of relayCreateDispute: - // relayCreateDispute methodId + - // (createDispute methodId + uint256 _choices + bytes _extraData) - // 4 + 4 + 32 + dynamic - uint256 calldatasize = 40 + _extraData.length; - - uint256 bridgeCost = l1bridge.getSubmissionPrice(calldatasize); - return bridgeCost + internalArbitrationCost; - } - - /** - * Relay the rule call from the home gateway to the arbitrable. - */ - function relayRule(bytes32 _disputeHash, uint256 _ruling) external onlyFromL2 { - DisputeData memory dispute = disputeHashtoDisputeData[_disputeHash]; - - IArbitrable arbitrable = IArbitrable(dispute.arbitrable); - arbitrable.rule(dispute.id, _ruling); - } - - function foreignDisputeHashToID(bytes32 _disputeHash) external view returns (uint256) { - return disputeHashtoDisputeData[_disputeHash].id; - } - - function disputeID(uint256 _foreignDisputeID) external view returns (uint256) { - bytes32 disputeHash = disputeIDtoHash[_foreignDisputeID]; - require(disputeHash != 0, "Dispute does not exist"); - - return homeGateway.homeDisputeHashToID(disputeHash); - } - - function homeChainID(uint256 _disputeID) external view returns (uint256) { - return homeGateway.chainID(); - } - - function homeBridge(uint256 _disputeID) external view returns (address) { - return address(homeGateway); - } + IHomeGateway _homeGateway, + address _l2Target, + address _inbox + ) BaseForeignGateway(_arbitrationCost, _homeGateway) ArbL1Bridge(_l2Target, _inbox) {} } diff --git a/contracts/src/gateway/IForeignEvidence.sol b/contracts/src/gateway/interfaces/IForeignEvidence.sol similarity index 100% rename from contracts/src/gateway/IForeignEvidence.sol rename to contracts/src/gateway/interfaces/IForeignEvidence.sol diff --git a/contracts/src/gateway/IForeignGateway.sol b/contracts/src/gateway/interfaces/IForeignGateway.sol similarity index 72% rename from contracts/src/gateway/IForeignGateway.sol rename to contracts/src/gateway/interfaces/IForeignGateway.sol index b110eb260..5732f12a9 100644 --- a/contracts/src/gateway/IForeignGateway.sol +++ b/contracts/src/gateway/interfaces/IForeignGateway.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.0; -import "../arbitration/IArbitrator.sol"; +import "../../arbitration/IArbitrator.sol"; +import "./IForeignEvidence.sol"; -interface IForeignGateway is IArbitrator { +interface IForeignGateway is IArbitrator, IForeignEvidence { function chainID() external view returns (uint256); function foreignDisputeHashToID(bytes32 _disputeHash) external view returns (uint256); diff --git a/contracts/src/gateway/IHomeEvidence.sol b/contracts/src/gateway/interfaces/IHomeEvidence.sol similarity index 90% rename from contracts/src/gateway/IHomeEvidence.sol rename to contracts/src/gateway/interfaces/IHomeEvidence.sol index 4352db591..bda0011d4 100644 --- a/contracts/src/gateway/IHomeEvidence.sol +++ b/contracts/src/gateway/interfaces/IHomeEvidence.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import "../evidence/IEvidence.sol"; +import "../../evidence/IEvidence.sol"; interface IHomeEvidence is IEvidence { function homeToForeignDisputeID(uint256 _homeDisputeID) external view returns (uint256); diff --git a/contracts/src/gateway/IHomeGateway.sol b/contracts/src/gateway/interfaces/IHomeGateway.sol similarity index 77% rename from contracts/src/gateway/IHomeGateway.sol rename to contracts/src/gateway/interfaces/IHomeGateway.sol index 90dbef292..4f018d9c1 100644 --- a/contracts/src/gateway/IHomeGateway.sol +++ b/contracts/src/gateway/interfaces/IHomeGateway.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.0; -import "../arbitration/IArbitrable.sol"; +import "../../arbitration/IArbitrable.sol"; +import "./IHomeEvidence.sol"; -interface IHomeGateway is IArbitrable { +interface IHomeGateway is IArbitrable, IHomeEvidence { function chainID() external view returns (uint256); function homeDisputeHashToID(bytes32 _disputeHash) external view returns (uint256); From 81b688b722a9d93d74754dbaa70e27cf2148d65d Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Wed, 5 Jan 2022 13:59:03 +0530 Subject: [PATCH 08/11] feat: add xdai gateway contracts --- .../src/gateway/xdai/EthereumGateway.sol | 18 ++++++++++++++++++ contracts/src/gateway/xdai/xDaiGateway.sol | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 contracts/src/gateway/xdai/EthereumGateway.sol create mode 100644 contracts/src/gateway/xdai/xDaiGateway.sol diff --git a/contracts/src/gateway/xdai/EthereumGateway.sol b/contracts/src/gateway/xdai/EthereumGateway.sol new file mode 100644 index 000000000..44759d8bd --- /dev/null +++ b/contracts/src/gateway/xdai/EthereumGateway.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../bridge/xdai/xDaiL1Bridge.sol"; +import "../../bridge/xdai/interfaces/IAMB.sol"; + +import "../interfaces/IForeignGateway.sol"; +import "../BaseForeignGateway.sol"; + +contract EthereumGateway is BaseForeignGateway, xDaiL1Bridge { + constructor( + uint256 _arbitrationCost, + IHomeGateway _homeGateway, + address _l2Target, + IAMB _amb + ) BaseForeignGateway(_arbitrationCost, _homeGateway) xDaiL1Bridge(_l2Target, _amb) {} +} diff --git a/contracts/src/gateway/xdai/xDaiGateway.sol b/contracts/src/gateway/xdai/xDaiGateway.sol new file mode 100644 index 000000000..6effc4b92 --- /dev/null +++ b/contracts/src/gateway/xdai/xDaiGateway.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../arbitration/IArbitrator.sol"; +import "../../bridge/xdai/xDaiL2Bridge.sol"; +import "../../bridge/xdai/interfaces/IAMB.sol"; + +import "../interfaces/IHomeGateway.sol"; +import "../BaseHomeGateway.sol"; + +contract xDaiGateway is BaseHomeGateway, xDaiL2Bridge { + constructor( + IArbitrator _arbitrator, + IForeignGateway _foreignGateway, + address _l1Target, + IAMB _amb + ) BaseHomeGateway(_arbitrator, _foreignGateway) xDaiL2Bridge(_l1Target, _amb) {} +} From f22d90e00603b034c817e7012b2cdf5617a9f3d7 Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Wed, 5 Jan 2022 09:16:32 +0000 Subject: [PATCH 09/11] fix(gateway): fix calldata size calculation for bridging cost --- contracts/src/gateway/BaseForeignGateway.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/src/gateway/BaseForeignGateway.sol b/contracts/src/gateway/BaseForeignGateway.sol index 51252cbe0..63ac8fd87 100644 --- a/contracts/src/gateway/BaseForeignGateway.sol +++ b/contracts/src/gateway/BaseForeignGateway.sol @@ -85,9 +85,9 @@ abstract contract BaseForeignGateway is IL1Bridge, IForeignGateway { // as that is a factor for the bridging cost. // Calldata size of relayCreateDispute: // relayCreateDispute methodId + - // (createDispute methodId + uint256 _choices + bytes _extraData) - // 4 + 4 + 32 + dynamic - uint256 calldatasize = 40 + _extraData.length; + // (createDispute methodId + bytes32 disputeHash + uint256 _choices + bytes _extraData) + // 4 + 4 + 32 + 32 + dynamic + uint256 calldatasize = 82 + _extraData.length; uint256 bridgeCost = this.getSubmissionPrice(calldatasize); return bridgeCost + internalArbitrationCost; From a9df0ab04df8659375441be995ad39ce9158922a Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Fri, 14 Jan 2022 09:10:05 +0000 Subject: [PATCH 10/11] refactor(gateway): start the foreignDisputeID from 1 --- contracts/src/gateway/BaseForeignGateway.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/src/gateway/BaseForeignGateway.sol b/contracts/src/gateway/BaseForeignGateway.sol index 63ac8fd87..678b22968 100644 --- a/contracts/src/gateway/BaseForeignGateway.sol +++ b/contracts/src/gateway/BaseForeignGateway.sol @@ -9,7 +9,9 @@ import "./interfaces/IHomeGateway.sol"; import "./interfaces/IForeignGateway.sol"; abstract contract BaseForeignGateway is IL1Bridge, IForeignGateway { - uint256 internal localDisputeID; + // @dev Note the disputeID needs to start from one as + // the KlerosV1 proxy governor depends on this implementation. + uint256 internal localDisputeID = 1; // For now this is just a constant, but we'd probably need to // implement the same arbitrationCost calculation code we'll have From 26d46413dcd0714b45cf82c8b89503f92a7f1971 Mon Sep 17 00:00:00 2001 From: Shaleen Jain Date: Tue, 18 Jan 2022 07:39:16 +0000 Subject: [PATCH 11/11] fix(gateway): update disputeHash cal --- contracts/src/gateway/BaseForeignGateway.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/src/gateway/BaseForeignGateway.sol b/contracts/src/gateway/BaseForeignGateway.sol index 678b22968..802e5d907 100644 --- a/contracts/src/gateway/BaseForeignGateway.sol +++ b/contracts/src/gateway/BaseForeignGateway.sol @@ -55,7 +55,8 @@ abstract contract BaseForeignGateway is IL1Bridge, IForeignGateway { "createDispute", disputeID, arbitrationCost(_extraData), - _extraData + _extraData, + msg.sender ) ); disputeIDtoHash[disputeID] = disputeHash;