-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from kleros/feature/gateway
Add arbitrum specific gateway contracts
- Loading branch information
Showing
18 changed files
with
539 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "./interfaces/IAMB.sol"; | ||
|
||
import "../IL1Bridge.sol"; | ||
|
||
contract xDaiL1Bridge 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() 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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "./interfaces/IAMB.sol"; | ||
|
||
import "../IL2Bridge.sol"; | ||
|
||
contract xDaiL2Bridge 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() 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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// 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 { | ||
// @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 | ||
// 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, | ||
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); | ||
|
||
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 + bytes32 disputeHash + uint256 _choices + bytes _extraData) | ||
// 4 + 4 + 32 + 32 + dynamic | ||
uint256 calldatasize = 82 + _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); | ||
} | ||
} |
Oops, something went wrong.