@lodestar/state-transition 1.44.0-dev.7418ddd9f7 → 1.44.0-dev.762dd5f186
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 +2 -0
- package/lib/block/index.d.ts.map +1 -1
- package/lib/block/index.js +3 -1
- package/lib/block/index.js.map +1 -1
- package/lib/block/processBuilderDepositRequest.d.ts +10 -0
- package/lib/block/processBuilderDepositRequest.d.ts.map +1 -0
- package/lib/block/processBuilderDepositRequest.js +27 -0
- package/lib/block/processBuilderDepositRequest.js.map +1 -0
- package/lib/block/processBuilderExitRequest.d.ts +13 -0
- package/lib/block/processBuilderExitRequest.d.ts.map +1 -0
- package/lib/block/processBuilderExitRequest.js +29 -0
- package/lib/block/processBuilderExitRequest.js.map +1 -0
- package/lib/block/processDepositRequest.d.ts +2 -8
- package/lib/block/processDepositRequest.d.ts.map +1 -1
- package/lib/block/processDepositRequest.js +3 -98
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts +2 -3
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
- package/lib/block/processExecutionPayloadBid.js +16 -8
- package/lib/block/processExecutionPayloadBid.js.map +1 -1
- package/lib/block/processOperations.d.ts.map +1 -1
- package/lib/block/processOperations.js +3 -2
- package/lib/block/processOperations.js.map +1 -1
- package/lib/block/processParentExecutionPayload.d.ts +2 -2
- package/lib/block/processParentExecutionPayload.d.ts.map +1 -1
- package/lib/block/processParentExecutionPayload.js +16 -8
- package/lib/block/processParentExecutionPayload.js.map +1 -1
- package/lib/block/processProposerSlashing.d.ts.map +1 -1
- package/lib/block/processProposerSlashing.js +9 -1
- package/lib/block/processProposerSlashing.js.map +1 -1
- package/lib/block/processVoluntaryExit.d.ts +1 -1
- package/lib/block/processVoluntaryExit.d.ts.map +1 -1
- package/lib/block/processVoluntaryExit.js +1 -35
- package/lib/block/processVoluntaryExit.js.map +1 -1
- package/lib/epoch/processPendingDeposits.d.ts.map +1 -1
- package/lib/epoch/processPendingDeposits.js +4 -3
- package/lib/epoch/processPendingDeposits.js.map +1 -1
- package/lib/epoch/processProposerLookahead.d.ts.map +1 -1
- package/lib/epoch/processProposerLookahead.js +7 -2
- package/lib/epoch/processProposerLookahead.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +35 -29
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +4 -2
- package/lib/stateView/beaconStateView.d.ts.map +1 -1
- package/lib/stateView/beaconStateView.js +6 -0
- package/lib/stateView/beaconStateView.js.map +1 -1
- package/lib/stateView/interface.d.ts +29 -2
- package/lib/stateView/interface.d.ts.map +1 -1
- package/lib/stateView/interface.js.map +1 -1
- package/lib/stateView/nativeBeaconStateView.d.ts +230 -0
- package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -0
- package/lib/stateView/nativeBeaconStateView.js +717 -0
- package/lib/stateView/nativeBeaconStateView.js.map +1 -0
- package/lib/stateView/stateViewFactory.d.ts.map +1 -1
- package/lib/stateView/stateViewFactory.js +2 -0
- package/lib/stateView/stateViewFactory.js.map +1 -1
- package/lib/util/epochShuffling.js +1 -1
- package/lib/util/epochShuffling.js.map +1 -1
- package/lib/util/gloas.d.ts +17 -0
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +59 -1
- package/lib/util/gloas.js.map +1 -1
- package/lib/util/shuffling.d.ts +2 -2
- package/lib/util/shuffling.d.ts.map +1 -1
- package/lib/util/shuffling.js +4 -3
- package/lib/util/shuffling.js.map +1 -1
- package/package.json +7 -8
- package/src/block/index.ts +6 -1
- package/src/block/processBuilderDepositRequest.ts +43 -0
- package/src/block/processBuilderExitRequest.ts +39 -0
- package/src/block/processDepositRequest.ts +4 -124
- package/src/block/processExecutionPayloadBid.ts +24 -9
- package/src/block/processOperations.ts +3 -2
- package/src/block/processParentExecutionPayload.ts +23 -11
- package/src/block/processProposerSlashing.ts +9 -4
- package/src/block/processVoluntaryExit.ts +2 -61
- package/src/epoch/processPendingDeposits.ts +2 -0
- package/src/epoch/processProposerLookahead.ts +8 -1
- package/src/slot/upgradeStateToGloas.ts +49 -33
- package/src/stateView/beaconStateView.ts +10 -1
- package/src/stateView/interface.ts +33 -1
- package/src/stateView/nativeBeaconStateView.ts +915 -0
- package/src/stateView/stateViewFactory.ts +2 -0
- package/src/util/epochShuffling.ts +1 -1
- package/src/util/gloas.ts +76 -1
- package/src/util/shuffling.ts +4 -3
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import {PublicKey, Signature, verify} from "@chainsafe/blst";
|
|
2
|
-
import {BUILDER_INDEX_SELF_BUILD,
|
|
3
|
-
import {
|
|
2
|
+
import {BUILDER_INDEX_SELF_BUILD, GENESIS_SLOT, PAYLOAD_BUILDER_VERSION, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
3
|
+
import {gloas, ssz} from "@lodestar/types";
|
|
4
4
|
import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
|
|
5
5
|
import {G2_POINT_AT_INFINITY} from "../constants/constants.js";
|
|
6
6
|
import {getExecutionPayloadBidSigningRoot} from "../signatureSets/executionPayloadBid.js";
|
|
7
7
|
import {CachedBeaconStateGloas} from "../types.js";
|
|
8
8
|
import {canBuilderCoverBid, isActiveBuilder} from "../util/gloas.js";
|
|
9
|
-
import {getCurrentEpoch, getRandaoMix} from "../util/index.js";
|
|
9
|
+
import {getBlockRootAtSlot, getCurrentEpoch, getRandaoMix} from "../util/index.js";
|
|
10
10
|
|
|
11
|
-
export function processExecutionPayloadBid(
|
|
12
|
-
|
|
11
|
+
export function processExecutionPayloadBid(
|
|
12
|
+
state: CachedBeaconStateGloas,
|
|
13
|
+
signedBid: gloas.SignedExecutionPayloadBid
|
|
14
|
+
): void {
|
|
13
15
|
const bid = signedBid.message;
|
|
14
16
|
const {builderIndex, value: amount} = bid;
|
|
15
17
|
|
|
@@ -31,6 +33,13 @@ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block:
|
|
|
31
33
|
throw Error(`Invalid execution payload bid: builder ${builderIndex} is not active`);
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
// Verify that the builder is a payload builder
|
|
37
|
+
if (builder.version !== PAYLOAD_BUILDER_VERSION) {
|
|
38
|
+
throw Error(
|
|
39
|
+
`Invalid execution payload bid: builder ${builderIndex} version ${builder.version} != ${PAYLOAD_BUILDER_VERSION}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
// Verify that the builder has funds to cover the bid
|
|
35
44
|
if (!canBuilderCoverBid(state, builderIndex, amount)) {
|
|
36
45
|
throw Error(`Invalid execution payload bid: builder ${builderIndex} has insufficient balance`);
|
|
@@ -42,8 +51,12 @@ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block:
|
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
if (bid.slot !==
|
|
46
|
-
throw Error(`Bid slot ${bid.slot} does not match
|
|
54
|
+
if (bid.slot !== state.slot) {
|
|
55
|
+
throw Error(`Bid slot ${bid.slot} does not match state slot ${state.slot}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (state.slot <= GENESIS_SLOT) {
|
|
59
|
+
throw Error(`Execution payload bid not allowed at genesis slot ${state.slot}`);
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
if (!byteArrayEquals(bid.parentBlockHash, state.latestBlockHash)) {
|
|
@@ -52,9 +65,10 @@ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block:
|
|
|
52
65
|
);
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
|
|
68
|
+
const parentBlockRoot = getBlockRootAtSlot(state, state.slot - 1);
|
|
69
|
+
if (!byteArrayEquals(bid.parentBlockRoot, parentBlockRoot)) {
|
|
56
70
|
throw Error(
|
|
57
|
-
`Parent block root ${toRootHex(bid.parentBlockRoot)} of bid does not match
|
|
71
|
+
`Parent block root ${toRootHex(bid.parentBlockRoot)} of bid does not match state's parent block root ${toRootHex(parentBlockRoot)}`
|
|
58
72
|
);
|
|
59
73
|
}
|
|
60
74
|
|
|
@@ -79,6 +93,7 @@ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block:
|
|
|
79
93
|
amount,
|
|
80
94
|
builderIndex,
|
|
81
95
|
}),
|
|
96
|
+
proposerIndex: state.epochCtx.getBeaconProposer(state.slot),
|
|
82
97
|
});
|
|
83
98
|
|
|
84
99
|
state.builderPendingPayments.set(SLOTS_PER_EPOCH + (bid.slot % SLOTS_PER_EPOCH), pendingPaymentView);
|
|
@@ -39,8 +39,9 @@ export function processOperations(
|
|
|
39
39
|
opts: ProcessBlockOpts = {verifySignatures: true},
|
|
40
40
|
metrics?: BeaconStateTransitionMetrics | null
|
|
41
41
|
): void {
|
|
42
|
-
// verify that outstanding deposits are processed up to the maximum number of deposits
|
|
43
|
-
|
|
42
|
+
// verify that outstanding deposits are processed up to the maximum number of deposits.
|
|
43
|
+
// From Fulu the eth1 bridge deposit mechanism was removed, so blocks must not contain any deposits.
|
|
44
|
+
const maxDeposits = fork >= ForkSeq.fulu ? 0 : getEth1DepositCount(state);
|
|
44
45
|
if (body.deposits.length !== maxDeposits) {
|
|
45
46
|
throw new Error(
|
|
46
47
|
`Block contains incorrect number of deposits: depositCount=${body.deposits.length} expected=${maxDeposits}`
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {ForkPostGloas, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
2
|
-
import {BeaconBlock,
|
|
2
|
+
import {BeaconBlock, gloas, ssz} from "@lodestar/types";
|
|
3
3
|
import {byteArrayEquals, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {CachedBeaconStateGloas} from "../types.js";
|
|
5
5
|
import {computeEpochAtSlot} from "../util/epoch.js";
|
|
6
|
-
import {
|
|
6
|
+
import {processBuilderDepositRequest} from "./processBuilderDepositRequest.js";
|
|
7
|
+
import {processBuilderExitRequest} from "./processBuilderExitRequest.js";
|
|
7
8
|
import {processConsolidationRequest} from "./processConsolidationRequest.js";
|
|
8
9
|
import {processDepositRequest} from "./processDepositRequest.js";
|
|
9
10
|
import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
|
|
@@ -26,7 +27,7 @@ export function processParentExecutionPayload(state: CachedBeaconStateGloas, blo
|
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// Parent was FULL -- verify the bid commitment and apply the payload
|
|
29
|
-
const requestsRoot = ssz.
|
|
30
|
+
const requestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(requests);
|
|
30
31
|
if (!byteArrayEquals(requestsRoot, parentBid.executionRequestsRoot)) {
|
|
31
32
|
throw new Error(
|
|
32
33
|
`Parent execution requests root mismatch actual=${toRootHex(requestsRoot)} expected=${toRootHex(parentBid.executionRequestsRoot)}`
|
|
@@ -45,7 +46,7 @@ export function processParentExecutionPayload(state: CachedBeaconStateGloas, blo
|
|
|
45
46
|
*
|
|
46
47
|
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/beacon-chain.md#new-apply_parent_execution_payload
|
|
47
48
|
*/
|
|
48
|
-
export function applyParentExecutionPayload(state: CachedBeaconStateGloas, requests:
|
|
49
|
+
export function applyParentExecutionPayload(state: CachedBeaconStateGloas, requests: gloas.ExecutionRequests): void {
|
|
49
50
|
const fork = state.config.getForkSeq(state.slot);
|
|
50
51
|
const parentBid = state.latestExecutionPayloadBid;
|
|
51
52
|
const parentSlot = parentBid.slot;
|
|
@@ -54,11 +55,8 @@ export function applyParentExecutionPayload(state: CachedBeaconStateGloas, reque
|
|
|
54
55
|
|
|
55
56
|
// Process execution requests from parent's payload. The execution
|
|
56
57
|
// requests are processed at state.slot (child's slot), not the parent's slot.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
for (const deposit of requests.deposits) {
|
|
60
|
-
processDepositRequest(fork, state, deposit, pendingDepositsLookup);
|
|
61
|
-
}
|
|
58
|
+
for (const deposit of requests.deposits) {
|
|
59
|
+
processDepositRequest(fork, state, deposit);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
for (const withdrawal of requests.withdrawals) {
|
|
@@ -69,6 +67,14 @@ export function applyParentExecutionPayload(state: CachedBeaconStateGloas, reque
|
|
|
69
67
|
processConsolidationRequest(state, consolidation);
|
|
70
68
|
}
|
|
71
69
|
|
|
70
|
+
for (const builderDeposit of requests.builderDeposits) {
|
|
71
|
+
processBuilderDepositRequest(state, builderDeposit);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const builderExit of requests.builderExits) {
|
|
75
|
+
processBuilderExitRequest(state, builderExit);
|
|
76
|
+
}
|
|
77
|
+
|
|
72
78
|
// Settle the builder payment
|
|
73
79
|
if (parentEpoch === currentEpoch) {
|
|
74
80
|
settleBuilderPayment(state, SLOTS_PER_EPOCH + (parentSlot % SLOTS_PER_EPOCH));
|
|
@@ -110,8 +116,14 @@ function settleBuilderPayment(state: CachedBeaconStateGloas, paymentIndex: numbe
|
|
|
110
116
|
state.builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
|
|
111
117
|
}
|
|
112
118
|
|
|
113
|
-
function assertEmptyExecutionRequests(requests:
|
|
114
|
-
if (
|
|
119
|
+
function assertEmptyExecutionRequests(requests: gloas.ExecutionRequests): void {
|
|
120
|
+
if (
|
|
121
|
+
requests.deposits.length !== 0 ||
|
|
122
|
+
requests.withdrawals.length !== 0 ||
|
|
123
|
+
requests.consolidations.length !== 0 ||
|
|
124
|
+
requests.builderDeposits.length !== 0 ||
|
|
125
|
+
requests.builderExits.length !== 0
|
|
126
|
+
) {
|
|
115
127
|
throw new Error("Parent execution requests must be empty when parent block is EMPTY");
|
|
116
128
|
}
|
|
117
129
|
}
|
|
@@ -32,6 +32,10 @@ export function processProposerSlashing(
|
|
|
32
32
|
);
|
|
33
33
|
|
|
34
34
|
if (fork >= ForkSeq.gloas) {
|
|
35
|
+
// Remove the BuilderPendingPayment corresponding to this proposal if it is still in the
|
|
36
|
+
// 2-epoch window. Only clear it when the slashed validator is the proposer associated with
|
|
37
|
+
// the payment; otherwise an unrelated same-slot equivocation could grief an honest proposer's
|
|
38
|
+
// payment.
|
|
35
39
|
const slot = Number(proposerSlashing.signedHeader1.message.slot);
|
|
36
40
|
const proposalEpoch = computeEpochAtSlot(slot);
|
|
37
41
|
const currentEpoch = state.epochCtx.epoch;
|
|
@@ -45,10 +49,11 @@ export function processProposerSlashing(
|
|
|
45
49
|
: undefined;
|
|
46
50
|
|
|
47
51
|
if (paymentIndex !== undefined) {
|
|
48
|
-
(state as CachedBeaconStateGloas).builderPendingPayments
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
const builderPendingPayments = (state as CachedBeaconStateGloas).builderPendingPayments;
|
|
53
|
+
const payment = builderPendingPayments.get(paymentIndex);
|
|
54
|
+
if (payment.proposerIndex === proposerSlashing.signedHeader1.message.proposerIndex) {
|
|
55
|
+
builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
59
|
|
|
@@ -2,14 +2,7 @@ import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params";
|
|
|
2
2
|
import {phase0} from "@lodestar/types";
|
|
3
3
|
import {verifyVoluntaryExitSignature} from "../signatureSets/index.js";
|
|
4
4
|
import {BeaconStateView} from "../stateView/beaconStateView.js";
|
|
5
|
-
import {CachedBeaconStateAllForks, CachedBeaconStateElectra
|
|
6
|
-
import {
|
|
7
|
-
convertValidatorIndexToBuilderIndex,
|
|
8
|
-
getPendingBalanceToWithdrawForBuilder,
|
|
9
|
-
initiateBuilderExit,
|
|
10
|
-
isActiveBuilder,
|
|
11
|
-
isBuilderIndex,
|
|
12
|
-
} from "../util/gloas.js";
|
|
5
|
+
import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
|
|
13
6
|
import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js";
|
|
14
7
|
import {initiateValidatorExit} from "./index.js";
|
|
15
8
|
|
|
@@ -24,7 +17,7 @@ export enum VoluntaryExitValidity {
|
|
|
24
17
|
}
|
|
25
18
|
|
|
26
19
|
/**
|
|
27
|
-
* Process a VoluntaryExit operation. Initiates the exit of a validator
|
|
20
|
+
* Process a VoluntaryExit operation. Initiates the exit of a validator.
|
|
28
21
|
*
|
|
29
22
|
* PERF: Work depends on number of VoluntaryExit per block. On regular networks the average is 0 / block.
|
|
30
23
|
*/
|
|
@@ -34,21 +27,11 @@ export function processVoluntaryExit(
|
|
|
34
27
|
signedVoluntaryExit: phase0.SignedVoluntaryExit,
|
|
35
28
|
verifySignature = true
|
|
36
29
|
): void {
|
|
37
|
-
const voluntaryExit = signedVoluntaryExit.message;
|
|
38
|
-
|
|
39
30
|
const validity = getVoluntaryExitValidity(fork, state, signedVoluntaryExit, verifySignature);
|
|
40
31
|
if (validity !== VoluntaryExitValidity.valid) {
|
|
41
32
|
throw Error(`Invalid voluntary exit at forkSeq=${fork} reason=${validity}`);
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
if (fork >= ForkSeq.gloas && isBuilderIndex(voluntaryExit.validatorIndex)) {
|
|
45
|
-
initiateBuilderExit(
|
|
46
|
-
state as CachedBeaconStateGloas,
|
|
47
|
-
convertValidatorIndexToBuilderIndex(voluntaryExit.validatorIndex)
|
|
48
|
-
);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
35
|
const validator = state.validators.get(signedVoluntaryExit.message.validatorIndex);
|
|
53
36
|
initiateValidatorExit(fork, state, validator);
|
|
54
37
|
}
|
|
@@ -67,51 +50,9 @@ export function getVoluntaryExitValidity(
|
|
|
67
50
|
return VoluntaryExitValidity.earlyEpoch;
|
|
68
51
|
}
|
|
69
52
|
|
|
70
|
-
// Check if this is a builder exit
|
|
71
|
-
if (fork >= ForkSeq.gloas && isBuilderIndex(voluntaryExit.validatorIndex)) {
|
|
72
|
-
return getBuilderVoluntaryExitValidity(state as CachedBeaconStateGloas, signedVoluntaryExit, verifySignature);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
53
|
return getValidatorVoluntaryExitValidity(fork, state, signedVoluntaryExit, verifySignature);
|
|
76
54
|
}
|
|
77
55
|
|
|
78
|
-
function getBuilderVoluntaryExitValidity(
|
|
79
|
-
state: CachedBeaconStateGloas,
|
|
80
|
-
signedVoluntaryExit: phase0.SignedVoluntaryExit,
|
|
81
|
-
verifySignature: boolean
|
|
82
|
-
): VoluntaryExitValidity {
|
|
83
|
-
const {config, epochCtx} = state;
|
|
84
|
-
const builderIndex = convertValidatorIndexToBuilderIndex(signedVoluntaryExit.message.validatorIndex);
|
|
85
|
-
|
|
86
|
-
if (builderIndex >= state.builders.length) {
|
|
87
|
-
return VoluntaryExitValidity.inactive;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const builder = state.builders.getReadonly(builderIndex);
|
|
91
|
-
|
|
92
|
-
// Verify the builder is active
|
|
93
|
-
if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
|
|
94
|
-
return builder.withdrawableEpoch !== FAR_FUTURE_EPOCH
|
|
95
|
-
? VoluntaryExitValidity.alreadyExited
|
|
96
|
-
: VoluntaryExitValidity.inactive;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Only exit builder if it has no pending withdrawals in the queue
|
|
100
|
-
if (getPendingBalanceToWithdrawForBuilder(state, builderIndex) !== 0) {
|
|
101
|
-
return VoluntaryExitValidity.pendingWithdrawals;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Verify signature
|
|
105
|
-
if (
|
|
106
|
-
verifySignature &&
|
|
107
|
-
!verifyVoluntaryExitSignature(config, epochCtx.pubkeyCache, new BeaconStateView(state), signedVoluntaryExit)
|
|
108
|
-
) {
|
|
109
|
-
return VoluntaryExitValidity.invalidSignature;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return VoluntaryExitValidity.valid;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
56
|
function getValidatorVoluntaryExitValidity(
|
|
116
57
|
fork: ForkSeq,
|
|
117
58
|
state: CachedBeaconStateAllForks,
|
|
@@ -37,7 +37,9 @@ export function processPendingDeposits(state: CachedBeaconStateElectra, cache: E
|
|
|
37
37
|
|
|
38
38
|
for (const deposit of deposits) {
|
|
39
39
|
// Do not process deposit requests if Eth1 bridge deposits are not yet applied.
|
|
40
|
+
// The eth1 bridge deposit mechanism was removed in Fulu, so this gate only applies pre-Fulu.
|
|
40
41
|
if (
|
|
42
|
+
fork < ForkSeq.fulu &&
|
|
41
43
|
// Is deposit request
|
|
42
44
|
deposit.slot > GENESIS_SLOT &&
|
|
43
45
|
// There are pending Eth1 bridge deposits
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {ForkSeq, MIN_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
2
|
import {ssz} from "@lodestar/types";
|
|
3
3
|
import {CachedBeaconStateFulu, EpochTransitionCache} from "../types.js";
|
|
4
|
+
import {FLAG_UNSLASHED} from "../util/attesterStatus.js";
|
|
4
5
|
import {computeEpochShuffling} from "../util/epochShuffling.js";
|
|
5
6
|
import {computeProposerIndices} from "../util/seed.js";
|
|
6
7
|
|
|
@@ -26,7 +27,13 @@ export function processProposerLookahead(
|
|
|
26
27
|
// Save shuffling to cache so afterProcessEpoch can reuse it instead of recomputing
|
|
27
28
|
cache.nextShuffling = shuffling;
|
|
28
29
|
|
|
29
|
-
const
|
|
30
|
+
const activeIndices =
|
|
31
|
+
fork >= ForkSeq.gloas
|
|
32
|
+
? // Exclude slashed validators from proposing (EIP-8045)
|
|
33
|
+
shuffling.activeIndices.filter((index) => (cache.flags[index] & FLAG_UNSLASHED) !== 0)
|
|
34
|
+
: shuffling.activeIndices;
|
|
35
|
+
|
|
36
|
+
const lastEpochProposerLookahead = computeProposerIndices(fork, state, {activeIndices}, epoch);
|
|
30
37
|
|
|
31
38
|
state.proposerLookahead = ssz.fulu.ProposerLookahead.toViewDU([
|
|
32
39
|
...remainingProposerLookahead,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
1
|
+
import {PAYLOAD_BUILDER_VERSION, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
2
2
|
import {ssz} from "@lodestar/types";
|
|
3
3
|
import {toPubkeyHex} from "@lodestar/utils";
|
|
4
|
-
import {
|
|
4
|
+
import {isValidDepositSignature} from "../block/processDeposit.js";
|
|
5
5
|
import {getCachedBeaconState} from "../cache/stateCache.js";
|
|
6
6
|
import {CachedBeaconStateFulu, CachedBeaconStateGloas} from "../types.js";
|
|
7
|
-
import {initializePtcWindow, isBuilderWithdrawalCredential} from "../util/gloas.js";
|
|
7
|
+
import {addBuilderToRegistry, initializePtcWindow, isBuilderWithdrawalCredential} from "../util/gloas.js";
|
|
8
8
|
import {isValidatorKnown} from "../util/index.js";
|
|
9
9
|
import {PendingDepositsLookup} from "../util/pendingDepositsLookup.js";
|
|
10
10
|
|
|
@@ -48,9 +48,9 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
|
|
|
48
48
|
stateGloasView.currentSyncCommittee = stateGloasCloned.currentSyncCommittee;
|
|
49
49
|
stateGloasView.nextSyncCommittee = stateGloasCloned.nextSyncCommittee;
|
|
50
50
|
stateGloasView.latestExecutionPayloadBid.blockHash = stateFulu.latestExecutionPayloadHeader.blockHash;
|
|
51
|
-
stateGloasView.latestExecutionPayloadBid.gasLimit =
|
|
52
|
-
stateGloasView.latestExecutionPayloadBid.executionRequestsRoot = ssz.
|
|
53
|
-
ssz.
|
|
51
|
+
stateGloasView.latestExecutionPayloadBid.gasLimit = stateFulu.latestExecutionPayloadHeader.gasLimit;
|
|
52
|
+
stateGloasView.latestExecutionPayloadBid.executionRequestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(
|
|
53
|
+
ssz.gloas.ExecutionRequests.defaultValue()
|
|
54
54
|
);
|
|
55
55
|
stateGloasView.nextWithdrawalIndex = stateGloasCloned.nextWithdrawalIndex;
|
|
56
56
|
stateGloasView.nextWithdrawalValidatorIndex = stateGloasCloned.nextWithdrawalValidatorIndex;
|
|
@@ -90,7 +90,8 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
|
|
|
90
90
|
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.8/specs/gloas/fork.md#new-onboard_builders_from_pending_deposits
|
|
91
91
|
*/
|
|
92
92
|
function onboardBuildersFromPendingDeposits(state: CachedBeaconStateGloas): void {
|
|
93
|
-
// Track pubkeys of new builders added when applying deposits
|
|
93
|
+
// Track pubkeys of new builders added when applying deposits. `state.builders` starts empty
|
|
94
|
+
// at the fork, so every builder pubkey here is one added in an earlier iteration.
|
|
94
95
|
const builderPubkeys = new Set<string>();
|
|
95
96
|
|
|
96
97
|
const pendingDeposits = ssz.electra.PendingDeposits.defaultViewDU();
|
|
@@ -109,40 +110,55 @@ function onboardBuildersFromPendingDeposits(state: CachedBeaconStateGloas): void
|
|
|
109
110
|
continue;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (!isBuilderWithdrawalCredential(deposit.withdrawalCredentials)) {
|
|
121
|
-
pendingDeposits.push(deposit);
|
|
122
|
-
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (pendingDepositsLookup.hasPendingValidator(state.config, pubkeyHex)) {
|
|
126
|
-
pendingDeposits.push(deposit);
|
|
127
|
-
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
128
|
-
continue;
|
|
113
|
+
if (builderPubkeys.has(pubkeyHex)) {
|
|
114
|
+
// Top up an already-onboarded builder
|
|
115
|
+
// TODO GLOAS: linear search; consider builder pubkey cache when we drop the upgrade-time set
|
|
116
|
+
for (let j = 0; j < state.builders.length; j++) {
|
|
117
|
+
if (toPubkeyHex(state.builders.getReadonly(j).pubkey) === pubkeyHex) {
|
|
118
|
+
state.builders.get(j).balance += deposit.amount;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
129
121
|
}
|
|
122
|
+
continue;
|
|
130
123
|
}
|
|
131
124
|
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
125
|
+
// Deposits for non-builders stay in the pending queue. If there is a valid pending
|
|
126
|
+
// deposit for a new validator with this pubkey, keep this deposit pending so the validator
|
|
127
|
+
// can pick it up later.
|
|
128
|
+
if (!isBuilderWithdrawalCredential(deposit.withdrawalCredentials)) {
|
|
129
|
+
pendingDeposits.push(deposit);
|
|
130
|
+
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (pendingDepositsLookup.hasPendingValidator(state.config, pubkeyHex)) {
|
|
134
|
+
pendingDeposits.push(deposit);
|
|
135
|
+
pendingDepositsLookup.add(deposit, pubkeyHex);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Verify the deposit signature (proof of possession). If invalid the deposit is silently
|
|
140
|
+
// dropped — stake is forfeited, matching the validator deposit contract behavior.
|
|
141
|
+
if (
|
|
142
|
+
!isValidDepositSignature(
|
|
143
|
+
state.config,
|
|
144
|
+
deposit.pubkey,
|
|
145
|
+
deposit.withdrawalCredentials,
|
|
146
|
+
deposit.amount,
|
|
147
|
+
deposit.signature
|
|
148
|
+
)
|
|
149
|
+
) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
addBuilderToRegistry(
|
|
136
154
|
state,
|
|
137
155
|
deposit.pubkey,
|
|
138
|
-
|
|
156
|
+
PAYLOAD_BUILDER_VERSION,
|
|
157
|
+
deposit.withdrawalCredentials.subarray(12),
|
|
139
158
|
deposit.amount,
|
|
140
|
-
deposit.signature,
|
|
141
159
|
deposit.slot
|
|
142
160
|
);
|
|
143
|
-
|
|
144
|
-
builderPubkeys.add(pubkeyHex);
|
|
145
|
-
}
|
|
161
|
+
builderPubkeys.add(pubkeyHex);
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
state.pendingDeposits = pendingDeposits;
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
BlindedBeaconBlock,
|
|
9
9
|
BuilderIndex,
|
|
10
10
|
Bytes32,
|
|
11
|
+
CommitteeIndex,
|
|
11
12
|
Epoch,
|
|
12
13
|
ExecutionPayloadBid,
|
|
13
14
|
ExecutionPayloadHeader,
|
|
@@ -452,6 +453,14 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
452
453
|
return this.cachedState.epochCtx.getShufflingAtEpoch(epoch);
|
|
453
454
|
}
|
|
454
455
|
|
|
456
|
+
getBeaconCommittee(slot: Slot, index: CommitteeIndex): Uint32Array {
|
|
457
|
+
return this.cachedState.epochCtx.getBeaconCommittee(slot, index);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
getBeaconCommitteeCountPerSlot(epoch: Epoch): number {
|
|
461
|
+
return this.cachedState.epochCtx.getCommitteeCountPerSlot(epoch);
|
|
462
|
+
}
|
|
463
|
+
|
|
455
464
|
get previousDecisionRoot(): RootHex {
|
|
456
465
|
return this.cachedState.epochCtx.previousDecisionRoot;
|
|
457
466
|
}
|
|
@@ -822,7 +831,7 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
822
831
|
/**
|
|
823
832
|
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/validator.md#executionpayload
|
|
824
833
|
*/
|
|
825
|
-
withParentPayloadApplied(executionRequests:
|
|
834
|
+
withParentPayloadApplied(executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas {
|
|
826
835
|
if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
|
|
827
836
|
throw new Error("withParentPayloadApplied is not available before Gloas");
|
|
828
837
|
}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
BlindedBeaconBlock,
|
|
24
24
|
BuilderIndex,
|
|
25
25
|
Bytes32,
|
|
26
|
+
CommitteeIndex,
|
|
26
27
|
Epoch,
|
|
27
28
|
ExecutionPayloadBid,
|
|
28
29
|
ExecutionPayloadHeader,
|
|
@@ -75,6 +76,8 @@ export interface IBeaconStateView {
|
|
|
75
76
|
|
|
76
77
|
// Shuffling and committees
|
|
77
78
|
getShufflingAtEpoch(epoch: Epoch): EpochShuffling;
|
|
79
|
+
getBeaconCommittee(slot: Slot, index: CommitteeIndex): Uint32Array;
|
|
80
|
+
getBeaconCommitteeCountPerSlot(epoch: Epoch): number;
|
|
78
81
|
// Decision roots
|
|
79
82
|
previousDecisionRoot: RootHex;
|
|
80
83
|
currentDecisionRoot: RootHex;
|
|
@@ -261,7 +264,7 @@ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
|
|
|
261
264
|
* operation selection (e.g. voluntary exits) see the same post-apply state that the block
|
|
262
265
|
* processor will see at import.
|
|
263
266
|
*/
|
|
264
|
-
withParentPayloadApplied(executionRequests:
|
|
267
|
+
withParentPayloadApplied(executionRequests: gloas.ExecutionRequests): IBeaconStateViewGloas;
|
|
265
268
|
}
|
|
266
269
|
|
|
267
270
|
/**
|
|
@@ -278,6 +281,35 @@ export type IBeaconStateViewLatestFork = Omit<
|
|
|
278
281
|
latestExecutionPayloadHeader: ExecutionPayloadHeader;
|
|
279
282
|
payloadBlockNumber: number;
|
|
280
283
|
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Contract a BeaconStateView backing implementation must satisfy.
|
|
287
|
+
*
|
|
288
|
+
* Differs from `IBeaconStateViewLatestFork` in two ways:
|
|
289
|
+
* - `executionPayloadAvailability` is a raw `{uint8Array, bitLen}` POJO — a
|
|
290
|
+
* native (`.node`) binding cannot construct a `BitArray` across FFI.
|
|
291
|
+
* `NativeBeaconStateView` lifts it back to `BitArray` for beacon-node.
|
|
292
|
+
* - Methods that produce another view (`stateTransition`, `processSlots`,
|
|
293
|
+
* `loadOtherState`, `withParentPayloadApplied`) return `IBeaconStateViewNative`
|
|
294
|
+
* so callers can re-wrap without an `as unknown` cast. Param lists are reused
|
|
295
|
+
* via `Parameters<...>` to avoid duplicating signatures.
|
|
296
|
+
*
|
|
297
|
+
* The TS-side `BeaconStateView` also structurally satisfies this contract since
|
|
298
|
+
* `BitArray` exposes `uint8Array` and `bitLen`.
|
|
299
|
+
*/
|
|
300
|
+
export type IBeaconStateViewNative = Omit<
|
|
301
|
+
IBeaconStateViewLatestFork,
|
|
302
|
+
"executionPayloadAvailability" | "loadOtherState" | "stateTransition" | "processSlots" | "withParentPayloadApplied"
|
|
303
|
+
> & {
|
|
304
|
+
executionPayloadAvailability: {uint8Array: Uint8Array; bitLen: number};
|
|
305
|
+
loadOtherState(...args: Parameters<IBeaconStateViewLatestFork["loadOtherState"]>): IBeaconStateViewNative;
|
|
306
|
+
stateTransition(...args: Parameters<IBeaconStateViewLatestFork["stateTransition"]>): IBeaconStateViewNative;
|
|
307
|
+
processSlots(...args: Parameters<IBeaconStateViewLatestFork["processSlots"]>): IBeaconStateViewNative;
|
|
308
|
+
withParentPayloadApplied(
|
|
309
|
+
...args: Parameters<IBeaconStateViewLatestFork["withParentPayloadApplied"]>
|
|
310
|
+
): IBeaconStateViewNative;
|
|
311
|
+
};
|
|
312
|
+
|
|
281
313
|
export function isStatePostAltair(state: IBeaconStateView): state is IBeaconStateViewAltair {
|
|
282
314
|
return isForkPostAltair(state.forkName);
|
|
283
315
|
}
|