@lodestar/state-transition 1.40.0-dev.0ae7a89ead → 1.40.0-dev.1c71f4299a

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.
Files changed (50) hide show
  1. package/lib/block/index.d.ts +1 -0
  2. package/lib/block/index.d.ts.map +1 -1
  3. package/lib/block/index.js +1 -0
  4. package/lib/block/index.js.map +1 -1
  5. package/lib/block/processDepositRequest.d.ts +8 -2
  6. package/lib/block/processDepositRequest.d.ts.map +1 -1
  7. package/lib/block/processDepositRequest.js +81 -8
  8. package/lib/block/processDepositRequest.js.map +1 -1
  9. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
  10. package/lib/block/processExecutionPayloadBid.js +14 -27
  11. package/lib/block/processExecutionPayloadBid.js.map +1 -1
  12. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -1
  13. package/lib/block/processExecutionPayloadEnvelope.js +25 -24
  14. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
  15. package/lib/block/processOperations.js +1 -1
  16. package/lib/block/processOperations.js.map +1 -1
  17. package/lib/block/processVoluntaryExit.d.ts +1 -1
  18. package/lib/block/processVoluntaryExit.d.ts.map +1 -1
  19. package/lib/block/processVoluntaryExit.js +44 -2
  20. package/lib/block/processVoluntaryExit.js.map +1 -1
  21. package/lib/block/processWithdrawals.d.ts +1 -0
  22. package/lib/block/processWithdrawals.d.ts.map +1 -1
  23. package/lib/block/processWithdrawals.js +121 -66
  24. package/lib/block/processWithdrawals.js.map +1 -1
  25. package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -1
  26. package/lib/epoch/processBuilderPendingPayments.js +1 -4
  27. package/lib/epoch/processBuilderPendingPayments.js.map +1 -1
  28. package/lib/util/electra.d.ts.map +1 -1
  29. package/lib/util/electra.js +1 -2
  30. package/lib/util/electra.js.map +1 -1
  31. package/lib/util/gloas.d.ts +43 -3
  32. package/lib/util/gloas.d.ts.map +1 -1
  33. package/lib/util/gloas.js +93 -5
  34. package/lib/util/gloas.js.map +1 -1
  35. package/lib/util/validator.d.ts +5 -0
  36. package/lib/util/validator.d.ts.map +1 -1
  37. package/lib/util/validator.js +25 -2
  38. package/lib/util/validator.js.map +1 -1
  39. package/package.json +7 -7
  40. package/src/block/index.ts +1 -0
  41. package/src/block/processDepositRequest.ts +101 -8
  42. package/src/block/processExecutionPayloadBid.ts +18 -40
  43. package/src/block/processExecutionPayloadEnvelope.ts +33 -29
  44. package/src/block/processOperations.ts +1 -1
  45. package/src/block/processVoluntaryExit.ts +59 -4
  46. package/src/block/processWithdrawals.ts +168 -70
  47. package/src/epoch/processBuilderPendingPayments.ts +1 -5
  48. package/src/util/electra.ts +1 -4
  49. package/src/util/gloas.ts +109 -8
  50. package/src/util/validator.ts +31 -1
@@ -26,5 +26,10 @@ export declare function getBalanceChurnLimitFromCache(epochCtx: EpochCache): num
26
26
  export declare function getActivationExitChurnLimit(epochCtx: EpochCache): number;
27
27
  export declare function getConsolidationChurnLimit(epochCtx: EpochCache): number;
28
28
  export declare function getMaxEffectiveBalance(withdrawalCredentials: Uint8Array): number;
29
+ /**
30
+ * Check if validator is partially withdrawable.
31
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/electra/beacon-chain.md#modified-is_partially_withdrawable_validator
32
+ */
33
+ export declare function isPartiallyWithdrawableValidator(fork: ForkSeq, validator: phase0.Validator, balance: number): boolean;
29
34
  export declare function getPendingBalanceToWithdraw(fork: ForkSeq, state: CachedBeaconStateElectra | CachedBeaconStateGloas, validatorIndex: ValidatorIndex): number;
30
35
  //# sourceMappingURL=validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/util/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAEL,OAAO,EAGR,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAC,mBAAmB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,UAAU,EAAC,MAAM,aAAa,CAAC;AAG9G;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAEpF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,CAW/F;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAKpH;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,4BAA4B,EAAE,MAAM,EACpC,kBAAkB,EAAE,MAAM,EAC1B,qBAAqB,EAAE,MAAM,GAC5B,MAAM,CAQR;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAM1E;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAExE;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAEvE;AAED,wBAAgB,sBAAsB,CAAC,qBAAqB,EAAE,UAAU,GAAG,MAAM,CAMhF;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,wBAAwB,GAAG,sBAAsB,EACxD,cAAc,EAAE,cAAc,GAC7B,MAAM,CAuBR"}
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/util/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAEL,OAAO,EAIR,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAC,mBAAmB,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,UAAU,EAAC,MAAM,aAAa,CAAC;AAI9G;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAEpF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,CAW/F;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAKpH;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,4BAA4B,EAAE,MAAM,EACpC,kBAAkB,EAAE,MAAM,EAC1B,qBAAqB,EAAE,MAAM,GAC5B,MAAM,CAQR;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAM1E;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAExE;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAEvE;AAED,wBAAgB,sBAAsB,CAAC,qBAAqB,EAAE,UAAU,GAAG,MAAM,CAMhF;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAsBrH;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,wBAAwB,GAAG,sBAAsB,EACxD,cAAc,EAAE,cAAc,GAC7B,MAAM,CAuBR"}
@@ -1,6 +1,7 @@
1
- import { EFFECTIVE_BALANCE_INCREMENT, ForkSeq, MAX_EFFECTIVE_BALANCE_ELECTRA, MIN_ACTIVATION_BALANCE, } from "@lodestar/params";
1
+ import { EFFECTIVE_BALANCE_INCREMENT, ForkSeq, MAX_EFFECTIVE_BALANCE, MAX_EFFECTIVE_BALANCE_ELECTRA, MIN_ACTIVATION_BALANCE, } from "@lodestar/params";
2
2
  import { intDiv } from "@lodestar/utils";
3
- import { hasCompoundingWithdrawalCredential } from "./electra.js";
3
+ import { hasEth1WithdrawalCredential } from "./capella.js";
4
+ import { hasCompoundingWithdrawalCredential, hasExecutionWithdrawalCredential } from "./electra.js";
4
5
  /**
5
6
  * Check if [[validator]] is active
6
7
  */
@@ -61,6 +62,28 @@ export function getMaxEffectiveBalance(withdrawalCredentials) {
61
62
  }
62
63
  return MIN_ACTIVATION_BALANCE;
63
64
  }
65
+ /**
66
+ * Check if validator is partially withdrawable.
67
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/electra/beacon-chain.md#modified-is_partially_withdrawable_validator
68
+ */
69
+ export function isPartiallyWithdrawableValidator(fork, validator, balance) {
70
+ const isPostElectra = fork >= ForkSeq.electra;
71
+ // Check withdrawal credentials
72
+ const hasWithdrawableCredentials = isPostElectra
73
+ ? hasExecutionWithdrawalCredential(validator.withdrawalCredentials)
74
+ : hasEth1WithdrawalCredential(validator.withdrawalCredentials);
75
+ if (!hasWithdrawableCredentials) {
76
+ return false;
77
+ }
78
+ // Get max effective balance based on fork
79
+ const maxEffectiveBalance = isPostElectra
80
+ ? getMaxEffectiveBalance(validator.withdrawalCredentials)
81
+ : MAX_EFFECTIVE_BALANCE;
82
+ // Check if at max effective balance and has excess balance
83
+ const hasMaxEffectiveBalance = validator.effectiveBalance === maxEffectiveBalance;
84
+ const hasExcessBalance = balance > maxEffectiveBalance;
85
+ return hasMaxEffectiveBalance && hasExcessBalance;
86
+ }
64
87
  export function getPendingBalanceToWithdraw(fork, state, validatorIndex) {
65
88
  let total = 0;
66
89
  for (const item of state.pendingPartialWithdrawals.getAllReadonly()) {
@@ -1 +1 @@
1
- {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/util/validator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/util/validator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/ChainSafe/lodestar/issues"
13
13
  },
14
- "version": "1.40.0-dev.0ae7a89ead",
14
+ "version": "1.40.0-dev.1c71f4299a",
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
@@ -62,14 +62,14 @@
62
62
  "@chainsafe/pubkey-index-map": "^3.0.0",
63
63
  "@chainsafe/ssz": "^1.2.2",
64
64
  "@chainsafe/swap-or-not-shuffle": "^1.2.1",
65
- "@lodestar/config": "^1.40.0-dev.0ae7a89ead",
66
- "@lodestar/params": "^1.40.0-dev.0ae7a89ead",
67
- "@lodestar/types": "^1.40.0-dev.0ae7a89ead",
68
- "@lodestar/utils": "^1.40.0-dev.0ae7a89ead",
65
+ "@lodestar/config": "^1.40.0-dev.1c71f4299a",
66
+ "@lodestar/params": "^1.40.0-dev.1c71f4299a",
67
+ "@lodestar/types": "^1.40.0-dev.1c71f4299a",
68
+ "@lodestar/utils": "^1.40.0-dev.1c71f4299a",
69
69
  "bigint-buffer": "^1.1.5"
70
70
  },
71
71
  "devDependencies": {
72
- "@lodestar/api": "^1.40.0-dev.0ae7a89ead"
72
+ "@lodestar/api": "^1.40.0-dev.1c71f4299a"
73
73
  },
74
74
  "keywords": [
75
75
  "ethereum",
@@ -77,5 +77,5 @@
77
77
  "beacon",
78
78
  "blockchain"
79
79
  ],
80
- "gitHead": "757f82afe00660128b35e96d623c226d22ad9444"
80
+ "gitHead": "2e53e59d8a74209d99f9bc716e1628d7688c9fda"
81
81
  }
@@ -38,6 +38,7 @@ export {
38
38
  export * from "./externalData.js";
39
39
  export * from "./initiateValidatorExit.js";
40
40
  export * from "./isValidIndexedAttestation.js";
41
+ export * from "./processDepositRequest.js";
41
42
  export * from "./processOperations.js";
42
43
 
43
44
  export function processBlock(
@@ -1,21 +1,114 @@
1
- import {UNSET_DEPOSIT_REQUESTS_START_INDEX} from "@lodestar/params";
2
- import {electra, ssz} from "@lodestar/types";
1
+ import {FAR_FUTURE_EPOCH, ForkSeq, UNSET_DEPOSIT_REQUESTS_START_INDEX} from "@lodestar/params";
2
+ import {BLSPubkey, Bytes32, UintNum64, electra, ssz} from "@lodestar/types";
3
3
  import {CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
4
+ import {findBuilderIndexByPubkey, isBuilderWithdrawalCredential} from "../util/gloas.js";
5
+ import {computeEpochAtSlot, isValidatorKnown} from "../util/index.js";
6
+ import {isValidDepositSignature} from "./processDeposit.js";
7
+
8
+ /**
9
+ * Apply a deposit for a builder. Either increases balance for existing builder or adds new builder to registry.
10
+ * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.1/specs/gloas/beacon-chain.md#new-apply_deposit_for_builder
11
+ */
12
+ export function applyDepositForBuilder(
13
+ state: CachedBeaconStateGloas,
14
+ pubkey: BLSPubkey,
15
+ withdrawalCredentials: Bytes32,
16
+ amount: UintNum64,
17
+ signature: Bytes32
18
+ ): void {
19
+ const builderIndex = findBuilderIndexByPubkey(state, pubkey);
20
+
21
+ if (builderIndex !== null) {
22
+ // Existing builder - increase balance
23
+ const builder = state.builders.get(builderIndex);
24
+ builder.balance += amount;
25
+ } else {
26
+ // New builder - verify signature and add to registry
27
+ if (isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)) {
28
+ addBuilderToRegistry(state, pubkey, withdrawalCredentials, amount);
29
+ }
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Add a new builder to the builders registry.
35
+ * Reuses slots from exited and fully withdrawn builders if available.
36
+ */
37
+ function addBuilderToRegistry(
38
+ state: CachedBeaconStateGloas,
39
+ pubkey: BLSPubkey,
40
+ withdrawalCredentials: Bytes32,
41
+ amount: UintNum64
42
+ ): void {
43
+ const currentEpoch = computeEpochAtSlot(state.slot);
44
+
45
+ // Try to find a reusable slot from an exited builder with zero balance
46
+ let builderIndex = state.builders.length;
47
+ for (let i = 0; i < state.builders.length; i++) {
48
+ const builder = state.builders.getReadonly(i);
49
+ if (builder.withdrawableEpoch <= currentEpoch && builder.balance === 0) {
50
+ builderIndex = i;
51
+ break;
52
+ }
53
+ }
54
+
55
+ // Create new builder
56
+ const newBuilder = ssz.gloas.Builder.toViewDU({
57
+ pubkey,
58
+ version: withdrawalCredentials[0],
59
+ executionAddress: withdrawalCredentials.subarray(12),
60
+ balance: amount,
61
+ depositEpoch: currentEpoch,
62
+ withdrawableEpoch: FAR_FUTURE_EPOCH,
63
+ });
64
+
65
+ if (builderIndex < state.builders.length) {
66
+ // Reuse existing slot
67
+ state.builders.set(builderIndex, newBuilder);
68
+ } else {
69
+ // Append to end
70
+ state.builders.push(newBuilder);
71
+ }
72
+ }
4
73
 
5
74
  export function processDepositRequest(
75
+ fork: ForkSeq,
6
76
  state: CachedBeaconStateElectra | CachedBeaconStateGloas,
7
77
  depositRequest: electra.DepositRequest
8
78
  ): void {
9
- if (state.depositRequestsStartIndex === UNSET_DEPOSIT_REQUESTS_START_INDEX) {
79
+ const {pubkey, withdrawalCredentials, amount, signature} = depositRequest;
80
+
81
+ // Check if this is a builder or validator deposit
82
+ if (fork >= ForkSeq.gloas) {
83
+ const stateGloas = state as CachedBeaconStateGloas;
84
+ const builderIndex = findBuilderIndexByPubkey(stateGloas, pubkey);
85
+ const validatorIndex = state.epochCtx.getValidatorIndex(pubkey);
86
+
87
+ // Regardless of the withdrawal credentials prefix, if a builder/validator
88
+ // already exists with this pubkey, apply the deposit to their balance
89
+ const isBuilder = builderIndex !== null;
90
+ const isValidator = isValidatorKnown(state, validatorIndex);
91
+ const isBuilderPrefix = isBuilderWithdrawalCredential(withdrawalCredentials);
92
+
93
+ // Route to builder if it's an existing builder OR has builder prefix and is not a validator
94
+ if (isBuilder || (isBuilderPrefix && !isValidator)) {
95
+ // Apply builder deposits immediately
96
+ applyDepositForBuilder(stateGloas, pubkey, withdrawalCredentials, amount, signature);
97
+ return;
98
+ }
99
+ }
100
+
101
+ // Only set deposit_requests_start_index in Electra fork, not Gloas
102
+ if (fork < ForkSeq.gloas && state.depositRequestsStartIndex === UNSET_DEPOSIT_REQUESTS_START_INDEX) {
10
103
  state.depositRequestsStartIndex = depositRequest.index;
11
104
  }
12
105
 
13
- // Create pending deposit
106
+ // Add validator deposits to the queue
14
107
  const pendingDeposit = ssz.electra.PendingDeposit.toViewDU({
15
- pubkey: depositRequest.pubkey,
16
- withdrawalCredentials: depositRequest.withdrawalCredentials,
17
- amount: depositRequest.amount,
18
- signature: depositRequest.signature,
108
+ pubkey,
109
+ withdrawalCredentials,
110
+ amount,
111
+ signature,
19
112
  slot: state.slot,
20
113
  });
21
114
  state.pendingDeposits.push(pendingDeposit);
@@ -1,68 +1,47 @@
1
1
  import {PublicKey, Signature, verify} from "@chainsafe/blst";
2
2
  import {byteArrayEquals} from "@chainsafe/ssz";
3
- import {
4
- DOMAIN_BEACON_BUILDER,
5
- FAR_FUTURE_EPOCH,
6
- ForkPostGloas,
7
- MIN_ACTIVATION_BALANCE,
8
- SLOTS_PER_EPOCH,
9
- } from "@lodestar/params";
3
+ import {BUILDER_INDEX_SELF_BUILD, DOMAIN_BEACON_BUILDER, ForkPostGloas, SLOTS_PER_EPOCH} from "@lodestar/params";
10
4
  import {BeaconBlock, gloas, ssz} from "@lodestar/types";
11
5
  import {toHex, toRootHex} from "@lodestar/utils";
12
6
  import {G2_POINT_AT_INFINITY} from "../constants/constants.ts";
13
7
  import {CachedBeaconStateGloas} from "../types.ts";
14
- import {hasBuilderWithdrawalCredential} from "../util/gloas.ts";
15
- import {computeSigningRoot, getCurrentEpoch, getRandaoMix, isActiveValidator} from "../util/index.ts";
8
+ import {canBuilderCoverBid, isActiveBuilder} from "../util/gloas.ts";
9
+ import {computeSigningRoot, getCurrentEpoch, getRandaoMix} from "../util/index.ts";
16
10
 
17
11
  export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block: BeaconBlock<ForkPostGloas>): void {
18
12
  const signedBid = block.body.signedExecutionPayloadBid;
19
13
  const bid = signedBid.message;
20
14
  const {builderIndex, value: amount} = bid;
21
- const builder = state.validators.getReadonly(builderIndex);
22
15
 
23
16
  // For self-builds, amount must be zero regardless of withdrawal credential prefix
24
- if (builderIndex === block.proposerIndex) {
17
+ if (builderIndex === BUILDER_INDEX_SELF_BUILD) {
25
18
  if (amount !== 0) {
26
19
  throw Error(`Invalid execution payload bid: self-build with non-zero amount ${amount}`);
27
20
  }
28
21
  if (!byteArrayEquals(signedBid.signature, G2_POINT_AT_INFINITY)) {
29
22
  throw Error("Invalid execution payload bid: self-build with non-zero signature");
30
23
  }
31
- // Non-self builds require builder withdrawal credential
32
- } else {
33
- if (!hasBuilderWithdrawalCredential(builder.withdrawalCredentials)) {
34
- throw Error(`Invalid execution payload bid: builder ${builderIndex} does not have builder withdrawal credential`);
24
+ }
25
+ // Non-self builds require active builder with valid signature
26
+ else {
27
+ const builder = state.builders.getReadonly(builderIndex);
28
+
29
+ // Verify that the builder is active
30
+ if (!isActiveBuilder(state, builderIndex)) {
31
+ throw Error(`Invalid execution payload bid: builder ${builderIndex} is not active`);
32
+ }
33
+
34
+ // Verify that the builder has funds to cover the bid
35
+ if (!canBuilderCoverBid(state, builderIndex, amount)) {
36
+ throw Error(`Invalid execution payload bid: builder ${builderIndex} has insufficient balance`);
35
37
  }
36
38
 
39
+ // Verify that the bid signature is valid
37
40
  if (!verifyExecutionPayloadBidSignature(state, builder.pubkey, signedBid)) {
38
41
  throw Error(`Invalid execution payload bid: invalid signature for builder ${builderIndex}`);
39
42
  }
40
43
  }
41
44
 
42
- if (!isActiveValidator(builder, getCurrentEpoch(state))) {
43
- throw Error(`Invalid execution payload bid: builder ${builderIndex} is not active`);
44
- }
45
-
46
- if (builder.slashed) {
47
- throw Error(`Invalid execution payload bid: builder ${builderIndex} is slashed`);
48
- }
49
-
50
- const pendingPayments = state.builderPendingPayments
51
- .getAllReadonly()
52
- .filter((payment) => payment.withdrawal.builderIndex === builderIndex)
53
- .reduce((acc, payment) => acc + payment.withdrawal.amount, 0);
54
- const pendingWithdrawals = state.builderPendingWithdrawals
55
- .getAllReadonly()
56
- .filter((withdrawal) => withdrawal.builderIndex === builderIndex)
57
- .reduce((acc, withdrawal) => acc + withdrawal.amount, 0);
58
-
59
- if (
60
- amount !== 0 &&
61
- state.balances.get(builderIndex) < amount + pendingPayments + pendingWithdrawals + MIN_ACTIVATION_BALANCE
62
- ) {
63
- throw Error("Insufficient builder balance");
64
- }
65
-
66
45
  if (bid.slot !== block.slot) {
67
46
  throw Error(`Bid slot ${bid.slot} does not match block slot ${block.slot}`);
68
47
  }
@@ -91,7 +70,6 @@ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block:
91
70
  feeRecipient: bid.feeRecipient,
92
71
  amount,
93
72
  builderIndex,
94
- withdrawableEpoch: FAR_FUTURE_EPOCH,
95
73
  }),
96
74
  });
97
75
 
@@ -1,10 +1,15 @@
1
1
  import {PublicKey, Signature, verify} from "@chainsafe/blst";
2
2
  import {byteArrayEquals} from "@chainsafe/ssz";
3
- import {DOMAIN_BEACON_BUILDER, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
3
+ import {
4
+ BUILDER_INDEX_SELF_BUILD,
5
+ DOMAIN_BEACON_BUILDER,
6
+ SLOTS_PER_EPOCH,
7
+ SLOTS_PER_HISTORICAL_ROOT,
8
+ } from "@lodestar/params";
4
9
  import {gloas, ssz} from "@lodestar/types";
5
10
  import {toHex, toRootHex} from "@lodestar/utils";
6
11
  import {CachedBeaconStateGloas} from "../types.ts";
7
- import {computeExitEpochAndUpdateChurn, computeSigningRoot, computeTimeAtSlot} from "../util/index.ts";
12
+ import {computeSigningRoot, computeTimeAtSlot} from "../util/index.ts";
8
13
  import {processConsolidationRequest} from "./processConsolidationRequest.ts";
9
14
  import {processDepositRequest} from "./processDepositRequest.ts";
10
15
  import {processWithdrawalRequest} from "./processWithdrawalRequest.ts";
@@ -19,13 +24,8 @@ export function processExecutionPayloadEnvelope(
19
24
  const payload = envelope.payload;
20
25
  const fork = state.config.getForkSeq(envelope.slot);
21
26
 
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
- }
27
+ if (verify && !verifyExecutionPayloadEnvelopeSignature(state, signedEnvelope)) {
28
+ throw Error(`Execution payload envelope has invalid signature builderIndex=${envelope.builderIndex}`);
29
29
  }
30
30
 
31
31
  validateExecutionPayloadEnvelope(state, envelope);
@@ -33,7 +33,7 @@ export function processExecutionPayloadEnvelope(
33
33
  const requests = envelope.executionRequests;
34
34
 
35
35
  for (const deposit of requests.deposits) {
36
- processDepositRequest(state, deposit);
36
+ processDepositRequest(fork, state, deposit);
37
37
  }
38
38
 
39
39
  for (const withdrawal of requests.withdrawals) {
@@ -50,9 +50,6 @@ export function processExecutionPayloadEnvelope(
50
50
  const amount = payment.withdrawal.amount;
51
51
 
52
52
  if (amount > 0) {
53
- const exitQueueEpoch = computeExitEpochAndUpdateChurn(state, BigInt(amount));
54
-
55
- payment.withdrawal.withdrawableEpoch = exitQueueEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
56
53
  state.builderPendingWithdrawals.push(payment.withdrawal);
57
54
  }
58
55
 
@@ -75,6 +72,7 @@ function validateExecutionPayloadEnvelope(
75
72
  ): void {
76
73
  const payload = envelope.payload;
77
74
 
75
+ // Cache latest block header state root
78
76
  if (byteArrayEquals(state.latestBlockHeader.stateRoot, ssz.Root.defaultValue())) {
79
77
  const previousStateRoot = state.hashTreeRoot();
80
78
  state.latestBlockHeader.stateRoot = previousStateRoot;
@@ -87,20 +85,18 @@ function validateExecutionPayloadEnvelope(
87
85
  );
88
86
  }
89
87
 
90
- // Verify consistency with the beacon block
91
88
  if (envelope.slot !== state.slot) {
92
89
  throw new Error(`Slot mismatch between envelope and state envelope=${envelope.slot} state=${state.slot}`);
93
90
  }
94
91
 
95
- const committedBid = state.latestExecutionPayloadBid;
96
92
  // Verify consistency with the committed bid
93
+ const committedBid = state.latestExecutionPayloadBid;
97
94
  if (envelope.builderIndex !== committedBid.builderIndex) {
98
95
  throw new Error(
99
96
  `Builder index mismatch between envelope and committed bid envelope=${envelope.builderIndex} committedBid=${committedBid.builderIndex}`
100
97
  );
101
98
  }
102
99
 
103
- // Verify consistency with the committed bid
104
100
  const envelopeKzgRoot = ssz.deneb.BlobKzgCommitments.hashTreeRoot(envelope.blobKzgCommitments);
105
101
  if (!byteArrayEquals(committedBid.blobKzgCommitmentsRoot, envelopeKzgRoot)) {
106
102
  throw new Error(
@@ -108,11 +104,18 @@ function validateExecutionPayloadEnvelope(
108
104
  );
109
105
  }
110
106
 
111
- // Verify the withdrawals root
112
- const envelopeWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(envelope.payload.withdrawals);
113
- if (!byteArrayEquals(state.latestWithdrawalsRoot, envelopeWithdrawalsRoot)) {
107
+ if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
108
+ throw new Error(
109
+ `Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`
110
+ );
111
+ }
112
+
113
+ // Verify consistency with expected withdrawals
114
+ const payloadWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(payload.withdrawals);
115
+ const expectedWithdrawalsRoot = state.payloadExpectedWithdrawals.hashTreeRoot();
116
+ if (!byteArrayEquals(payloadWithdrawalsRoot, expectedWithdrawalsRoot)) {
114
117
  throw new Error(
115
- `Withdrawals root mismatch between envelope and latest withdrawals root envelope=${toRootHex(envelopeWithdrawalsRoot)} latestWithdrawalRoot=${toRootHex(state.latestWithdrawalsRoot)}`
118
+ `Withdrawals mismatch between payload and expected withdrawals payload=${toRootHex(payloadWithdrawalsRoot)} expected=${toRootHex(expectedWithdrawalsRoot)}`
116
119
  );
117
120
  }
118
121
 
@@ -137,13 +140,6 @@ function validateExecutionPayloadEnvelope(
137
140
  );
138
141
  }
139
142
 
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
143
  // Verify timestamp
148
144
  if (payload.timestamp !== computeTimeAtSlot(state.config, state.slot, state.genesisTime)) {
149
145
  throw new Error(
@@ -164,14 +160,22 @@ function validateExecutionPayloadEnvelope(
164
160
 
165
161
  function verifyExecutionPayloadEnvelopeSignature(
166
162
  state: CachedBeaconStateGloas,
167
- pubkey: Uint8Array,
168
163
  signedEnvelope: gloas.SignedExecutionPayloadEnvelope
169
164
  ): boolean {
165
+ const builderIndex = signedEnvelope.message.builderIndex;
166
+
170
167
  const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_BUILDER);
171
168
  const signingRoot = computeSigningRoot(ssz.gloas.ExecutionPayloadEnvelope, signedEnvelope.message, domain);
172
169
 
173
170
  try {
174
- const publicKey = PublicKey.fromBytes(pubkey);
171
+ let publicKey: PublicKey;
172
+
173
+ if (builderIndex === BUILDER_INDEX_SELF_BUILD) {
174
+ const validatorIndex = state.latestBlockHeader.proposerIndex;
175
+ publicKey = state.epochCtx.index2pubkey[validatorIndex];
176
+ } else {
177
+ publicKey = PublicKey.fromBytes(state.builders.getReadonly(builderIndex).pubkey);
178
+ }
175
179
  const signature = Signature.fromBytes(signedEnvelope.signature, true);
176
180
 
177
181
  return verify(signingRoot, publicKey, signature);
@@ -75,7 +75,7 @@ export function processOperations(
75
75
  const bodyElectra = body as electra.BeaconBlockBody;
76
76
 
77
77
  for (const depositRequest of bodyElectra.executionRequests.deposits) {
78
- processDepositRequest(stateElectra, depositRequest);
78
+ processDepositRequest(fork, stateElectra, depositRequest);
79
79
  }
80
80
 
81
81
  for (const elWithdrawalRequest of bodyElectra.executionRequests.withdrawals) {
@@ -1,8 +1,16 @@
1
+ import {PublicKey, Signature, verify} from "@chainsafe/blst";
1
2
  import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params";
2
- import {phase0} from "@lodestar/types";
3
+ import {phase0, ssz} from "@lodestar/types";
3
4
  import {verifyVoluntaryExitSignature} from "../signatureSets/index.js";
4
- import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
5
- import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js";
5
+ import {CachedBeaconStateAllForks, CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
6
+ import {
7
+ convertValidatorIndexToBuilderIndex,
8
+ getPendingBalanceToWithdrawForBuilder,
9
+ initiateBuilderExit,
10
+ isActiveBuilder,
11
+ isBuilderIndex,
12
+ } from "../util/gloas.js";
13
+ import {computeSigningRoot, getCurrentEpoch, getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js";
6
14
  import {initiateValidatorExit} from "./index.js";
7
15
 
8
16
  export enum VoluntaryExitValidity {
@@ -16,7 +24,7 @@ export enum VoluntaryExitValidity {
16
24
  }
17
25
 
18
26
  /**
19
- * Process a VoluntaryExit operation. Initiates the exit of a validator.
27
+ * Process a VoluntaryExit operation. Initiates the exit of a validator or builder.
20
28
  *
21
29
  * PERF: Work depends on number of VoluntaryExit per block. On regular networks the average is 0 / block.
22
30
  */
@@ -26,6 +34,53 @@ export function processVoluntaryExit(
26
34
  signedVoluntaryExit: phase0.SignedVoluntaryExit,
27
35
  verifySignature = true
28
36
  ): void {
37
+ const voluntaryExit = signedVoluntaryExit.message;
38
+ const currentEpoch = getCurrentEpoch(state);
39
+
40
+ // Exits must specify an epoch when they become valid; they are not valid before then
41
+ if (currentEpoch < voluntaryExit.epoch) {
42
+ throw Error(`Voluntary exit epoch ${voluntaryExit.epoch} is after current epoch ${currentEpoch}`);
43
+ }
44
+
45
+ // Check if this is a builder exit
46
+ if (fork >= ForkSeq.gloas && isBuilderIndex(voluntaryExit.validatorIndex)) {
47
+ const stateGloas = state as CachedBeaconStateGloas;
48
+ const builderIndex = convertValidatorIndexToBuilderIndex(voluntaryExit.validatorIndex);
49
+ const builder = stateGloas.builders.getReadonly(builderIndex);
50
+
51
+ // Verify the builder is active
52
+ if (!isActiveBuilder(stateGloas, builderIndex)) {
53
+ throw Error(`Builder ${builderIndex} is not active`);
54
+ }
55
+
56
+ // Only exit builder if it has no pending withdrawals in the queue
57
+ if (getPendingBalanceToWithdrawForBuilder(stateGloas, builderIndex) !== 0) {
58
+ throw Error(`Builder ${builderIndex} has pending withdrawals`);
59
+ }
60
+
61
+ // Verify signature
62
+ if (verifySignature) {
63
+ const domain = state.config.getDomainForVoluntaryExit(state.slot);
64
+ const signingRoot = computeSigningRoot(ssz.phase0.VoluntaryExit, voluntaryExit, domain);
65
+
66
+ try {
67
+ const publicKey = PublicKey.fromBytes(builder.pubkey);
68
+ const signature = Signature.fromBytes(signedVoluntaryExit.signature, true);
69
+
70
+ if (!verify(signingRoot, publicKey, signature)) {
71
+ throw Error("BLS verify failed");
72
+ }
73
+ } catch (e) {
74
+ throw Error(`Builder ${builderIndex} invalid exit signature reason=${(e as Error).message}`);
75
+ }
76
+ }
77
+
78
+ // Initiate builder exit
79
+ initiateBuilderExit(stateGloas, builderIndex);
80
+ return;
81
+ }
82
+
83
+ // Handle validator exit
29
84
  const validity = getVoluntaryExitValidity(fork, state, signedVoluntaryExit, verifySignature);
30
85
  if (validity !== VoluntaryExitValidity.valid) {
31
86
  throw Error(`Invalid voluntary exit at forkSeq=${fork} reason=${validity}`);