@lodestar/state-transition 1.37.0-rc.0 → 1.38.0-dev.1ddbe5d870
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/block/index.d.ts +4 -1
- package/lib/block/index.d.ts.map +1 -1
- package/lib/block/index.js +19 -8
- package/lib/block/index.js.map +1 -1
- package/lib/block/isValidIndexedAttestation.d.ts +3 -2
- package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
- package/lib/block/isValidIndexedAttestation.js +4 -4
- package/lib/block/isValidIndexedAttestation.js.map +1 -1
- package/lib/block/isValidIndexedPayloadAttestation.d.ts +4 -0
- package/lib/block/isValidIndexedPayloadAttestation.d.ts.map +1 -0
- package/lib/block/isValidIndexedPayloadAttestation.js +14 -0
- package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -0
- package/lib/block/processAttestationPhase0.d.ts.map +1 -1
- package/lib/block/processAttestationPhase0.js +7 -2
- package/lib/block/processAttestationPhase0.js.map +1 -1
- package/lib/block/processAttestationsAltair.d.ts +3 -3
- package/lib/block/processAttestationsAltair.d.ts.map +1 -1
- package/lib/block/processAttestationsAltair.js +47 -5
- package/lib/block/processAttestationsAltair.js.map +1 -1
- package/lib/block/processAttesterSlashing.d.ts +2 -1
- package/lib/block/processAttesterSlashing.d.ts.map +1 -1
- package/lib/block/processAttesterSlashing.js +5 -4
- package/lib/block/processAttesterSlashing.js.map +1 -1
- package/lib/block/processConsolidationRequest.d.ts +3 -2
- package/lib/block/processConsolidationRequest.d.ts.map +1 -1
- package/lib/block/processConsolidationRequest.js +2 -2
- package/lib/block/processConsolidationRequest.js.map +1 -1
- package/lib/block/processDepositRequest.d.ts +2 -2
- package/lib/block/processDepositRequest.d.ts.map +1 -1
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts +5 -0
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -0
- package/lib/block/processExecutionPayloadBid.js +89 -0
- package/lib/block/processExecutionPayloadBid.js.map +1 -0
- package/lib/block/processExecutionPayloadEnvelope.d.ts +4 -0
- package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -0
- package/lib/block/processExecutionPayloadEnvelope.js +118 -0
- package/lib/block/processExecutionPayloadEnvelope.js.map +1 -0
- package/lib/block/processOperations.d.ts.map +1 -1
- package/lib/block/processOperations.js +8 -2
- package/lib/block/processOperations.js.map +1 -1
- package/lib/block/processPayloadAttestation.d.ts +4 -0
- package/lib/block/processPayloadAttestation.d.ts.map +1 -0
- package/lib/block/processPayloadAttestation.js +16 -0
- package/lib/block/processPayloadAttestation.js.map +1 -0
- package/lib/block/processProposerSlashing.d.ts.map +1 -1
- package/lib/block/processProposerSlashing.js +17 -2
- package/lib/block/processProposerSlashing.js.map +1 -1
- package/lib/block/processRandao.js +1 -1
- package/lib/block/processRandao.js.map +1 -1
- package/lib/block/processSyncCommittee.d.ts +2 -1
- package/lib/block/processSyncCommittee.d.ts.map +1 -1
- package/lib/block/processSyncCommittee.js +3 -4
- package/lib/block/processSyncCommittee.js.map +1 -1
- package/lib/block/processVoluntaryExit.js +2 -2
- package/lib/block/processVoluntaryExit.js.map +1 -1
- package/lib/block/processWithdrawalRequest.d.ts +2 -2
- package/lib/block/processWithdrawalRequest.d.ts.map +1 -1
- package/lib/block/processWithdrawalRequest.js +1 -1
- package/lib/block/processWithdrawalRequest.js.map +1 -1
- package/lib/block/processWithdrawals.d.ts +4 -3
- package/lib/block/processWithdrawals.d.ts.map +1 -1
- package/lib/block/processWithdrawals.js +89 -19
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/cache/epochCache.d.ts +5 -1
- package/lib/cache/epochCache.d.ts.map +1 -1
- package/lib/cache/epochCache.js +34 -1
- package/lib/cache/epochCache.js.map +1 -1
- package/lib/epoch/index.d.ts +4 -2
- package/lib/epoch/index.d.ts.map +1 -1
- package/lib/epoch/index.js +10 -1
- package/lib/epoch/index.js.map +1 -1
- package/lib/epoch/processBuilderPendingPayments.d.ts +6 -0
- package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -0
- package/lib/epoch/processBuilderPendingPayments.js +28 -0
- package/lib/epoch/processBuilderPendingPayments.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/signatureSets/attesterSlashings.d.ts +4 -3
- package/lib/signatureSets/attesterSlashings.d.ts.map +1 -1
- package/lib/signatureSets/attesterSlashings.js +6 -6
- package/lib/signatureSets/attesterSlashings.js.map +1 -1
- package/lib/signatureSets/index.d.ts +4 -2
- package/lib/signatureSets/index.d.ts.map +1 -1
- package/lib/signatureSets/index.js +9 -8
- package/lib/signatureSets/index.js.map +1 -1
- package/lib/signatureSets/indexedAttestation.d.ts +4 -3
- package/lib/signatureSets/indexedAttestation.d.ts.map +1 -1
- package/lib/signatureSets/indexedAttestation.js +9 -7
- package/lib/signatureSets/indexedAttestation.js.map +1 -1
- package/lib/signatureSets/indexedPayloadAttestation.d.ts +6 -0
- package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -0
- package/lib/signatureSets/indexedPayloadAttestation.js +11 -0
- package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -0
- package/lib/signatureSets/proposer.d.ts +5 -4
- package/lib/signatureSets/proposer.d.ts.map +1 -1
- package/lib/signatureSets/proposer.js +12 -12
- package/lib/signatureSets/proposer.js.map +1 -1
- package/lib/signatureSets/proposerSlashings.d.ts +3 -2
- package/lib/signatureSets/proposerSlashings.d.ts.map +1 -1
- package/lib/signatureSets/proposerSlashings.js +4 -5
- package/lib/signatureSets/proposerSlashings.js.map +1 -1
- package/lib/signatureSets/randao.d.ts +3 -2
- package/lib/signatureSets/randao.d.ts.map +1 -1
- package/lib/signatureSets/randao.js +4 -5
- package/lib/signatureSets/randao.js.map +1 -1
- package/lib/signatureSets/voluntaryExits.d.ts +4 -3
- package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
- package/lib/signatureSets/voluntaryExits.js +6 -7
- package/lib/signatureSets/voluntaryExits.js.map +1 -1
- package/lib/slot/index.d.ts +2 -1
- package/lib/slot/index.d.ts.map +1 -1
- package/lib/slot/index.js +6 -2
- package/lib/slot/index.js.map +1 -1
- package/lib/slot/upgradeStateToAltair.js +1 -1
- package/lib/slot/upgradeStateToAltair.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.d.ts +0 -1
- package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +47 -5
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateTransition.js +5 -4
- package/lib/stateTransition.js.map +1 -1
- package/lib/util/electra.d.ts +5 -5
- package/lib/util/electra.d.ts.map +1 -1
- package/lib/util/electra.js +2 -1
- package/lib/util/electra.js.map +1 -1
- package/lib/util/epoch.d.ts +3 -3
- package/lib/util/epoch.d.ts.map +1 -1
- package/lib/util/epoch.js.map +1 -1
- package/lib/util/gloas.d.ts +11 -0
- package/lib/util/gloas.d.ts.map +1 -0
- package/lib/util/gloas.js +35 -0
- package/lib/util/gloas.js.map +1 -0
- package/lib/util/seed.d.ts +5 -1
- package/lib/util/seed.d.ts.map +1 -1
- package/lib/util/seed.js +33 -1
- package/lib/util/seed.js.map +1 -1
- package/lib/util/validator.d.ts +2 -2
- package/lib/util/validator.d.ts.map +1 -1
- package/lib/util/validator.js +14 -1
- package/lib/util/validator.js.map +1 -1
- package/package.json +6 -6
- package/src/block/index.ts +35 -14
- package/src/block/isValidIndexedAttestation.ts +5 -2
- package/src/block/isValidIndexedPayloadAttestation.ts +23 -0
- package/src/block/processAttestationPhase0.ts +13 -2
- package/src/block/processAttestationsAltair.ts +63 -6
- package/src/block/processAttesterSlashing.ts +6 -3
- package/src/block/processConsolidationRequest.ts +6 -5
- package/src/block/processDepositRequest.ts +5 -2
- package/src/block/processExecutionPayloadBid.ts +120 -0
- package/src/block/processExecutionPayloadEnvelope.ts +181 -0
- package/src/block/processOperations.ts +16 -4
- package/src/block/processPayloadAttestation.ts +25 -0
- package/src/block/processProposerSlashing.ts +25 -4
- package/src/block/processRandao.ts +1 -1
- package/src/block/processSyncCommittee.ts +4 -3
- package/src/block/processVoluntaryExit.ts +2 -2
- package/src/block/processWithdrawalRequest.ts +4 -4
- package/src/block/processWithdrawals.ts +118 -27
- package/src/cache/epochCache.ts +58 -1
- package/src/epoch/index.ts +12 -0
- package/src/epoch/processBuilderPendingPayments.ts +31 -0
- package/src/index.ts +2 -0
- package/src/signatureSets/attesterSlashings.ts +7 -3
- package/src/signatureSets/index.ts +12 -7
- package/src/signatureSets/indexedAttestation.ts +20 -9
- package/src/signatureSets/indexedPayloadAttestation.ts +24 -0
- package/src/signatureSets/proposer.ts +13 -7
- package/src/signatureSets/proposerSlashings.ts +5 -3
- package/src/signatureSets/randao.ts +13 -5
- package/src/signatureSets/voluntaryExits.ts +7 -4
- package/src/slot/index.ts +11 -3
- package/src/slot/upgradeStateToAltair.ts +2 -1
- package/src/slot/upgradeStateToGloas.ts +49 -5
- package/src/stateTransition.ts +5 -5
- package/src/util/electra.ts +15 -6
- package/src/util/epoch.ts +6 -3
- package/src/util/gloas.ts +58 -0
- package/src/util/seed.ts +57 -1
- package/src/util/validator.ts +21 -2
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {PublicKey, Signature, verify} from "@chainsafe/blst";
|
|
2
|
+
import {byteArrayEquals} from "@chainsafe/ssz";
|
|
3
|
+
import {DOMAIN_BEACON_BUILDER, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
4
|
+
import {gloas, ssz} from "@lodestar/types";
|
|
5
|
+
import {toHex, toRootHex} from "@lodestar/utils";
|
|
6
|
+
import {CachedBeaconStateGloas} from "../types.ts";
|
|
7
|
+
import {computeExitEpochAndUpdateChurn, computeSigningRoot, computeTimeAtSlot} from "../util/index.ts";
|
|
8
|
+
import {processConsolidationRequest} from "./processConsolidationRequest.ts";
|
|
9
|
+
import {processDepositRequest} from "./processDepositRequest.ts";
|
|
10
|
+
import {processWithdrawalRequest} from "./processWithdrawalRequest.ts";
|
|
11
|
+
|
|
12
|
+
// This function does not call execution engine to verify payload. Need to call it from other place
|
|
13
|
+
export function processExecutionPayloadEnvelope(
|
|
14
|
+
state: CachedBeaconStateGloas,
|
|
15
|
+
signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
|
|
16
|
+
verify: boolean
|
|
17
|
+
): void {
|
|
18
|
+
const envelope = signedEnvelope.message;
|
|
19
|
+
const payload = envelope.payload;
|
|
20
|
+
const fork = state.config.getForkSeq(envelope.slot);
|
|
21
|
+
|
|
22
|
+
if (verify) {
|
|
23
|
+
const builderIndex = envelope.builderIndex;
|
|
24
|
+
const pubkey = state.validators.getReadonly(builderIndex).pubkey;
|
|
25
|
+
|
|
26
|
+
if (!verifyExecutionPayloadEnvelopeSignature(state, pubkey, signedEnvelope)) {
|
|
27
|
+
throw new Error("Payload Envelope has invalid signature");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
validateExecutionPayloadEnvelope(state, envelope);
|
|
32
|
+
|
|
33
|
+
const requests = envelope.executionRequests;
|
|
34
|
+
|
|
35
|
+
for (const deposit of requests.deposits) {
|
|
36
|
+
processDepositRequest(state, deposit);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const withdrawal of requests.withdrawals) {
|
|
40
|
+
processWithdrawalRequest(fork, state, withdrawal);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const consolidation of requests.consolidations) {
|
|
44
|
+
processConsolidationRequest(fork, state, consolidation);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Queue the builder payment
|
|
48
|
+
const paymentIndex = SLOTS_PER_EPOCH + (state.slot % SLOTS_PER_EPOCH);
|
|
49
|
+
const payment = state.builderPendingPayments.get(paymentIndex).clone();
|
|
50
|
+
const amount = payment.withdrawal.amount;
|
|
51
|
+
|
|
52
|
+
if (amount > 0) {
|
|
53
|
+
const exitQueueEpoch = computeExitEpochAndUpdateChurn(state, BigInt(amount));
|
|
54
|
+
|
|
55
|
+
payment.withdrawal.withdrawableEpoch = exitQueueEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
|
|
56
|
+
state.builderPendingWithdrawals.push(payment.withdrawal);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
state.builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
|
|
60
|
+
|
|
61
|
+
// Cache the execution payload hash
|
|
62
|
+
state.executionPayloadAvailability.set(state.slot % SLOTS_PER_HISTORICAL_ROOT, true);
|
|
63
|
+
state.latestBlockHash = payload.blockHash;
|
|
64
|
+
|
|
65
|
+
if (verify && !byteArrayEquals(envelope.stateRoot, state.hashTreeRoot())) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Envelope's state root does not match state envelope=${toRootHex(envelope.stateRoot)} state=${toRootHex(state.hashTreeRoot())}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function validateExecutionPayloadEnvelope(
|
|
73
|
+
state: CachedBeaconStateGloas,
|
|
74
|
+
envelope: gloas.ExecutionPayloadEnvelope
|
|
75
|
+
): void {
|
|
76
|
+
const payload = envelope.payload;
|
|
77
|
+
|
|
78
|
+
if (byteArrayEquals(state.latestBlockHeader.stateRoot, ssz.Root.defaultValue())) {
|
|
79
|
+
const previousStateRoot = state.hashTreeRoot();
|
|
80
|
+
state.latestBlockHeader.stateRoot = previousStateRoot;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Verify consistency with the beacon block
|
|
84
|
+
if (!byteArrayEquals(envelope.beaconBlockRoot, state.latestBlockHeader.hashTreeRoot())) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Envelope's block is not the latest block header envelope=${toRootHex(envelope.beaconBlockRoot)} latestBlockHeader=${toRootHex(state.latestBlockHeader.hashTreeRoot())}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Verify consistency with the beacon block
|
|
91
|
+
if (envelope.slot !== state.slot) {
|
|
92
|
+
throw new Error(`Slot mismatch between envelope and state envelope=${envelope.slot} state=${state.slot}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const committedBid = state.latestExecutionPayloadBid;
|
|
96
|
+
// Verify consistency with the committed bid
|
|
97
|
+
if (envelope.builderIndex !== committedBid.builderIndex) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Builder index mismatch between envelope and committed bid envelope=${envelope.builderIndex} committedBid=${committedBid.builderIndex}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Verify consistency with the committed bid
|
|
104
|
+
const envelopeKzgRoot = ssz.deneb.BlobKzgCommitments.hashTreeRoot(envelope.blobKzgCommitments);
|
|
105
|
+
if (!byteArrayEquals(committedBid.blobKzgCommitmentsRoot, envelopeKzgRoot)) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Kzg commitment root mismatch between envelope and committed bid envelope=${toRootHex(envelopeKzgRoot)} committedBid=${toRootHex(committedBid.blobKzgCommitmentsRoot)}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Verify the withdrawals root
|
|
112
|
+
const envelopeWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(envelope.payload.withdrawals);
|
|
113
|
+
if (!byteArrayEquals(state.latestWithdrawalsRoot, envelopeWithdrawalsRoot)) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Withdrawals root mismatch between envelope and latest withdrawals root envelope=${toRootHex(envelopeWithdrawalsRoot)} latestWithdrawalRoot=${toRootHex(state.latestWithdrawalsRoot)}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Verify the gas_limit
|
|
120
|
+
if (Number(committedBid.gasLimit) !== payload.gasLimit) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Gas limit mismatch between envelope's payload and committed bid envelope=${payload.gasLimit} committedBid=${Number(committedBid.gasLimit)}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Verify the block hash
|
|
127
|
+
if (!byteArrayEquals(committedBid.blockHash, payload.blockHash)) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`Block hash mismatch between envelope's payload and committed bid envelope=${toRootHex(payload.blockHash)} committedBid=${toRootHex(committedBid.blockHash)}`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Verify consistency of the parent hash with respect to the previous execution payload
|
|
134
|
+
if (!byteArrayEquals(payload.parentHash, state.latestBlockHash)) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Parent hash mismatch between envelope's payload and state envelope=${toRootHex(payload.parentHash)} state=${toRootHex(state.latestBlockHash)}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Verify prev_randao matches committed bid
|
|
141
|
+
if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Verify timestamp
|
|
148
|
+
if (payload.timestamp !== computeTimeAtSlot(state.config, state.slot, state.genesisTime)) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Timestamp mismatch between envelope's payload and state envelope=${payload.timestamp} state=${computeTimeAtSlot(state.config, state.slot, state.genesisTime)}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Verify commitments are under limit
|
|
155
|
+
const maxBlobsPerBlock = state.config.getMaxBlobsPerBlock(state.epochCtx.epoch);
|
|
156
|
+
if (envelope.blobKzgCommitments.length > maxBlobsPerBlock) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Kzg commitments exceed limit commitment.length=${envelope.blobKzgCommitments.length} limit=${maxBlobsPerBlock}`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Skipped: Verify the execution payload is valid
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function verifyExecutionPayloadEnvelopeSignature(
|
|
166
|
+
state: CachedBeaconStateGloas,
|
|
167
|
+
pubkey: Uint8Array,
|
|
168
|
+
signedEnvelope: gloas.SignedExecutionPayloadEnvelope
|
|
169
|
+
): boolean {
|
|
170
|
+
const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_BUILDER);
|
|
171
|
+
const signingRoot = computeSigningRoot(ssz.gloas.ExecutionPayloadEnvelope, signedEnvelope.message, domain);
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const publicKey = PublicKey.fromBytes(pubkey);
|
|
175
|
+
const signature = Signature.fromBytes(signedEnvelope.signature, true);
|
|
176
|
+
|
|
177
|
+
return verify(signingRoot, publicKey, signature);
|
|
178
|
+
} catch (_e) {
|
|
179
|
+
return false; // Catch all BLS errors: failed key validation, failed signature validation, invalid signature
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import {ForkSeq} from "@lodestar/params";
|
|
2
|
-
import {BeaconBlockBody, capella, electra} from "@lodestar/types";
|
|
2
|
+
import {BeaconBlockBody, capella, electra, gloas} from "@lodestar/types";
|
|
3
3
|
import {BeaconStateTransitionMetrics} from "../metrics.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CachedBeaconStateAllForks,
|
|
6
|
+
CachedBeaconStateCapella,
|
|
7
|
+
CachedBeaconStateElectra,
|
|
8
|
+
CachedBeaconStateGloas,
|
|
9
|
+
} from "../types.js";
|
|
5
10
|
import {getEth1DepositCount} from "../util/deposit.js";
|
|
6
11
|
import {processAttestations} from "./processAttestations.js";
|
|
7
12
|
import {processAttesterSlashing} from "./processAttesterSlashing.js";
|
|
@@ -9,6 +14,7 @@ import {processBlsToExecutionChange} from "./processBlsToExecutionChange.js";
|
|
|
9
14
|
import {processConsolidationRequest} from "./processConsolidationRequest.js";
|
|
10
15
|
import {processDeposit} from "./processDeposit.js";
|
|
11
16
|
import {processDepositRequest} from "./processDepositRequest.js";
|
|
17
|
+
import {processPayloadAttestation} from "./processPayloadAttestation.ts";
|
|
12
18
|
import {processProposerSlashing} from "./processProposerSlashing.js";
|
|
13
19
|
import {processVoluntaryExit} from "./processVoluntaryExit.js";
|
|
14
20
|
import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
|
|
@@ -64,7 +70,7 @@ export function processOperations(
|
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
if (fork >= ForkSeq.electra) {
|
|
73
|
+
if (fork >= ForkSeq.electra && fork < ForkSeq.gloas) {
|
|
68
74
|
const stateElectra = state as CachedBeaconStateElectra;
|
|
69
75
|
const bodyElectra = body as electra.BeaconBlockBody;
|
|
70
76
|
|
|
@@ -77,7 +83,13 @@ export function processOperations(
|
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
for (const elConsolidationRequest of bodyElectra.executionRequests.consolidations) {
|
|
80
|
-
processConsolidationRequest(stateElectra, elConsolidationRequest);
|
|
86
|
+
processConsolidationRequest(fork, stateElectra, elConsolidationRequest);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (fork >= ForkSeq.gloas) {
|
|
91
|
+
for (const payloadAttestation of (body as gloas.BeaconBlockBody).payloadAttestations) {
|
|
92
|
+
processPayloadAttestation(state as CachedBeaconStateGloas, payloadAttestation);
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {byteArrayEquals} from "@chainsafe/ssz";
|
|
2
|
+
import {gloas} from "@lodestar/types";
|
|
3
|
+
import {CachedBeaconStateGloas} from "../types.ts";
|
|
4
|
+
import {isValidIndexedPayloadAttestation} from "./isValidIndexedPayloadAttestation.ts";
|
|
5
|
+
|
|
6
|
+
export function processPayloadAttestation(
|
|
7
|
+
state: CachedBeaconStateGloas,
|
|
8
|
+
payloadAttestation: gloas.PayloadAttestation
|
|
9
|
+
): void {
|
|
10
|
+
const data = payloadAttestation.data;
|
|
11
|
+
|
|
12
|
+
if (!byteArrayEquals(data.beaconBlockRoot, state.latestBlockHeader.parentRoot)) {
|
|
13
|
+
throw Error("Payload attestation is referring to the wrong block");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (data.slot + 1 !== state.slot) {
|
|
17
|
+
throw Error("Payload attestation is not from previous slot");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const indexedPayloadAttestation = state.epochCtx.getIndexedPayloadAttestation(data.slot, payloadAttestation);
|
|
21
|
+
|
|
22
|
+
if (!isValidIndexedPayloadAttestation(state, indexedPayloadAttestation, true)) {
|
|
23
|
+
throw Error("Invalid payload attestation");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {ForkSeq} from "@lodestar/params";
|
|
1
|
+
import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
2
|
import {phase0, ssz} from "@lodestar/types";
|
|
3
3
|
import {getProposerSlashingSignatureSets} from "../signatureSets/index.js";
|
|
4
|
-
import {CachedBeaconStateAllForks} from "../types.js";
|
|
5
|
-
import {isSlashableValidator} from "../util/index.js";
|
|
4
|
+
import {CachedBeaconStateAllForks, CachedBeaconStateGloas} from "../types.js";
|
|
5
|
+
import {computeEpochAtSlot, isSlashableValidator} from "../util/index.js";
|
|
6
6
|
import {verifySignatureSet} from "../util/signatureSets.js";
|
|
7
7
|
import {slashValidator} from "./slashValidator.js";
|
|
8
8
|
|
|
@@ -20,6 +20,27 @@ export function processProposerSlashing(
|
|
|
20
20
|
): void {
|
|
21
21
|
assertValidProposerSlashing(state, proposerSlashing, verifySignatures);
|
|
22
22
|
|
|
23
|
+
if (fork >= ForkSeq.gloas) {
|
|
24
|
+
const slot = Number(proposerSlashing.signedHeader1.message.slot);
|
|
25
|
+
const proposalEpoch = computeEpochAtSlot(slot);
|
|
26
|
+
const currentEpoch = state.epochCtx.epoch;
|
|
27
|
+
const previousEpoch = currentEpoch - 1;
|
|
28
|
+
|
|
29
|
+
const paymentIndex =
|
|
30
|
+
proposalEpoch === currentEpoch
|
|
31
|
+
? SLOTS_PER_EPOCH + (slot % SLOTS_PER_EPOCH)
|
|
32
|
+
: proposalEpoch === previousEpoch
|
|
33
|
+
? slot % SLOTS_PER_EPOCH
|
|
34
|
+
: undefined;
|
|
35
|
+
|
|
36
|
+
if (paymentIndex !== undefined) {
|
|
37
|
+
(state as CachedBeaconStateGloas).builderPendingPayments.set(
|
|
38
|
+
paymentIndex,
|
|
39
|
+
ssz.gloas.BuilderPendingPayment.defaultViewDU()
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
23
44
|
slashValidator(fork, state, proposerSlashing.signedHeader1.message.proposerIndex);
|
|
24
45
|
}
|
|
25
46
|
|
|
@@ -56,7 +77,7 @@ export function assertValidProposerSlashing(
|
|
|
56
77
|
|
|
57
78
|
// verify signatures
|
|
58
79
|
if (verifySignatures) {
|
|
59
|
-
const signatureSets = getProposerSlashingSignatureSets(state, proposerSlashing);
|
|
80
|
+
const signatureSets = getProposerSlashingSignatureSets(state.epochCtx.index2pubkey, state, proposerSlashing);
|
|
60
81
|
for (let i = 0; i < signatureSets.length; i++) {
|
|
61
82
|
if (!verifySignatureSet(signatureSets[i])) {
|
|
62
83
|
throw new Error(`ProposerSlashing header${i + 1} signature invalid`);
|
|
@@ -17,7 +17,7 @@ export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlo
|
|
|
17
17
|
const randaoReveal = block.body.randaoReveal;
|
|
18
18
|
|
|
19
19
|
// verify RANDAO reveal
|
|
20
|
-
if (verifySignature && !verifyRandaoSignature(state, block)) {
|
|
20
|
+
if (verifySignature && !verifyRandaoSignature(epochCtx.index2pubkey, state, block)) {
|
|
21
21
|
throw new Error("RANDAO reveal is an invalid signature");
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {byteArrayEquals} from "@chainsafe/ssz";
|
|
2
2
|
import {DOMAIN_SYNC_COMMITTEE, SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
|
3
3
|
import {altair, ssz} from "@lodestar/types";
|
|
4
|
+
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
|
4
5
|
import {G2_POINT_AT_INFINITY} from "../constants/index.js";
|
|
5
6
|
import {CachedBeaconStateAllForks} from "../types.js";
|
|
6
7
|
import {
|
|
@@ -23,7 +24,7 @@ export function processSyncAggregate(
|
|
|
23
24
|
if (verifySignatures) {
|
|
24
25
|
// This is to conform to the spec - we want the signature to be verified
|
|
25
26
|
const participantIndices = block.body.syncAggregate.syncCommitteeBits.intersectValues(committeeIndices);
|
|
26
|
-
const signatureSet = getSyncCommitteeSignatureSet(state, block, participantIndices);
|
|
27
|
+
const signatureSet = getSyncCommitteeSignatureSet(state.epochCtx.index2pubkey, state, block, participantIndices);
|
|
27
28
|
// When there's no participation we consider the signature valid and just ignore i
|
|
28
29
|
if (signatureSet !== null && !verifySignatureSet(signatureSet)) {
|
|
29
30
|
throw Error("Sync committee signature invalid");
|
|
@@ -63,12 +64,12 @@ export function processSyncAggregate(
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
export function getSyncCommitteeSignatureSet(
|
|
67
|
+
index2pubkey: Index2PubkeyCache,
|
|
66
68
|
state: CachedBeaconStateAllForks,
|
|
67
69
|
block: altair.BeaconBlock,
|
|
68
70
|
/** Optional parameter to prevent computing it twice */
|
|
69
71
|
participantIndices?: number[]
|
|
70
72
|
): ISignatureSet | null {
|
|
71
|
-
const {epochCtx} = state;
|
|
72
73
|
const {syncAggregate} = block.body;
|
|
73
74
|
const signature = syncAggregate.syncCommitteeSignature;
|
|
74
75
|
|
|
@@ -110,7 +111,7 @@ export function getSyncCommitteeSignatureSet(
|
|
|
110
111
|
|
|
111
112
|
return {
|
|
112
113
|
type: SignatureSetType.aggregate,
|
|
113
|
-
pubkeys: participantIndices.map((i) =>
|
|
114
|
+
pubkeys: participantIndices.map((i) => index2pubkey[i]),
|
|
114
115
|
signingRoot: computeSigningRoot(ssz.Root, rootSigned, domain),
|
|
115
116
|
signature,
|
|
116
117
|
};
|
|
@@ -69,12 +69,12 @@ export function getVoluntaryExitValidity(
|
|
|
69
69
|
// only exit validator if it has no pending withdrawals in the queue
|
|
70
70
|
if (
|
|
71
71
|
fork >= ForkSeq.electra &&
|
|
72
|
-
getPendingBalanceToWithdraw(state as CachedBeaconStateElectra, voluntaryExit.validatorIndex) !== 0
|
|
72
|
+
getPendingBalanceToWithdraw(fork, state as CachedBeaconStateElectra, voluntaryExit.validatorIndex) !== 0
|
|
73
73
|
) {
|
|
74
74
|
return VoluntaryExitValidity.pendingWithdrawals;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
if (verifySignature && !verifyVoluntaryExitSignature(state, signedVoluntaryExit)) {
|
|
77
|
+
if (verifySignature && !verifyVoluntaryExitSignature(epochCtx.index2pubkey, state, signedVoluntaryExit)) {
|
|
78
78
|
return VoluntaryExitValidity.invalidSignature;
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "@lodestar/params";
|
|
8
8
|
import {electra, phase0, ssz} from "@lodestar/types";
|
|
9
9
|
import {toHex} from "@lodestar/utils";
|
|
10
|
-
import {CachedBeaconStateElectra} from "../types.js";
|
|
10
|
+
import {CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
|
|
11
11
|
import {hasCompoundingWithdrawalCredential, hasExecutionWithdrawalCredential} from "../util/electra.js";
|
|
12
12
|
import {computeExitEpochAndUpdateChurn} from "../util/epoch.js";
|
|
13
13
|
import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/validator.js";
|
|
@@ -15,7 +15,7 @@ import {initiateValidatorExit} from "./initiateValidatorExit.js";
|
|
|
15
15
|
|
|
16
16
|
export function processWithdrawalRequest(
|
|
17
17
|
fork: ForkSeq,
|
|
18
|
-
state: CachedBeaconStateElectra,
|
|
18
|
+
state: CachedBeaconStateElectra | CachedBeaconStateGloas,
|
|
19
19
|
withdrawalRequest: electra.WithdrawalRequest
|
|
20
20
|
): void {
|
|
21
21
|
const amount = Number(withdrawalRequest.amount);
|
|
@@ -42,7 +42,7 @@ export function processWithdrawalRequest(
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// TODO Electra: Consider caching pendingPartialWithdrawals
|
|
45
|
-
const pendingBalanceToWithdraw = getPendingBalanceToWithdraw(state, validatorIndex);
|
|
45
|
+
const pendingBalanceToWithdraw = getPendingBalanceToWithdraw(fork, state, validatorIndex);
|
|
46
46
|
const validatorBalance = state.balances.get(validatorIndex);
|
|
47
47
|
|
|
48
48
|
if (isFullExitRequest) {
|
|
@@ -81,7 +81,7 @@ export function processWithdrawalRequest(
|
|
|
81
81
|
function isValidatorEligibleForWithdrawOrExit(
|
|
82
82
|
validator: phase0.Validator,
|
|
83
83
|
sourceAddress: Uint8Array,
|
|
84
|
-
state: CachedBeaconStateElectra
|
|
84
|
+
state: CachedBeaconStateElectra | CachedBeaconStateGloas
|
|
85
85
|
): boolean {
|
|
86
86
|
const {withdrawalCredentials} = validator;
|
|
87
87
|
const addressStr = toHex(withdrawalCredentials.subarray(12));
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
} from "@lodestar/params";
|
|
11
11
|
import {ValidatorIndex, capella, ssz} from "@lodestar/types";
|
|
12
12
|
import {MapDef, toRootHex} from "@lodestar/utils";
|
|
13
|
-
import {CachedBeaconStateCapella, CachedBeaconStateElectra} from "../types.js";
|
|
13
|
+
import {CachedBeaconStateCapella, CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
|
|
14
|
+
import {isBuilderPaymentWithdrawable, isParentBlockFull} from "../util/gloas.ts";
|
|
14
15
|
import {
|
|
15
16
|
decreaseBalance,
|
|
16
17
|
getMaxEffectiveBalance,
|
|
@@ -21,31 +22,48 @@ import {
|
|
|
21
22
|
|
|
22
23
|
export function processWithdrawals(
|
|
23
24
|
fork: ForkSeq,
|
|
24
|
-
state: CachedBeaconStateCapella | CachedBeaconStateElectra,
|
|
25
|
-
payload
|
|
25
|
+
state: CachedBeaconStateCapella | CachedBeaconStateElectra | CachedBeaconStateGloas,
|
|
26
|
+
payload?: capella.FullOrBlindedExecutionPayload
|
|
26
27
|
): void {
|
|
28
|
+
// Return early if the parent block is empty
|
|
29
|
+
if (fork >= ForkSeq.gloas && !isParentBlockFull(state as CachedBeaconStateGloas)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
// processedPartialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
|
|
28
|
-
|
|
34
|
+
// processedBuilderWithdrawalsCount is withdrawals coming from builder payment since gloas (EIP-7732)
|
|
35
|
+
const {
|
|
36
|
+
withdrawals: expectedWithdrawals,
|
|
37
|
+
processedPartialWithdrawalsCount,
|
|
38
|
+
processedBuilderWithdrawalsCount,
|
|
39
|
+
} = getExpectedWithdrawals(fork, state);
|
|
29
40
|
const numWithdrawals = expectedWithdrawals.length;
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
throw Error(
|
|
36
|
-
`Invalid withdrawalsRoot of executionPayloadHeader, expected=${toRootHex(
|
|
37
|
-
expectedWithdrawalsRoot
|
|
38
|
-
)}, actual=${toRootHex(actualWithdrawalsRoot)}`
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
if (expectedWithdrawals.length !== payload.withdrawals.length) {
|
|
43
|
-
throw Error(`Invalid withdrawals length expected=${numWithdrawals} actual=${payload.withdrawals.length}`);
|
|
42
|
+
// After gloas, withdrawals are verified later in processExecutionPayloadEnvelope
|
|
43
|
+
if (fork < ForkSeq.gloas) {
|
|
44
|
+
if (payload === undefined) {
|
|
45
|
+
throw Error("payload is required for pre-gloas processWithdrawals");
|
|
44
46
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
|
|
48
|
+
if (isCapellaPayloadHeader(payload)) {
|
|
49
|
+
const expectedWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(expectedWithdrawals);
|
|
50
|
+
const actualWithdrawalsRoot = payload.withdrawalsRoot;
|
|
51
|
+
if (!byteArrayEquals(expectedWithdrawalsRoot, actualWithdrawalsRoot)) {
|
|
52
|
+
throw Error(
|
|
53
|
+
`Invalid withdrawalsRoot of executionPayloadHeader, expected=${toRootHex(
|
|
54
|
+
expectedWithdrawalsRoot
|
|
55
|
+
)}, actual=${toRootHex(actualWithdrawalsRoot)}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
if (expectedWithdrawals.length !== payload.withdrawals.length) {
|
|
60
|
+
throw Error(`Invalid withdrawals length expected=${numWithdrawals} actual=${payload.withdrawals.length}`);
|
|
61
|
+
}
|
|
62
|
+
for (let i = 0; i < numWithdrawals; i++) {
|
|
63
|
+
const withdrawal = expectedWithdrawals[i];
|
|
64
|
+
if (!ssz.capella.Withdrawal.equals(withdrawal, payload.withdrawals[i])) {
|
|
65
|
+
throw Error(`Withdrawal mismatch at index=${i}`);
|
|
66
|
+
}
|
|
49
67
|
}
|
|
50
68
|
}
|
|
51
69
|
}
|
|
@@ -62,6 +80,24 @@ export function processWithdrawals(
|
|
|
62
80
|
);
|
|
63
81
|
}
|
|
64
82
|
|
|
83
|
+
if (fork >= ForkSeq.gloas) {
|
|
84
|
+
const stateGloas = state as CachedBeaconStateGloas;
|
|
85
|
+
stateGloas.latestWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(expectedWithdrawals);
|
|
86
|
+
|
|
87
|
+
const unprocessedWithdrawals = stateGloas.builderPendingWithdrawals
|
|
88
|
+
.getAllReadonly()
|
|
89
|
+
.slice(0, processedBuilderWithdrawalsCount)
|
|
90
|
+
.filter((w) => !isBuilderPaymentWithdrawable(stateGloas, w));
|
|
91
|
+
const remainingWithdrawals = stateGloas.builderPendingWithdrawals
|
|
92
|
+
.sliceFrom(processedBuilderWithdrawalsCount)
|
|
93
|
+
.getAllReadonly();
|
|
94
|
+
|
|
95
|
+
stateGloas.builderPendingWithdrawals = ssz.gloas.BeaconState.fields.builderPendingWithdrawals.toViewDU([
|
|
96
|
+
...unprocessedWithdrawals,
|
|
97
|
+
...remainingWithdrawals,
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
|
|
65
101
|
// Update the nextWithdrawalIndex
|
|
66
102
|
const latestWithdrawal = expectedWithdrawals.at(-1);
|
|
67
103
|
if (latestWithdrawal) {
|
|
@@ -82,11 +118,12 @@ export function processWithdrawals(
|
|
|
82
118
|
|
|
83
119
|
export function getExpectedWithdrawals(
|
|
84
120
|
fork: ForkSeq,
|
|
85
|
-
state: CachedBeaconStateCapella | CachedBeaconStateElectra
|
|
121
|
+
state: CachedBeaconStateCapella | CachedBeaconStateElectra | CachedBeaconStateGloas
|
|
86
122
|
): {
|
|
87
123
|
withdrawals: capella.Withdrawal[];
|
|
88
124
|
sampledValidators: number;
|
|
89
125
|
processedPartialWithdrawalsCount: number;
|
|
126
|
+
processedBuilderWithdrawalsCount: number;
|
|
90
127
|
} {
|
|
91
128
|
if (fork < ForkSeq.capella) {
|
|
92
129
|
throw new Error(`getExpectedWithdrawals not supported at forkSeq=${fork} < ForkSeq.capella`);
|
|
@@ -99,17 +136,71 @@ export function getExpectedWithdrawals(
|
|
|
99
136
|
const withdrawals: capella.Withdrawal[] = [];
|
|
100
137
|
const withdrawnBalances = new MapDef<ValidatorIndex, number>(() => 0);
|
|
101
138
|
const isPostElectra = fork >= ForkSeq.electra;
|
|
139
|
+
const isPostGloas = fork >= ForkSeq.gloas;
|
|
102
140
|
// partialWithdrawalsCount is withdrawals coming from EL since electra (EIP-7002)
|
|
103
141
|
let processedPartialWithdrawalsCount = 0;
|
|
142
|
+
// builderWithdrawalsCount is withdrawals coming from builder payments since Gloas (EIP-7732)
|
|
143
|
+
let processedBuilderWithdrawalsCount = 0;
|
|
144
|
+
|
|
145
|
+
if (isPostGloas) {
|
|
146
|
+
const stateGloas = state as CachedBeaconStateGloas;
|
|
147
|
+
|
|
148
|
+
const allBuilderPendingWithdrawals =
|
|
149
|
+
stateGloas.builderPendingWithdrawals.length <= MAX_WITHDRAWALS_PER_PAYLOAD
|
|
150
|
+
? stateGloas.builderPendingWithdrawals.getAllReadonly()
|
|
151
|
+
: null;
|
|
152
|
+
|
|
153
|
+
for (let i = 0; i < stateGloas.builderPendingWithdrawals.length; i++) {
|
|
154
|
+
const withdrawal = allBuilderPendingWithdrawals
|
|
155
|
+
? allBuilderPendingWithdrawals[i]
|
|
156
|
+
: stateGloas.builderPendingWithdrawals.getReadonly(i);
|
|
157
|
+
|
|
158
|
+
if (withdrawal.withdrawableEpoch > epoch || withdrawals.length + 1 === MAX_WITHDRAWALS_PER_PAYLOAD) {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (isBuilderPaymentWithdrawable(stateGloas, withdrawal)) {
|
|
163
|
+
const totalWithdrawn = withdrawnBalances.getOrDefault(withdrawal.builderIndex);
|
|
164
|
+
const balance = state.balances.get(withdrawal.builderIndex) - totalWithdrawn;
|
|
165
|
+
const builder = state.validators.get(withdrawal.builderIndex);
|
|
166
|
+
|
|
167
|
+
let withdrawableBalance = 0;
|
|
168
|
+
|
|
169
|
+
if (builder.slashed) {
|
|
170
|
+
withdrawableBalance = balance < withdrawal.amount ? balance : withdrawal.amount;
|
|
171
|
+
} else if (balance > MIN_ACTIVATION_BALANCE) {
|
|
172
|
+
withdrawableBalance =
|
|
173
|
+
balance - MIN_ACTIVATION_BALANCE < withdrawal.amount ? balance - MIN_ACTIVATION_BALANCE : withdrawal.amount;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (withdrawableBalance > 0) {
|
|
177
|
+
withdrawals.push({
|
|
178
|
+
index: withdrawalIndex,
|
|
179
|
+
validatorIndex: withdrawal.builderIndex,
|
|
180
|
+
address: withdrawal.feeRecipient,
|
|
181
|
+
amount: BigInt(withdrawableBalance),
|
|
182
|
+
});
|
|
183
|
+
withdrawalIndex++;
|
|
184
|
+
withdrawnBalances.set(withdrawal.builderIndex, totalWithdrawn + withdrawableBalance);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
processedBuilderWithdrawalsCount++;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
104
190
|
|
|
105
191
|
if (isPostElectra) {
|
|
192
|
+
// In pre-gloas, partialWithdrawalBound == MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP
|
|
193
|
+
const partialWithdrawalBound = Math.min(
|
|
194
|
+
withdrawals.length + MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
|
|
195
|
+
MAX_WITHDRAWALS_PER_PAYLOAD - 1
|
|
196
|
+
);
|
|
106
197
|
const stateElectra = state as CachedBeaconStateElectra;
|
|
107
198
|
|
|
108
199
|
// MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP = 8, PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728 so we should only call getAllReadonly() if it makes sense
|
|
109
200
|
// pendingPartialWithdrawals comes from EIP-7002 smart contract where it takes fee so it's more likely than not validator is in correct condition to withdraw
|
|
110
201
|
// also we may break early if withdrawableEpoch > epoch
|
|
111
202
|
const allPendingPartialWithdrawals =
|
|
112
|
-
stateElectra.pendingPartialWithdrawals.length <=
|
|
203
|
+
stateElectra.pendingPartialWithdrawals.length <= partialWithdrawalBound
|
|
113
204
|
? stateElectra.pendingPartialWithdrawals.getAllReadonly()
|
|
114
205
|
: null;
|
|
115
206
|
|
|
@@ -118,7 +209,7 @@ export function getExpectedWithdrawals(
|
|
|
118
209
|
const withdrawal = allPendingPartialWithdrawals
|
|
119
210
|
? allPendingPartialWithdrawals[i]
|
|
120
211
|
: stateElectra.pendingPartialWithdrawals.getReadonly(i);
|
|
121
|
-
if (withdrawal.withdrawableEpoch > epoch || withdrawals.length ===
|
|
212
|
+
if (withdrawal.withdrawableEpoch > epoch || withdrawals.length === partialWithdrawalBound) {
|
|
122
213
|
break;
|
|
123
214
|
}
|
|
124
215
|
|
|
@@ -147,11 +238,11 @@ export function getExpectedWithdrawals(
|
|
|
147
238
|
}
|
|
148
239
|
}
|
|
149
240
|
|
|
150
|
-
const
|
|
241
|
+
const withdrawalBound = Math.min(validators.length, MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP);
|
|
151
242
|
let n = 0;
|
|
152
243
|
// Just run a bounded loop max iterating over all withdrawals
|
|
153
244
|
// however breaks out once we have MAX_WITHDRAWALS_PER_PAYLOAD
|
|
154
|
-
for (n = 0; n <
|
|
245
|
+
for (n = 0; n < withdrawalBound; n++) {
|
|
155
246
|
// Get next validator in turn
|
|
156
247
|
const validatorIndex = (nextWithdrawalValidatorIndex + n) % validators.length;
|
|
157
248
|
|
|
@@ -203,5 +294,5 @@ export function getExpectedWithdrawals(
|
|
|
203
294
|
}
|
|
204
295
|
}
|
|
205
296
|
|
|
206
|
-
return {withdrawals, sampledValidators: n, processedPartialWithdrawalsCount};
|
|
297
|
+
return {withdrawals, sampledValidators: n, processedPartialWithdrawalsCount, processedBuilderWithdrawalsCount};
|
|
207
298
|
}
|