Skip to content

Commit

Permalink
DRY common exit/liquidation validation code
Browse files Browse the repository at this point in the history
The signature verification code in these two functions is virtually
identical now (there was some slight optimization that had been applied
to one of these but not the other, but is functionally identical).
  • Loading branch information
jagerman committed Nov 7, 2024
1 parent 0f08d95 commit 29bca90
Showing 1 changed file with 41 additions and 39 deletions.
80 changes: 41 additions & 39 deletions contracts/ServiceNodeRewards.sol
Original file line number Diff line number Diff line change
Expand Up @@ -542,26 +542,9 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
BLSSignatureParams calldata blsSignature,
uint64[] memory ids
) external whenNotPaused whenStarted hasEnoughSigners(ids.length) {
bytes memory pubkeyBytes = BN256G1.getKeyForG1Point(blsPubkey);
uint64 serviceNodeID = serviceNodeIDs[pubkeyBytes];
if (serviceNodeID == 0)
revert BLSPubkeyDoesNotExist(blsPubkey);
if (signatureTimestampHasExpired(timestamp)) {
revert SignatureExpired(serviceNodeID, timestamp, block.timestamp);
}

if (
blsPubkey.X != _serviceNodes[serviceNodeID].blsPubkey.X || blsPubkey.Y != _serviceNodes[serviceNodeID].blsPubkey.Y
) revert BLSPubkeyDoesNotMatch(serviceNodeID, blsPubkey);
if (block.timestamp < node.addedTimestamp + MINIMUM_EXIT_AGE)
revert ExitTooEarly(serviceNodeID, node.addedTimestamp, block.timestamp);

// NOTE: Validate signature
{
bytes memory encodedMessage = abi.encodePacked(exitTag, blsPubkey.X, blsPubkey.Y, timestamp);
BN256G2.G2Point memory Hm = BN256G2.hashToG2(encodedMessage, hashToG2Tag);
validateSignatureOrRevert(ids, blsSignature, Hm);
}
(uint64 serviceNodeID,) = _validateBLSExitWithSignature(
blsPubkey, timestamp, blsSignature, exitTag, ids);

_exitBLSPublicKey(serviceNodeID, _serviceNodes[serviceNodeID].deposit);
}
Expand Down Expand Up @@ -599,49 +582,68 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
emit ServiceNodeExit(serviceNodeID, operator, returnedAmount, pubkey);
}

/// @notice Exits a service node by liquidating their node from the
/// network rewarding the caller for maintaining the list.
///
/// This function can be called by anyone, but requires the network to
/// approve the liquidation by aggregating a valid BLS signature. The nodes
/// will only provide this signature if the consensus rules permit the node
/// to be forcibly exited (e.g. the node was deregistered by consensus in
/// Oxen's state-chain, or does not exist on the Oxen chain).
/// @dev Internal function to handle common liquidate/exit checks when
/// exiting/liquidating with a service node network signature.
///
/// @param blsPubkey 64 byte BLS public key for the service node to be
/// exited.
/// @param timestamp The signature creation time.
/// @param blsSignature 128 byte BLS signature that affirms that the
/// `blsPubkey` is to be liquidated.
/// @param ids Array of service node IDs that didn't sign the signature
/// and are to be excluded from verification.
function liquidateBLSPublicKeyWithSignature(
/// @return serviceNodeID the ID of the service node containing `blsPubkey`
/// @return node the ServiceNode info for the node
function _validateBLSExitWithSignature(
BN256G1.G1Point calldata blsPubkey,
uint256 timestamp,
BLSSignatureParams calldata blsSignature,
bytes32 signatureTag,
uint64[] memory ids
) external whenNotPaused whenStarted hasEnoughSigners(ids.length) {
) internal returns (uint64 serviceNodeID, ServiceNode memory node) {

bytes memory pubkeyBytes = BN256G1.getKeyForG1Point(blsPubkey);
uint64 serviceNodeID = serviceNodeIDs[pubkeyBytes];
serviceNodeID = serviceNodeIDs[pubkeyBytes];
if (serviceNodeID == 0)
revert BLSPubkeyDoesNotExist(blsPubkey);
if (signatureTimestampHasExpired(timestamp)) {
revert SignatureExpired(serviceNodeID, timestamp, block.timestamp);
}

ServiceNode memory node = _serviceNodes[serviceNodeID];
node = _serviceNodes[serviceNodeID];
if (blsPubkey.X != node.blsPubkey.X || blsPubkey.Y != node.blsPubkey.Y) {
revert BLSPubkeyDoesNotMatch(serviceNodeID, blsPubkey);
}

if (block.timestamp < node.addedTimestamp + MINIMUM_EXIT_AGE)
revert ExitTooEarly(serviceNodeID, node.addedTimestamp, block.timestamp);

// NOTE: Validate signature
{
bytes memory encodedMessage = abi.encodePacked(liquidateTag, blsPubkey.X, blsPubkey.Y, timestamp);
bytes memory encodedMessage = abi.encodePacked(signatureTag, blsPubkey.X, blsPubkey.Y, timestamp);
BN256G2.G2Point memory Hm = BN256G2.hashToG2(encodedMessage, hashToG2Tag);
validateSignatureOrRevert(ids, blsSignature, Hm);
}
}

/// @notice Exits a service node by liquidating their node from the
/// network rewarding the caller for maintaining the list.
///
/// This function can be called by anyone, but requires the network to
/// approve the liquidation by aggregating a valid BLS signature. The nodes
/// will only provide this signature if the consensus rules permit the node
/// to be forcibly exited (e.g. the node was deregistered by consensus in
/// Oxen's state-chain, or does not exist on the Oxen chain).
///
/// @param blsPubkey 64 byte BLS public key for the service node to be
/// exited.
/// @param timestamp The signature creation time.
/// @param blsSignature 128 byte BLS signature that affirms that the
/// `blsPubkey` is to be liquidated.
/// @param ids Array of service node IDs that didn't sign the signature
/// and are to be excluded from verification.
function liquidateBLSPublicKeyWithSignature(
BN256G1.G1Point calldata blsPubkey,
uint256 timestamp,
BLSSignatureParams calldata blsSignature,
uint64[] memory ids
) external whenNotPaused whenStarted hasEnoughSigners(ids.length) {

(uint64 serviceNodeID, ServiceNode memory node) = _validateBLSExitWithSignature(
blsPubkey, timestamp, blsSignature, liquidateTag, ids);

// Calculating how much liquidator is paid out
emit ServiceNodeLiquidated(serviceNodeID, node.operator, node.blsPubkey);
Expand Down

0 comments on commit 29bca90

Please sign in to comment.