@arbitrum/nitro-contracts 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|