Skip to content

Commit cbde56a

Browse files
committed
Merge branch 'master' into feat/core-plurality-disputekit-jaybuidl
2 parents 25fbf41 + 4e13c91 commit cbde56a

21 files changed

+857
-4
lines changed

contracts/src/bridge/IL1Bridge.sol

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
interface IL1Bridge {
6+
/**
7+
* Sends an arbitrary message from one domain to another.
8+
*
9+
* @dev The caller needs to pay some ETH to cover the gas costs
10+
* of the call on L2. Excess ETH that is not used by gas costs will
11+
* be refunded to the `msg.sender` address on L2.
12+
*
13+
* @notice if a user does not desire immediate redemption, they should
14+
* provide a DepositValue of at least CallValue + MaxSubmissionCost.
15+
* If they do desire immediate execution, they should provide a DepositValue
16+
* of at least CallValue + MaxSubmissionCost + (GasPrice x MaxGas).
17+
*
18+
* @param _calldata The L2 encoded message data.
19+
* @param _maxGas Gas limit for immediate L2 execution attempt.
20+
* @param _gasPriceBid L2 Gas price bid for immediate L2 execution attempt.
21+
* @return Unique id to track the message request/transaction.
22+
*/
23+
function sendCrossDomainMessage(
24+
bytes memory _calldata,
25+
uint256 _maxGas,
26+
uint256 _gasPriceBid
27+
) external payable returns (uint256);
28+
29+
function getSubmissionPrice(uint256 _calldatasize) external view returns (uint256);
30+
31+
function onlyAuthorized() external;
32+
}

contracts/src/bridge/IL2Bridge.sol

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
interface IL2Bridge {
6+
/**
7+
* Sends an arbitrary message from one domain to another.
8+
*
9+
* @param _calldata The L1 encoded message data.
10+
* @return Unique id to track the message request/transaction.
11+
*/
12+
function sendCrossDomainMessage(bytes memory _calldata) external returns (uint256);
13+
14+
function onlyAuthorized() external;
15+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
/*
4+
* Copyright 2019-2021, Offchain Labs, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
pragma solidity >=0.7.0;
20+
21+
library AddressAliasHelper {
22+
uint160 constant offset = uint160(0x1111000000000000000000000000000000001111);
23+
24+
/// @notice Utility function that converts the address in the L1 that submitted a tx to
25+
/// the inbox to the msg.sender viewed in the L2
26+
/// @param l1Address the address in the L1 that triggered the tx to L2
27+
/// @return l2Address L2 address as viewed in msg.sender
28+
function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
29+
l2Address = address(uint160(l1Address) + offset);
30+
}
31+
32+
/// @notice Utility function that converts the msg.sender viewed in the L2 to the
33+
/// address in the L1 that submitted a tx to the inbox
34+
/// @param l2Address L2 address as viewed in msg.sender
35+
/// @return l1Address the address in the L1 that triggered the tx to L2
36+
function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
37+
l1Address = address(uint160(l2Address) - offset);
38+
}
39+
}

contracts/src/bridge/arbitrum/L1Bridge.sol renamed to contracts/src/bridge/arbitrum/ArbL1Bridge.sol

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
pragma solidity ^0.8.0;
44

55
import "./interfaces/IInbox.sol";
6+
import "./interfaces/IOutbox.sol";
67
import "./interfaces/IArbRetryableTx.sol";
78

8-
contract L1Bridge {
9+
import "../IL1Bridge.sol";
10+
11+
contract ArbL1Bridge is IL1Bridge {
912
address public l2Target;
1013
IInbox public inbox;
1114
IArbRetryableTx constant arbRetryableTx = IArbRetryableTx(address(110));
@@ -39,7 +42,8 @@ contract L1Bridge {
3942
uint256 _maxGas,
4043
uint256 _gasPriceBid
4144
) external payable returns (uint256) {
42-
(uint256 baseSubmissionCost, ) = arbRetryableTx.getSubmissionPrice(_calldata.length);
45+
uint256 baseSubmissionCost = getSubmissionPrice(_calldata.length);
46+
require(msg.value >= baseSubmissionCost + (_maxGas * _gasPriceBid));
4347

4448
uint256 ticketID = inbox.createRetryableTicket{value: msg.value}(
4549
l2Target,
@@ -55,4 +59,15 @@ contract L1Bridge {
5559
emit RetryableTicketCreated(ticketID);
5660
return ticketID;
5761
}
62+
63+
function getSubmissionPrice(uint256 _calldatasize) public view returns (uint256) {
64+
(uint256 submissionCost, ) = arbRetryableTx.getSubmissionPrice(_calldatasize);
65+
return submissionCost;
66+
}
67+
68+
function onlyAuthorized() external {
69+
IOutbox outbox = IOutbox(inbox.bridge().activeOutbox());
70+
address l2Sender = outbox.l2ToL1Sender();
71+
require(l2Sender == l2Target, "Only L2 target");
72+
}
5873
}

contracts/src/bridge/arbitrum/L2Bridge.sol renamed to contracts/src/bridge/arbitrum/ArbL2Bridge.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
pragma solidity ^0.8.0;
44

55
import "./interfaces/IArbSys.sol";
6+
import "./AddressAliasHelper.sol";
67

7-
contract L2Bridge {
8+
import "../IL2Bridge.sol";
9+
10+
contract ArbL2Bridge is IL2Bridge {
811
address public l1Target;
912
IArbSys constant arbsys = IArbSys(address(100));
1013

@@ -20,10 +23,14 @@ contract L2Bridge {
2023
* @param _calldata The L1 encoded message data.
2124
* @return Unique id to track the message request/transaction.
2225
*/
23-
function sendCrossDomainMessage(bytes memory _calldata) external payable returns (uint256) {
26+
function sendCrossDomainMessage(bytes memory _calldata) external returns (uint256) {
2427
uint256 withdrawalId = arbsys.sendTxToL1(l1Target, _calldata);
2528

2629
emit L2ToL1TxCreated(withdrawalId);
2730
return withdrawalId;
2831
}
32+
33+
function onlyAuthorized() external {
34+
require(msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Target), "Only L1 target");
35+
}
2936
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
interface IAMB {
5+
function requireToPassMessage(
6+
address _contract,
7+
bytes memory _data,
8+
uint256 _gas
9+
) external returns (bytes32);
10+
11+
function maxGasPerTx() external view returns (uint256);
12+
13+
function messageSender() external view returns (address);
14+
15+
function messageSourceChainId() external view returns (bytes32);
16+
17+
function messageId() external view returns (bytes32);
18+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import "./interfaces/IAMB.sol";
6+
7+
import "../IL1Bridge.sol";
8+
9+
contract xDaiL1Bridge is IL1Bridge {
10+
address public l2Target;
11+
IAMB amb;
12+
13+
constructor(address _l2Target, IAMB _amb) {
14+
l2Target = _l2Target;
15+
amb = _amb;
16+
}
17+
18+
function sendCrossDomainMessage(
19+
bytes memory _calldata,
20+
uint256 _maxGas,
21+
uint256 _gasPriceBid
22+
) external payable returns (uint256) {
23+
bytes32 id = amb.requireToPassMessage(l2Target, _calldata, amb.maxGasPerTx());
24+
return uint256(id);
25+
}
26+
27+
/**
28+
* @dev The xDai bridge gas cost doesn't depend on the calldata size
29+
*
30+
*/
31+
function getSubmissionPrice(
32+
uint256 /* _calldatasize */
33+
) public view returns (uint256) {
34+
return 0;
35+
}
36+
37+
function onlyAuthorized() external {
38+
require(msg.sender == address(amb), "Only AMB allowed");
39+
// require(amb.messageSourceChainId() == foreignChainId, "Only foreign chain allowed");
40+
require(amb.messageSender() == l2Target, "Only foreign gateway allowed");
41+
}
42+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import "./interfaces/IAMB.sol";
6+
7+
import "../IL2Bridge.sol";
8+
9+
contract xDaiL2Bridge is IL2Bridge {
10+
address public l1Target;
11+
IAMB amb;
12+
13+
constructor(address _l1Target, IAMB _amb) {
14+
l1Target = _l1Target;
15+
amb = _amb;
16+
}
17+
18+
function sendCrossDomainMessage(bytes memory _calldata) external returns (uint256) {
19+
bytes32 id = amb.requireToPassMessage(l1Target, _calldata, amb.maxGasPerTx());
20+
return uint256(id);
21+
}
22+
23+
function onlyAuthorized() external {
24+
require(msg.sender == address(amb), "Only AMB allowed");
25+
// require(amb.messageSourceChainId() == homeChainId, "Only home chain allowed");
26+
require(amb.messageSender() == l1Target, "Only home gateway allowed");
27+
}
28+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import "../arbitration/IArbitrable.sol";
6+
import "../bridge/IL1Bridge.sol";
7+
8+
import "./interfaces/IHomeGateway.sol";
9+
import "./interfaces/IForeignGateway.sol";
10+
11+
abstract contract BaseForeignGateway is IL1Bridge, IForeignGateway {
12+
// @dev Note the disputeID needs to start from one as
13+
// the KlerosV1 proxy governor depends on this implementation.
14+
uint256 internal localDisputeID = 1;
15+
16+
// For now this is just a constant, but we'd probably need to
17+
// implement the same arbitrationCost calculation code we'll have
18+
// in the V2 court.
19+
uint256 internal internalArbitrationCost;
20+
21+
struct DisputeData {
22+
uint256 id;
23+
address arbitrable;
24+
}
25+
mapping(uint256 => bytes32) public disputeIDtoHash;
26+
mapping(bytes32 => DisputeData) public disputeHashtoDisputeData;
27+
28+
IHomeGateway public homeGateway;
29+
uint256 public chainID;
30+
31+
modifier onlyFromL2() {
32+
this.onlyAuthorized();
33+
_;
34+
}
35+
36+
constructor(uint256 _arbitrationCost, IHomeGateway _homeGateway) {
37+
internalArbitrationCost = _arbitrationCost;
38+
homeGateway = _homeGateway;
39+
40+
uint256 id;
41+
assembly {
42+
id := chainid()
43+
}
44+
chainID = id;
45+
}
46+
47+
function createDispute(uint256 _choices, bytes calldata _extraData) external payable returns (uint256 disputeID) {
48+
require(msg.value >= arbitrationCost(_extraData), "Not paid enough for arbitration");
49+
50+
disputeID = localDisputeID++;
51+
bytes32 disputeHash = keccak256(
52+
abi.encodePacked(
53+
chainID,
54+
blockhash(block.number - 1),
55+
"createDispute",
56+
disputeID,
57+
arbitrationCost(_extraData),
58+
_extraData,
59+
msg.sender
60+
)
61+
);
62+
disputeIDtoHash[disputeID] = disputeHash;
63+
disputeHashtoDisputeData[disputeHash] = DisputeData({id: disputeID, arbitrable: msg.sender});
64+
65+
bytes4 methodSelector = IHomeGateway.relayCreateDispute.selector;
66+
bytes memory data = abi.encodeWithSelector(methodSelector, disputeHash, _choices, _extraData);
67+
68+
uint256 bridgeCost = this.getSubmissionPrice(data.length);
69+
// We only pay for the submissionPrice gas cost
70+
// which is minimum gas cost required for submitting a
71+
// arbitrum retryable ticket to the retry buffer for
72+
// bridge to L2.
73+
// For immediate inclusion a user/bot needs to pay (GasPrice x MaxGas)
74+
// with the associated ticketId that is emitted by this function
75+
// after the ticket is successfully submitted.
76+
// For more details, see:
77+
// https://developer.offchainlabs.com/docs/l1_l2_messages#retryable-tickets-contract-api
78+
//
79+
// We do NOT forward the arbitrationCost ETH funds to the HomeGateway yet,
80+
// only the calldata.
81+
this.sendCrossDomainMessage{value: bridgeCost}(data, 0, 0);
82+
83+
emit DisputeCreation(disputeID, IArbitrable(msg.sender));
84+
}
85+
86+
function arbitrationCost(bytes calldata _extraData) public view returns (uint256 cost) {
87+
// Calculate the size of calldata that will be passed to the L2 bridge
88+
// as that is a factor for the bridging cost.
89+
// Calldata size of relayCreateDispute:
90+
// relayCreateDispute methodId +
91+
// (createDispute methodId + bytes32 disputeHash + uint256 _choices + bytes _extraData)
92+
// 4 + 4 + 32 + 32 + dynamic
93+
uint256 calldatasize = 82 + _extraData.length;
94+
95+
uint256 bridgeCost = this.getSubmissionPrice(calldatasize);
96+
return bridgeCost + internalArbitrationCost;
97+
}
98+
99+
/**
100+
* Relay the rule call from the home gateway to the arbitrable.
101+
*/
102+
function relayRule(bytes32 _disputeHash, uint256 _ruling) external onlyFromL2 {
103+
DisputeData memory dispute = disputeHashtoDisputeData[_disputeHash];
104+
105+
IArbitrable arbitrable = IArbitrable(dispute.arbitrable);
106+
arbitrable.rule(dispute.id, _ruling);
107+
}
108+
109+
function foreignDisputeHashToID(bytes32 _disputeHash) external view returns (uint256) {
110+
return disputeHashtoDisputeData[_disputeHash].id;
111+
}
112+
113+
function disputeID(uint256 _foreignDisputeID) external view returns (uint256) {
114+
bytes32 disputeHash = disputeIDtoHash[_foreignDisputeID];
115+
require(disputeHash != 0, "Dispute does not exist");
116+
117+
return homeGateway.homeDisputeHashToID(disputeHash);
118+
}
119+
120+
function homeChainID(uint256 _disputeID) external view returns (uint256) {
121+
return homeGateway.chainID();
122+
}
123+
124+
function homeBridge(uint256 _disputeID) external view returns (address) {
125+
return address(homeGateway);
126+
}
127+
}

0 commit comments

Comments
 (0)