From ab7546377bf9dc19e5b2a4fe3f3ddfcb8352fa36 Mon Sep 17 00:00:00 2001 From: pk910 Date: Tue, 3 Dec 2024 14:17:20 +0100 Subject: [PATCH] add exit reason to validator details page --- handlers/validator.go | 83 ++++++++++++++++++++++++- templates/validator/recentDeposits.html | 6 +- templates/validator/validator.html | 33 ++++++++++ types/models/validator.go | 59 +++++++++++------- 4 files changed, 153 insertions(+), 28 deletions(-) diff --git a/handlers/validator.go b/handlers/validator.go index bc1e12cd..43726671 100644 --- a/handlers/validator.go +++ b/handlers/validator.go @@ -170,7 +170,7 @@ func buildValidatorPageData(validatorIndex uint64, tabView string) (*models.Vali pageData.ExitEpoch = uint64(validator.Validator.ExitEpoch) pageData.ExitTs = chainState.EpochToTime(validator.Validator.ExitEpoch) } - if validator.Validator.WithdrawalCredentials[0] == 0x01 { + if validator.Validator.WithdrawalCredentials[0] == 0x01 || validator.Validator.WithdrawalCredentials[0] == 0x02 { pageData.ShowWithdrawAddress = true pageData.WithdrawAddress = validator.Validator.WithdrawalCredentials[12:] } @@ -583,5 +583,86 @@ func buildValidatorPageData(validatorIndex uint64, tabView string) (*models.Vali pageData.ConsolidationRequestCount = uint64(len(pageData.ConsolidationRequests)) } + // Check for exit reason if validator is exiting or has exited + if pageData.ShowExit { + zeroAmount := uint64(0) + exitSlot := uint64(chainState.EpochToSlot(validator.Validator.ExitEpoch)) + + // Check for slashing + if slashings, totalSlashings := services.GlobalBeaconService.GetSlashingsByFilter(&dbtypes.SlashingFilter{ + MinIndex: validatorIndex, + MaxIndex: validatorIndex, + }, 0, 1); totalSlashings > 0 && len(slashings) > 0 { + pageData.ExitReason = "Validator was slashed" + pageData.ExitReasonSlashing = true + pageData.ExitReasonSlot = slashings[0].SlotNumber + pageData.ExitReasonSlashingReason = uint64(slashings[0].Reason) + + // Check for voluntary exit + } else if exits, totalExits := services.GlobalBeaconService.GetVoluntaryExitsByFilter(&dbtypes.VoluntaryExitFilter{ + MinIndex: validatorIndex, + MaxIndex: validatorIndex, + }, 0, 1); totalExits > 0 && len(exits) > 0 { + pageData.ExitReason = "Validator submitted a voluntary exit request" + pageData.ExitReasonVoluntaryExit = true + pageData.ExitReasonSlot = exits[0].SlotNumber + + // Check for full withdrawal request + } else if withdrawals, totalWithdrawals := services.GlobalBeaconService.GetWithdrawalRequestsByFilter(&services.CombinedWithdrawalRequestFilter{ + Filter: &dbtypes.WithdrawalRequestFilter{ + PublicKey: validator.Validator.PublicKey[:], + SourceAddress: pageData.WithdrawAddress, + MaxAmount: &zeroAmount, + MaxSlot: exitSlot, + }, + }, 0, 1); totalWithdrawals > 0 && len(withdrawals) > 0 && pageData.ShowWithdrawAddress { + withdrawal := withdrawals[0] + pageData.ExitReason = "Validator submitted a full withdrawal request" + pageData.ExitReasonWithdrawal = true + pageData.ExitReasonSlot = withdrawal.Request.SlotNumber + + if withdrawal.Transaction != nil { + pageData.ExitReasonTxHash = withdrawal.Transaction.TxHash + pageData.ExitReasonTxDetails = &models.ValidatorPageDataWithdrawalTxDetails{ + BlockNumber: withdrawal.Transaction.BlockNumber, + BlockHash: fmt.Sprintf("%#x", withdrawal.Transaction.BlockRoot), + BlockTime: withdrawal.Transaction.BlockTime, + TxOrigin: common.Address(withdrawal.Transaction.TxSender).Hex(), + TxTarget: common.Address(withdrawal.Transaction.TxTarget).Hex(), + TxHash: fmt.Sprintf("%#x", withdrawal.Transaction.TxHash), + } + } + // Check for consolidation request + } else if consolidations, totalConsolidations := services.GlobalBeaconService.GetConsolidationRequestsByFilter(&services.CombinedConsolidationRequestFilter{ + Filter: &dbtypes.ConsolidationRequestFilter{ + PublicKey: validator.Validator.PublicKey[:], + SourceAddress: pageData.WithdrawAddress, + MaxSlot: exitSlot, + }, + }, 0, 1); totalConsolidations > 0 && len(consolidations) > 0 && pageData.ShowWithdrawAddress { + consolidation := consolidations[0] + pageData.ExitReason = "Validator was consolidated" + pageData.ExitReasonConsolidation = true + pageData.ExitReasonSlot = consolidation.Request.SlotNumber + + if targetIndex := consolidation.TargetIndex(); targetIndex != nil { + pageData.ExitReasonTargetIndex = *targetIndex + pageData.ExitReasonTargetName = services.GlobalBeaconService.GetValidatorName(*targetIndex) + } + + if consolidation.Transaction != nil { + pageData.ExitReasonTxHash = consolidation.Transaction.TxHash + pageData.ExitReasonTxDetails = &models.ValidatorPageDataWithdrawalTxDetails{ + BlockNumber: consolidation.Transaction.BlockNumber, + BlockHash: fmt.Sprintf("%#x", consolidation.Transaction.BlockRoot), + BlockTime: consolidation.Transaction.BlockTime, + TxOrigin: common.Address(consolidation.Transaction.TxSender).Hex(), + TxTarget: common.Address(consolidation.Transaction.TxTarget).Hex(), + TxHash: fmt.Sprintf("%#x", consolidation.Transaction.TxHash), + } + } + } + } + return pageData, 10 * time.Minute } diff --git a/templates/validator/recentDeposits.html b/templates/validator/recentDeposits.html index 9f2b6adf..41862a61 100644 --- a/templates/validator/recentDeposits.html +++ b/templates/validator/recentDeposits.html @@ -44,10 +44,10 @@
-
- -
+
+
+ {{- else }} ? {{- end }} diff --git a/templates/validator/validator.html b/templates/validator/validator.html index 892f87fd..8fb9810c 100644 --- a/templates/validator/validator.html +++ b/templates/validator/validator.html @@ -152,6 +152,39 @@

Validator {{ forma {{ end }} + {{ if .ExitReason }} +
+
Exit Reason:
+
+ {{ if .ExitReasonSlashing }} + Validator was slashed in slot {{ .ExitReasonSlot }} + ({{ if eq .ExitReasonSlashingReason 1 }}Proposer Slashing{{ else if eq .ExitReasonSlashingReason 2 }}Attester Slashing{{ else }}Unknown Slashing{{ end }}) + {{ else if .ExitReasonVoluntaryExit }} + Validator submitted a voluntary exit request in slot {{ .ExitReasonSlot }} + {{ else if .ExitReasonWithdrawal }} + Validator submitted a full withdrawal request in slot {{ .ExitReasonSlot }} + {{ if .ExitReasonTxDetails }} +
+ + Transaction: {{ ethTransactionLink .ExitReasonTxHash 0 }} + + + {{ end }} + {{ else if .ExitReasonConsolidation }} + Validator was consolidated into validator {{ formatValidatorWithIndex .ExitReasonTargetIndex .ExitReasonTargetName }} (Requested in slot {{ .ExitReasonSlot }}) + {{ if .ExitReasonTxDetails }} +
+ + Transaction: {{ ethTransactionLink .ExitReasonTxHash 0 }} + + + {{ end }} + {{ else }} + {{ .ExitReason }} + {{ end }} +
+
+ {{ end }} diff --git a/types/models/validator.go b/types/models/validator.go index d3d3b9ab..7155d9ee 100644 --- a/types/models/validator.go +++ b/types/models/validator.go @@ -6,30 +6,41 @@ import ( // ValidatorPageData is a struct to hold info for the validator page type ValidatorPageData struct { - CurrentEpoch uint64 `json:"current_epoch"` - Index uint64 `json:"index"` - Name string `json:"name"` - PublicKey []byte `json:"pubkey"` - Balance uint64 `json:"balance"` - EffectiveBalance uint64 `json:"eff_balance"` - State string `json:"state"` - BeaconState string `json:"beacon_state"` - ShowEligible bool `json:"show_eligible"` - EligibleTs time.Time `json:"eligible_ts"` - EligibleEpoch uint64 `json:"eligible_epoch"` - ShowActivation bool `json:"show_activation"` - ActivationTs time.Time `json:"activation_ts"` - ActivationEpoch uint64 `json:"activation_epoch"` - IsActive bool `json:"is_active"` - WasActive bool `json:"was_active"` - UpcheckActivity uint8 `json:"upcheck_act"` - UpcheckMaximum uint8 `json:"upcheck_max"` - ShowExit bool `json:"show_exit"` - ExitTs time.Time `json:"exit_ts"` - ExitEpoch uint64 `json:"exit_epoch"` - WithdrawCredentials []byte `json:"withdraw_credentials"` - ShowWithdrawAddress bool `json:"show_withdraw_address"` - WithdrawAddress []byte `json:"withdraw_address"` + CurrentEpoch uint64 `json:"current_epoch"` + Index uint64 `json:"index"` + Name string `json:"name"` + PublicKey []byte `json:"pubkey"` + Balance uint64 `json:"balance"` + EffectiveBalance uint64 `json:"eff_balance"` + State string `json:"state"` + BeaconState string `json:"beacon_state"` + ShowEligible bool `json:"show_eligible"` + EligibleTs time.Time `json:"eligible_ts"` + EligibleEpoch uint64 `json:"eligible_epoch"` + ShowActivation bool `json:"show_activation"` + ActivationTs time.Time `json:"activation_ts"` + ActivationEpoch uint64 `json:"activation_epoch"` + IsActive bool `json:"is_active"` + WasActive bool `json:"was_active"` + UpcheckActivity uint8 `json:"upcheck_act"` + UpcheckMaximum uint8 `json:"upcheck_max"` + ShowExit bool `json:"show_exit"` + ExitTs time.Time `json:"exit_ts"` + ExitEpoch uint64 `json:"exit_epoch"` + WithdrawCredentials []byte `json:"withdraw_credentials"` + ShowWithdrawAddress bool `json:"show_withdraw_address"` + WithdrawAddress []byte `json:"withdraw_address"` + ExitReason string `json:"exit_reason"` + ExitReasonSlot uint64 `json:"exit_reason_slot"` + ExitReasonSlashing bool `json:"exit_reason_slashing"` + ExitReasonSlashingReason uint64 `json:"exit_reason_slashing_reason"` + ExitReasonVoluntaryExit bool `json:"exit_reason_voluntary_exit"` + ExitReasonWithdrawal bool `json:"exit_reason_withdrawal"` + ExitReasonConsolidation bool `json:"exit_reason_consolidation"` + ExitReasonTargetIndex uint64 `json:"exit_reason_target_index"` + ExitReasonTargetName string `json:"exit_reason_target_name"` + ExitReasonTxHash []byte `json:"exit_reason_tx_hash"` + ExitReasonTxDetails *ValidatorPageDataWithdrawalTxDetails `json:"exit_reason_tx_details"` TabView string `json:"tab_view"` ElectraIsActive bool `json:"electra_is_active"`