• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

renproject / darknode-sol / cb0c000c-08ed-4fae-b264-72d86dd92fdc

pending completion
cb0c000c-08ed-4fae-b264-72d86dd92fdc

push

circleci

Jaz Gulati
feat: upgrade testnet dnr for subnet support

49 of 244 branches covered (20.08%)

Branch coverage included in aggregate %.

221 of 559 relevant lines covered (39.53%)

6.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

21.75
/contracts/DarknodeRegistry/DarknodeRegistry.sol
1
pragma solidity 0.5.17;
2

3
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
4
import "@openzeppelin/contracts-ethereum-package/contracts/cryptography/ECDSA.sol";
5
import "@openzeppelin/upgrades/contracts/upgradeability/InitializableAdminUpgradeabilityProxy.sol";
6
import "@openzeppelin/upgrades/contracts/Initializable.sol";
7

8
import "../RenToken/RenToken.sol";
9
import "./DarknodeRegistryStore.sol";
10
import "../Governance/Claimable.sol";
11
import "../libraries/CanReclaimTokens.sol";
12
import "./DarknodeRegistryV1.sol";
13

14
contract DarknodeRegistryStateV2 {
15
    // RenVM can have a maximum of 255 subnets, and a darknodes inclusion or
16
    // exclusion is set using the ith bit of the below subnet, 0th bit is used
17
    // for RenVM and it should always be set to 1 when a darknode is registered
18
    mapping(address => uint256) public subnets;
19

20
    // subnetLastUpdated tracks when the subnet was last updated, subnet can
21
    // only be changed once per epoch
22
    mapping(address => uint256) public subnetLastUpdated;
23
}
24

25
/// @notice DarknodeRegistry is responsible for the registration and
26
/// deregistration of Darknodes.
27
/// @dev Emits a LogDarknodeSubnetUpdated event to set the subnet to 0 
28
/// during deregistration, and subnets map is set to 0 on refund. This is 
29
/// to allow slasher to slash a DN that is deregistered
30
contract DarknodeRegistryLogicV2 is
31
    Claimable,
32
    CanReclaimTokens,
33
    DarknodeRegistryStateV1,
34
    DarknodeRegistryStateV2
35
{
36
    using SafeMath for uint256;
37

38
    /// @notice Emitted when a darknode is registered.
39
    /// @param _darknodeOperator The owner of the darknode.
40
    /// @param _darknodeID The ID of the darknode that was registered.
41
    /// @param _bond The amount of REN that was transferred as bond.
42
    event LogDarknodeRegistered(
43
        address indexed _darknodeOperator,
44
        address indexed _darknodeID,
45
        uint256 _bond
46
    );
47

48
    /// @notice Emitted when a darknode is deregistered.
49
    /// @param _darknodeOperator The owner of the darknode.
50
    /// @param _darknodeID The ID of the darknode that was deregistered.
51
    event LogDarknodeDeregistered(
52
        address indexed _darknodeOperator,
53
        address indexed _darknodeID
54
    );
55

56
    /// @notice Emitted when a refund has been made.
57
    /// @param _darknodeOperator The owner of the darknode.
58
    /// @param _darknodeID The ID of the darknode that was refunded.
59
    /// @param _amount The amount of REN that was refunded.
60
    event LogDarknodeRefunded(
61
        address indexed _darknodeOperator,
62
        address indexed _darknodeID,
63
        uint256 _amount
64
    );
65

66
    /// @notice Emitted when a recovery has been made.
67
    /// @param _darknodeOperator The owner of the darknode.
68
    /// @param _darknodeID The ID of the darknode that was recovered.
69
    /// @param _bondRecipient The address that received the bond.
70
    /// @param _submitter The address that called the recover method.
71
    event LogDarknodeRecovered(
72
        address indexed _darknodeOperator,
73
        address indexed _darknodeID,
74
        address _bondRecipient,
75
        address indexed _submitter
76
    );
77

78
    /// @notice Emitted when a darknode's bond is slashed.
79
    /// @param _darknodeOperator The owner of the darknode.
80
    /// @param _darknodeID The ID of the darknode that was slashed.
81
    /// @param _challenger The address of the account that submitted the challenge.
82
    /// @param _percentage The total percentage  of bond slashed.
83
    event LogDarknodeSlashed(
84
        address indexed _darknodeOperator,
85
        address indexed _darknodeID,
86
        address indexed _challenger,
87
        uint8 _subnetID,
88
        uint256 _percentage
89
    );
90

91
    /// @notice Emitted when a darknode joins a subnet.
92
    /// @param _darknodeID The ID of the darknode that was registered.
93
    /// @param _subnet The subnets the current darknode is part of.
94
    event LogDarknodeSubnetUpdated(
95
        address indexed _darknodeID,
96
        uint256 indexed _subnet
97
    );
98

99
    /// @notice Emitted when a new epoch has begun.
100
    event LogNewEpoch(uint256 indexed epochhash);
101

102
    /// @notice Emitted when a constructor parameter has been updated.
103
    event LogMinimumBondUpdated(
104
        uint256 _previousMinimumBond,
105
        uint256 _nextMinimumBond
106
    );
107
    event LogMinimumPodSizeUpdated(
108
        uint256 _previousMinimumPodSize,
109
        uint256 _nextMinimumPodSize
110
    );
111
    event LogMinimumEpochIntervalUpdated(
112
        uint256 _previousMinimumEpochInterval,
113
        uint256 _nextMinimumEpochInterval
114
    );
115
    event LogSlasherUpdated(
116
        address indexed _previousSlasher,
117
        address indexed _nextSlasher
118
    );
119
    event LogDarknodePaymentUpdated(
120
        address indexed _previousDarknodePayment,
121
        address indexed _nextDarknodePayment
122
    );
123

124
    /// @notice Restrict a function to the owner that registered the darknode.
125
    modifier onlyDarknodeOperator(address _darknodeID) {
126
        require(
18!
127
            store.darknodeOperator(_darknodeID) == msg.sender,
128
            "DarknodeRegistry: must be darknode owner"
129
        );
130
        _;
18✔
131
    }
132

133
    /// @notice Restrict a function to unregistered darknodes.
134
    modifier onlyRefunded(address _darknodeID) {
135
        require(
×
136
            isRefunded(_darknodeID),
137
            "DarknodeRegistry: must be refunded or never registered"
138
        );
139
        _;
×
140
    }
141

142
    /// @notice Restrict a function to refundable darknodes.
143
    modifier onlyRefundable(address _darknodeID) {
144
        require(
18!
145
            isRefundable(_darknodeID),
146
            "DarknodeRegistry: must be deregistered for at least one epoch"
147
        );
148
        _;
18✔
149
    }
150

151
    /// @notice Restrict a function to registered nodes without a pending
152
    /// deregistration.
153
    modifier onlyDeregisterable(address _darknodeID) {
154
        require(
×
155
            isDeregisterable(_darknodeID),
156
            "DarknodeRegistry: must be deregisterable"
157
        );
158
        _;
×
159
    }
160

161
    /// @notice Restrict a function to the Slasher contract.
162
    modifier onlySlasher() {
163
        require(
×
164
            address(slasher) == msg.sender,
165
            "DarknodeRegistry: must be slasher"
166
        );
167
        _;
×
168
    }
169

170
    /// @notice Restrict a function to registered and deregistered nodes.
171
    modifier onlyDarknode(address _darknodeID) {
172
        require(
×
173
            isRegistered(_darknodeID) || isDeregistered(_darknodeID),
174
            "DarknodeRegistry: invalid darknode"
175
        );
176
        _;
×
177
    }
178

179
    /// @notice Restrict a function to nodes on the specific subnet.
180
    modifier onSubnet(address _darknodeID, uint8 _subnetID) {
181
        require(
×
182
            _subnetID == 0 ||
183
                subnets[_darknodeID] & (2**uint256(_subnetID)) ==
184
                2**uint256(_subnetID),
185
            "DarknodeRegistry: darknode not part of the subnet"
186
        );
187
        _;
×
188
    }
189

190
    /// @notice The contract constructor.
191
    ///
192
    /// @param _VERSION A string defining the contract version.
193
    /// @param _renAddress The address of the RenToken contract.
194
    /// @param _storeAddress The address of the DarknodeRegistryStore contract.
195
    /// @param _minimumBond The minimum bond amount that can be submitted by a
196
    ///        Darknode.
197
    /// @param _minimumPodSize The minimum size of a Darknode pod.
198
    /// @param _minimumEpochIntervalSeconds The minimum number of seconds between epochs.
199
    function initialize(
200
        string memory _VERSION,
201
        RenToken _renAddress,
202
        DarknodeRegistryStore _storeAddress,
203
        uint256 _minimumBond,
204
        uint256 _minimumPodSize,
205
        uint256 _minimumEpochIntervalSeconds,
206
        uint256 _deregistrationIntervalSeconds
207
    ) public initializer {
208
        Claimable.initialize(msg.sender);
2✔
209
        CanReclaimTokens.initialize(msg.sender);
2✔
210
        VERSION = _VERSION;
2✔
211

212
        store = _storeAddress;
2✔
213
        ren = _renAddress;
2✔
214

215
        minimumBond = _minimumBond;
2✔
216
        nextMinimumBond = minimumBond;
2✔
217

218
        minimumPodSize = _minimumPodSize;
2✔
219
        nextMinimumPodSize = minimumPodSize;
2✔
220

221
        minimumEpochInterval = _minimumEpochIntervalSeconds;
2✔
222
        nextMinimumEpochInterval = minimumEpochInterval;
2✔
223
        deregistrationInterval = _deregistrationIntervalSeconds;
2✔
224

225
        uint256 epochhash = uint256(blockhash(block.number - 1));
2✔
226
        currentEpoch = Epoch({
2✔
227
            epochhash: epochhash,
228
            blocktime: block.timestamp
229
        });
230
        emit LogNewEpoch(epochhash);
2✔
231
    }
232

233
    /// @notice Register a darknode and transfer the bond to this contract.
234
    /// Before registering, the bond transfer must be approved in the REN
235
    /// contract. The caller must provide a public encryption key for the
236
    /// darknode. The darknode will remain pending registration until the next
237
    /// epoch. Only after this period can the darknode be deregistered. The
238
    /// caller of this method will be stored as the owner of the darknode.
239
    ///
240
    /// @param _darknodeID The darknode ID that will be registered.
241
    function registerNode(address _darknodeID, uint256 _subnet)
242
        public
243
        onlyRefunded(_darknodeID)
244
    {
245
        require(
×
246
            _darknodeID != address(0),
247
            "DarknodeRegistry: darknode address cannot be zero"
248
        );
249

250
        // Use the current minimum bond as the darknode's bond and transfer bond to store
251
        require(
×
252
            ren.transferFrom(msg.sender, address(store), minimumBond),
253
            "DarknodeRegistry: bond transfer failed"
254
        );
255

256
        require(
×
257
            _subnet % 2 == 1,
258
            "DarknodeRegistry: can not remove RenVM inclusion"
259
        );
260

261
        // Flag this darknode for registration
262
        store.appendDarknode(
×
263
            _darknodeID,
264
            msg.sender,
265
            minimumBond,
266
            "",
267
            currentEpoch.blocktime.add(minimumEpochInterval),
268
            0
269
        );
270

271
        numDarknodesNextEpoch = numDarknodesNextEpoch.add(1);
×
272

273
        // Emit an event.
274
        emit LogDarknodeRegistered(msg.sender, _darknodeID, minimumBond);
×
275
        updateDarknodeSubnet(_darknodeID, _subnet);
×
276
    }
277

278
    /// @notice An alias for `registerNode` that includes the legacy public key
279
    /// parameter.
280
    /// @param _darknodeID The darknode ID that will be registered.
281
    /// @param _publicKey Deprecated parameter - see `registerNode`.
282
    function register(address _darknodeID, bytes calldata _publicKey) external {
283
        // Default subnets to only the core RenVM subnet.
284
        return registerNode(_darknodeID, 1);
×
285
    }
286

287
    /// @notice update the subnets the darknode is part of, can be used to join or leave subnets.
288
    /// @param _darknodeID The darknode ID that will be registered.
289
    /// @param _subnet The subnets the darknode want to be part of.
290
    function updateSubnet(address _darknodeID, uint256 _subnet)
291
        public
292
        onlyDarknodeOperator(_darknodeID)
293
    {
294
        require(
×
295
            _subnet % 2 == 1,
296
            "DarknodeRegistry: can not remove RenVM inclusion"
297
        );
298
        uint256 currentHash = currentEpoch.epochhash;
×
299
        require(
×
300
            subnetLastUpdated[_darknodeID] != currentHash,
301
            "DarknodeRegistry: can only update subnet once per epoch"
302
        );
303
        updateDarknodeSubnet(_darknodeID, _subnet);
×
304
    }
305

306
    /// @notice update the subnets the darknode is part of, can be used to join or leave subnets.
307
    /// @param _darknodeIDs The list of darknode IDs that will be updated.
308
    /// @param _subnet The subnets the darknode want to be part of.
309
    function updateSubnetMultiple(
310
        address[] calldata _darknodeIDs,
311
        uint256 _subnet
312
    ) external {
313
        require(
×
314
            _subnet % 2 == 1,
315
            "DarknodeRegistry: can not remove RenVM inclusion"
316
        );
317

318
        for (uint256 i = 0; i < _darknodeIDs.length; i++) {
×
319
            require(
×
320
                store.darknodeOperator(_darknodeIDs[i]) == msg.sender,
321
                "DarknodeRegistry: can only be called by the darknode operator"
322
            );
323
            require(
×
324
                subnetLastUpdated[_darknodeIDs[i]] != currentEpoch.epochhash,
325
                "DarknodeRegistry: can only update subnet once per epoch"
326
            );
327
            updateDarknodeSubnet(_darknodeIDs[i], _subnet);
×
328
        }
329
    }
330

331
    /// @notice Register multiple darknodes and transfer the bonds to this contract.
332
    /// Before registering, the bonds transfer must be approved in the REN contract.
333
    /// The darknodes will remain pending registration until the next epoch. Only
334
    /// after this period can the darknodes be deregistered. The caller of this method
335
    /// will be stored as the owner of each darknode. If one registration fails, all
336
    /// registrations fail.
337
    /// @param _darknodeIDs The darknode IDs that will be registered.
338
    /// @param _subnet The subnets the darknodes want to be part of.
339
    function registerMultiple(address[] calldata _darknodeIDs, uint256 _subnet)
340
        external
341
    {
342
        // Save variables in memory to prevent redundant reads from storage
343
        DarknodeRegistryStore _store = store;
×
344
        Epoch memory _currentEpoch = currentEpoch;
×
345
        uint256 nextRegisteredAt = _currentEpoch.blocktime.add(
×
346
            minimumEpochInterval
347
        );
348
        uint256 _minimumBond = minimumBond;
×
349

350
        require(
×
351
            _subnet % 2 == 1,
352
            "DarknodeRegistry: can not remove RenVM inclusion"
353
        );
354

355
        require(
×
356
            ren.transferFrom(
357
                msg.sender,
358
                address(_store),
359
                _minimumBond.mul(_darknodeIDs.length)
360
            ),
361
            "DarknodeRegistry: bond transfers failed"
362
        );
363

364
        for (uint256 i = 0; i < _darknodeIDs.length; i++) {
×
365
            address darknodeID = _darknodeIDs[i];
×
366

367
            uint256 registeredAt = _store.darknodeRegisteredAt(darknodeID);
×
368
            uint256 deregisteredAt = _store.darknodeDeregisteredAt(darknodeID);
×
369

370
            require(
×
371
                _isRefunded(registeredAt, deregisteredAt),
372
                "DarknodeRegistry: must be refunded or never registered"
373
            );
374

375
            require(
×
376
                darknodeID != address(0),
377
                "DarknodeRegistry: darknode address cannot be zero"
378
            );
379

380
            _store.appendDarknode(
×
381
                darknodeID,
382
                msg.sender,
383
                _minimumBond,
384
                "",
385
                nextRegisteredAt,
386
                0
387
            );
388

389
            emit LogDarknodeRegistered(msg.sender, darknodeID, _minimumBond);
×
390

391
            updateDarknodeSubnet(darknodeID, _subnet);
×
392
        }
393

394
        numDarknodesNextEpoch = numDarknodesNextEpoch.add(_darknodeIDs.length);
×
395
    }
396

397
    /// @notice Deregister a darknode. The darknode will not be deregistered
398
    /// until the end of the epoch. After another epoch, the bond can be
399
    /// refunded by calling the refund method.
400
    /// @param _darknodeID The darknode ID that will be deregistered. The caller
401
    ///        of this method must be the owner of this darknode.
402
    function deregister(address _darknodeID)
403
        external
404
        onlyDeregisterable(_darknodeID)
405
        onlyDarknodeOperator(_darknodeID)
406
    {
407
        deregisterDarknode(_darknodeID);
×
408
    }
409

410
    /// @notice Deregister multiple darknodes. The darknodes will not be
411
    /// deregistered until the end of the epoch. After another epoch, their
412
    /// bonds can be refunded by calling the refund or refundMultiple methods.
413
    /// If one deregistration fails, all deregistrations fail.
414
    /// @param _darknodeIDs The darknode IDs that will be deregistered. The
415
    /// caller of this method must be the owner of each darknode.
416
    function deregisterMultiple(address[] calldata _darknodeIDs) external {
417
        // Save variables in memory to prevent redundant reads from storage
418
        DarknodeRegistryStore _store = store;
×
419
        Epoch memory _currentEpoch = currentEpoch;
×
420
        uint256 nextDeregisteredAt = _currentEpoch.blocktime.add(
×
421
            minimumEpochInterval
422
        );
423

424
        for (uint256 i = 0; i < _darknodeIDs.length; i++) {
×
425
            address darknodeID = _darknodeIDs[i];
×
426

427
            uint256 deregisteredAt = _store.darknodeDeregisteredAt(darknodeID);
×
428
            bool registered = isRegisteredInEpoch(
×
429
                _store.darknodeRegisteredAt(darknodeID),
430
                deregisteredAt,
431
                _currentEpoch
432
            );
433

434
            require(
×
435
                _isDeregisterable(registered, deregisteredAt),
436
                "DarknodeRegistry: must be deregisterable"
437
            );
438

439
            require(
×
440
                _store.darknodeOperator(darknodeID) == msg.sender,
441
                "DarknodeRegistry: must be darknode owner"
442
            );
443

444
            _store.updateDarknodeDeregisteredAt(darknodeID, nextDeregisteredAt);
×
445

446
            emit LogDarknodeSubnetUpdated(darknodeID, 0);
×
447

448
            emit LogDarknodeDeregistered(msg.sender, darknodeID);
×
449
        }
450

451
        numDarknodesNextEpoch = numDarknodesNextEpoch.sub(_darknodeIDs.length);
×
452
    }
453

454
    /// @notice Progress the epoch if it is possible to do so. This captures
455
    /// the current timestamp and current blockhash and overrides the current
456
    /// epoch.
457
    function epoch() external {
458
        if (previousEpoch.blocktime == 0) {
3!
459
            // The first epoch must be called by the owner of the contract
460
            require(
×
461
                msg.sender == owner(),
462
                "DarknodeRegistry: not authorized to call first epoch"
463
            );
464
        }
465

466
        // Require that the epoch interval has passed
467
        require(
3✔
468
            block.timestamp >= currentEpoch.blocktime.add(minimumEpochInterval),
469
            "DarknodeRegistry: epoch interval has not passed"
470
        );
471
        uint256 epochhash = uint256(blockhash(block.number - 1));
1✔
472

473
        // Update the epoch hash and timestamp
474
        previousEpoch = currentEpoch;
1✔
475
        currentEpoch = Epoch({
1✔
476
            epochhash: epochhash,
477
            blocktime: block.timestamp
478
        });
479

480
        // Update the registry information
481
        numDarknodesPreviousEpoch = numDarknodes;
1✔
482
        numDarknodes = numDarknodesNextEpoch;
1✔
483

484
        // If any update functions have been called, update the values now
485
        if (nextMinimumBond != minimumBond) {
1!
486
            minimumBond = nextMinimumBond;
×
487
            emit LogMinimumBondUpdated(minimumBond, nextMinimumBond);
×
488
        }
489
        if (nextMinimumPodSize != minimumPodSize) {
1!
490
            minimumPodSize = nextMinimumPodSize;
×
491
            emit LogMinimumPodSizeUpdated(minimumPodSize, nextMinimumPodSize);
×
492
        }
493
        if (nextMinimumEpochInterval != minimumEpochInterval) {
1!
494
            minimumEpochInterval = nextMinimumEpochInterval;
×
495
            emit LogMinimumEpochIntervalUpdated(
×
496
                minimumEpochInterval,
497
                nextMinimumEpochInterval
498
            );
499
        }
500
        if (nextSlasher != slasher) {
1!
501
            slasher = nextSlasher;
×
502
            emit LogSlasherUpdated(address(slasher), address(nextSlasher));
×
503
        }
504

505
        // Emit an event
506
        emit LogNewEpoch(epochhash);
1✔
507
    }
508

509
    /// @notice Allows the contract owner to initiate an ownership transfer of
510
    /// the DarknodeRegistryStore.
511
    /// @param _newOwner The address to transfer the ownership to.
512
    function transferStoreOwnership(DarknodeRegistryLogicV2 _newOwner)
513
        external
514
        onlyOwner
515
    {
516
        store.transferOwnership(address(_newOwner));
×
517
        _newOwner.claimStoreOwnership();
×
518
    }
519

520
    /// @notice Claims ownership of the store passed in to the constructor.
521
    /// `transferStoreOwnership` must have previously been called when
522
    /// transferring from another Darknode Registry.
523
    function claimStoreOwnership() external {
524
        store.claimOwnership();
1✔
525

526
        // Sync state with new store.
527
        // Note: numDarknodesPreviousEpoch is set to 0 for a newly deployed DNR.
528
        (
1✔
529
            numDarknodesPreviousEpoch,
530
            numDarknodes,
531
            numDarknodesNextEpoch
532
        ) = getDarknodeCountFromEpochs();
533
    }
534

535
    /// @notice Allows the contract owner to update the minimum bond.
536
    /// @param _nextMinimumBond The minimum bond amount that can be submitted by
537
    ///        a darknode.
538
    function updateMinimumBond(uint256 _nextMinimumBond) external onlyOwner {
539
        // Will be updated next epoch
540
        nextMinimumBond = _nextMinimumBond;
×
541
    }
542

543
    /// @notice Allows the contract owner to update the minimum pod size.
544
    /// @param _nextMinimumPodSize The minimum size of a pod.
545
    function updateMinimumPodSize(uint256 _nextMinimumPodSize)
546
        external
547
        onlyOwner
548
    {
549
        // Will be updated next epoch
550
        nextMinimumPodSize = _nextMinimumPodSize;
×
551
    }
552

553
    /// @notice Allows the contract owner to update the minimum epoch interval.
554
    /// @param _nextMinimumEpochInterval The minimum number of blocks between epochs.
555
    function updateMinimumEpochInterval(uint256 _nextMinimumEpochInterval)
556
        external
557
        onlyOwner
558
    {
559
        // Will be updated next epoch
560
        nextMinimumEpochInterval = _nextMinimumEpochInterval;
×
561
    }
562

563
    /// @notice Allow the contract owner to update the DarknodeSlasher contract
564
    /// address.
565
    /// @param _slasher The new slasher address.
566
    function updateSlasher(IDarknodeSlasher _slasher) external onlyOwner {
567
        nextSlasher = _slasher;
1✔
568
    }
569

570
    /// @notice Allow the DarknodeSlasher contract to slash a portion of darknode's
571
    ///         bond and deregister it.
572
    /// @param _guilty The guilty prover whose bond is being slashed.
573
    /// @param _challenger The challenger who should receive a portion of the bond as reward.
574
    /// @param _percentage The total percentage  of bond to be slashed.
575
    function slash(
576
        uint8 _subnetID,
577
        address _guilty,
578
        address _challenger,
579
        uint256 _percentage
580
    ) external onlySlasher onSubnet(_guilty, _subnetID) onlyDarknode(_guilty) {
581
        require(_percentage <= 100, "DarknodeRegistry: invalid percent");
×
582

583
        // If the darknode has not been deregistered then deregister it
584
        if (isDeregisterable(_guilty)) {
×
585
            deregisterDarknode(_guilty);
×
586
        }
587

588
        uint256 totalBond = store.darknodeBond(_guilty);
×
589
        uint256 penalty = totalBond.div(100).mul(_percentage);
×
590
        uint256 challengerReward = penalty.div(2);
×
591
        uint256 slasherPortion = penalty.sub(challengerReward);
×
592
        if (challengerReward > 0) {
×
593
            // Slash the bond of the failed prover
594
            store.updateDarknodeBond(_guilty, totalBond.sub(penalty));
×
595

596
            // Forward the remaining amount to be handled by the slasher.
597
            require(
×
598
                ren.transfer(msg.sender, slasherPortion),
599
                "DarknodeRegistry: reward transfer to slasher failed"
600
            );
601
            require(
×
602
                ren.transfer(_challenger, challengerReward),
603
                "DarknodeRegistry: reward transfer to challenger failed"
604
            );
605
        }
606

607
        emit LogDarknodeSlashed(
×
608
            store.darknodeOperator(_guilty),
609
            _guilty,
610
            _challenger,
611
            _subnetID,
612
            _percentage
613
        );
614
    }
615

616
    /// @notice Refund the bond of a deregistered darknode. This will make the
617
    /// darknode available for registration again.
618
    ///
619
    /// @param _darknodeID The darknode ID that will be refunded.
620
    function refund(address _darknodeID)
621
        external
622
        onlyRefundable(_darknodeID)
623
        onlyDarknodeOperator(_darknodeID)
624
    {
625
        // Remember the bond amount
626
        uint256 amount = store.darknodeBond(_darknodeID);
18✔
627

628
        // Erase the darknode from the registry
629
        store.removeDarknode(_darknodeID);
18✔
630

631
        // Remove from all subnets
632
        subnets[_darknodeID] = 0;
18✔
633
        delete(subnetLastUpdated[_darknodeID]);
18✔
634

635
        // Refund the operator by transferring REN
636
        require(
18!
637
            ren.transfer(msg.sender, amount),
638
            "DarknodeRegistry: bond transfer failed"
639
        );
640

641
        // Emit an event.
642
        emit LogDarknodeRefunded(msg.sender, _darknodeID, amount);
18✔
643
    }
644

645
    /// @notice A permissioned method for refunding a darknode without the usual
646
    /// delay. The operator must provide a signature of the darknode ID and the
647
    /// bond recipient, but the call must come from the contract's owner. The
648
    /// main use case is for when an operator's keys have been compromised,
649
    /// allowing for the bonds to be recovered by the operator through the
650
    /// GatewayRegistry's governance. It is expected that this process would
651
    /// happen towards the end of the darknode's deregistered period, so that
652
    /// a malicious operator can't use this to quickly exit their stake after
653
    /// attempting an attack on the network. It's also expected that the
654
    /// operator will not re-register the same darknode again.
655
    function recover(
656
        address _darknodeID,
657
        address _bondRecipient,
658
        bytes calldata _signature
659
    ) external onlyOwner {
660
        require(
2!
661
            isRefundable(_darknodeID) || isDeregistered(_darknodeID),
662
            "DarknodeRegistry: must be deregistered"
663
        );
664

665
        address darknodeOperator = store.darknodeOperator(_darknodeID);
2✔
666

667
        require(
2!
668
            ECDSA.recover(
669
                keccak256(
670
                    abi.encodePacked(
671
                        "\x19Ethereum Signed Message:\n64",
672
                        "DarknodeRegistry.recover",
673
                        _darknodeID,
674
                        _bondRecipient
675
                    )
676
                ),
677
                _signature
678
            ) == darknodeOperator,
679
            "DarknodeRegistry: invalid signature"
680
        );
681

682
        // Remember the bond amount
683
        uint256 amount = store.darknodeBond(_darknodeID);
2✔
684

685
        // Erase the darknode from the registry
686
        store.removeDarknode(_darknodeID);
2✔
687

688
        // Refund the operator by transferring REN
689
        require(
2!
690
            ren.transfer(_bondRecipient, amount),
691
            "DarknodeRegistry: bond transfer failed"
692
        );
693

694
        // Emit an event.
695
        emit LogDarknodeRefunded(darknodeOperator, _darknodeID, amount);
2✔
696
        emit LogDarknodeRecovered(
2✔
697
            darknodeOperator,
698
            _darknodeID,
699
            _bondRecipient,
700
            msg.sender
701
        );
702
    }
703

704
    /// @notice Refund the bonds of multiple deregistered darknodes. This will
705
    /// make the darknodes available for registration again. If one refund fails,
706
    /// all refunds fail.
707
    /// @param _darknodeIDs The darknode IDs that will be refunded.
708
    function refundMultiple(address[] calldata _darknodeIDs) external {
709
        // Save variables in memory to prevent redundant reads from storage
710
        DarknodeRegistryStore _store = store;
×
711
        Epoch memory _currentEpoch = currentEpoch;
×
712
        Epoch memory _previousEpoch = previousEpoch;
×
713
        uint256 _deregistrationInterval = deregistrationInterval;
×
714

715
        // The sum of bonds to refund
716
        uint256 sum;
×
717

718
        for (uint256 i = 0; i < _darknodeIDs.length; i++) {
×
719
            address darknodeID = _darknodeIDs[i];
×
720

721
            uint256 deregisteredAt = _store.darknodeDeregisteredAt(darknodeID);
×
722
            bool deregistered = _isDeregistered(deregisteredAt, _currentEpoch);
×
723

724
            require(
×
725
                _isRefundable(
726
                    deregistered,
727
                    deregisteredAt,
728
                    _previousEpoch,
729
                    _deregistrationInterval
730
                ),
731
                "DarknodeRegistry: must be deregistered for at least one epoch"
732
            );
733

734
            require(
×
735
                _store.darknodeOperator(darknodeID) == msg.sender,
736
                "DarknodeRegistry: must be darknode owner"
737
            );
738

739
            // Remember the bond amount
740
            uint256 amount = _store.darknodeBond(darknodeID);
×
741

742
            // Erase the darknode from the registry
743
            _store.removeDarknode(darknodeID);
×
744

745
            // Remove from all subnets
746
            subnets[darknodeID] = 0;
×
747
            delete(subnetLastUpdated[darknodeID]);
×
748

749
            // Emit an event
750
            emit LogDarknodeRefunded(msg.sender, darknodeID, amount);
×
751

752
            // Increment the sum of bonds to be transferred
753
            sum = sum.add(amount);
×
754
        }
755

756
        // Transfer all bonds together
757
        require(
×
758
            ren.transfer(msg.sender, sum),
759
            "DarknodeRegistry: bond transfers failed"
760
        );
761
    }
762

763
    /// @notice Retrieves the address of the account that registered a darknode.
764
    /// @param _darknodeID The ID of the darknode to retrieve the owner for.
765
    function getDarknodeOperator(address _darknodeID)
766
        external
767
        view
768
        returns (address payable)
769
    {
770
        return store.darknodeOperator(_darknodeID);
×
771
    }
772

773
    /// @notice Retrieves the bond amount of a darknode in 10^-18 REN.
774
    /// @param _darknodeID The ID of the darknode to retrieve the bond for.
775
    function getDarknodeBond(address _darknodeID)
776
        external
777
        view
778
        returns (uint256)
779
    {
780
        return store.darknodeBond(_darknodeID);
×
781
    }
782

783
    /// @notice Retrieves the encryption public key of the darknode.
784
    /// @param _darknodeID The ID of the darknode to retrieve the public key for.
785
    function getDarknodePublicKey(address _darknodeID)
786
        external
787
        view
788
        returns (bytes memory)
789
    {
790
        return store.darknodePublicKey(_darknodeID);
×
791
    }
792

793
    /// @notice Retrieves a list of darknodes which are registered for the
794
    /// current epoch.
795
    /// @param _start A darknode ID used as an offset for the list. If _start is
796
    ///        0x0, the first dark node will be used. _start won't be
797
    ///        included it is not registered for the epoch.
798
    /// @param _count The number of darknodes to retrieve starting from _start.
799
    ///        If _count is 0, all of the darknodes from _start are
800
    ///        retrieved. If _count is more than the remaining number of
801
    ///        registered darknodes, the rest of the list will contain
802
    ///        0x0s.
803
    function getDarknodes(address _start, uint256 _count)
804
        external
805
        view
806
        returns (address[] memory)
807
    {
808
        uint256 count = _count;
×
809
        if (count == 0) {
×
810
            count = numDarknodes;
×
811
        }
812
        return getDarknodesFromEpochs(_start, count, false);
×
813
    }
814

815
    /// @notice Retrieves a list of darknodes which were registered for the
816
    /// previous epoch. See `getDarknodes` for the parameter documentation.
817
    function getPreviousDarknodes(address _start, uint256 _count)
818
        external
819
        view
820
        returns (address[] memory)
821
    {
822
        uint256 count = _count;
×
823
        if (count == 0) {
×
824
            count = numDarknodesPreviousEpoch;
×
825
        }
826
        return getDarknodesFromEpochs(_start, count, true);
×
827
    }
828

829
    /// @notice Returns whether a darknode is scheduled to become registered
830
    /// at next epoch.
831
    /// @param _darknodeID The ID of the darknode to return.
832
    function isPendingRegistration(address _darknodeID)
833
        public
834
        view
835
        returns (bool)
836
    {
837
        uint256 registeredAt = store.darknodeRegisteredAt(_darknodeID);
×
838
        return registeredAt != 0 && registeredAt > currentEpoch.blocktime;
×
839
    }
840

841
    /// @notice Returns if a darknode is in the pending deregistered state. In
842
    /// this state a darknode is still considered registered.
843
    function isPendingDeregistration(address _darknodeID)
844
        public
845
        view
846
        returns (bool)
847
    {
848
        uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID);
×
849
        return deregisteredAt != 0 && deregisteredAt > currentEpoch.blocktime;
×
850
    }
851

852
    /// @notice Returns if a darknode is in the deregistered state.
853
    function isDeregistered(address _darknodeID) public view returns (bool) {
854
        uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID);
1✔
855
        return _isDeregistered(deregisteredAt, currentEpoch);
1✔
856
    }
857

858
    /// @notice Returns if a darknode can be deregistered. This is true if the
859
    /// darknodes is in the registered state and has not attempted to
860
    /// deregister yet.
861
    function isDeregisterable(address _darknodeID) public view returns (bool) {
862
        DarknodeRegistryStore _store = store;
×
863
        uint256 deregisteredAt = _store.darknodeDeregisteredAt(_darknodeID);
×
864
        bool registered = isRegisteredInEpoch(
×
865
            _store.darknodeRegisteredAt(_darknodeID),
866
            deregisteredAt,
867
            currentEpoch
868
        );
869
        return _isDeregisterable(registered, deregisteredAt);
×
870
    }
871

872
    /// @notice Returns if a darknode is in the refunded state. This is true
873
    /// for darknodes that have never been registered, or darknodes that have
874
    /// been deregistered and refunded.
875
    function isRefunded(address _darknodeID) public view returns (bool) {
876
        DarknodeRegistryStore _store = store;
×
877
        uint256 registeredAt = _store.darknodeRegisteredAt(_darknodeID);
×
878
        uint256 deregisteredAt = _store.darknodeDeregisteredAt(_darknodeID);
×
879
        return _isRefunded(registeredAt, deregisteredAt);
×
880
    }
881

882
    /// @notice Returns if a darknode is refundable. This is true for darknodes
883
    /// that have been in the deregistered state for one full epoch.
884
    function isRefundable(address _darknodeID) public view returns (bool) {
885
        uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID);
20✔
886
        bool deregistered = _isDeregistered(deregisteredAt, currentEpoch);
20✔
887

888
        return
20✔
889
            _isRefundable(
890
                deregistered,
891
                deregisteredAt,
892
                previousEpoch,
893
                deregistrationInterval
894
            );
895
    }
896

897
    /// @notice Returns the registration time of a given darknode.
898
    function darknodeRegisteredAt(address darknodeID)
899
        external
900
        view
901
        returns (uint256)
902
    {
903
        return store.darknodeRegisteredAt(darknodeID);
×
904
    }
905

906
    /// @notice Returns the deregistration time of a given darknode.
907
    function darknodeDeregisteredAt(address darknodeID)
908
        external
909
        view
910
        returns (uint256)
911
    {
912
        return store.darknodeDeregisteredAt(darknodeID);
×
913
    }
914

915
    /// @notice Returns if a darknode is in the registered state.
916
    function isRegistered(address _darknodeID) public view returns (bool) {
917
        DarknodeRegistryStore _store = store;
×
918
        uint256 registeredAt = _store.darknodeRegisteredAt(_darknodeID);
×
919
        uint256 deregisteredAt = _store.darknodeDeregisteredAt(_darknodeID);
×
920
        return isRegisteredInEpoch(registeredAt, deregisteredAt, currentEpoch);
×
921
    }
922

923
    /// @notice Returns if a darknode was in the registered state last epoch.
924
    function isRegisteredInPreviousEpoch(address _darknodeID)
925
        public
926
        view
927
        returns (bool)
928
    {
929
        DarknodeRegistryStore _store = store;
×
930
        uint256 registeredAt = _store.darknodeRegisteredAt(_darknodeID);
×
931
        uint256 deregisteredAt = _store.darknodeDeregisteredAt(_darknodeID);
×
932
        return isRegisteredInEpoch(registeredAt, deregisteredAt, previousEpoch);
×
933
    }
934

935
    function getOperatorDarknodes(address _operator)
936
        public
937
        view
938
        returns (address[] memory)
939
    {
940
        address[] memory nodesPadded = new address[](numDarknodes);
×
941

942
        address[] memory allNodes = getDarknodesFromEpochs(
×
943
            address(0),
944
            numDarknodes,
945
            false
946
        );
947

948
        uint256 j = 0;
×
949
        for (uint256 i = 0; i < allNodes.length; i++) {
×
950
            if (store.darknodeOperator(allNodes[i]) == _operator) {
×
951
                nodesPadded[j] = (allNodes[i]);
×
952
                j++;
×
953
            }
954
        }
955

956
        address[] memory nodes = new address[](j);
×
957
        for (uint256 i = 0; i < j; i++) {
×
958
            nodes[i] = nodesPadded[i];
×
959
        }
960

961
        return nodes;
×
962
    }
963

964
    /// @notice Returns if a darknode was in the registered state for a given
965
    /// epoch.
966
    /// @param _epoch One of currentEpoch, previousEpoch.
967
    function isRegisteredInEpoch(
968
        uint256 _registeredAt,
969
        uint256 _deregisteredAt,
970
        Epoch memory _epoch
971
    ) private pure returns (bool) {
972
        bool registered = _registeredAt != 0 &&
×
973
            _registeredAt <= _epoch.blocktime;
974
        bool notDeregistered = _deregisteredAt == 0 ||
×
975
            _deregisteredAt > _epoch.blocktime;
976
        // The Darknode has been registered and has not yet been deregistered,
977
        // although it might be pending deregistration
978
        return registered && notDeregistered;
×
979
    }
980

981
    /// Private function called by `isDeregistered`, `isRefundable`, and `refundMultiple`.
982
    function _isDeregistered(
983
        uint256 _deregisteredAt,
984
        Epoch memory _currentEpoch
985
    ) private pure returns (bool) {
986
        return
21✔
987
            _deregisteredAt != 0 && _deregisteredAt <= _currentEpoch.blocktime;
988
    }
989

990
    /// Private function called by `isDeregisterable` and `deregisterMultiple`.
991
    function _isDeregisterable(bool _registered, uint256 _deregisteredAt)
992
        private
993
        pure
994
        returns (bool)
995
    {
996
        // The Darknode is currently in the registered state and has not been
997
        // transitioned to the pending deregistration, or deregistered, state
998
        return _registered && _deregisteredAt == 0;
×
999
    }
1000

1001
    /// Private function called by `isRefunded` and `registerMultiple`.
1002
    function _isRefunded(uint256 registeredAt, uint256 deregisteredAt)
1003
        private
1004
        pure
1005
        returns (bool)
1006
    {
1007
        return registeredAt == 0 && deregisteredAt == 0;
×
1008
    }
1009

1010
    /// Private function called by `isRefundable` and `refundMultiple`.
1011
    function _isRefundable(
1012
        bool _deregistered,
1013
        uint256 _deregisteredAt,
1014
        Epoch memory _previousEpoch,
1015
        uint256 _deregistrationInterval
1016
    ) private pure returns (bool) {
1017
        return
20✔
1018
            _deregistered &&
1019
            _deregisteredAt <=
1020
            (_previousEpoch.blocktime - _deregistrationInterval);
1021
    }
1022

1023
    /// @notice Returns a list of darknodes registered for either the current
1024
    /// or the previous epoch. See `getDarknodes` for documentation on the
1025
    /// parameters `_start` and `_count`.
1026
    /// @param _usePreviousEpoch If true, use the previous epoch, otherwise use
1027
    ///        the current epoch.
1028
    function getDarknodesFromEpochs(
1029
        address _start,
1030
        uint256 _count,
1031
        bool _usePreviousEpoch
1032
    ) private view returns (address[] memory) {
1033
        uint256 count = _count;
×
1034
        if (count == 0) {
×
1035
            count = numDarknodes;
×
1036
        }
1037

1038
        address[] memory nodes = new address[](count);
×
1039

1040
        // Begin with the first node in the list
1041
        uint256 n = 0;
×
1042
        address next = _start;
×
1043
        if (next == address(0)) {
×
1044
            next = store.begin();
×
1045
        }
1046

1047
        // Iterate until all registered Darknodes have been collected
1048
        while (n < count) {
×
1049
            if (next == address(0)) {
×
1050
                break;
×
1051
            }
1052
            // Only include Darknodes that are currently registered
1053
            bool includeNext;
×
1054
            if (_usePreviousEpoch) {
×
1055
                includeNext = isRegisteredInPreviousEpoch(next);
×
1056
            } else {
1057
                includeNext = isRegistered(next);
×
1058
            }
1059
            if (!includeNext) {
×
1060
                next = store.next(next);
×
1061
                continue;
×
1062
            }
1063
            nodes[n] = next;
×
1064
            next = store.next(next);
×
1065
            n += 1;
×
1066
        }
1067
        return nodes;
×
1068
    }
1069

1070
    /// Private function called by `deregister` and `slash`
1071
    function deregisterDarknode(address _darknodeID) private {
1072
        address darknodeOperator = store.darknodeOperator(_darknodeID);
×
1073

1074
        // Flag the darknode for deregistration
1075
        store.updateDarknodeDeregisteredAt(
×
1076
            _darknodeID,
1077
            currentEpoch.blocktime.add(minimumEpochInterval)
1078
        );
1079
        numDarknodesNextEpoch = numDarknodesNextEpoch.sub(1);
×
1080

1081
        emit LogDarknodeSubnetUpdated(_darknodeID, 0);
×
1082
        // Emit an event
1083
        emit LogDarknodeDeregistered(darknodeOperator, _darknodeID);
×
1084
    }
1085

1086
    function updateDarknodeSubnet(address _darknodeID, uint256 _subnet)
1087
        private
1088
    {
1089
        subnets[_darknodeID] = _subnet;
×
1090
        subnetLastUpdated[_darknodeID] = currentEpoch.epochhash;
×
1091
        emit LogDarknodeSubnetUpdated(_darknodeID, _subnet);
×
1092
    }
1093

1094
    function getDarknodeCountFromEpochs()
1095
        private
1096
        view
1097
        returns (
1098
            uint256,
1099
            uint256,
1100
            uint256
1101
        )
1102
    {
1103
        // Begin with the first node in the list
1104
        uint256 nPreviousEpoch = 0;
1✔
1105
        uint256 nCurrentEpoch = 0;
1✔
1106
        uint256 nNextEpoch = 0;
1✔
1107
        address next = store.begin();
1✔
1108

1109
        // Iterate until all registered Darknodes have been collected
1110
        while (true) {
1✔
1111
            // End of darknode list.
1112
            if (next == address(0)) {
1!
1113
                break;
1✔
1114
            }
1115

1116
            if (isRegisteredInPreviousEpoch(next)) {
×
1117
                nPreviousEpoch += 1;
×
1118
            }
1119

1120
            if (isRegistered(next)) {
×
1121
                nCurrentEpoch += 1;
×
1122
            }
1123

1124
            // Darknode is registered and has not deregistered, or is pending
1125
            // becoming registered.
1126
            if (
×
1127
                ((isRegistered(next) && !isPendingDeregistration(next)) ||
1128
                    isPendingRegistration(next))
1129
            ) {
1130
                nNextEpoch += 1;
×
1131
            }
1132
            next = store.next(next);
×
1133
        }
1134
        return (nPreviousEpoch, nCurrentEpoch, nNextEpoch);
1✔
1135
    }
1136
}
1137

1138
/* solium-disable-next-line no-empty-blocks */
1139
contract DarknodeRegistryProxy is InitializableAdminUpgradeabilityProxy {
1140

1141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc