Skip to content

Commit c304ef2

Browse files
authored
Merge pull request #2961 from keep-network/authorization-stuff-3
Introduced authorization decrease change period parameter to ECDSA See threshold-network/solidity-contracts#99 This value protect against malicious operators who manipulate their weight by overwriting authorization decrease request, and lowering or increasing their eligible stake this way. Authorization decrease change period is the time period before the authorization decrease delay end, during which the authorization decrease request can be overwritten. When the request is overwritten, the authorization decrease delay is reset. For example, if `authorizationDecraseChangePeriod` is set to 4 days, `authorizationDecreaseDelay` is set to 14 days, and someone requested authorization decrease, it means they can not request another decrease for the first 10 days. After 10 days pass, they can request again and overwrite the previous authorization decrease request. The delay time will reset for them and they will have to wait another 10 days to alter it and 14 days to approve it. If set to a value equal to `authorizationDecreaseDelay, it means that authorization decrease request can be always overwritten. If set to zero, it means authorization decrease request can not be overwritten until the delay end, and one needs to wait for the entire authorization decrease delay to approve their decrease or to alter it. ``` (1) authorization decrease requested timestamp (2) from this moment authorization decrease request can be overwritten (3) from this moment authorization decrease request can be approved, assuming it was NOT overwritten in (2) (1) (2) (3) --x------------------------------x--------------------------x----> | \________________________/ | authorizationDecreaseChangePeriod \______________________________________________________/ authorizationDecreaseDelay ```
2 parents 8dec5d6 + 47b0a05 commit c304ef2

File tree

8 files changed

+994
-90
lines changed

8 files changed

+994
-90
lines changed

solidity/ecdsa/contracts/WalletRegistry.sol

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ contract WalletRegistry is
149149

150150
event AuthorizationParametersUpdated(
151151
uint96 minimumAuthorization,
152-
uint64 authorizationDecreaseDelay
152+
uint64 authorizationDecreaseDelay,
153+
uint64 authorizationDecreaseChangePeriod
153154
);
154155

155156
event RewardParametersUpdated(
@@ -263,6 +264,7 @@ contract WalletRegistry is
263264
// slither-disable-next-line too-many-digits
264265
authorization.setMinimumAuthorization(400000e18); // 400k T
265266
authorization.setAuthorizationDecreaseDelay(5184000); // 60 days
267+
authorization.setAuthorizationDecreaseChangePeriod(5184000);
266268

267269
dkg.init(_sortitionPool, _ecdsaDkgValidator);
268270
dkg.setSeedTimeout(1440); // ~6h assuming 15s block time
@@ -457,18 +459,25 @@ contract WalletRegistry is
457459
/// @param _minimumAuthorization New minimum authorization amount
458460
/// @param _authorizationDecreaseDelay New authorization decrease delay in
459461
/// seconds
462+
/// @param _authorizationDecreaseChangePeriod New authorization decrease
463+
/// change period in seconds
460464
function updateAuthorizationParameters(
461465
uint96 _minimumAuthorization,
462-
uint64 _authorizationDecreaseDelay
466+
uint64 _authorizationDecreaseDelay,
467+
uint64 _authorizationDecreaseChangePeriod
463468
) external onlyGovernance {
464469
authorization.setMinimumAuthorization(_minimumAuthorization);
465470
authorization.setAuthorizationDecreaseDelay(
466471
_authorizationDecreaseDelay
467472
);
473+
authorization.setAuthorizationDecreaseChangePeriod(
474+
_authorizationDecreaseChangePeriod
475+
);
468476

469477
emit AuthorizationParametersUpdated(
470478
_minimumAuthorization,
471-
_authorizationDecreaseDelay
479+
_authorizationDecreaseDelay,
480+
_authorizationDecreaseChangePeriod
472481
);
473482
}
474483

@@ -951,13 +960,6 @@ contract WalletRegistry is
951960
return authorization.parameters.minimumAuthorization;
952961
}
953962

954-
/// @notice Delay in seconds that needs to pass between the time
955-
/// authorization decrease is requested and the time that request
956-
/// can get approved.
957-
function authorizationDecreaseDelay() external view returns (uint64) {
958-
return authorization.parameters.authorizationDecreaseDelay;
959-
}
960-
961963
/// @notice Returns the current value of the staking provider's eligible
962964
/// stake. Eligible stake is defined as the currently authorized
963965
/// stake minus the pending authorization decrease. Eligible stake
@@ -1057,6 +1059,41 @@ contract WalletRegistry is
10571059
return dkg.parameters;
10581060
}
10591061

1062+
/// @notice Returns authorization-related parameters.
1063+
/// @dev The minimum authorization is also returned by `minimumAuthorization()`
1064+
/// function, as a requirement of `IApplication` interface.
1065+
/// @return minimumAuthorization The minimum authorization amount required
1066+
/// so that operator can participate in the random beacon. This
1067+
/// amount is required to execute slashing for providing a malicious
1068+
/// DKG result or when a relay entry times out.
1069+
/// @return authorizationDecreaseDelay Delay in seconds that needs to pass
1070+
/// between the time authorization decrease is requested and the
1071+
/// time that request gets approved. Protects against free-riders
1072+
/// earning rewards and not being active in the network.
1073+
/// @return authorizationDecreaseChangePeriod Authorization decrease change
1074+
/// period in seconds. It is the time, before authorization decrease
1075+
/// delay end, during which the pending authorization decrease
1076+
/// request can be overwritten.
1077+
/// If set to 0, pending authorization decrease request can not be
1078+
/// overwritten until the endire `authorizationDecreaseDelay` ends.
1079+
/// If set to value equal `authorizationDecreaseDelay`, request can
1080+
/// always be overwritten.
1081+
function authorizationParameters()
1082+
external
1083+
view
1084+
returns (
1085+
uint96 minimumAuthorization,
1086+
uint64 authorizationDecreaseDelay,
1087+
uint64 authorizationDecreaseChangePeriod
1088+
)
1089+
{
1090+
return (
1091+
authorization.parameters.minimumAuthorization,
1092+
authorization.parameters.authorizationDecreaseDelay,
1093+
authorization.parameters.authorizationDecreaseChangePeriod
1094+
);
1095+
}
1096+
10601097
/// @notice Retrieves reward-related parameters.
10611098
/// @return maliciousDkgResultNotificationRewardMultiplier Percentage of the
10621099
/// staking contract malicious behavior notification reward which

solidity/ecdsa/contracts/WalletRegistryGovernance.sol

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ contract WalletRegistryGovernance is Ownable {
4040
uint64 public newAuthorizationDecreaseDelay;
4141
uint256 public authorizationDecreaseDelayChangeInitiated;
4242

43+
uint64 public newAuthorizationDecreaseChangePeriod;
44+
uint256 public authorizationDecreaseChangePeriodChangeInitiated;
45+
4346
uint96 public newMaliciousDkgResultSlashingAmount;
4447
uint256 public maliciousDkgResultSlashingAmountChangeInitiated;
4548

@@ -107,6 +110,15 @@ contract WalletRegistryGovernance is Ownable {
107110
);
108111
event AuthorizationDecreaseDelayUpdated(uint64 authorizationDecreaseDelay);
109112

113+
event AuthorizationDecreaseChangePeriodUpdateStarted(
114+
uint64 authorizationDecreaseChangePeriod,
115+
uint256 timestamp
116+
);
117+
118+
event AuthorizationDecreaseChangePeriodUpdated(
119+
uint64 authorizationDecreaseChangePeriod
120+
);
121+
110122
event MaliciousDkgResultSlashingAmountUpdateStarted(
111123
uint256 maliciousDkgResultSlashingAmount,
112124
uint256 timestamp
@@ -357,10 +369,16 @@ contract WalletRegistryGovernance is Ownable {
357369
onlyAfterGovernanceDelay(minimumAuthorizationChangeInitiated)
358370
{
359371
emit MinimumAuthorizationUpdated(newMinimumAuthorization);
372+
(
373+
,
374+
uint64 authorizationDecreaseDelay,
375+
uint64 authorizationDecreaseChangePeriod
376+
) = walletRegistry.authorizationParameters();
360377
// slither-disable-next-line reentrancy-no-eth
361378
walletRegistry.updateAuthorizationParameters(
362379
newMinimumAuthorization,
363-
walletRegistry.authorizationDecreaseDelay()
380+
authorizationDecreaseDelay,
381+
authorizationDecreaseChangePeriod
364382
);
365383
minimumAuthorizationChangeInitiated = 0;
366384
newMinimumAuthorization = 0;
@@ -391,15 +409,65 @@ contract WalletRegistryGovernance is Ownable {
391409
onlyAfterGovernanceDelay(authorizationDecreaseDelayChangeInitiated)
392410
{
393411
emit AuthorizationDecreaseDelayUpdated(newAuthorizationDecreaseDelay);
412+
(
413+
uint96 minimumAuthorization,
414+
uint64 authorizationDecreaseChangePeriod,
415+
416+
) = walletRegistry.authorizationParameters();
394417
// slither-disable-next-line reentrancy-no-eth
395418
walletRegistry.updateAuthorizationParameters(
396-
walletRegistry.minimumAuthorization(),
397-
newAuthorizationDecreaseDelay
419+
minimumAuthorization,
420+
newAuthorizationDecreaseDelay,
421+
authorizationDecreaseChangePeriod
398422
);
399423
authorizationDecreaseDelayChangeInitiated = 0;
400424
newAuthorizationDecreaseDelay = 0;
401425
}
402426

427+
/// @notice Begins the authorization decrease change period update process.
428+
/// @dev Can be called only by the contract owner.
429+
/// @param _newAuthorizationDecreaseChangePeriod New authorization decrease change period
430+
function beginAuthorizationDecreaseChangePeriodUpdate(
431+
uint64 _newAuthorizationDecreaseChangePeriod
432+
) external onlyOwner {
433+
/* solhint-disable not-rely-on-time */
434+
newAuthorizationDecreaseChangePeriod = _newAuthorizationDecreaseChangePeriod;
435+
authorizationDecreaseChangePeriodChangeInitiated = block.timestamp;
436+
emit AuthorizationDecreaseChangePeriodUpdateStarted(
437+
_newAuthorizationDecreaseChangePeriod,
438+
block.timestamp
439+
);
440+
/* solhint-enable not-rely-on-time */
441+
}
442+
443+
/// @notice Finalizes the authorization decrease change period update process.
444+
/// @dev Can be called only by the contract owner, after the governance
445+
/// delay elapses.
446+
function finalizeAuthorizationDecreaseChangePeriodUpdate()
447+
external
448+
onlyOwner
449+
onlyAfterGovernanceDelay(
450+
authorizationDecreaseChangePeriodChangeInitiated
451+
)
452+
{
453+
emit AuthorizationDecreaseChangePeriodUpdated(
454+
newAuthorizationDecreaseChangePeriod
455+
);
456+
(
457+
uint96 minimumAuthorization,
458+
uint64 authorizationDecreaseDelay,
459+
460+
) = walletRegistry.authorizationParameters();
461+
// slither-disable-next-line reentrancy-no-eth
462+
walletRegistry.updateAuthorizationParameters(
463+
minimumAuthorization,
464+
authorizationDecreaseDelay,
465+
newAuthorizationDecreaseChangePeriod
466+
);
467+
authorizationDecreaseChangePeriodChangeInitiated = 0;
468+
newAuthorizationDecreaseChangePeriod = 0;
469+
}
470+
403471
/// @notice Begins the malicious DKG result slashing amount update process.
404472
/// @dev Can be called only by the contract owner.
405473
/// @param _newMaliciousDkgResultSlashingAmount New malicious DKG result
@@ -911,6 +979,20 @@ contract WalletRegistryGovernance is Ownable {
911979
getRemainingChangeTime(authorizationDecreaseDelayChangeInitiated);
912980
}
913981

982+
/// @notice Get the time remaining until the authorization decrease change
983+
/// period can be updated.
984+
/// @return Remaining time in seconds.
985+
function getRemainingAuthorizationDecreaseChangePeriodUpdateTime()
986+
external
987+
view
988+
returns (uint256)
989+
{
990+
return
991+
getRemainingChangeTime(
992+
authorizationDecreaseChangePeriodChangeInitiated
993+
);
994+
}
995+
914996
/// @notice Get the time remaining until the malicious DKG result
915997
/// slashing amount can be updated.
916998
/// @return Remaining time in seconds.

solidity/ecdsa/contracts/libraries/EcdsaAuthorization.sol

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@ library EcdsaAuthorization {
3131
// authorization decrease amount is small, significant, or if it is
3232
// a decrease to zero.
3333
uint64 authorizationDecreaseDelay;
34+
// The time period before the authorization decrease delay end,
35+
// during which the authorization decrease request can be overwritten.
36+
//
37+
// When the request is overwritten, the authorization decrease delay is
38+
// reset.
39+
//
40+
// For example, if `authorizationDecraseChangePeriod` is set to 4
41+
// days, `authorizationDecreaseDelay` is set to 14 days, and someone
42+
// requested authorization decrease, it means they can not
43+
// request another decrease for the first 10 days. After 10 days pass,
44+
// they can request again and overwrite the previous authorization
45+
// decrease request. The delay time will reset for them and they
46+
// will have to wait another 10 days to alter it and 14 days to
47+
// approve it.
48+
//
49+
// This value protects against malicious operators who manipulate
50+
// their weight by overwriting authorization decrease request, and
51+
// lowering or increasing their eligible stake this way.
52+
//
53+
// If set to a value equal to `authorizationDecreaseDelay, it means
54+
// that authorization decrease request can be always overwritten.
55+
// If set to zero, it means authorization decrease request can not be
56+
// overwritten until the delay end, and one needs to wait for the entire
57+
// authorization decrease delay to approve their decrease and request
58+
// for another one or to overwrite the pending one.
59+
//
60+
// (1) authorization decrease requested timestamp
61+
// (2) from this moment authorization decrease request can be
62+
// overwritten
63+
// (3) from this moment authorization decrease request can be
64+
// approved, assuming it was NOT overwritten in (2)
65+
//
66+
// (1) (2) (3)
67+
// --x------------------------------x--------------------------x---->
68+
// | \________________________/
69+
// | authorizationDecreaseChangePeriod
70+
// \______________________________________________________/
71+
// authorizationDecreaseDelay
72+
//
73+
uint64 authorizationDecreaseChangePeriod;
3474
}
3575

3676
struct AuthorizationDecrease {
@@ -107,6 +147,19 @@ library EcdsaAuthorization {
107147
.authorizationDecreaseDelay = _authorizationDecreaseDelay;
108148
}
109149

150+
/// @notice Sets the authorization decrease change period. It is the time
151+
/// period before the authorization decrease delay end,
152+
/// during which the authorization decrease request can be
153+
/// overwritten.
154+
function setAuthorizationDecreaseChangePeriod(
155+
Data storage self,
156+
uint64 _authorizationDecreaseChangePeriod
157+
) internal {
158+
self
159+
.parameters
160+
.authorizationDecreaseChangePeriod = _authorizationDecreaseChangePeriod;
161+
}
162+
110163
/// @notice Used by staking provider to set operator address that will
111164
/// operate ECDSA node. The given staking provider can set operator
112165
/// address only one time. The operator address can not be changed
@@ -191,6 +244,10 @@ library EcdsaAuthorization {
191244
/// Reverts if the amount after deauthorization would be non-zero
192245
/// and lower than the minimum authorization.
193246
///
247+
/// Reverts if another authorization decrease request is pending for
248+
/// the staking provider and not enough time passed since the
249+
/// original request (see `authorizationDecreaseChangePeriod`).
250+
///
194251
/// If the operator is not known (`registerOperator` was not called)
195252
/// it lets to `approveAuthorizationDecrease` immediately. If the
196253
/// operator is known (`registerOperator` was called), the operator
@@ -204,7 +261,8 @@ library EcdsaAuthorization {
204261
/// `approveAuthorizationDecrease` function.
205262
///
206263
/// If there is a pending authorization decrease request, it is
207-
/// overwritten.
264+
/// overwritten, but only if enough time passed since the original
265+
/// request. Otherwise, the function reverts.
208266
///
209267
/// @dev Should only be callable by T staking contract.
210268
function authorizationDecreaseRequested(
@@ -239,17 +297,32 @@ library EcdsaAuthorization {
239297
// For now, we set `decreasingAt` as "never decreasing" and let
240298
// it be updated by `joinSortitionPool` or `updateOperatorStatus`
241299
// once we know the sortition pool is in sync.
242-
243-
// solhint-disable-next-line not-rely-on-time
244300
decreasingAt = type(uint64).max;
245301
}
246302

247303
uint96 decreasingBy = fromAmount - toAmount;
248304

249-
self.pendingDecreases[stakingProvider] = AuthorizationDecrease(
250-
decreasingBy,
251-
decreasingAt
252-
);
305+
AuthorizationDecrease storage decreaseRequest = self.pendingDecreases[
306+
stakingProvider
307+
];
308+
309+
uint64 pendingDecreaseAt = decreaseRequest.decreasingAt;
310+
if (pendingDecreaseAt != 0 && pendingDecreaseAt != type(uint64).max) {
311+
// If there is already a pending authorization decrease request for
312+
// this staking provider and that request has been activated
313+
// (sortition pool was updated), require enough time to pass before
314+
// it can be overwritten.
315+
require(
316+
// solhint-disable-next-line not-rely-on-time
317+
block.timestamp >=
318+
pendingDecreaseAt -
319+
self.parameters.authorizationDecreaseChangePeriod,
320+
"Not enough time passed since the original request"
321+
);
322+
}
323+
324+
decreaseRequest.decreasingBy = decreasingBy;
325+
decreaseRequest.decreasingAt = decreasingAt;
253326

254327
emit AuthorizationDecreaseRequested(
255328
stakingProvider,

0 commit comments

Comments
 (0)