Skip to content

Commit 6eaf4d5

Browse files
committed
Involuntarily decrease all authorizations in case of slashing
The original approach was to decrease authorization on the slashing application and decrease authorizations on other applications only if necessary, that is, if the staked amount after the slashing was lower than the authorization on the other application. For example, let's assume the staking provider has 1000 T delegated and three applications authorized: A application authorized for 950 T B application authorized for 500 T C application authorized for 100 T If C slashed for 50 T, authorization on C would be reduced by 50 T, to 100 - 50 = 50 T. If A slashed for 950 T, authorizations on A would be reduced to 0, and authorizations on B and C would be reduced to 50 T. The new approach assumes all applications are decreasing authorizations in case of slashing, no matter which application executed the slash. If C slashed for 50 T, authorization on A would be reduced to 950 - 50 = 900 T, authorization on B would be reduced to 500 - 50 = 450 T, and authorization on C would be reduced to 100 - 50 = 50 T. If A slashed for 950 T, authorizations on A, B, and C would be reduced to 0 T. Depending on how staker thinks about setting their risk profile, the first or second approach could make more sense for them. One staker could argue slashing event on one application should not affect their authorizations on other applications, and another staker would argue they set their original risk profile based on another staked amount, and given that the situations changed and they have less tokens now, the risk profile should be adjusted. Most importantly though, this change is made to avoid storing the address of slashing application in the slashing event structure. This change allows reducing the gas cost of slashing large groups SIGNIFICANTLY given that we store one EVM word less per each member seat in the group. For a group of 64 members, it means storing 64 EVM words less. For a group of 100 members, it means storing 100 EVM words less. This is extremely important for random beacon and tBTC where signing groups have 64 and 100 operators respectively. This change was tested with the `RandomBeacon` contract's `reportUnauthorizedSigning` call with the cost of `TokenStaking.seize` extracted. Before this change: - for group size 64, gas cost: 3 142 093, - for group size 100, gas cost: 4 860 500. After this change: - for group size 64, gas cost: 1 720 849, - for group size 100: gas cost: 2 639 758
1 parent 702aa5f commit 6eaf4d5

File tree

2 files changed

+17
-39
lines changed

2 files changed

+17
-39
lines changed

contracts/staking/TokenStaking.sol

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
7777
struct SlashingEvent {
7878
address stakingProvider;
7979
uint96 amount;
80-
address application;
8180
}
8281

8382
uint256 internal constant SLASHING_REWARD_PERCENT = 5;
@@ -1000,8 +999,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
1000999
authorizationDecrease(
10011000
stakingProvider,
10021001
stakingProviderStruct,
1003-
slashedAmount,
1004-
address(0)
1002+
slashedAmount
10051003
);
10061004
}
10071005

@@ -1044,8 +1042,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
10441042
authorizationDecrease(
10451043
stakingProvider,
10461044
stakingProviderStruct,
1047-
slashedAmount,
1048-
address(0)
1045+
slashedAmount
10491046
);
10501047
decreaseStakeCheckpoint(
10511048
stakingProvider,
@@ -1472,11 +1469,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
14721469
continue;
14731470
}
14741471
slashingQueue.push(
1475-
SlashingEvent(
1476-
stakingProvider,
1477-
amountToSlash.toUint96(),
1478-
msg.sender
1479-
)
1472+
SlashingEvent(stakingProvider, amountToSlash.toUint96())
14801473
);
14811474
}
14821475

@@ -1548,8 +1541,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
15481541
authorizationDecrease(
15491542
slashing.stakingProvider,
15501543
stakingProviderStruct,
1551-
slashedAmount,
1552-
slashing.application
1544+
slashedAmount
15531545
);
15541546
uint96 newStake = stakingProviderStruct.tStake +
15551547
stakingProviderStruct.keepInTStake +
@@ -1561,8 +1553,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
15611553
function authorizationDecrease(
15621554
address stakingProvider,
15631555
StakingProviderInfo storage stakingProviderStruct,
1564-
uint96 slashedAmount,
1565-
address application
1556+
uint96 slashedAmount
15661557
) internal {
15671558
uint96 totalStake = stakingProviderStruct.tStake +
15681559
stakingProviderStruct.nuInTStake +
@@ -1578,16 +1569,11 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
15781569
AppAuthorization storage authorization = stakingProviderStruct
15791570
.authorizations[authorizedApplication];
15801571
uint96 fromAmount = authorization.authorized;
1581-
if (
1582-
application == address(0) ||
1583-
authorizedApplication == application
1584-
) {
1585-
authorization.authorized -= MathUpgradeable
1586-
.min(fromAmount, slashedAmount)
1587-
.toUint96();
1588-
} else if (fromAmount <= totalStake) {
1589-
continue;
1590-
}
1572+
1573+
authorization.authorized -= MathUpgradeable
1574+
.min(fromAmount, slashedAmount)
1575+
.toUint96();
1576+
15911577
if (authorization.authorized > totalStake) {
15921578
authorization.authorized = totalStake;
15931579
}

test/staking/TokenStaking.test.js

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6454,12 +6454,10 @@ describe("TokenStaking", () => {
64546454
expect(await tokenStaking.slashingQueue(0)).to.deep.equal([
64556455
stakingProvider.address,
64566456
amount,
6457-
application1Mock.address,
64586457
])
64596458
expect(await tokenStaking.slashingQueue(1)).to.deep.equal([
64606459
otherStaker.address,
64616460
amountToSlash,
6462-
application1Mock.address,
64636461
])
64646462
expect(await tokenStaking.getSlashingQueueLength()).to.equal(2)
64656463
})
@@ -6520,12 +6518,10 @@ describe("TokenStaking", () => {
65206518
expect(await tokenStaking.slashingQueue(0)).to.deep.equal([
65216519
stakingProvider.address,
65226520
amountToSlash,
6523-
application1Mock.address,
65246521
])
65256522
expect(await tokenStaking.slashingQueue(1)).to.deep.equal([
65266523
otherStaker.address,
65276524
amountToSlash,
6528-
application1Mock.address,
65296525
])
65306526
expect(await tokenStaking.getSlashingQueueLength()).to.equal(2)
65316527
})
@@ -6603,12 +6599,10 @@ describe("TokenStaking", () => {
66036599
expect(await tokenStaking.slashingQueue(0)).to.deep.equal([
66046600
otherStaker.address,
66056601
amountToSlash,
6606-
application1Mock.address,
66076602
])
66086603
expect(await tokenStaking.slashingQueue(1)).to.deep.equal([
66096604
stakingProvider.address,
66106605
amountToSlash,
6611-
application1Mock.address,
66126606
])
66136607
expect(await tokenStaking.getSlashingQueueLength()).to.equal(2)
66146608
})
@@ -6643,8 +6637,7 @@ describe("TokenStaking", () => {
66436637
it("should add one slashing event", async () => {
66446638
expect(await tokenStaking.slashingQueue(0)).to.deep.equal([
66456639
stakingProvider.address,
6646-
amountToSlash,
6647-
application1Mock.address,
6640+
amountToSlash
66486641
])
66496642
expect(await tokenStaking.getSlashingQueueLength()).to.equal(1)
66506643
})
@@ -6687,7 +6680,6 @@ describe("TokenStaking", () => {
66876680
expect(await tokenStaking.slashingQueue(0)).to.deep.equal([
66886681
otherStaker.address,
66896682
amountToSlash,
6690-
application1Mock.address,
66916683
])
66926684
})
66936685

@@ -7001,7 +6993,7 @@ describe("TokenStaking", () => {
70016993
).to.equal(0)
70026994
})
70036995

7004-
it("should decrease authorized amount only for one application", async () => {
6996+
it("should decrease authorized amounts only for one provider", async () => {
70056997
expect(
70066998
await tokenStaking.authorizedStake(
70076999
stakingProvider.address,
@@ -7013,7 +7005,7 @@ describe("TokenStaking", () => {
70137005
stakingProvider.address,
70147006
application2Mock.address
70157007
)
7016-
).to.equal(provider1Authorized2)
7008+
).to.equal(provider1Authorized2.sub(amountToSlash))
70177009
expect(
70187010
await tokenStaking.authorizedStake(
70197011
otherStaker.address,
@@ -7045,13 +7037,13 @@ describe("TokenStaking", () => {
70457037
).to.be.revertedWith("Too many applications")
70467038
})
70477039

7048-
it("should inform only one application", async () => {
7040+
it("should inform all applications", async () => {
70497041
expect(
70507042
await application1Mock.stakingProviders(stakingProvider.address)
70517043
).to.deep.equal([provider1Authorized1.sub(amountToSlash), Zero])
70527044
expect(
70537045
await application2Mock.stakingProviders(stakingProvider.address)
7054-
).to.deep.equal([provider1Authorized2, Zero])
7046+
).to.deep.equal([provider1Authorized2.sub(amountToSlash), Zero])
70557047
expect(
70567048
await application1Mock.stakingProviders(otherStaker.address)
70577049
).to.deep.equal([provider2Authorized1, Zero])
@@ -7269,7 +7261,7 @@ describe("TokenStaking", () => {
72697261
})
72707262

72717263
context(
7272-
"when decrease authorized amount to zero for one application",
7264+
"when decrease authorized amount to zero",
72737265
() => {
72747266
const tAmount = initialStakerBalance
72757267

@@ -7326,7 +7318,7 @@ describe("TokenStaking", () => {
73267318
stakingProvider.address,
73277319
application2Mock.address
73287320
)
7329-
).to.equal(authorized)
7321+
).to.equal(0)
73307322
})
73317323

73327324
it("should allow to authorize one more application", async () => {

0 commit comments

Comments
 (0)