@@ -52,7 +52,7 @@ contract Staking is Governed {
52
52
// IndexNode stake tracking : indexNode => Stake
53
53
mapping (address => Stakes.IndexNode) public stakes;
54
54
55
- // Payment channels : channelID => Channel
55
+ // Channels : channelID => Channel
56
56
mapping (address => Channel) public channels;
57
57
58
58
// Rebate pools : epoch => Pool
@@ -96,19 +96,22 @@ contract Staking is Governed {
96
96
97
97
/**
98
98
* @dev Emitted when `indexNode` allocated `tokens` amount to `subgraphID`
99
- * during `epoch` and registered `channelID` as payment channel.
99
+ * during `epoch`.
100
+ * `channelID` is the address of the index node in the channel multisig.
101
+ * `channelPubKey` is the public key used for routing payments to the index node channel.
100
102
*/
101
103
event AllocationCreated (
102
104
address indexed indexNode ,
103
105
bytes32 indexed subgraphID ,
104
106
uint256 epoch ,
105
107
uint256 tokens ,
106
- address channelID
108
+ address channelID ,
109
+ bytes channelPubKey
107
110
);
108
111
109
112
/**
110
113
* @dev Emitted when `indexNode` settled an allocation of `tokens` amount to `subgraphID`
111
- * during `epoch` using `channelID` as payment channel.
114
+ * during `epoch` using `channelID` as channel.
112
115
*
113
116
* NOTE: `from` tracks the multisig contract from where it was settled.
114
117
*/
@@ -153,10 +156,12 @@ contract Staking is Governed {
153
156
* @param _epochManager Address of the EpochManager contract
154
157
* @param _curation Address of the Curation contract
155
158
*/
156
- constructor (address _governor , address _token , address _epochManager , address _curation )
157
- public
158
- Governed (_governor)
159
- {
159
+ constructor (
160
+ address _governor ,
161
+ address _token ,
162
+ address _epochManager ,
163
+ address _curation
164
+ ) public Governed (_governor) {
160
165
token = GraphToken (_token);
161
166
epochManager = EpochManager (_epochManager);
162
167
curation = Curation (_curation);
@@ -216,7 +221,7 @@ contract Staking is Governed {
216
221
217
222
/**
218
223
* @dev Return if channelID (address) is already used
219
- * @param _channelID Address used as signer for index node in payment channel
224
+ * @param _channelID Address used as signer for index node in channel
220
225
* @return True if channelID already used
221
226
*/
222
227
function isChannel (address _channelID ) public view returns (bool ) {
@@ -262,26 +267,37 @@ contract Staking is Governed {
262
267
* @param _reward Amount of reward tokens to send to a beneficiary
263
268
* @param _beneficiary Address of a beneficiary to receive a reward for the slashing
264
269
*/
265
- function slash (address _indexNode , uint256 _tokens , uint256 _reward , address _beneficiary )
266
- external
267
- onlySlasher
268
- {
269
- uint256 tokensToSlash = _tokens;
270
+ function slash (
271
+ address _indexNode ,
272
+ uint256 _tokens ,
273
+ uint256 _reward ,
274
+ address _beneficiary
275
+ ) external onlySlasher {
270
276
Stakes.IndexNode storage stake = stakes[_indexNode];
271
277
272
278
require (stake.hasTokens (), "Slashing: index node has no stakes " );
273
279
require (_beneficiary != address (0 ), "Slashing: beneficiary must not be an empty address " );
274
- require (tokensToSlash >= _reward, "Slashing: reward cannot be higher than slashed amount " );
280
+ require (_tokens >= _reward, "Slashing: reward cannot be higher than slashed amount " );
275
281
require (
276
- tokensToSlash <= stake.tokensIndexNode ,
277
- "Slashing: cannot slash more than available stake "
282
+ _tokens <= stake.tokensSlashable () ,
283
+ "Slashing: cannot slash more than staked amount "
278
284
);
279
285
280
- // Slash stake
281
- stake.release (tokensToSlash);
286
+ // Slashing more tokens than freely available (over allocation condition)
287
+ // Unlock locked tokens to avoid the indexer to withdraw them
288
+ if (_tokens > stake.tokensAvailable () && stake.tokensLocked > 0 ) {
289
+ uint256 tokensOverAllocated = _tokens.sub (stake.tokensAvailable ());
290
+ uint256 tokensToUnlock = (tokensOverAllocated > stake.tokensLocked)
291
+ ? stake.tokensLocked
292
+ : tokensOverAllocated;
293
+ stake.unlockTokens (tokensToUnlock);
294
+ }
295
+
296
+ // Remove tokens to slash from the stake
297
+ stake.release (_tokens);
282
298
283
299
// Set apart the reward for the beneficiary and burn remaining slashed stake
284
- uint256 tokensToBurn = tokensToSlash .sub (_reward);
300
+ uint256 tokensToBurn = _tokens .sub (_reward);
285
301
if (tokensToBurn > 0 ) {
286
302
token.burn (tokensToBurn);
287
303
}
@@ -294,18 +310,19 @@ contract Staking is Governed {
294
310
);
295
311
}
296
312
297
- emit StakeSlashed (_indexNode, tokensToBurn , _reward, _beneficiary);
313
+ emit StakeSlashed (_indexNode, _tokens , _reward, _beneficiary);
298
314
}
299
315
300
316
/**
301
317
* @dev Accept tokens and handle staking registration functions
302
318
* @param _from Token holder's address
303
319
* @param _value Amount of Graph Tokens
304
320
*/
305
- function tokensReceived (address _from , uint256 _value , bytes calldata _data )
306
- external
307
- returns (bool )
308
- {
321
+ function tokensReceived (
322
+ address _from ,
323
+ uint256 _value ,
324
+ bytes calldata _data
325
+ ) external returns (bool ) {
309
326
// Make sure the token is the caller of this function
310
327
require (msg .sender == address (token), "Caller is not the GRT token contract " );
311
328
@@ -328,36 +345,47 @@ contract Staking is Governed {
328
345
* @dev Allocate available tokens to a subgraph
329
346
* @param _subgraphID ID of the subgraph where tokens will be allocated
330
347
* @param _tokens Amount of tokens to allocate
331
- * @param _channelID The signer address used by the IndexNode to setup the off-chain payment channel
348
+ * @param _channelPubKey The public key used by the IndexNode to setup the off-chain channel
332
349
*/
333
- function allocate (bytes32 _subgraphID , uint256 _tokens , address _channelID ) external {
350
+ function allocate (
351
+ bytes32 _subgraphID ,
352
+ uint256 _tokens ,
353
+ bytes calldata _channelPubKey
354
+ ) external {
334
355
address indexNode = msg .sender ;
335
356
Stakes.IndexNode storage stake = stakes[indexNode];
336
357
358
+ // Only allocations with a token amount are allowed
337
359
require (_tokens > 0 , "Allocation: cannot allocate zero tokens " );
360
+ // Need to have tokens in our stake to be able to allocate
338
361
require (stake.hasTokens (), "Allocation: index node has no stakes " );
362
+ // Need to have free tokens not used for other purposes to allocate
339
363
require (
340
364
stake.tokensAvailable () >= _tokens,
341
365
"Allocation: not enough tokens available to allocate "
342
366
);
367
+ // Can only allocate tokens to a subgraph if not currently allocated
343
368
require (
344
369
stake.hasAllocation (_subgraphID) == false ,
345
370
"Allocation: cannot allocate if already allocated "
346
371
);
347
- require (isChannel (_channelID) == false , "Allocation: channel ID already in use " );
372
+ // Cannot reuse a channelID that has been used in the past
373
+ address channelID = publicKeyToAddress (bytes (_channelPubKey[1 :])); // solium-disable-line
374
+ require (isChannel (channelID) == false , "Allocation: channel ID already in use " );
348
375
349
376
// Allocate and setup channel
350
377
Stakes.Allocation storage alloc = stake.allocateTokens (_subgraphID, _tokens);
351
- alloc.channelID = _channelID ;
378
+ alloc.channelID = channelID ;
352
379
alloc.createdAtEpoch = epochManager.currentEpoch ();
353
- channels[_channelID ] = Channel (indexNode, _subgraphID);
380
+ channels[channelID ] = Channel (indexNode, _subgraphID);
354
381
355
382
emit AllocationCreated (
356
383
indexNode,
357
384
_subgraphID,
358
385
alloc.createdAtEpoch,
359
386
alloc.tokens,
360
- _channelID
387
+ channelID,
388
+ _channelPubKey
361
389
);
362
390
}
363
391
@@ -401,7 +429,11 @@ contract Staking is Governed {
401
429
* @param _subgraphID Subgraph we are claiming tokens from
402
430
* @param _restake True if restake fees instead of transfer to index node
403
431
*/
404
- function claim (uint256 _epoch , bytes32 _subgraphID , bool _restake ) external {
432
+ function claim (
433
+ uint256 _epoch ,
434
+ bytes32 _subgraphID ,
435
+ bool _restake
436
+ ) external {
405
437
address indexNode = msg .sender ;
406
438
Rebates.Pool storage pool = rebates[_epoch];
407
439
Rebates.Settlement storage settlement = pool.settlements[indexNode][_subgraphID];
@@ -460,7 +492,11 @@ contract Staking is Governed {
460
492
* @param _from Multisig channel address that triggered settlement
461
493
* @param _tokens Amount of tokens to settle
462
494
*/
463
- function _settle (address _channelID , address _from , uint256 _tokens ) private {
495
+ function _settle (
496
+ address _channelID ,
497
+ address _from ,
498
+ uint256 _tokens
499
+ ) private {
464
500
address indexNode = channels[_channelID].indexNode;
465
501
bytes32 subgraphID = channels[_channelID].subgraphID;
466
502
Stakes.IndexNode storage stake = stakes[indexNode];
@@ -508,6 +544,14 @@ contract Staking is Governed {
508
544
emit AllocationSettled (indexNode, subgraphID, currentEpoch, _tokens, _channelID, _from);
509
545
}
510
546
547
+ /**
548
+ * @dev Get whether curation rewards are active or not
549
+ * @return true if curation fees are enabled
550
+ */
551
+ function isCurationEnabled () private view returns (bool ) {
552
+ return curationPercentage > 0 && address (curation) != address (0 );
553
+ }
554
+
511
555
/**
512
556
* @dev Get the running network chain ID
513
557
* @return The chain ID
@@ -521,10 +565,13 @@ contract Staking is Governed {
521
565
}
522
566
523
567
/**
524
- * @dev Get whether curation rewards are active or not
525
- * @return true if curation fees are enabled
568
+ * @dev Convert an uncompressed public key to an Ethereum address
569
+ * @param _publicKey Public key in uncompressed format without the 1 byte prefix
570
+ * @return An Ethereum address corresponding to the public key
526
571
*/
527
- function isCurationEnabled () private view returns (bool ) {
528
- return curationPercentage > 0 && address (curation) != address (0 );
572
+ function publicKeyToAddress (bytes memory _publicKey ) private pure returns (address ) {
573
+ uint256 mask = 2 ** (8 * 21 ) - 1 ;
574
+ uint256 value = uint256 (keccak256 (_publicKey));
575
+ return address (value & mask);
529
576
}
530
577
}
0 commit comments