@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,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
|
+
}
|