@@ -206,6 +206,14 @@ func (or *Oracle) AdvanceStateToNextSlot(fullBlock *FullBlock) (uint64, error) {
206
206
// Handle the donations from this block
207
207
or .handleDonations (blockDonations )
208
208
209
+ // Manual bans/unbans should always be the last thing to be processed in each block, since
210
+ // we want to ensure they persist to the next block
211
+ // Handle manual bans
212
+ or .handleManualBans (fullBlock .Events .BanValidator )
213
+
214
+ // Handle manual unbans
215
+ or .handleManualUnbans (fullBlock .Events .UnbanValidator )
216
+
209
217
// Handle validator cleanup: redisitribute the pending rewards of validators subscribed to the pool
210
218
// that are not in the beacon chain anymore (exited/slashed). We dont run this on every slot because
211
219
// its expensive. Runs every 4 hours.
@@ -1211,6 +1219,98 @@ func (or *Oracle) handleManualUnsubscriptions(
1211
1219
}
1212
1220
}
1213
1221
1222
+ func (or * Oracle ) handleManualBans (
1223
+ banEvents []* contract.ContractBanValidator ) {
1224
+
1225
+ // Return immediately if there are no ban events. Nothing to process!
1226
+ if len (banEvents ) == 0 {
1227
+ return
1228
+ }
1229
+
1230
+ // FIRST: healthy checks, ensure the bans events are okay.
1231
+ // Ensure the bans events are from the same block
1232
+ if len (banEvents ) > 0 {
1233
+ blockReference := banEvents [0 ].Raw .BlockNumber
1234
+ for _ , ban := range banEvents {
1235
+ if ban .Raw .BlockNumber != blockReference {
1236
+ log .Fatal ("Handling manual bans from different blocks is not possible: " ,
1237
+ ban .Raw .BlockNumber , " vs " , blockReference )
1238
+ }
1239
+ }
1240
+ }
1241
+
1242
+ totalPending := big .NewInt (0 )
1243
+ // SECOND: iterate over the ban events.
1244
+ // - Advance state machine of all banned validators (move them to Banned state).
1245
+ // - Sum all the pending rewards of the banned validators and share them among the rest.
1246
+ // - Reset the pending rewards of the banned validators (sets pending to 0).
1247
+ for _ , ban := range banEvents {
1248
+ log .WithFields (log.Fields {
1249
+ "BlockNumber" : ban .Raw .BlockNumber ,
1250
+ "TxHash" : ban .Raw .TxHash ,
1251
+ "ValidatorIndex" : ban .ValidatorID ,
1252
+ }).Info ("[Ban] Ban event received" )
1253
+
1254
+ //Check if the validator is subscribed. If not, we log it and dont do anything
1255
+ if ! or .isSubscribed (ban .ValidatorID ) {
1256
+ log .Warn ("Validator is not subscribed, skipping ban event" )
1257
+ continue
1258
+ }
1259
+
1260
+ or .advanceStateMachine (ban .ValidatorID , ManualBan )
1261
+ totalPending .Add (totalPending , or .state .Validators [ban .ValidatorID ].PendingRewardsWei )
1262
+ or .resetPendingRewards (ban .ValidatorID )
1263
+
1264
+ }
1265
+
1266
+ // THIRD: share the total pending rewards of the banned validators among the rest. This has to be done
1267
+ // once all the bans have been processed. This should also be only done if banEvents is not empty, thats
1268
+ // why we have the check at the beginning of the function.
1269
+
1270
+ // If totalPending is negative, log a fatal error. We should never have negative rewards to share.
1271
+ if totalPending .Cmp (big .NewInt (0 )) < 0 {
1272
+ log .Fatal ("Total pending rewards is negative. Aborting reward sharing." )
1273
+ }
1274
+
1275
+ // Only share rewards if totalPending is greater than zero.
1276
+ if totalPending .Cmp (big .NewInt (0 )) > 0 {
1277
+ or .increaseAllPendingRewards (totalPending )
1278
+ }
1279
+ }
1280
+
1281
+ func (or * Oracle ) handleManualUnbans (
1282
+ unbanEvents []* contract.ContractUnbanValidator ) {
1283
+
1284
+ // FIRST: healthy checks, ensure the unbans events are okay.
1285
+ if len (unbanEvents ) > 0 {
1286
+ blockReference := unbanEvents [0 ].Raw .BlockNumber
1287
+ for _ , ban := range unbanEvents {
1288
+ if ban .Raw .BlockNumber != blockReference {
1289
+ log .Fatal ("Handling manual unbans from different blocks is not possible: " ,
1290
+ ban .Raw .BlockNumber , " vs " , blockReference )
1291
+ }
1292
+ }
1293
+ }
1294
+
1295
+ // SECOND: iterate over the unban events.
1296
+ // - Advance state machine of all unbanned validators (move them to Active state).
1297
+ for _ , unban := range unbanEvents {
1298
+ log .WithFields (log.Fields {
1299
+ "BlockNumber" : unban .Raw .BlockNumber ,
1300
+ "TxHash" : unban .Raw .TxHash ,
1301
+ "ValidatorIndex" : unban .ValidatorID ,
1302
+ }).Info ("[Unban] Unban event received" )
1303
+
1304
+ // Check if the validator is banned. If not, we log it and dont do anything
1305
+ if ! or .isBanned (unban .ValidatorID ) {
1306
+ log .Warn ("Validator is not banned, skipping unban event" )
1307
+ continue
1308
+ }
1309
+
1310
+ or .advanceStateMachine (unban .ValidatorID , ManualUnban )
1311
+ }
1312
+ }
1313
+
1214
1314
// Banning a validator implies sharing its pending rewards among the rest
1215
1315
// of the validators and setting its pending to zero.
1216
1316
func (or * Oracle ) handleBanValidator (block SummarizedBlock ) {
@@ -1490,6 +1590,18 @@ func GetWithdrawalAndType(validator *v1.Validator) (string, WithdrawalType) {
1490
1590
// See the spec for state diagram with states and transitions. This tracks all the different
1491
1591
// states and state transitions that a given validator can have from the oracle point of view
1492
1592
func (or * Oracle ) advanceStateMachine (valIndex uint64 , event Event ) {
1593
+
1594
+ // Safety check, if the validator does not exist, we log it and return
1595
+ validator , exists := or .state .Validators [valIndex ]
1596
+ if ! exists || validator == nil {
1597
+ // Handle the case where the validator does not exist or is nil
1598
+ log .WithFields (log.Fields {
1599
+ "ValidatorIndex" : valIndex ,
1600
+ "Error" : "Validator not found or is nil" ,
1601
+ }).Warn ("Called advanceStateMachine with a validator that does not exist or is nil" )
1602
+ return
1603
+ }
1604
+
1493
1605
switch or .state .Validators [valIndex ].ValidatorStatus {
1494
1606
case Active :
1495
1607
switch event {
@@ -1525,6 +1637,15 @@ func (or *Oracle) advanceStateMachine(valIndex uint64, event Event) {
1525
1637
"Slot" : or .state .NextSlotToProcess ,
1526
1638
}).Info ("Validator state change" )
1527
1639
or .state .Validators [valIndex ].ValidatorStatus = NotSubscribed
1640
+ case ManualBan :
1641
+ log .WithFields (log.Fields {
1642
+ "Event" : "ManualBan" ,
1643
+ "StateChange" : "Active -> Banned" ,
1644
+ "ValidatorIndex" : valIndex ,
1645
+ "Slot" : or .state .NextSlotToProcess ,
1646
+ }).Info ("Validator state change" )
1647
+ or .state .Validators [valIndex ].ValidatorStatus = Banned
1648
+
1528
1649
}
1529
1650
case YellowCard :
1530
1651
switch event {
@@ -1560,6 +1681,14 @@ func (or *Oracle) advanceStateMachine(valIndex uint64, event Event) {
1560
1681
"Slot" : or .state .NextSlotToProcess ,
1561
1682
}).Info ("Validator state change" )
1562
1683
or .state .Validators [valIndex ].ValidatorStatus = NotSubscribed
1684
+ case ManualBan :
1685
+ log .WithFields (log.Fields {
1686
+ "Event" : "ManualBan" ,
1687
+ "StateChange" : "YellowCard -> Banned" ,
1688
+ "ValidatorIndex" : valIndex ,
1689
+ "Slot" : or .state .NextSlotToProcess ,
1690
+ }).Info ("Validator state change" )
1691
+ or .state .Validators [valIndex ].ValidatorStatus = Banned
1563
1692
}
1564
1693
case RedCard :
1565
1694
switch event {
@@ -1595,6 +1724,14 @@ func (or *Oracle) advanceStateMachine(valIndex uint64, event Event) {
1595
1724
"Slot" : or .state .NextSlotToProcess ,
1596
1725
}).Info ("Validator state change" )
1597
1726
or .state .Validators [valIndex ].ValidatorStatus = NotSubscribed
1727
+ case ManualBan :
1728
+ log .WithFields (log.Fields {
1729
+ "Event" : "ManualBan" ,
1730
+ "StateChange" : "RedCard -> Banned" ,
1731
+ "ValidatorIndex" : valIndex ,
1732
+ "Slot" : or .state .NextSlotToProcess ,
1733
+ }).Info ("Validator state change" )
1734
+ or .state .Validators [valIndex ].ValidatorStatus = Banned
1598
1735
}
1599
1736
case NotSubscribed :
1600
1737
switch event {
@@ -1615,5 +1752,18 @@ func (or *Oracle) advanceStateMachine(valIndex uint64, event Event) {
1615
1752
}).Info ("Validator state change" )
1616
1753
or .state .Validators [valIndex ].ValidatorStatus = Active
1617
1754
}
1755
+ // A validator could return to the state it was after being banned, but we
1756
+ // return it always to the Active state for the sake of simplicity.
1757
+ case Banned :
1758
+ switch event {
1759
+ case ManualUnban :
1760
+ log .WithFields (log.Fields {
1761
+ "Event" : "ManualUnban" ,
1762
+ "StateChange" : "Banned -> Active" ,
1763
+ "ValidatorIndex" : valIndex ,
1764
+ "Slot" : or .state .NextSlotToProcess ,
1765
+ }).Info ("Validator state change" )
1766
+ or .state .Validators [valIndex ].ValidatorStatus = Active
1767
+ }
1618
1768
}
1619
1769
}
0 commit comments