Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track uptimes for delegation period #520

Merged
merged 8 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

75 changes: 45 additions & 30 deletions contracts/staking/PoSValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@
uint64 minimumStakeDuration,
IRewardCalculator rewardCalculator
) internal onlyInitializing {
PoSValidatorManagerStorage storage s = _getPoSValidatorManagerStorage();
s._minimumStakeAmount = minimumStakeAmount;
s._maximumStakeAmount = maximumStakeAmount;
s._minimumStakeDuration = minimumStakeDuration;
s._rewardCalculator = rewardCalculator;
PoSValidatorManagerStorage storage $ = _getPoSValidatorManagerStorage();
$._minimumStakeAmount = minimumStakeAmount;
$._maximumStakeAmount = maximumStakeAmount;
$._minimumStakeDuration = minimumStakeDuration;
$._rewardCalculator = rewardCalculator;
}

function initializeEndValidation(
Expand All @@ -92,34 +92,38 @@
uint32 messageIndex
) external {
if (includeUptimeProof) {
PoSValidatorManagerStorage storage $ = _getPoSValidatorManagerStorage();
(WarpMessage memory warpMessage, bool valid) =
WARP_MESSENGER.getVerifiedWarpMessage(messageIndex);
require(valid, "PoSValidatorManager: invalid warp message");

require(
warpMessage.sourceChainID == WARP_MESSENGER.getBlockchainID(),
"PoSValidatorManager: invalid source chain ID"
);
require(
warpMessage.originSenderAddress == address(0),
"PoSValidatorManager: invalid origin sender address"
);

(bytes32 uptimeValidationID, uint64 uptime) =
ValidatorMessages.unpackValidationUptimeMessage(warpMessage.payload);
require(
validationID == uptimeValidationID,
"PoSValidatorManager: invalid uptime validation ID"
);

$._validatorUptimes[validationID] = uptime;
emit ValidationUptimeUpdated(validationID, uptime);
_updateUptime(validationID, messageIndex);
}

_initializeEndValidation(validationID);
}

function _updateUptime(bytes32 validationID, uint32 messageIndex) internal returns (uint64) {
PoSValidatorManagerStorage storage $ = _getPoSValidatorManagerStorage();
(WarpMessage memory warpMessage, bool valid) =
WARP_MESSENGER.getVerifiedWarpMessage(messageIndex);
require(valid, "PoSValidatorManager: invalid warp message");

require(
warpMessage.sourceChainID == WARP_MESSENGER.getBlockchainID(),
"PoSValidatorManager: invalid source chain ID"
);
require(
warpMessage.originSenderAddress == address(0),
"PoSValidatorManager: invalid origin sender address"
);

(bytes32 uptimeValidationID, uint64 uptime) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my own understanding, does uptime return the absolute amount of time that the validator was up, or a percent of time that it was up during the staking period? I'm just thinking of whether other information is necessary to calculate rewards.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns the absolute number of seconds the validator has been up

ValidatorMessages.unpackValidationUptimeMessage(warpMessage.payload);
require(
validationID == uptimeValidationID, "PoSValidatorManager: invalid uptime validation ID"
);

$._validatorUptimes[validationID] = uptime;
emit ValidationUptimeUpdated(validationID, uptime);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only update the validator's uptime if the uptime provided is greater than the previously provided uptime. This way delegators may not have to provide an uptime proof to get their rewards, provided the validator has had a sufficiently high uptime.


return uptime;
}

function _processStake(uint256 stakeAmount) internal virtual returns (uint64) {
PoSValidatorManagerStorage storage $ = _getPoSValidatorManagerStorage();
// Lock the stake in the contract.
Expand Down Expand Up @@ -185,6 +189,7 @@
endedAt: 0,
startingNonce: nonce,
endingNonce: 0,
validatorUptime: 0,
status: DelegatorStatus.PendingAdded
});

Expand Down Expand Up @@ -241,7 +246,16 @@
});
}

function initializeEndDelegation(bytes32 validationID) external {
function initializeEndDelegation(
bytes32 validationID,
bool includeUptimeProof,
uint32 messageIndex
) external {
uint64 uptime;
Fixed Show fixed Hide fixed
if (includeUptimeProof) {
uptime = _updateUptime(validationID, messageIndex);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should read from state instead of using a return from _updateUptime. That way if the validator's uptime has been updated recently, anyone ending their delegation won't have to submit another uptime proof, provided that the latest reported uptime up until now hits the 80% threshold.

}

PoSValidatorManagerStorage storage $ = _getPoSValidatorManagerStorage();

// Ensure the delegator is active
Expand All @@ -253,6 +267,7 @@
delegator.status = DelegatorStatus.PendingRemoved;
delegator.endedAt = uint64(block.timestamp);
delegator.endingNonce = nonce;
delegator.validatorUptime = uptime;

$._delegatorStakes[validationID][_msgSender()] = delegator;

Expand Down
11 changes: 10 additions & 1 deletion contracts/staking/interfaces/IPoSValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct Delegator {
uint64 endedAt;
uint64 startingNonce;
uint64 endingNonce;
uint64 validatorUptime;
DelegatorStatus status;
}

Expand Down Expand Up @@ -139,8 +140,16 @@ interface IPoSValidatorManager is IValidatorManager {
* @notice Begins the process of removing a delegator from a validation period. The delegator must have been previously
* registered with the given validationID.
* @param validationID The ID of the validation period being removed.
* @param includeUptimeProof Whether or not an uptime proof is provided for the validation period.
* If no uptime proof is provided, the validation uptime for the delegation period will be assumed to be 0.
* @param messageIndex If {includeUptimeProof} is true, the index of the Warp message to be received providing the
* uptime proof.
*/
function initializeEndDelegation(bytes32 validationID) external;
function initializeEndDelegation(
bytes32 validationID,
bool includeUptimeProof,
uint32 messageIndex
) external;

/**
* @notice Resubmits a delegator end message to be sent to the P-Chain.
Expand Down
2 changes: 1 addition & 1 deletion contracts/staking/tests/PoSValidatorManagerTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ abstract contract PoSValidatorManagerTest is ValidatorManagerTest {
endTime: endDelegationTimestamp
});
vm.prank(delegator);
posValidatorManager.initializeEndDelegation(validationID);
posValidatorManager.initializeEndDelegation(validationID, false, 0);
return validationID;
}

Expand Down
4 changes: 4 additions & 0 deletions tests/utils/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,8 @@ func InitializeEndERC20Delegation(
tx, err := stakingManager.InitializeEndDelegation(
opts,
validationID,
false,
0,
)
Expect(err).Should(BeNil())
return WaitForTransactionSuccess(context.Background(), subnet, tx.Hash())
Expand Down Expand Up @@ -632,6 +634,8 @@ func InitializeEndNativeDelegation(
tx, err := stakingManager.InitializeEndDelegation(
opts,
validationID,
false,
0,
)
Expect(err).Should(BeNil())
return WaitForTransactionSuccess(context.Background(), subnet, tx.Hash())
Expand Down