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