@@ -3,31 +3,38 @@ import {FAR_FUTURE_EPOCH, MIN_ACTIVATION_BALANCE, PENDING_CONSOLIDATIONS_LIMIT}
33
44import { CachedBeaconStateElectra } from "../types.js" ;
55import { getConsolidationChurnLimit , isActiveValidator } from "../util/validator.js" ;
6- import { hasExecutionWithdrawalCredential } from "../util/electra.js" ;
6+ import { hasExecutionWithdrawalCredential , switchToCompoundingValidator } from "../util/electra.js" ;
77import { computeConsolidationEpochAndUpdateChurn } from "../util/epoch.js" ;
8+ import { hasEth1WithdrawalCredential } from "../util/capella.js" ;
89
10+ // TODO Electra: Clean up necessary as there is a lot of overlap with isValidSwitchToCompoundRequest
911export function processConsolidationRequest (
1012 state : CachedBeaconStateElectra ,
1113 consolidationRequest : electra . ConsolidationRequest
1214) : void {
13- // If the pending consolidations queue is full, consolidation requests are ignored
14- if ( state . pendingConsolidations . length >= PENDING_CONSOLIDATIONS_LIMIT ) {
15+ const { sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest ;
16+ const sourceIndex = state . epochCtx . getValidatorIndex ( sourcePubkey ) ;
17+ const targetIndex = state . epochCtx . getValidatorIndex ( targetPubkey ) ;
18+
19+ if ( sourceIndex === null || targetIndex === null ) {
1520 return ;
1621 }
1722
18- // If there is too little available consolidation churn limit, consolidation requests are ignored
19- if ( getConsolidationChurnLimit ( state . epochCtx ) <= MIN_ACTIVATION_BALANCE ) {
23+ if ( isValidSwitchToCompoundRequest ( state , consolidationRequest ) ) {
24+ switchToCompoundingValidator ( state , sourceIndex ) ;
25+ // Early return since we have already switched validator to compounding
2026 return ;
2127 }
2228
23- const { sourcePubkey, targetPubkey} = consolidationRequest ;
24- const sourceIndex = state . epochCtx . getValidatorIndex ( sourcePubkey ) ;
25- const targetIndex = state . epochCtx . getValidatorIndex ( targetPubkey ) ;
26-
27- if ( sourceIndex === null || targetIndex === null ) {
29+ // If the pending consolidations queue is full, consolidation requests are ignored
30+ if ( state . pendingConsolidations . length >= PENDING_CONSOLIDATIONS_LIMIT ) {
2831 return ;
2932 }
3033
34+ // If there is too little available consolidation churn limit, consolidation requests are ignored
35+ if ( getConsolidationChurnLimit ( state . epochCtx ) <= MIN_ACTIVATION_BALANCE ) {
36+ return ;
37+ }
3138 // Verify that source != target, so a consolidation cannot be used as an exit.
3239 if ( sourceIndex === targetIndex ) {
3340 return ;
@@ -46,7 +53,7 @@ export function processConsolidationRequest(
4653 return ;
4754 }
4855
49- if ( Buffer . compare ( sourceWithdrawalAddress , consolidationRequest . sourceAddress ) !== 0 ) {
56+ if ( Buffer . compare ( sourceWithdrawalAddress , sourceAddress ) !== 0 ) {
5057 return ;
5158 }
5259
@@ -70,4 +77,55 @@ export function processConsolidationRequest(
7077 targetIndex,
7178 } ) ;
7279 state . pendingConsolidations . push ( pendingConsolidation ) ;
80+
81+ // Churn any target excess active balance of target and raise its max
82+ if ( hasEth1WithdrawalCredential ( targetValidator . withdrawalCredentials ) ) {
83+ switchToCompoundingValidator ( state , targetIndex ) ;
84+ }
85+ }
86+
87+ /**
88+ * Determine if we should set consolidation target validator to compounding credential
89+ */
90+ function isValidSwitchToCompoundRequest (
91+ state : CachedBeaconStateElectra ,
92+ consolidationRequest : electra . ConsolidationRequest
93+ ) : boolean {
94+ const { sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest ;
95+ const sourceIndex = state . epochCtx . getValidatorIndex ( sourcePubkey ) ;
96+ const targetIndex = state . epochCtx . getValidatorIndex ( targetPubkey ) ;
97+
98+ // Verify pubkey exists
99+ if ( sourceIndex === null ) {
100+ return false ;
101+ }
102+
103+ // Switch to compounding requires source and target be equal
104+ if ( sourceIndex !== targetIndex ) {
105+ return false ;
106+ }
107+
108+ const sourceValidator = state . validators . getReadonly ( sourceIndex ) ;
109+ const sourceWithdrawalAddress = sourceValidator . withdrawalCredentials . subarray ( 12 ) ;
110+ // Verify request has been authorized
111+ if ( Buffer . compare ( sourceWithdrawalAddress , sourceAddress ) !== 0 ) {
112+ return false ;
113+ }
114+
115+ // Verify source withdrawal credentials
116+ if ( ! hasEth1WithdrawalCredential ( sourceValidator . withdrawalCredentials ) ) {
117+ return false ;
118+ }
119+
120+ // Verify the source is active
121+ if ( ! isActiveValidator ( sourceValidator , state . epochCtx . epoch ) ) {
122+ return false ;
123+ }
124+
125+ // Verify exit for source has not been initiated
126+ if ( sourceValidator . exitEpoch !== FAR_FUTURE_EPOCH ) {
127+ return false ;
128+ }
129+
130+ return true ;
73131}
0 commit comments