@arbitrum/nitro-contracts 1.0.0-beta.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.prettierrc +5 -0
- package/.solhint.json +18 -0
- package/deploy/BridgeStubCreator.js +10 -0
- package/deploy/HashProofHelper.js +13 -0
- package/deploy/InboxStubCreator.js +17 -0
- package/deploy/OneStepProofEntryCreator.js +19 -0
- package/deploy/OneStepProver0Creator.js +14 -0
- package/deploy/OneStepProverHostIoCreator.js +14 -0
- package/deploy/OneStepProverMathCreator.js +14 -0
- package/deploy/OneStepProverMemoryCreator.js +14 -0
- package/deploy/SequencerInboxStubCreator.js +13 -0
- package/deploy/ValueArrayTesterCreator.js +13 -0
- package/hardhat.config.ts +47 -0
- package/hardhat.prod-config.js +18 -0
- package/package.json +49 -0
- package/scripts/build.bash +5 -0
- package/src/bridge/Bridge.sol +168 -0
- package/src/bridge/IBridge.sol +68 -0
- package/src/bridge/IInbox.sol +80 -0
- package/src/bridge/IMessageProvider.sol +11 -0
- package/src/bridge/IOutbox.sol +52 -0
- package/src/bridge/ISequencerInbox.sol +85 -0
- package/src/bridge/Inbox.sol +414 -0
- package/src/bridge/Messages.sol +38 -0
- package/src/bridge/Outbox.sol +188 -0
- package/src/bridge/SequencerInbox.sol +274 -0
- package/src/challenge/ChallengeLib.sol +135 -0
- package/src/challenge/ChallengeManager.sol +367 -0
- package/src/challenge/IChallengeManager.sol +75 -0
- package/src/challenge/IChallengeResultReceiver.sol +13 -0
- package/src/libraries/AddressAliasHelper.sol +29 -0
- package/src/libraries/AdminFallbackProxy.sol +153 -0
- package/src/libraries/ArbitrumProxy.sol +20 -0
- package/src/libraries/Constants.sol +10 -0
- package/src/libraries/CryptographyPrimitives.sol +323 -0
- package/src/libraries/DelegateCallAware.sol +44 -0
- package/src/libraries/Error.sol +38 -0
- package/src/libraries/IGasRefunder.sol +35 -0
- package/src/libraries/MerkleLib.sol +46 -0
- package/src/libraries/MessageTypes.sol +14 -0
- package/src/libraries/SecondaryLogicUUPSUpgradeable.sol +58 -0
- package/src/libraries/UUPSNotUpgradeable.sol +56 -0
- package/src/mocks/BridgeStub.sol +115 -0
- package/src/mocks/Counter.sol +13 -0
- package/src/mocks/ExecutionManager.sol +41 -0
- package/src/mocks/InboxStub.sol +131 -0
- package/src/mocks/MockResultReceiver.sol +59 -0
- package/src/mocks/SequencerInboxStub.sol +42 -0
- package/src/mocks/SimpleProxy.sol +19 -0
- package/src/node-interface/NodeInterface.sol +50 -0
- package/src/osp/HashProofHelper.sol +154 -0
- package/src/osp/IOneStepProofEntry.sol +20 -0
- package/src/osp/IOneStepProver.sol +27 -0
- package/src/osp/OneStepProofEntry.sol +129 -0
- package/src/osp/OneStepProver0.sol +566 -0
- package/src/osp/OneStepProverHostIo.sol +357 -0
- package/src/osp/OneStepProverMath.sol +514 -0
- package/src/osp/OneStepProverMemory.sol +313 -0
- package/src/precompiles/ArbAddressTable.sol +60 -0
- package/src/precompiles/ArbAggregator.sol +62 -0
- package/src/precompiles/ArbBLS.sol +53 -0
- package/src/precompiles/ArbDebug.sol +39 -0
- package/src/precompiles/ArbFunctionTable.sol +29 -0
- package/src/precompiles/ArbGasInfo.sol +121 -0
- package/src/precompiles/ArbInfo.sol +15 -0
- package/src/precompiles/ArbOwner.sol +65 -0
- package/src/precompiles/ArbOwnerPublic.sol +18 -0
- package/src/precompiles/ArbRetryableTx.sol +89 -0
- package/src/precompiles/ArbStatistics.sol +29 -0
- package/src/precompiles/ArbSys.sol +134 -0
- package/src/precompiles/ArbosActs.sol +41 -0
- package/src/precompiles/ArbosTest.sol +14 -0
- package/src/rollup/BridgeCreator.sol +120 -0
- package/src/rollup/IRollupCore.sol +152 -0
- package/src/rollup/IRollupLogic.sol +183 -0
- package/src/rollup/Node.sol +99 -0
- package/src/rollup/RollupAdminLogic.sol +322 -0
- package/src/rollup/RollupCore.sol +627 -0
- package/src/rollup/RollupCreator.sol +133 -0
- package/src/rollup/RollupEventBridge.sol +46 -0
- package/src/rollup/RollupLib.sol +135 -0
- package/src/rollup/RollupUserLogic.sol +712 -0
- package/src/rollup/ValidatorUtils.sol +243 -0
- package/src/rollup/ValidatorWallet.sol +76 -0
- package/src/rollup/ValidatorWalletCreator.sol +43 -0
- package/src/state/Deserialize.sol +321 -0
- package/src/state/GlobalState.sol +44 -0
- package/src/state/Instructions.sol +159 -0
- package/src/state/Machine.sol +65 -0
- package/src/state/MerkleProof.sol +99 -0
- package/src/state/Module.sol +33 -0
- package/src/state/ModuleMemory.sol +42 -0
- package/src/state/PcArray.sol +45 -0
- package/src/state/PcStack.sol +32 -0
- package/src/state/StackFrame.sol +63 -0
- package/src/state/Value.sol +65 -0
- package/src/state/ValueArray.sol +47 -0
- package/src/state/ValueStack.sol +39 -0
- package/src/test-helpers/CryptographyPrimitivesTester.sol +27 -0
- package/src/test-helpers/MessageTester.sol +34 -0
- package/src/test-helpers/ValueArrayTester.sol +34 -0
- package/test/contract/arbRollup.spec.ts +869 -0
- package/test/contract/common/challengeLib.ts +43 -0
- package/test/contract/common/globalStateLib.ts +17 -0
- package/test/contract/common/rolluplib.ts +259 -0
- package/test/contract/cryptographyPrimitives.spec.ts +82 -0
- package/test/contract/sequencerInboxForceInclude.spec.ts +516 -0
- package/test/contract/utils.ts +40 -0
- package/test/prover/hash-proofs.ts +75 -0
- package/test/prover/one-step-proof.ts +93 -0
- package/test/prover/proofs/.gitkeep +0 -0
- package/test/prover/value-arrays.ts +11 -0
- package/tsconfig.json +13 -0
@@ -0,0 +1,188 @@
|
|
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 "./IBridge.sol";
|
8
|
+
import "./IOutbox.sol";
|
9
|
+
import "../libraries/MerkleLib.sol";
|
10
|
+
import "../libraries/DelegateCallAware.sol";
|
11
|
+
|
12
|
+
contract Outbox is DelegateCallAware, IOutbox {
|
13
|
+
address public rollup; // the rollup contract
|
14
|
+
IBridge public bridge; // the bridge contract
|
15
|
+
|
16
|
+
mapping(uint256 => bool) public spent; // maps leaf number => if spent
|
17
|
+
mapping(bytes32 => bytes32) public roots; // maps root hashes => L2 block hash
|
18
|
+
|
19
|
+
struct L2ToL1Context {
|
20
|
+
uint128 l2Block;
|
21
|
+
uint128 l1Block;
|
22
|
+
uint128 timestamp;
|
23
|
+
bytes32 outputId;
|
24
|
+
address sender;
|
25
|
+
}
|
26
|
+
// Note, these variables are set and then wiped during a single transaction.
|
27
|
+
// Therefore their values don't need to be maintained, and their slots will
|
28
|
+
// be empty outside of transactions
|
29
|
+
L2ToL1Context internal context;
|
30
|
+
uint128 public constant OUTBOX_VERSION = 2;
|
31
|
+
|
32
|
+
function initialize(address _rollup, IBridge _bridge) external onlyDelegated {
|
33
|
+
if (rollup != address(0)) revert AlreadyInit();
|
34
|
+
rollup = _rollup;
|
35
|
+
bridge = _bridge;
|
36
|
+
}
|
37
|
+
|
38
|
+
function updateSendRoot(bytes32 root, bytes32 l2BlockHash) external override {
|
39
|
+
if (msg.sender != rollup) revert NotRollup(msg.sender, rollup);
|
40
|
+
roots[root] = l2BlockHash;
|
41
|
+
emit SendRootUpdated(root, l2BlockHash);
|
42
|
+
}
|
43
|
+
|
44
|
+
/// @notice When l2ToL1Sender returns a nonzero address, the message was originated by an L2 account
|
45
|
+
/// When the return value is zero, that means this is a system message
|
46
|
+
/// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies
|
47
|
+
function l2ToL1Sender() external view override returns (address) {
|
48
|
+
return context.sender;
|
49
|
+
}
|
50
|
+
|
51
|
+
function l2ToL1Block() external view override returns (uint256) {
|
52
|
+
return uint256(context.l2Block);
|
53
|
+
}
|
54
|
+
|
55
|
+
function l2ToL1EthBlock() external view override returns (uint256) {
|
56
|
+
return uint256(context.l1Block);
|
57
|
+
}
|
58
|
+
|
59
|
+
function l2ToL1Timestamp() external view override returns (uint256) {
|
60
|
+
return uint256(context.timestamp);
|
61
|
+
}
|
62
|
+
|
63
|
+
// @deprecated batch number is now always 0
|
64
|
+
function l2ToL1BatchNum() external pure override returns (uint256) {
|
65
|
+
return 0;
|
66
|
+
}
|
67
|
+
|
68
|
+
function l2ToL1OutputId() external view override returns (bytes32) {
|
69
|
+
return context.outputId;
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* @notice Executes a messages in an Outbox entry.
|
74
|
+
* @dev Reverts if dispute period hasn't expired, since the outbox entry
|
75
|
+
* is only created once the rollup confirms the respective assertion.
|
76
|
+
* @param proof Merkle proof of message inclusion in send root
|
77
|
+
* @param index Merkle path to message
|
78
|
+
* @param l2Sender sender if original message (i.e., caller of ArbSys.sendTxToL1)
|
79
|
+
* @param to destination address for L1 contract call
|
80
|
+
* @param l2Block l2 block number at which sendTxToL1 call was made
|
81
|
+
* @param l1Block l1 block number at which sendTxToL1 call was made
|
82
|
+
* @param l2Timestamp l2 Timestamp at which sendTxToL1 call was made
|
83
|
+
* @param value wei in L1 message
|
84
|
+
* @param data abi-encoded L1 message data
|
85
|
+
*/
|
86
|
+
function executeTransaction(
|
87
|
+
bytes32[] calldata proof,
|
88
|
+
uint256 index,
|
89
|
+
address l2Sender,
|
90
|
+
address to,
|
91
|
+
uint256 l2Block,
|
92
|
+
uint256 l1Block,
|
93
|
+
uint256 l2Timestamp,
|
94
|
+
uint256 value,
|
95
|
+
bytes calldata data
|
96
|
+
) external virtual {
|
97
|
+
bytes32 outputId;
|
98
|
+
{
|
99
|
+
bytes32 userTx = calculateItemHash(
|
100
|
+
l2Sender,
|
101
|
+
to,
|
102
|
+
l2Block,
|
103
|
+
l1Block,
|
104
|
+
l2Timestamp,
|
105
|
+
value,
|
106
|
+
data
|
107
|
+
);
|
108
|
+
|
109
|
+
outputId = recordOutputAsSpent(proof, index, userTx);
|
110
|
+
emit OutBoxTransactionExecuted(to, l2Sender, 0, index);
|
111
|
+
}
|
112
|
+
|
113
|
+
// we temporarily store the previous values so the outbox can naturally
|
114
|
+
// unwind itself when there are nested calls to `executeTransaction`
|
115
|
+
L2ToL1Context memory prevContext = context;
|
116
|
+
|
117
|
+
context = L2ToL1Context({
|
118
|
+
sender: l2Sender,
|
119
|
+
l2Block: uint128(l2Block),
|
120
|
+
l1Block: uint128(l1Block),
|
121
|
+
timestamp: uint128(l2Timestamp),
|
122
|
+
outputId: outputId
|
123
|
+
});
|
124
|
+
|
125
|
+
// set and reset vars around execution so they remain valid during call
|
126
|
+
executeBridgeCall(to, value, data);
|
127
|
+
|
128
|
+
context = prevContext;
|
129
|
+
}
|
130
|
+
|
131
|
+
function recordOutputAsSpent(
|
132
|
+
bytes32[] memory proof,
|
133
|
+
uint256 index,
|
134
|
+
bytes32 item
|
135
|
+
) internal returns (bytes32) {
|
136
|
+
if (proof.length >= 256) revert ProofTooLong(proof.length);
|
137
|
+
if (index >= 2**proof.length) revert PathNotMinimal(index, 2**proof.length);
|
138
|
+
|
139
|
+
// Hash the leaf an extra time to prove it's a leaf
|
140
|
+
bytes32 calcRoot = calculateMerkleRoot(proof, index, item);
|
141
|
+
if (roots[calcRoot] == bytes32(0)) revert UnknownRoot(calcRoot);
|
142
|
+
|
143
|
+
if (spent[index]) revert AlreadySpent(index);
|
144
|
+
spent[index] = true;
|
145
|
+
|
146
|
+
return bytes32(index);
|
147
|
+
}
|
148
|
+
|
149
|
+
function executeBridgeCall(
|
150
|
+
address to,
|
151
|
+
uint256 value,
|
152
|
+
bytes memory data
|
153
|
+
) internal {
|
154
|
+
(bool success, bytes memory returndata) = bridge.executeCall(to, value, data);
|
155
|
+
if (!success) {
|
156
|
+
if (returndata.length > 0) {
|
157
|
+
// solhint-disable-next-line no-inline-assembly
|
158
|
+
assembly {
|
159
|
+
let returndata_size := mload(returndata)
|
160
|
+
revert(add(32, returndata), returndata_size)
|
161
|
+
}
|
162
|
+
} else {
|
163
|
+
revert BridgeCallFailed();
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
function calculateItemHash(
|
169
|
+
address l2Sender,
|
170
|
+
address to,
|
171
|
+
uint256 l2Block,
|
172
|
+
uint256 l1Block,
|
173
|
+
uint256 l2Timestamp,
|
174
|
+
uint256 value,
|
175
|
+
bytes calldata data
|
176
|
+
) public pure returns (bytes32) {
|
177
|
+
return
|
178
|
+
keccak256(abi.encodePacked(l2Sender, to, l2Block, l1Block, l2Timestamp, value, data));
|
179
|
+
}
|
180
|
+
|
181
|
+
function calculateMerkleRoot(
|
182
|
+
bytes32[] memory proof,
|
183
|
+
uint256 path,
|
184
|
+
bytes32 item
|
185
|
+
) public pure returns (bytes32) {
|
186
|
+
return MerkleLib.calculateRoot(proof, path, keccak256(abi.encodePacked(item)));
|
187
|
+
}
|
188
|
+
}
|
@@ -0,0 +1,274 @@
|
|
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.0;
|
6
|
+
|
7
|
+
import "./IBridge.sol";
|
8
|
+
import "./ISequencerInbox.sol";
|
9
|
+
import "./Messages.sol";
|
10
|
+
|
11
|
+
import {GasRefundEnabled, IGasRefunder} from "../libraries/IGasRefunder.sol";
|
12
|
+
import "../libraries/DelegateCallAware.sol";
|
13
|
+
import {MAX_DATA_SIZE} from "../libraries/Constants.sol";
|
14
|
+
|
15
|
+
/**
|
16
|
+
* @title Accepts batches from the sequencer and adds them to the rollup inbox.
|
17
|
+
* @notice Contains the inbox accumulator which is the ordering of all data and transactions to be processed by the rollup.
|
18
|
+
* As part of submitting a batch the sequencer is also expected to include items enqueued
|
19
|
+
* in the delayed inbox (Bridge.sol). If items in the delayed inbox are not included by a
|
20
|
+
* sequencer within a time limit they can be force included into the rollup inbox by anyone.
|
21
|
+
*/
|
22
|
+
contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox {
|
23
|
+
bytes32[] public override inboxAccs;
|
24
|
+
uint256 public totalDelayedMessagesRead;
|
25
|
+
|
26
|
+
IBridge public delayedBridge;
|
27
|
+
|
28
|
+
/// @dev The size of the batch header
|
29
|
+
uint256 public constant HEADER_LENGTH = 40;
|
30
|
+
/// @dev If the first batch data byte after the header has this bit set,
|
31
|
+
/// the sequencer inbox has authenticated the data. Currently not used.
|
32
|
+
bytes1 public constant DATA_AUTHENTICATED_FLAG = 0x40;
|
33
|
+
|
34
|
+
address public rollup;
|
35
|
+
mapping(address => bool) public isBatchPoster;
|
36
|
+
ISequencerInbox.MaxTimeVariation public maxTimeVariation;
|
37
|
+
|
38
|
+
function initialize(
|
39
|
+
IBridge delayedBridge_,
|
40
|
+
address rollup_,
|
41
|
+
ISequencerInbox.MaxTimeVariation calldata maxTimeVariation_
|
42
|
+
) external onlyDelegated {
|
43
|
+
if (delayedBridge != IBridge(address(0))) revert AlreadyInit();
|
44
|
+
if (delayedBridge_ == IBridge(address(0))) revert HadZeroInit();
|
45
|
+
delayedBridge = delayedBridge_;
|
46
|
+
rollup = rollup_;
|
47
|
+
maxTimeVariation = maxTimeVariation_;
|
48
|
+
}
|
49
|
+
|
50
|
+
function getTimeBounds() internal view virtual returns (TimeBounds memory) {
|
51
|
+
TimeBounds memory bounds;
|
52
|
+
if (block.timestamp > maxTimeVariation.delaySeconds) {
|
53
|
+
bounds.minTimestamp = uint64(block.timestamp - maxTimeVariation.delaySeconds);
|
54
|
+
}
|
55
|
+
bounds.maxTimestamp = uint64(block.timestamp + maxTimeVariation.futureSeconds);
|
56
|
+
if (block.number > maxTimeVariation.delayBlocks) {
|
57
|
+
bounds.minBlockNumber = uint64(block.number - maxTimeVariation.delayBlocks);
|
58
|
+
}
|
59
|
+
bounds.maxBlockNumber = uint64(block.number + maxTimeVariation.futureBlocks);
|
60
|
+
return bounds;
|
61
|
+
}
|
62
|
+
|
63
|
+
/// @notice Force messages from the delayed inbox to be included in the chain
|
64
|
+
/// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and maxTimeVariation.delaySeconds
|
65
|
+
/// has elapsed. As part of normal behaviour the sequencer will include these messages
|
66
|
+
/// so it's only necessary to call this if the sequencer is down, or not including
|
67
|
+
/// any delayed messages.
|
68
|
+
/// @param _totalDelayedMessagesRead The total number of messages to read up to
|
69
|
+
/// @param kind The kind of the last message to be included
|
70
|
+
/// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included
|
71
|
+
/// @param baseFeeL1 The l1 gas price of the last message to be included
|
72
|
+
/// @param sender The sender of the last message to be included
|
73
|
+
/// @param messageDataHash The messageDataHash of the last message to be included
|
74
|
+
function forceInclusion(
|
75
|
+
uint256 _totalDelayedMessagesRead,
|
76
|
+
uint8 kind,
|
77
|
+
uint64[2] calldata l1BlockAndTime,
|
78
|
+
uint256 baseFeeL1,
|
79
|
+
address sender,
|
80
|
+
bytes32 messageDataHash
|
81
|
+
) external {
|
82
|
+
if (_totalDelayedMessagesRead <= totalDelayedMessagesRead) revert DelayedBackwards();
|
83
|
+
bytes32 messageHash = Messages.messageHash(
|
84
|
+
kind,
|
85
|
+
sender,
|
86
|
+
l1BlockAndTime[0],
|
87
|
+
l1BlockAndTime[1],
|
88
|
+
_totalDelayedMessagesRead - 1,
|
89
|
+
baseFeeL1,
|
90
|
+
messageDataHash
|
91
|
+
);
|
92
|
+
// Can only force-include after the Sequencer-only window has expired.
|
93
|
+
if (l1BlockAndTime[0] + maxTimeVariation.delayBlocks >= block.number)
|
94
|
+
revert ForceIncludeBlockTooSoon();
|
95
|
+
if (l1BlockAndTime[1] + maxTimeVariation.delaySeconds >= block.timestamp)
|
96
|
+
revert ForceIncludeTimeTooSoon();
|
97
|
+
|
98
|
+
// Verify that message hash represents the last message sequence of delayed message to be included
|
99
|
+
bytes32 prevDelayedAcc = 0;
|
100
|
+
if (_totalDelayedMessagesRead > 1) {
|
101
|
+
prevDelayedAcc = delayedBridge.inboxAccs(_totalDelayedMessagesRead - 2);
|
102
|
+
}
|
103
|
+
if (
|
104
|
+
delayedBridge.inboxAccs(_totalDelayedMessagesRead - 1) !=
|
105
|
+
Messages.accumulateInboxMessage(prevDelayedAcc, messageHash)
|
106
|
+
) revert IncorrectMessagePreimage();
|
107
|
+
|
108
|
+
(bytes32 dataHash, TimeBounds memory timeBounds) = formEmptyDataHash(
|
109
|
+
_totalDelayedMessagesRead
|
110
|
+
);
|
111
|
+
(bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl(
|
112
|
+
dataHash,
|
113
|
+
_totalDelayedMessagesRead
|
114
|
+
);
|
115
|
+
emit SequencerBatchDelivered(
|
116
|
+
inboxAccs.length - 1,
|
117
|
+
beforeAcc,
|
118
|
+
afterAcc,
|
119
|
+
delayedAcc,
|
120
|
+
totalDelayedMessagesRead,
|
121
|
+
timeBounds,
|
122
|
+
BatchDataLocation.NoData
|
123
|
+
);
|
124
|
+
}
|
125
|
+
|
126
|
+
function addSequencerL2BatchFromOrigin(
|
127
|
+
uint256 sequenceNumber,
|
128
|
+
bytes calldata data,
|
129
|
+
uint256 afterDelayedMessagesRead,
|
130
|
+
IGasRefunder gasRefunder
|
131
|
+
) external refundsGasWithCalldata(gasRefunder, payable(msg.sender)) {
|
132
|
+
// solhint-disable-next-line avoid-tx-origin
|
133
|
+
if (msg.sender != tx.origin) revert NotOrigin();
|
134
|
+
if (!isBatchPoster[msg.sender]) revert NotBatchPoster();
|
135
|
+
if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber();
|
136
|
+
(bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash(
|
137
|
+
data,
|
138
|
+
afterDelayedMessagesRead
|
139
|
+
);
|
140
|
+
(bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl(
|
141
|
+
dataHash,
|
142
|
+
afterDelayedMessagesRead
|
143
|
+
);
|
144
|
+
emit SequencerBatchDelivered(
|
145
|
+
inboxAccs.length - 1,
|
146
|
+
beforeAcc,
|
147
|
+
afterAcc,
|
148
|
+
delayedAcc,
|
149
|
+
totalDelayedMessagesRead,
|
150
|
+
timeBounds,
|
151
|
+
BatchDataLocation.TxInput
|
152
|
+
);
|
153
|
+
}
|
154
|
+
|
155
|
+
function addSequencerL2Batch(
|
156
|
+
uint256 sequenceNumber,
|
157
|
+
bytes calldata data,
|
158
|
+
uint256 afterDelayedMessagesRead,
|
159
|
+
IGasRefunder gasRefunder
|
160
|
+
) external override refundsGasNoCalldata(gasRefunder, payable(msg.sender)) {
|
161
|
+
if (!isBatchPoster[msg.sender] && msg.sender != rollup) revert NotBatchPoster();
|
162
|
+
if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber();
|
163
|
+
|
164
|
+
(bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash(
|
165
|
+
data,
|
166
|
+
afterDelayedMessagesRead
|
167
|
+
);
|
168
|
+
(bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl(
|
169
|
+
dataHash,
|
170
|
+
afterDelayedMessagesRead
|
171
|
+
);
|
172
|
+
emit SequencerBatchDelivered(
|
173
|
+
sequenceNumber,
|
174
|
+
beforeAcc,
|
175
|
+
afterAcc,
|
176
|
+
delayedAcc,
|
177
|
+
afterDelayedMessagesRead,
|
178
|
+
timeBounds,
|
179
|
+
BatchDataLocation.SeparateBatchEvent
|
180
|
+
);
|
181
|
+
emit SequencerBatchData(sequenceNumber, data);
|
182
|
+
}
|
183
|
+
|
184
|
+
function packHeader(uint256 afterDelayedMessagesRead)
|
185
|
+
internal
|
186
|
+
view
|
187
|
+
returns (bytes memory, TimeBounds memory)
|
188
|
+
{
|
189
|
+
TimeBounds memory timeBounds = getTimeBounds();
|
190
|
+
bytes memory header = abi.encodePacked(
|
191
|
+
timeBounds.minTimestamp,
|
192
|
+
timeBounds.maxTimestamp,
|
193
|
+
timeBounds.minBlockNumber,
|
194
|
+
timeBounds.maxBlockNumber,
|
195
|
+
uint64(afterDelayedMessagesRead)
|
196
|
+
);
|
197
|
+
// This must always be true from the packed encoding
|
198
|
+
assert(header.length == HEADER_LENGTH);
|
199
|
+
return (header, timeBounds);
|
200
|
+
}
|
201
|
+
|
202
|
+
function formDataHash(bytes calldata data, uint256 afterDelayedMessagesRead)
|
203
|
+
internal
|
204
|
+
view
|
205
|
+
returns (bytes32, TimeBounds memory)
|
206
|
+
{
|
207
|
+
uint256 fullDataLen = HEADER_LENGTH + data.length;
|
208
|
+
if (fullDataLen < HEADER_LENGTH) revert DataLengthOverflow();
|
209
|
+
if (fullDataLen > MAX_DATA_SIZE) revert DataTooLarge(fullDataLen, MAX_DATA_SIZE);
|
210
|
+
bytes memory fullData = new bytes(fullDataLen);
|
211
|
+
(bytes memory header, TimeBounds memory timeBounds) = packHeader(afterDelayedMessagesRead);
|
212
|
+
|
213
|
+
for (uint256 i = 0; i < HEADER_LENGTH; i++) {
|
214
|
+
fullData[i] = header[i];
|
215
|
+
}
|
216
|
+
if (data.length > 0 && (data[0] & DATA_AUTHENTICATED_FLAG) == DATA_AUTHENTICATED_FLAG) {
|
217
|
+
revert DataNotAuthenticated();
|
218
|
+
}
|
219
|
+
// copy data into fullData at offset of HEADER_LENGTH (the extra 32 offset is because solidity puts the array len first)
|
220
|
+
assembly {
|
221
|
+
calldatacopy(add(fullData, add(HEADER_LENGTH, 32)), data.offset, data.length)
|
222
|
+
}
|
223
|
+
return (keccak256(fullData), timeBounds);
|
224
|
+
}
|
225
|
+
|
226
|
+
function formEmptyDataHash(uint256 afterDelayedMessagesRead)
|
227
|
+
internal
|
228
|
+
view
|
229
|
+
returns (bytes32, TimeBounds memory)
|
230
|
+
{
|
231
|
+
(bytes memory header, TimeBounds memory timeBounds) = packHeader(afterDelayedMessagesRead);
|
232
|
+
return (keccak256(header), timeBounds);
|
233
|
+
}
|
234
|
+
|
235
|
+
function addSequencerL2BatchImpl(bytes32 dataHash, uint256 afterDelayedMessagesRead)
|
236
|
+
internal
|
237
|
+
returns (
|
238
|
+
bytes32 beforeAcc,
|
239
|
+
bytes32 delayedAcc,
|
240
|
+
bytes32 acc
|
241
|
+
)
|
242
|
+
{
|
243
|
+
if (afterDelayedMessagesRead < totalDelayedMessagesRead) revert DelayedBackwards();
|
244
|
+
if (afterDelayedMessagesRead > delayedBridge.messageCount()) revert DelayedTooFar();
|
245
|
+
|
246
|
+
if (inboxAccs.length > 0) {
|
247
|
+
beforeAcc = inboxAccs[inboxAccs.length - 1];
|
248
|
+
}
|
249
|
+
if (afterDelayedMessagesRead > 0) {
|
250
|
+
delayedAcc = delayedBridge.inboxAccs(afterDelayedMessagesRead - 1);
|
251
|
+
}
|
252
|
+
|
253
|
+
acc = keccak256(abi.encodePacked(beforeAcc, dataHash, delayedAcc));
|
254
|
+
inboxAccs.push(acc);
|
255
|
+
totalDelayedMessagesRead = afterDelayedMessagesRead;
|
256
|
+
}
|
257
|
+
|
258
|
+
function batchCount() external view override returns (uint256) {
|
259
|
+
return inboxAccs.length;
|
260
|
+
}
|
261
|
+
|
262
|
+
function setMaxTimeVariation(ISequencerInbox.MaxTimeVariation memory maxTimeVariation_)
|
263
|
+
external
|
264
|
+
override
|
265
|
+
{
|
266
|
+
if (msg.sender != rollup) revert NotRollup(msg.sender, rollup);
|
267
|
+
maxTimeVariation = maxTimeVariation_;
|
268
|
+
}
|
269
|
+
|
270
|
+
function setIsBatchPoster(address addr, bool isBatchPoster_) external override {
|
271
|
+
if (msg.sender != rollup) revert NotRollup(msg.sender, rollup);
|
272
|
+
isBatchPoster[addr] = isBatchPoster_;
|
273
|
+
}
|
274
|
+
}
|
@@ -0,0 +1,135 @@
|
|
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.0;
|
6
|
+
|
7
|
+
import "../state/Machine.sol";
|
8
|
+
import "../state/GlobalState.sol";
|
9
|
+
|
10
|
+
library ChallengeLib {
|
11
|
+
using MachineLib for Machine;
|
12
|
+
using ChallengeLib for Challenge;
|
13
|
+
|
14
|
+
/// @dev It's assumed that that uninitialzed challenges have mode NONE
|
15
|
+
enum ChallengeMode {
|
16
|
+
NONE,
|
17
|
+
BLOCK,
|
18
|
+
EXECUTION
|
19
|
+
}
|
20
|
+
|
21
|
+
struct Participant {
|
22
|
+
address addr;
|
23
|
+
uint256 timeLeft;
|
24
|
+
}
|
25
|
+
|
26
|
+
struct Challenge {
|
27
|
+
Participant current;
|
28
|
+
Participant next;
|
29
|
+
uint256 lastMoveTimestamp;
|
30
|
+
bytes32 wasmModuleRoot;
|
31
|
+
bytes32 challengeStateHash;
|
32
|
+
uint64 maxInboxMessages;
|
33
|
+
ChallengeMode mode;
|
34
|
+
}
|
35
|
+
|
36
|
+
struct SegmentSelection {
|
37
|
+
uint256 oldSegmentsStart;
|
38
|
+
uint256 oldSegmentsLength;
|
39
|
+
bytes32[] oldSegments;
|
40
|
+
uint256 challengePosition;
|
41
|
+
}
|
42
|
+
|
43
|
+
function timeUsedSinceLastMove(Challenge storage challenge) internal view returns (uint256) {
|
44
|
+
return block.timestamp - challenge.lastMoveTimestamp;
|
45
|
+
}
|
46
|
+
|
47
|
+
function isTimedOut(Challenge storage challenge) internal view returns (bool) {
|
48
|
+
return challenge.timeUsedSinceLastMove() > challenge.current.timeLeft;
|
49
|
+
}
|
50
|
+
|
51
|
+
function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot)
|
52
|
+
internal
|
53
|
+
pure
|
54
|
+
returns (bytes32)
|
55
|
+
{
|
56
|
+
// Start the value stack with the function call ABI for the entrypoint
|
57
|
+
Value[] memory startingValues = new Value[](3);
|
58
|
+
startingValues[0] = ValueLib.newRefNull();
|
59
|
+
startingValues[1] = ValueLib.newI32(0);
|
60
|
+
startingValues[2] = ValueLib.newI32(0);
|
61
|
+
ValueArray memory valuesArray = ValueArray({inner: startingValues});
|
62
|
+
ValueStack memory values = ValueStack({proved: valuesArray, remainingHash: 0});
|
63
|
+
ValueStack memory internalStack;
|
64
|
+
PcStack memory blocks;
|
65
|
+
StackFrameWindow memory frameStack;
|
66
|
+
|
67
|
+
Machine memory mach = Machine({
|
68
|
+
status: MachineStatus.RUNNING,
|
69
|
+
valueStack: values,
|
70
|
+
internalStack: internalStack,
|
71
|
+
blockStack: blocks,
|
72
|
+
frameStack: frameStack,
|
73
|
+
globalStateHash: globalStateHash,
|
74
|
+
moduleIdx: 0,
|
75
|
+
functionIdx: 0,
|
76
|
+
functionPc: 0,
|
77
|
+
modulesRoot: wasmModuleRoot
|
78
|
+
});
|
79
|
+
return mach.hash();
|
80
|
+
}
|
81
|
+
|
82
|
+
function getEndMachineHash(MachineStatus status, bytes32 globalStateHash)
|
83
|
+
internal
|
84
|
+
pure
|
85
|
+
returns (bytes32)
|
86
|
+
{
|
87
|
+
if (status == MachineStatus.FINISHED) {
|
88
|
+
return keccak256(abi.encodePacked("Machine finished:", globalStateHash));
|
89
|
+
} else if (status == MachineStatus.ERRORED) {
|
90
|
+
return keccak256(abi.encodePacked("Machine errored:"));
|
91
|
+
} else if (status == MachineStatus.TOO_FAR) {
|
92
|
+
return keccak256(abi.encodePacked("Machine too far:"));
|
93
|
+
} else {
|
94
|
+
revert("BAD_BLOCK_STATUS");
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
function extractChallengeSegment(SegmentSelection calldata selection)
|
99
|
+
internal
|
100
|
+
pure
|
101
|
+
returns (uint256 segmentStart, uint256 segmentLength)
|
102
|
+
{
|
103
|
+
uint256 oldChallengeDegree = selection.oldSegments.length - 1;
|
104
|
+
segmentLength = selection.oldSegmentsLength / oldChallengeDegree;
|
105
|
+
// Intentionally done before challengeLength is potentially added to for the final segment
|
106
|
+
segmentStart = selection.oldSegmentsStart + segmentLength * selection.challengePosition;
|
107
|
+
if (selection.challengePosition == selection.oldSegments.length - 2) {
|
108
|
+
segmentLength += selection.oldSegmentsLength % oldChallengeDegree;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
function hashChallengeState(
|
113
|
+
uint256 segmentsStart,
|
114
|
+
uint256 segmentsLength,
|
115
|
+
bytes32[] memory segments
|
116
|
+
) internal pure returns (bytes32) {
|
117
|
+
return keccak256(abi.encodePacked(segmentsStart, segmentsLength, segments));
|
118
|
+
}
|
119
|
+
|
120
|
+
function blockStateHash(MachineStatus status, bytes32 globalStateHash)
|
121
|
+
internal
|
122
|
+
pure
|
123
|
+
returns (bytes32)
|
124
|
+
{
|
125
|
+
if (status == MachineStatus.FINISHED) {
|
126
|
+
return keccak256(abi.encodePacked("Block state:", globalStateHash));
|
127
|
+
} else if (status == MachineStatus.ERRORED) {
|
128
|
+
return keccak256(abi.encodePacked("Block state, errored:", globalStateHash));
|
129
|
+
} else if (status == MachineStatus.TOO_FAR) {
|
130
|
+
return keccak256(abi.encodePacked("Block state, too far:"));
|
131
|
+
} else {
|
132
|
+
revert("BAD_BLOCK_STATUS");
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|