@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,627 @@
|
|
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 "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
|
8
|
+
|
9
|
+
import "./Node.sol";
|
10
|
+
import "./IRollupCore.sol";
|
11
|
+
import "./RollupLib.sol";
|
12
|
+
import "./RollupEventBridge.sol";
|
13
|
+
import "./IRollupCore.sol";
|
14
|
+
|
15
|
+
import "../challenge/IChallengeManager.sol";
|
16
|
+
|
17
|
+
import "../bridge/ISequencerInbox.sol";
|
18
|
+
import "../bridge/IBridge.sol";
|
19
|
+
import "../bridge/IOutbox.sol";
|
20
|
+
|
21
|
+
import {NO_CHAL_INDEX} from "../libraries/Constants.sol";
|
22
|
+
|
23
|
+
abstract contract RollupCore is IRollupCore, PausableUpgradeable {
|
24
|
+
using NodeLib for Node;
|
25
|
+
using GlobalStateLib for GlobalState;
|
26
|
+
|
27
|
+
// Rollup Config
|
28
|
+
uint64 public confirmPeriodBlocks;
|
29
|
+
uint64 public extraChallengeTimeBlocks;
|
30
|
+
uint256 public chainId;
|
31
|
+
uint256 public baseStake;
|
32
|
+
bytes32 public wasmModuleRoot;
|
33
|
+
|
34
|
+
IBridge public delayedBridge;
|
35
|
+
ISequencerInbox public sequencerBridge;
|
36
|
+
IOutbox public outbox;
|
37
|
+
RollupEventBridge public rollupEventBridge;
|
38
|
+
IChallengeManager public override challengeManager;
|
39
|
+
// when a staker loses a challenge, half of their funds get escrowed in this address
|
40
|
+
address public loserStakeEscrow;
|
41
|
+
address public stakeToken;
|
42
|
+
uint256 public minimumAssertionPeriod;
|
43
|
+
|
44
|
+
mapping(address => bool) public isValidator;
|
45
|
+
|
46
|
+
// Stakers become Zombies after losing a challenge
|
47
|
+
struct Zombie {
|
48
|
+
address stakerAddress;
|
49
|
+
uint64 latestStakedNode;
|
50
|
+
}
|
51
|
+
|
52
|
+
uint64 private _latestConfirmed;
|
53
|
+
uint64 private _firstUnresolvedNode;
|
54
|
+
uint64 private _latestNodeCreated;
|
55
|
+
uint64 private _lastStakeBlock;
|
56
|
+
mapping(uint64 => Node) private _nodes;
|
57
|
+
mapping(uint64 => mapping(address => bool)) private _nodeStakers;
|
58
|
+
|
59
|
+
address[] private _stakerList;
|
60
|
+
mapping(address => Staker) public _stakerMap;
|
61
|
+
|
62
|
+
Zombie[] private _zombies;
|
63
|
+
|
64
|
+
mapping(address => uint256) private _withdrawableFunds;
|
65
|
+
uint256 public totalWithdrawableFunds;
|
66
|
+
|
67
|
+
// The node number of the initial node
|
68
|
+
uint64 internal constant GENESIS_NODE = 0;
|
69
|
+
|
70
|
+
/**
|
71
|
+
* @notice Get a storage reference to the Node for the given node index
|
72
|
+
* @param nodeNum Index of the node
|
73
|
+
* @return Node struct
|
74
|
+
*/
|
75
|
+
function getNodeStorage(uint64 nodeNum) internal view returns (Node storage) {
|
76
|
+
return _nodes[nodeNum];
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* @notice Get the Node for the given index.
|
81
|
+
*/
|
82
|
+
function getNode(uint64 nodeNum) public view override returns (Node memory) {
|
83
|
+
return getNodeStorage(nodeNum);
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* @notice Check if the specified node has been staked on by the provided staker.
|
88
|
+
* Only accurate at the latest confirmed node and afterwards.
|
89
|
+
*/
|
90
|
+
function nodeHasStaker(uint64 nodeNum, address staker) public view override returns (bool) {
|
91
|
+
return _nodeStakers[nodeNum][staker];
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* @notice Get the address of the staker at the given index
|
96
|
+
* @param stakerNum Index of the staker
|
97
|
+
* @return Address of the staker
|
98
|
+
*/
|
99
|
+
function getStakerAddress(uint64 stakerNum) external view override returns (address) {
|
100
|
+
return _stakerList[stakerNum];
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* @notice Check whether the given staker is staked
|
105
|
+
* @param staker Staker address to check
|
106
|
+
* @return True or False for whether the staker was staked
|
107
|
+
*/
|
108
|
+
function isStaked(address staker) public view override returns (bool) {
|
109
|
+
return _stakerMap[staker].isStaked;
|
110
|
+
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* @notice Check whether the given staker is staked on the latest confirmed node,
|
114
|
+
* which includes if the staker is staked on a descendent of the latest confirmed node.
|
115
|
+
* @param staker Staker address to check
|
116
|
+
* @return True or False for whether the staker was staked
|
117
|
+
*/
|
118
|
+
function isStakedOnLatestConfirmed(address staker) public view returns (bool) {
|
119
|
+
return _stakerMap[staker].isStaked && nodeHasStaker(_latestConfirmed, staker);
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* @notice Get the latest staked node of the given staker
|
124
|
+
* @param staker Staker address to lookup
|
125
|
+
* @return Latest node staked of the staker
|
126
|
+
*/
|
127
|
+
function latestStakedNode(address staker) public view override returns (uint64) {
|
128
|
+
return _stakerMap[staker].latestStakedNode;
|
129
|
+
}
|
130
|
+
|
131
|
+
/**
|
132
|
+
* @notice Get the current challenge of the given staker
|
133
|
+
* @param staker Staker address to lookup
|
134
|
+
* @return Current challenge of the staker
|
135
|
+
*/
|
136
|
+
function currentChallenge(address staker) public view override returns (uint64) {
|
137
|
+
return _stakerMap[staker].currentChallenge;
|
138
|
+
}
|
139
|
+
|
140
|
+
/**
|
141
|
+
* @notice Get the amount staked of the given staker
|
142
|
+
* @param staker Staker address to lookup
|
143
|
+
* @return Amount staked of the staker
|
144
|
+
*/
|
145
|
+
function amountStaked(address staker) public view override returns (uint256) {
|
146
|
+
return _stakerMap[staker].amountStaked;
|
147
|
+
}
|
148
|
+
|
149
|
+
/**
|
150
|
+
* @notice Retrieves stored information about a requested staker
|
151
|
+
* @param staker Staker address to retrieve
|
152
|
+
* @return A structure with information about the requested staker
|
153
|
+
*/
|
154
|
+
function getStaker(address staker) external view override returns (Staker memory) {
|
155
|
+
return _stakerMap[staker];
|
156
|
+
}
|
157
|
+
|
158
|
+
/**
|
159
|
+
* @notice Get the original staker address of the zombie at the given index
|
160
|
+
* @param zombieNum Index of the zombie to lookup
|
161
|
+
* @return Original staker address of the zombie
|
162
|
+
*/
|
163
|
+
function zombieAddress(uint256 zombieNum) public view override returns (address) {
|
164
|
+
return _zombies[zombieNum].stakerAddress;
|
165
|
+
}
|
166
|
+
|
167
|
+
/**
|
168
|
+
* @notice Get Latest node that the given zombie at the given index is staked on
|
169
|
+
* @param zombieNum Index of the zombie to lookup
|
170
|
+
* @return Latest node that the given zombie is staked on
|
171
|
+
*/
|
172
|
+
function zombieLatestStakedNode(uint256 zombieNum) public view override returns (uint64) {
|
173
|
+
return _zombies[zombieNum].latestStakedNode;
|
174
|
+
}
|
175
|
+
|
176
|
+
/**
|
177
|
+
* @notice Retrieves stored information about a requested zombie
|
178
|
+
* @param zombieNum Index of the zombie to lookup
|
179
|
+
* @return A structure with information about the requested staker
|
180
|
+
*/
|
181
|
+
function getZombieStorage(uint256 zombieNum) internal view returns (Zombie storage) {
|
182
|
+
return _zombies[zombieNum];
|
183
|
+
}
|
184
|
+
|
185
|
+
/// @return Current number of un-removed zombies
|
186
|
+
function zombieCount() public view override returns (uint256) {
|
187
|
+
return _zombies.length;
|
188
|
+
}
|
189
|
+
|
190
|
+
function isZombie(address staker) public view override returns (bool) {
|
191
|
+
for (uint256 i = 0; i < _zombies.length; i++) {
|
192
|
+
if (staker == _zombies[i].stakerAddress) {
|
193
|
+
return true;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
return false;
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* @notice Get the amount of funds withdrawable by the given address
|
201
|
+
* @param user Address to check the funds of
|
202
|
+
* @return Amount of funds withdrawable by user
|
203
|
+
*/
|
204
|
+
function withdrawableFunds(address user) external view override returns (uint256) {
|
205
|
+
return _withdrawableFunds[user];
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* @return Index of the first unresolved node
|
210
|
+
* @dev If all nodes have been resolved, this will be latestNodeCreated + 1
|
211
|
+
*/
|
212
|
+
function firstUnresolvedNode() public view override returns (uint64) {
|
213
|
+
return _firstUnresolvedNode;
|
214
|
+
}
|
215
|
+
|
216
|
+
/// @return Index of the latest confirmed node
|
217
|
+
function latestConfirmed() public view override returns (uint64) {
|
218
|
+
return _latestConfirmed;
|
219
|
+
}
|
220
|
+
|
221
|
+
/// @return Index of the latest rollup node created
|
222
|
+
function latestNodeCreated() public view override returns (uint64) {
|
223
|
+
return _latestNodeCreated;
|
224
|
+
}
|
225
|
+
|
226
|
+
/// @return Ethereum block that the most recent stake was created
|
227
|
+
function lastStakeBlock() external view override returns (uint64) {
|
228
|
+
return _lastStakeBlock;
|
229
|
+
}
|
230
|
+
|
231
|
+
/// @return Number of active stakers currently staked
|
232
|
+
function stakerCount() public view override returns (uint64) {
|
233
|
+
return uint64(_stakerList.length);
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* @notice Initialize the core with an initial node
|
238
|
+
* @param initialNode Initial node to start the chain with
|
239
|
+
*/
|
240
|
+
function initializeCore(Node memory initialNode) internal {
|
241
|
+
__Pausable_init();
|
242
|
+
_nodes[GENESIS_NODE] = initialNode;
|
243
|
+
_firstUnresolvedNode = GENESIS_NODE + 1;
|
244
|
+
}
|
245
|
+
|
246
|
+
/**
|
247
|
+
* @notice React to a new node being created by storing it an incrementing the latest node counter
|
248
|
+
* @param node Node that was newly created
|
249
|
+
*/
|
250
|
+
function nodeCreated(Node memory node) internal {
|
251
|
+
_latestNodeCreated++;
|
252
|
+
_nodes[_latestNodeCreated] = node;
|
253
|
+
}
|
254
|
+
|
255
|
+
/// @notice Reject the next unresolved node
|
256
|
+
function _rejectNextNode() internal {
|
257
|
+
_firstUnresolvedNode++;
|
258
|
+
}
|
259
|
+
|
260
|
+
function confirmNode(
|
261
|
+
uint64 nodeNum,
|
262
|
+
bytes32 blockHash,
|
263
|
+
bytes32 sendRoot
|
264
|
+
) internal {
|
265
|
+
Node storage node = getNodeStorage(nodeNum);
|
266
|
+
// Authenticate data against node's confirm data pre-image
|
267
|
+
require(node.confirmData == RollupLib.confirmHash(blockHash, sendRoot), "CONFIRM_DATA");
|
268
|
+
|
269
|
+
// trusted external call to outbox
|
270
|
+
outbox.updateSendRoot(sendRoot, blockHash);
|
271
|
+
|
272
|
+
_latestConfirmed = nodeNum;
|
273
|
+
_firstUnresolvedNode = nodeNum + 1;
|
274
|
+
|
275
|
+
emit NodeConfirmed(nodeNum, blockHash, sendRoot);
|
276
|
+
}
|
277
|
+
|
278
|
+
/**
|
279
|
+
* @notice Create a new stake at latest confirmed node
|
280
|
+
* @param stakerAddress Address of the new staker
|
281
|
+
* @param depositAmount Stake amount of the new staker
|
282
|
+
*/
|
283
|
+
function createNewStake(address stakerAddress, uint256 depositAmount) internal {
|
284
|
+
uint64 stakerIndex = uint64(_stakerList.length);
|
285
|
+
_stakerList.push(stakerAddress);
|
286
|
+
_stakerMap[stakerAddress] = Staker(
|
287
|
+
depositAmount,
|
288
|
+
stakerIndex,
|
289
|
+
_latestConfirmed,
|
290
|
+
NO_CHAL_INDEX, // new staker is not in challenge
|
291
|
+
true
|
292
|
+
);
|
293
|
+
_nodeStakers[_latestConfirmed][stakerAddress] = true;
|
294
|
+
_lastStakeBlock = uint64(block.number);
|
295
|
+
emit UserStakeUpdated(stakerAddress, 0, depositAmount);
|
296
|
+
}
|
297
|
+
|
298
|
+
/**
|
299
|
+
* @notice Check to see whether the two stakers are in the same challenge
|
300
|
+
* @param stakerAddress1 Address of the first staker
|
301
|
+
* @param stakerAddress2 Address of the second staker
|
302
|
+
* @return Address of the challenge that the two stakers are in
|
303
|
+
*/
|
304
|
+
function inChallenge(address stakerAddress1, address stakerAddress2)
|
305
|
+
internal
|
306
|
+
view
|
307
|
+
returns (uint64)
|
308
|
+
{
|
309
|
+
Staker storage staker1 = _stakerMap[stakerAddress1];
|
310
|
+
Staker storage staker2 = _stakerMap[stakerAddress2];
|
311
|
+
uint64 challenge = staker1.currentChallenge;
|
312
|
+
require(challenge != NO_CHAL_INDEX, "NO_CHAL");
|
313
|
+
require(challenge == staker2.currentChallenge, "DIFF_IN_CHAL");
|
314
|
+
return challenge;
|
315
|
+
}
|
316
|
+
|
317
|
+
/**
|
318
|
+
* @notice Make the given staker as not being in a challenge
|
319
|
+
* @param stakerAddress Address of the staker to remove from a challenge
|
320
|
+
*/
|
321
|
+
function clearChallenge(address stakerAddress) internal {
|
322
|
+
Staker storage staker = _stakerMap[stakerAddress];
|
323
|
+
staker.currentChallenge = NO_CHAL_INDEX;
|
324
|
+
}
|
325
|
+
|
326
|
+
/**
|
327
|
+
* @notice Mark both the given stakers as engaged in the challenge
|
328
|
+
* @param staker1 Address of the first staker
|
329
|
+
* @param staker2 Address of the second staker
|
330
|
+
* @param challenge Address of the challenge both stakers are now in
|
331
|
+
*/
|
332
|
+
function challengeStarted(
|
333
|
+
address staker1,
|
334
|
+
address staker2,
|
335
|
+
uint64 challenge
|
336
|
+
) internal {
|
337
|
+
_stakerMap[staker1].currentChallenge = challenge;
|
338
|
+
_stakerMap[staker2].currentChallenge = challenge;
|
339
|
+
}
|
340
|
+
|
341
|
+
/**
|
342
|
+
* @notice Add to the stake of the given staker by the given amount
|
343
|
+
* @param stakerAddress Address of the staker to increase the stake of
|
344
|
+
* @param amountAdded Amount of stake to add to the staker
|
345
|
+
*/
|
346
|
+
function increaseStakeBy(address stakerAddress, uint256 amountAdded) internal {
|
347
|
+
Staker storage staker = _stakerMap[stakerAddress];
|
348
|
+
uint256 initialStaked = staker.amountStaked;
|
349
|
+
uint256 finalStaked = initialStaked + amountAdded;
|
350
|
+
staker.amountStaked = finalStaked;
|
351
|
+
emit UserStakeUpdated(stakerAddress, initialStaked, finalStaked);
|
352
|
+
}
|
353
|
+
|
354
|
+
/**
|
355
|
+
* @notice Reduce the stake of the given staker to the given target
|
356
|
+
* @param stakerAddress Address of the staker to reduce the stake of
|
357
|
+
* @param target Amount of stake to leave with the staker
|
358
|
+
* @return Amount of value released from the stake
|
359
|
+
*/
|
360
|
+
function reduceStakeTo(address stakerAddress, uint256 target) internal returns (uint256) {
|
361
|
+
Staker storage staker = _stakerMap[stakerAddress];
|
362
|
+
uint256 current = staker.amountStaked;
|
363
|
+
require(target <= current, "TOO_LITTLE_STAKE");
|
364
|
+
uint256 amountWithdrawn = current - target;
|
365
|
+
staker.amountStaked = target;
|
366
|
+
increaseWithdrawableFunds(stakerAddress, amountWithdrawn);
|
367
|
+
emit UserStakeUpdated(stakerAddress, current, target);
|
368
|
+
return amountWithdrawn;
|
369
|
+
}
|
370
|
+
|
371
|
+
/**
|
372
|
+
* @notice Remove the given staker and turn them into a zombie
|
373
|
+
* @param stakerAddress Address of the staker to remove
|
374
|
+
*/
|
375
|
+
function turnIntoZombie(address stakerAddress) internal {
|
376
|
+
Staker storage staker = _stakerMap[stakerAddress];
|
377
|
+
_zombies.push(Zombie(stakerAddress, staker.latestStakedNode));
|
378
|
+
deleteStaker(stakerAddress);
|
379
|
+
}
|
380
|
+
|
381
|
+
/**
|
382
|
+
* @notice Update the latest staked node of the zombie at the given index
|
383
|
+
* @param zombieNum Index of the zombie to move
|
384
|
+
* @param latest New latest node the zombie is staked on
|
385
|
+
*/
|
386
|
+
function zombieUpdateLatestStakedNode(uint256 zombieNum, uint64 latest) internal {
|
387
|
+
_zombies[zombieNum].latestStakedNode = latest;
|
388
|
+
}
|
389
|
+
|
390
|
+
/**
|
391
|
+
* @notice Remove the zombie at the given index
|
392
|
+
* @param zombieNum Index of the zombie to remove
|
393
|
+
*/
|
394
|
+
function removeZombie(uint256 zombieNum) internal {
|
395
|
+
_zombies[zombieNum] = _zombies[_zombies.length - 1];
|
396
|
+
_zombies.pop();
|
397
|
+
}
|
398
|
+
|
399
|
+
/**
|
400
|
+
* @notice Mark the given staker as staked on this node
|
401
|
+
* @param staker Address of the staker to mark
|
402
|
+
*/
|
403
|
+
function addStaker(uint64 nodeNum, address staker) internal {
|
404
|
+
require(!_nodeStakers[nodeNum][staker], "ALREADY_STAKED");
|
405
|
+
_nodeStakers[nodeNum][staker] = true;
|
406
|
+
Node storage node = getNodeStorage(nodeNum);
|
407
|
+
require(node.deadlineBlock != 0, "NO_NODE");
|
408
|
+
|
409
|
+
uint64 prevCount = node.stakerCount;
|
410
|
+
node.stakerCount = prevCount + 1;
|
411
|
+
|
412
|
+
if (nodeNum > GENESIS_NODE) {
|
413
|
+
Node storage parent = getNodeStorage(node.prevNum);
|
414
|
+
parent.childStakerCount++;
|
415
|
+
if (prevCount == 0) {
|
416
|
+
parent.newChildConfirmDeadline(uint64(block.number) + confirmPeriodBlocks);
|
417
|
+
}
|
418
|
+
}
|
419
|
+
}
|
420
|
+
|
421
|
+
/**
|
422
|
+
* @notice Remove the given staker from this node
|
423
|
+
* @param staker Address of the staker to remove
|
424
|
+
*/
|
425
|
+
function removeStaker(uint64 nodeNum, address staker) internal {
|
426
|
+
require(_nodeStakers[nodeNum][staker], "NOT_STAKED");
|
427
|
+
_nodeStakers[nodeNum][staker] = false;
|
428
|
+
|
429
|
+
Node storage node = getNodeStorage(nodeNum);
|
430
|
+
node.stakerCount--;
|
431
|
+
|
432
|
+
if (nodeNum > GENESIS_NODE) {
|
433
|
+
getNodeStorage(node.prevNum).childStakerCount--;
|
434
|
+
}
|
435
|
+
}
|
436
|
+
|
437
|
+
/**
|
438
|
+
* @notice Remove the given staker and return their stake
|
439
|
+
* This should not be called if the staker is staked on a descendent of the latest confirmed node
|
440
|
+
* @param stakerAddress Address of the staker withdrawing their stake
|
441
|
+
*/
|
442
|
+
function withdrawStaker(address stakerAddress) internal {
|
443
|
+
Staker storage staker = _stakerMap[stakerAddress];
|
444
|
+
uint64 latestConfirmedNum = latestConfirmed();
|
445
|
+
if (nodeHasStaker(latestConfirmedNum, stakerAddress)) {
|
446
|
+
// Withdrawing a staker whose latest staked node isn't resolved should be impossible
|
447
|
+
assert(staker.latestStakedNode == latestConfirmedNum);
|
448
|
+
removeStaker(latestConfirmedNum, stakerAddress);
|
449
|
+
}
|
450
|
+
uint256 initialStaked = staker.amountStaked;
|
451
|
+
increaseWithdrawableFunds(stakerAddress, initialStaked);
|
452
|
+
deleteStaker(stakerAddress);
|
453
|
+
emit UserStakeUpdated(stakerAddress, initialStaked, 0);
|
454
|
+
}
|
455
|
+
|
456
|
+
/**
|
457
|
+
* @notice Advance the given staker to the given node
|
458
|
+
* @param stakerAddress Address of the staker adding their stake
|
459
|
+
* @param nodeNum Index of the node to stake on
|
460
|
+
*/
|
461
|
+
function stakeOnNode(address stakerAddress, uint64 nodeNum) internal {
|
462
|
+
Staker storage staker = _stakerMap[stakerAddress];
|
463
|
+
addStaker(nodeNum, stakerAddress);
|
464
|
+
staker.latestStakedNode = nodeNum;
|
465
|
+
}
|
466
|
+
|
467
|
+
/**
|
468
|
+
* @notice Clear the withdrawable funds for the given address
|
469
|
+
* @param account Address of the account to remove funds from
|
470
|
+
* @return Amount of funds removed from account
|
471
|
+
*/
|
472
|
+
function withdrawFunds(address account) internal returns (uint256) {
|
473
|
+
uint256 amount = _withdrawableFunds[account];
|
474
|
+
_withdrawableFunds[account] = 0;
|
475
|
+
totalWithdrawableFunds -= amount;
|
476
|
+
emit UserWithdrawableFundsUpdated(account, amount, 0);
|
477
|
+
return amount;
|
478
|
+
}
|
479
|
+
|
480
|
+
/**
|
481
|
+
* @notice Increase the withdrawable funds for the given address
|
482
|
+
* @param account Address of the account to add withdrawable funds to
|
483
|
+
*/
|
484
|
+
function increaseWithdrawableFunds(address account, uint256 amount) internal {
|
485
|
+
uint256 initialWithdrawable = _withdrawableFunds[account];
|
486
|
+
uint256 finalWithdrawable = initialWithdrawable + amount;
|
487
|
+
_withdrawableFunds[account] = finalWithdrawable;
|
488
|
+
totalWithdrawableFunds += amount;
|
489
|
+
emit UserWithdrawableFundsUpdated(account, initialWithdrawable, finalWithdrawable);
|
490
|
+
}
|
491
|
+
|
492
|
+
/**
|
493
|
+
* @notice Remove the given staker
|
494
|
+
* @param stakerAddress Address of the staker to remove
|
495
|
+
*/
|
496
|
+
function deleteStaker(address stakerAddress) private {
|
497
|
+
Staker storage staker = _stakerMap[stakerAddress];
|
498
|
+
uint64 stakerIndex = staker.index;
|
499
|
+
_stakerList[stakerIndex] = _stakerList[_stakerList.length - 1];
|
500
|
+
_stakerMap[_stakerList[stakerIndex]].index = stakerIndex;
|
501
|
+
_stakerList.pop();
|
502
|
+
delete _stakerMap[stakerAddress];
|
503
|
+
}
|
504
|
+
|
505
|
+
struct StakeOnNewNodeFrame {
|
506
|
+
uint256 currentInboxSize;
|
507
|
+
Node node;
|
508
|
+
bytes32 executionHash;
|
509
|
+
Node prevNode;
|
510
|
+
bytes32 lastHash;
|
511
|
+
bool hasSibling;
|
512
|
+
uint64 deadlineBlock;
|
513
|
+
bytes32 sequencerBatchAcc;
|
514
|
+
}
|
515
|
+
|
516
|
+
function createNewNode(
|
517
|
+
RollupLib.Assertion calldata assertion,
|
518
|
+
uint64 prevNodeNum,
|
519
|
+
uint256 prevNodeInboxMaxCount,
|
520
|
+
bytes32 expectedNodeHash
|
521
|
+
) internal returns (bytes32 newNodeHash) {
|
522
|
+
require(
|
523
|
+
assertion.afterState.machineStatus == MachineStatus.FINISHED ||
|
524
|
+
assertion.afterState.machineStatus == MachineStatus.ERRORED,
|
525
|
+
"BAD_AFTER_STATUS"
|
526
|
+
);
|
527
|
+
|
528
|
+
StakeOnNewNodeFrame memory memoryFrame;
|
529
|
+
{
|
530
|
+
// validate data
|
531
|
+
memoryFrame.prevNode = getNode(prevNodeNum);
|
532
|
+
memoryFrame.currentInboxSize = sequencerBridge.batchCount();
|
533
|
+
|
534
|
+
// Make sure the previous state is correct against the node being built on
|
535
|
+
require(
|
536
|
+
RollupLib.stateHash(assertion.beforeState, prevNodeInboxMaxCount) ==
|
537
|
+
memoryFrame.prevNode.stateHash,
|
538
|
+
"PREV_STATE_HASH"
|
539
|
+
);
|
540
|
+
|
541
|
+
// Ensure that the assertion doesn't read past the end of the current inbox
|
542
|
+
uint64 afterInboxCount = assertion.afterState.globalState.getInboxPosition();
|
543
|
+
uint64 prevInboxPosition = assertion.beforeState.globalState.getInboxPosition();
|
544
|
+
require(afterInboxCount >= prevInboxPosition, "INBOX_BACKWARDS");
|
545
|
+
if (afterInboxCount == prevInboxPosition) {
|
546
|
+
require(
|
547
|
+
assertion.afterState.globalState.getPositionInMessage() >=
|
548
|
+
assertion.afterState.globalState.getPositionInMessage(),
|
549
|
+
"INBOX_POS_IN_MSG_BACKWARDS"
|
550
|
+
);
|
551
|
+
}
|
552
|
+
// See validator/assertion.go ExecutionState RequiredBatches() for reasoning
|
553
|
+
if (
|
554
|
+
assertion.afterState.machineStatus == MachineStatus.ERRORED ||
|
555
|
+
assertion.afterState.globalState.getPositionInMessage() > 0
|
556
|
+
) {
|
557
|
+
// The current inbox message was read
|
558
|
+
afterInboxCount++;
|
559
|
+
}
|
560
|
+
require(afterInboxCount <= memoryFrame.currentInboxSize, "INBOX_PAST_END");
|
561
|
+
// This gives replay protection against the state of the inbox
|
562
|
+
if (afterInboxCount > 0) {
|
563
|
+
memoryFrame.sequencerBatchAcc = sequencerBridge.inboxAccs(afterInboxCount - 1);
|
564
|
+
}
|
565
|
+
}
|
566
|
+
|
567
|
+
{
|
568
|
+
memoryFrame.executionHash = RollupLib.executionHash(assertion);
|
569
|
+
|
570
|
+
memoryFrame.deadlineBlock = uint64(block.number) + confirmPeriodBlocks;
|
571
|
+
|
572
|
+
memoryFrame.hasSibling = memoryFrame.prevNode.latestChildNumber > 0;
|
573
|
+
// here we don't use ternacy operator to remain compatible with slither
|
574
|
+
if (memoryFrame.hasSibling) {
|
575
|
+
memoryFrame.lastHash = getNodeStorage(memoryFrame.prevNode.latestChildNumber)
|
576
|
+
.nodeHash;
|
577
|
+
} else {
|
578
|
+
memoryFrame.lastHash = memoryFrame.prevNode.nodeHash;
|
579
|
+
}
|
580
|
+
|
581
|
+
newNodeHash = RollupLib.nodeHash(
|
582
|
+
memoryFrame.hasSibling,
|
583
|
+
memoryFrame.lastHash,
|
584
|
+
memoryFrame.executionHash,
|
585
|
+
memoryFrame.sequencerBatchAcc
|
586
|
+
);
|
587
|
+
require(newNodeHash == expectedNodeHash, "UNEXPECTED_NODE_HASH");
|
588
|
+
|
589
|
+
memoryFrame.node = NodeLib.createNode(
|
590
|
+
RollupLib.stateHash(assertion.afterState, memoryFrame.currentInboxSize),
|
591
|
+
RollupLib.challengeRootHash(
|
592
|
+
memoryFrame.executionHash,
|
593
|
+
block.number,
|
594
|
+
wasmModuleRoot
|
595
|
+
),
|
596
|
+
RollupLib.confirmHash(assertion),
|
597
|
+
prevNodeNum,
|
598
|
+
memoryFrame.deadlineBlock,
|
599
|
+
newNodeHash
|
600
|
+
);
|
601
|
+
}
|
602
|
+
|
603
|
+
{
|
604
|
+
uint64 nodeNum = latestNodeCreated() + 1;
|
605
|
+
|
606
|
+
// Fetch a storage reference to prevNode since we copied our other one into memory
|
607
|
+
// and we don't have enough stack available to keep to keep the previous storage reference around
|
608
|
+
Node storage prevNode = getNodeStorage(prevNodeNum);
|
609
|
+
prevNode.childCreated(nodeNum);
|
610
|
+
|
611
|
+
nodeCreated(memoryFrame.node);
|
612
|
+
}
|
613
|
+
|
614
|
+
emit NodeCreated(
|
615
|
+
latestNodeCreated(),
|
616
|
+
memoryFrame.prevNode.nodeHash,
|
617
|
+
newNodeHash,
|
618
|
+
memoryFrame.executionHash,
|
619
|
+
assertion,
|
620
|
+
memoryFrame.sequencerBatchAcc,
|
621
|
+
wasmModuleRoot,
|
622
|
+
memoryFrame.currentInboxSize
|
623
|
+
);
|
624
|
+
|
625
|
+
return newNodeHash;
|
626
|
+
}
|
627
|
+
}
|