Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit fce78e2

Browse files
committed
chore: add audited nitro-contracts 9d44f2d
1 parent 08f7f78 commit fce78e2

File tree

94 files changed

+12351
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+12351
-0
lines changed

contracts/src/bridge/Bridge.sol

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
// Copyright 2021-2022, Offchain Labs, Inc.
2+
// For license information, see https://github.com/nitro/blob/master/LICENSE
3+
// SPDX-License-Identifier: BUSL-1.1
4+
5+
pragma solidity ^0.8.4;
6+
7+
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
8+
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
9+
10+
import {
11+
NotContract,
12+
NotRollupOrOwner,
13+
NotDelayedInbox,
14+
NotSequencerInbox,
15+
NotOutbox,
16+
InvalidOutboxSet,
17+
BadSequencerMessageNumber
18+
} from "../libraries/Error.sol";
19+
import "./IBridge.sol";
20+
import "./Messages.sol";
21+
import "../libraries/DelegateCallAware.sol";
22+
23+
import {L1MessageType_batchPostingReport} from "../libraries/MessageTypes.sol";
24+
25+
/**
26+
* @title Staging ground for incoming and outgoing messages
27+
* @notice Holds the inbox accumulator for sequenced and delayed messages.
28+
* It is also the ETH escrow for value sent with these messages.
29+
* Since the escrow is held here, this contract also contains a list of allowed
30+
* outboxes that can make calls from here and withdraw this escrow.
31+
*/
32+
contract Bridge is Initializable, DelegateCallAware, IBridge {
33+
using AddressUpgradeable for address;
34+
35+
struct InOutInfo {
36+
uint256 index;
37+
bool allowed;
38+
}
39+
40+
mapping(address => InOutInfo) private allowedDelayedInboxesMap;
41+
mapping(address => InOutInfo) private allowedOutboxesMap;
42+
43+
address[] public allowedDelayedInboxList;
44+
address[] public allowedOutboxList;
45+
46+
address internal _activeOutbox;
47+
48+
/// @inheritdoc IBridge
49+
bytes32[] public delayedInboxAccs;
50+
51+
/// @inheritdoc IBridge
52+
bytes32[] public sequencerInboxAccs;
53+
54+
IOwnable public rollup;
55+
address public sequencerInbox;
56+
57+
uint256 public override sequencerReportedSubMessageCount;
58+
59+
address internal constant EMPTY_ACTIVEOUTBOX = address(type(uint160).max);
60+
61+
function initialize(IOwnable rollup_) external initializer onlyDelegated {
62+
_activeOutbox = EMPTY_ACTIVEOUTBOX;
63+
rollup = rollup_;
64+
}
65+
66+
modifier onlyRollupOrOwner() {
67+
if (msg.sender != address(rollup)) {
68+
address rollupOwner = rollup.owner();
69+
if (msg.sender != rollupOwner) {
70+
revert NotRollupOrOwner(msg.sender, address(rollup), rollupOwner);
71+
}
72+
}
73+
_;
74+
}
75+
76+
/// @dev returns the address of current active Outbox, or zero if no outbox is active
77+
function activeOutbox() public view returns (address) {
78+
address outbox = _activeOutbox;
79+
// address zero is returned if no outbox is set, but the value used in storage
80+
// is non-zero to save users some gas (as storage refunds are usually maxed out)
81+
// EIP-1153 would help here.
82+
// we don't return `EMPTY_ACTIVEOUTBOX` to avoid a breaking change on the current api
83+
if (outbox == EMPTY_ACTIVEOUTBOX) return address(0);
84+
return outbox;
85+
}
86+
87+
function allowedDelayedInboxes(address inbox) external view returns (bool) {
88+
return allowedDelayedInboxesMap[inbox].allowed;
89+
}
90+
91+
function allowedOutboxes(address outbox) external view returns (bool) {
92+
return allowedOutboxesMap[outbox].allowed;
93+
}
94+
95+
modifier onlySequencerInbox() {
96+
if (msg.sender != sequencerInbox) revert NotSequencerInbox(msg.sender);
97+
_;
98+
}
99+
100+
function enqueueSequencerMessage(
101+
bytes32 dataHash,
102+
uint256 afterDelayedMessagesRead,
103+
uint256 prevMessageCount,
104+
uint256 newMessageCount
105+
)
106+
external
107+
onlySequencerInbox
108+
returns (
109+
uint256 seqMessageIndex,
110+
bytes32 beforeAcc,
111+
bytes32 delayedAcc,
112+
bytes32 acc
113+
)
114+
{
115+
if (
116+
sequencerReportedSubMessageCount != prevMessageCount &&
117+
prevMessageCount != 0 &&
118+
sequencerReportedSubMessageCount != 0
119+
) {
120+
revert BadSequencerMessageNumber(sequencerReportedSubMessageCount, prevMessageCount);
121+
}
122+
sequencerReportedSubMessageCount = newMessageCount;
123+
seqMessageIndex = sequencerInboxAccs.length;
124+
if (sequencerInboxAccs.length > 0) {
125+
beforeAcc = sequencerInboxAccs[sequencerInboxAccs.length - 1];
126+
}
127+
if (afterDelayedMessagesRead > 0) {
128+
delayedAcc = delayedInboxAccs[afterDelayedMessagesRead - 1];
129+
}
130+
acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc));
131+
sequencerInboxAccs.push(acc);
132+
}
133+
134+
/// @inheritdoc IBridge
135+
function submitBatchSpendingReport(address sender, bytes32 messageDataHash)
136+
external
137+
onlySequencerInbox
138+
returns (uint256)
139+
{
140+
return
141+
addMessageToDelayedAccumulator(
142+
L1MessageType_batchPostingReport,
143+
sender,
144+
uint64(block.number),
145+
uint64(block.timestamp), // solhint-disable-line not-rely-on-time,
146+
block.basefee,
147+
messageDataHash
148+
);
149+
}
150+
151+
/// @inheritdoc IBridge
152+
function enqueueDelayedMessage(
153+
uint8 kind,
154+
address sender,
155+
bytes32 messageDataHash
156+
) external payable returns (uint256) {
157+
if (!allowedDelayedInboxesMap[msg.sender].allowed) revert NotDelayedInbox(msg.sender);
158+
return
159+
addMessageToDelayedAccumulator(
160+
kind,
161+
sender,
162+
uint64(block.number),
163+
uint64(block.timestamp), // solhint-disable-line not-rely-on-time
164+
block.basefee,
165+
messageDataHash
166+
);
167+
}
168+
169+
function addMessageToDelayedAccumulator(
170+
uint8 kind,
171+
address sender,
172+
uint64 blockNumber,
173+
uint64 blockTimestamp,
174+
uint256 baseFeeL1,
175+
bytes32 messageDataHash
176+
) internal returns (uint256) {
177+
uint256 count = delayedInboxAccs.length;
178+
bytes32 messageHash = Messages.messageHash(
179+
kind,
180+
sender,
181+
blockNumber,
182+
blockTimestamp,
183+
count,
184+
baseFeeL1,
185+
messageDataHash
186+
);
187+
bytes32 prevAcc = 0;
188+
if (count > 0) {
189+
prevAcc = delayedInboxAccs[count - 1];
190+
}
191+
delayedInboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash));
192+
emit MessageDelivered(
193+
count,
194+
prevAcc,
195+
msg.sender,
196+
kind,
197+
sender,
198+
messageDataHash,
199+
baseFeeL1,
200+
blockTimestamp
201+
);
202+
return count;
203+
}
204+
205+
function executeCall(
206+
address to,
207+
uint256 value,
208+
bytes calldata data
209+
) external returns (bool success, bytes memory returnData) {
210+
if (!allowedOutboxesMap[msg.sender].allowed) revert NotOutbox(msg.sender);
211+
if (data.length > 0 && !to.isContract()) revert NotContract(to);
212+
address prevOutbox = _activeOutbox;
213+
_activeOutbox = msg.sender;
214+
// We set and reset active outbox around external call so activeOutbox remains valid during call
215+
216+
// We use a low level call here since we want to bubble up whether it succeeded or failed to the caller
217+
// rather than reverting on failure as well as allow contract and non-contract calls
218+
// solhint-disable-next-line avoid-low-level-calls
219+
(success, returnData) = to.call{value: value}(data);
220+
_activeOutbox = prevOutbox;
221+
emit BridgeCallTriggered(msg.sender, to, value, data);
222+
}
223+
224+
function setSequencerInbox(address _sequencerInbox) external onlyRollupOrOwner {
225+
sequencerInbox = _sequencerInbox;
226+
emit SequencerInboxUpdated(_sequencerInbox);
227+
}
228+
229+
function setDelayedInbox(address inbox, bool enabled) external onlyRollupOrOwner {
230+
InOutInfo storage info = allowedDelayedInboxesMap[inbox];
231+
bool alreadyEnabled = info.allowed;
232+
emit InboxToggle(inbox, enabled);
233+
if (alreadyEnabled == enabled) {
234+
return;
235+
}
236+
if (enabled) {
237+
allowedDelayedInboxesMap[inbox] = InOutInfo(allowedDelayedInboxList.length, true);
238+
allowedDelayedInboxList.push(inbox);
239+
} else {
240+
allowedDelayedInboxList[info.index] = allowedDelayedInboxList[
241+
allowedDelayedInboxList.length - 1
242+
];
243+
allowedDelayedInboxesMap[allowedDelayedInboxList[info.index]].index = info.index;
244+
allowedDelayedInboxList.pop();
245+
delete allowedDelayedInboxesMap[inbox];
246+
}
247+
}
248+
249+
function setOutbox(address outbox, bool enabled) external onlyRollupOrOwner {
250+
if (outbox == EMPTY_ACTIVEOUTBOX) revert InvalidOutboxSet(outbox);
251+
252+
InOutInfo storage info = allowedOutboxesMap[outbox];
253+
bool alreadyEnabled = info.allowed;
254+
emit OutboxToggle(outbox, enabled);
255+
if (alreadyEnabled == enabled) {
256+
return;
257+
}
258+
if (enabled) {
259+
allowedOutboxesMap[outbox] = InOutInfo(allowedOutboxList.length, true);
260+
allowedOutboxList.push(outbox);
261+
} else {
262+
allowedOutboxList[info.index] = allowedOutboxList[allowedOutboxList.length - 1];
263+
allowedOutboxesMap[allowedOutboxList[info.index]].index = info.index;
264+
allowedOutboxList.pop();
265+
delete allowedOutboxesMap[outbox];
266+
}
267+
}
268+
269+
function setSequencerReportedSubMessageCount(uint256 newMsgCount) external onlyRollupOrOwner {
270+
sequencerReportedSubMessageCount = newMsgCount;
271+
}
272+
273+
function delayedMessageCount() external view override returns (uint256) {
274+
return delayedInboxAccs.length;
275+
}
276+
277+
function sequencerMessageCount() external view returns (uint256) {
278+
return sequencerInboxAccs.length;
279+
}
280+
281+
/// @dev For the classic -> nitro migration. TODO: remove post-migration.
282+
function acceptFundsFromOldBridge() external payable {}
283+
}

contracts/src/bridge/IBridge.sol

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2021-2022, Offchain Labs, Inc.
2+
// For license information, see https://github.com/nitro/blob/master/LICENSE
3+
// SPDX-License-Identifier: BUSL-1.1
4+
5+
// solhint-disable-next-line compiler-version
6+
pragma solidity >=0.6.9 <0.9.0;
7+
8+
import "./IOwnable.sol";
9+
10+
interface IBridge {
11+
event MessageDelivered(
12+
uint256 indexed messageIndex,
13+
bytes32 indexed beforeInboxAcc,
14+
address inbox,
15+
uint8 kind,
16+
address sender,
17+
bytes32 messageDataHash,
18+
uint256 baseFeeL1,
19+
uint64 timestamp
20+
);
21+
22+
event BridgeCallTriggered(
23+
address indexed outbox,
24+
address indexed to,
25+
uint256 value,
26+
bytes data
27+
);
28+
29+
event InboxToggle(address indexed inbox, bool enabled);
30+
31+
event OutboxToggle(address indexed outbox, bool enabled);
32+
33+
event SequencerInboxUpdated(address newSequencerInbox);
34+
35+
function allowedDelayedInboxList(uint256) external returns (address);
36+
37+
function allowedOutboxList(uint256) external returns (address);
38+
39+
/// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
40+
function delayedInboxAccs(uint256) external view returns (bytes32);
41+
42+
/// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
43+
function sequencerInboxAccs(uint256) external view returns (bytes32);
44+
45+
function rollup() external view returns (IOwnable);
46+
47+
function sequencerInbox() external view returns (address);
48+
49+
function activeOutbox() external view returns (address);
50+
51+
function allowedDelayedInboxes(address inbox) external view returns (bool);
52+
53+
function allowedOutboxes(address outbox) external view returns (bool);
54+
55+
function sequencerReportedSubMessageCount() external view returns (uint256);
56+
57+
/**
58+
* @dev Enqueue a message in the delayed inbox accumulator.
59+
* These messages are later sequenced in the SequencerInbox, either
60+
* by the sequencer as part of a normal batch, or by force inclusion.
61+
*/
62+
function enqueueDelayedMessage(
63+
uint8 kind,
64+
address sender,
65+
bytes32 messageDataHash
66+
) external payable returns (uint256);
67+
68+
function executeCall(
69+
address to,
70+
uint256 value,
71+
bytes calldata data
72+
) external returns (bool success, bytes memory returnData);
73+
74+
function delayedMessageCount() external view returns (uint256);
75+
76+
function sequencerMessageCount() external view returns (uint256);
77+
78+
// ---------- onlySequencerInbox functions ----------
79+
80+
function enqueueSequencerMessage(
81+
bytes32 dataHash,
82+
uint256 afterDelayedMessagesRead,
83+
uint256 prevMessageCount,
84+
uint256 newMessageCount
85+
)
86+
external
87+
returns (
88+
uint256 seqMessageIndex,
89+
bytes32 beforeAcc,
90+
bytes32 delayedAcc,
91+
bytes32 acc
92+
);
93+
94+
/**
95+
* @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type
96+
* This is done through a separate function entrypoint instead of allowing the sequencer inbox
97+
* to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either
98+
* every delayed inbox or every sequencer inbox call.
99+
*/
100+
function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
101+
external
102+
returns (uint256 msgNum);
103+
104+
// ---------- onlyRollupOrOwner functions ----------
105+
106+
function setSequencerInbox(address _sequencerInbox) external;
107+
108+
function setDelayedInbox(address inbox, bool enabled) external;
109+
110+
function setOutbox(address inbox, bool enabled) external;
111+
112+
// ---------- initializer ----------
113+
114+
function initialize(IOwnable rollup_) external;
115+
}

0 commit comments

Comments
 (0)