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

feat: implements partial slashing #402

Merged
merged 9 commits into from
Sep 17, 2024
Merged
31 changes: 31 additions & 0 deletions contracts-abi/abi/ProviderRegistry.abi
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,37 @@
],
"anonymous": false
},
{
"type": "event",
"name": "InsufficientFundsToSlash",
"inputs": [
{
"name": "provider",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "providerStake",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "residualAmount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "penaltyFee",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"type": "event",
"name": "OwnershipTransferStarted",
Expand Down
149 changes: 148 additions & 1 deletion contracts-abi/clients/ProviderRegistry/ProviderRegistry.go

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion contracts/contracts/core/ProviderRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,17 @@ contract ProviderRegistry is
) external nonReentrant onlyPreConfirmationEngine whenNotPaused {
uint256 residualAmt = (amt * residualBidPercentAfterDecay * PRECISION) / PERCENT;
uint256 penaltyFee = (residualAmt * uint256(feePercent) * PRECISION) / PERCENT;
require(providerStakes[provider] >= residualAmt + penaltyFee, "Insufficient funds to slash");

uint256 providerStake = providerStakes[provider];

ckartik marked this conversation as resolved.
Show resolved Hide resolved

if (providerStake < residualAmt + penaltyFee) {
emit InsufficientFundsToSlash(provider, providerStake, residualAmt, penaltyFee);
if (providerStake < residualAmt) {
residualAmt = providerStake;
}
ckartik marked this conversation as resolved.
Show resolved Hide resolved
penaltyFee = providerStake - residualAmt;
}
providerStakes[provider] -= residualAmt + penaltyFee;

penaltyFeeTracker.accumulatedAmount += penaltyFee;
Expand Down
8 changes: 8 additions & 0 deletions contracts/contracts/interfaces/IProviderRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ interface IProviderRegistry {
/// @dev Event emitted when the fee payout period in blocks is updated
event FeePayoutPeriodBlocksUpdated(uint256 indexed newFeePayoutPeriodBlocks);

/// @dev Event emitted when there are insufficient funds to slash
event InsufficientFundsToSlash(
address indexed provider,
uint256 providerStake,
uint256 residualAmount,
uint256 penaltyFee
);

function registerAndStake(bytes calldata blsPublicKey) external payable;

function stake() external payable;
Expand Down
50 changes: 47 additions & 3 deletions contracts/test/core/ProviderRegistryTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ contract ProviderRegistryTest is Test {
event FeeTransfer(uint256 amount, address indexed recipient);
event PenaltyFeeRecipientUpdated(address indexed newPenaltyFeeRecipient);
event FeePayoutPeriodBlocksUpdated(uint256 indexed newFeePayoutPeriodBlocks);
event InsufficientFundsToSlash(
address indexed provider,
uint256 providerStake,
uint256 residualAmt,
uint256 penaltyFee
);

function setUp() public {
testNumber = 42;
Expand Down Expand Up @@ -257,21 +263,59 @@ contract ProviderRegistryTest is Test {
vm.expectRevert(bytes(""));
providerRegistry.slash(1 ether, provider, payable(bidder),100);
}

function testFail_ShouldRetrieveFundsGreaterThanStake() public {
function test_ShouldRetrieveFundsWhenSlashIsGreaterThanStake() public {
vm.prank(address(this));
providerRegistry.setPreconfirmationsContract(address(this));

vm.deal(provider, 3 ether);
vm.prank(provider);
providerRegistry.registerAndStake{value: 2 ether}(validBLSPubkey);
address bidder = vm.addr(4);
vm.expectRevert(bytes(""));
vm.prank(address(this));

vm.expectEmit(true, true, true, true);
emit InsufficientFundsToSlash(provider, 2 ether, 3 ether, 0.3 ether);
providerRegistry.slash(3 ether, provider, payable(bidder), 100);

assertEq(providerRegistry.getAccumulatedPenaltyFee(), 0);
assertEq(providerRegistry.providerStakes(provider), 0 ether);
}

function test_ShouldRetrieveFundsWhenSlashIsGreaterThanStakePenaltyNotCovered() public {
vm.prank(address(this));
providerRegistry.setPreconfirmationsContract(address(this));

vm.deal(provider, 3 ether);
vm.prank(provider);
providerRegistry.registerAndStake{value: 3 ether}(validBLSPubkey);
address bidder = vm.addr(4);
vm.prank(address(this));

vm.expectEmit(true, true, true, true);
emit InsufficientFundsToSlash(provider, 3 ether, 3 ether, 0.3 ether);
providerRegistry.slash(3 ether, provider, payable(bidder), 100);

assertEq(providerRegistry.getAccumulatedPenaltyFee(), 0);
assertEq(providerRegistry.providerStakes(provider), 0 ether);
}

function test_ShouldRetrieveFundsWhenSlashIsGreaterThanStakePenaltyNotFullyCovered() public {
vm.prank(address(this));
providerRegistry.setPreconfirmationsContract(address(this));

vm.deal(provider, 3.1 ether);
vm.prank(provider);
providerRegistry.registerAndStake{value: 3.1 ether}(validBLSPubkey);
address bidder = vm.addr(4);
vm.prank(address(this));

vm.expectEmit(true, true, true, true);
emit InsufficientFundsToSlash(provider, 3.1 ether, 3 ether, 0.3 ether);
providerRegistry.slash(3 ether, provider, payable(bidder), 100);

assertEq(providerRegistry.getAccumulatedPenaltyFee(), 0.1 ether);
assertEq(providerRegistry.providerStakes(provider), 0 ether);
}
function test_PenaltyFeeBehavior() public {
providerRegistry.setNewPenaltyFeeRecipient(vm.addr(6));
vm.deal(provider, 3 ether);
Expand Down
Loading