@lodestar/state-transition 1.38.0-dev.bc1fed4d3d → 1.38.0-dev.ebc352f211

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 (191) hide show
  1. package/lib/block/index.d.ts +4 -1
  2. package/lib/block/index.d.ts.map +1 -1
  3. package/lib/block/index.js +18 -9
  4. package/lib/block/index.js.map +1 -1
  5. package/lib/block/isValidIndexedAttestation.d.ts +3 -2
  6. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
  7. package/lib/block/isValidIndexedAttestation.js +4 -4
  8. package/lib/block/isValidIndexedAttestation.js.map +1 -1
  9. package/lib/block/isValidIndexedPayloadAttestation.d.ts +4 -0
  10. package/lib/block/isValidIndexedPayloadAttestation.d.ts.map +1 -0
  11. package/lib/block/isValidIndexedPayloadAttestation.js +14 -0
  12. package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -0
  13. package/lib/block/processAttestationPhase0.d.ts.map +1 -1
  14. package/lib/block/processAttestationPhase0.js +7 -2
  15. package/lib/block/processAttestationPhase0.js.map +1 -1
  16. package/lib/block/processAttestationsAltair.d.ts +3 -3
  17. package/lib/block/processAttestationsAltair.d.ts.map +1 -1
  18. package/lib/block/processAttestationsAltair.js +47 -5
  19. package/lib/block/processAttestationsAltair.js.map +1 -1
  20. package/lib/block/processAttesterSlashing.d.ts +2 -1
  21. package/lib/block/processAttesterSlashing.d.ts.map +1 -1
  22. package/lib/block/processAttesterSlashing.js +5 -4
  23. package/lib/block/processAttesterSlashing.js.map +1 -1
  24. package/lib/block/processConsolidationRequest.d.ts +3 -2
  25. package/lib/block/processConsolidationRequest.d.ts.map +1 -1
  26. package/lib/block/processConsolidationRequest.js +2 -2
  27. package/lib/block/processConsolidationRequest.js.map +1 -1
  28. package/lib/block/processDepositRequest.d.ts +2 -2
  29. package/lib/block/processDepositRequest.d.ts.map +1 -1
  30. package/lib/block/processDepositRequest.js.map +1 -1
  31. package/lib/block/processExecutionPayload.d.ts.map +1 -1
  32. package/lib/block/processExecutionPayload.js +4 -6
  33. package/lib/block/processExecutionPayload.js.map +1 -1
  34. package/lib/block/processExecutionPayloadBid.d.ts +5 -0
  35. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -0
  36. package/lib/block/processExecutionPayloadBid.js +89 -0
  37. package/lib/block/processExecutionPayloadBid.js.map +1 -0
  38. package/lib/block/processExecutionPayloadEnvelope.d.ts +4 -0
  39. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -0
  40. package/lib/block/processExecutionPayloadEnvelope.js +118 -0
  41. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -0
  42. package/lib/block/processOperations.d.ts.map +1 -1
  43. package/lib/block/processOperations.js +8 -2
  44. package/lib/block/processOperations.js.map +1 -1
  45. package/lib/block/processPayloadAttestation.d.ts +4 -0
  46. package/lib/block/processPayloadAttestation.d.ts.map +1 -0
  47. package/lib/block/processPayloadAttestation.js +16 -0
  48. package/lib/block/processPayloadAttestation.js.map +1 -0
  49. package/lib/block/processProposerSlashing.d.ts.map +1 -1
  50. package/lib/block/processProposerSlashing.js +17 -2
  51. package/lib/block/processProposerSlashing.js.map +1 -1
  52. package/lib/block/processRandao.js +1 -1
  53. package/lib/block/processRandao.js.map +1 -1
  54. package/lib/block/processSyncCommittee.d.ts +2 -1
  55. package/lib/block/processSyncCommittee.d.ts.map +1 -1
  56. package/lib/block/processSyncCommittee.js +3 -4
  57. package/lib/block/processSyncCommittee.js.map +1 -1
  58. package/lib/block/processVoluntaryExit.js +2 -2
  59. package/lib/block/processVoluntaryExit.js.map +1 -1
  60. package/lib/block/processWithdrawalRequest.d.ts +2 -2
  61. package/lib/block/processWithdrawalRequest.d.ts.map +1 -1
  62. package/lib/block/processWithdrawalRequest.js +1 -1
  63. package/lib/block/processWithdrawalRequest.js.map +1 -1
  64. package/lib/block/processWithdrawals.d.ts +4 -3
  65. package/lib/block/processWithdrawals.d.ts.map +1 -1
  66. package/lib/block/processWithdrawals.js +89 -19
  67. package/lib/block/processWithdrawals.js.map +1 -1
  68. package/lib/cache/epochCache.d.ts +5 -1
  69. package/lib/cache/epochCache.d.ts.map +1 -1
  70. package/lib/cache/epochCache.js +34 -1
  71. package/lib/cache/epochCache.js.map +1 -1
  72. package/lib/epoch/index.d.ts +4 -2
  73. package/lib/epoch/index.d.ts.map +1 -1
  74. package/lib/epoch/index.js +10 -1
  75. package/lib/epoch/index.js.map +1 -1
  76. package/lib/epoch/processBuilderPendingPayments.d.ts +6 -0
  77. package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -0
  78. package/lib/epoch/processBuilderPendingPayments.js +28 -0
  79. package/lib/epoch/processBuilderPendingPayments.js.map +1 -0
  80. package/lib/index.d.ts +1 -1
  81. package/lib/index.d.ts.map +1 -1
  82. package/lib/index.js.map +1 -1
  83. package/lib/signatureSets/attesterSlashings.d.ts +4 -3
  84. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -1
  85. package/lib/signatureSets/attesterSlashings.js +6 -6
  86. package/lib/signatureSets/attesterSlashings.js.map +1 -1
  87. package/lib/signatureSets/index.d.ts +3 -1
  88. package/lib/signatureSets/index.d.ts.map +1 -1
  89. package/lib/signatureSets/index.js +9 -8
  90. package/lib/signatureSets/index.js.map +1 -1
  91. package/lib/signatureSets/indexedAttestation.d.ts +4 -3
  92. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -1
  93. package/lib/signatureSets/indexedAttestation.js +6 -6
  94. package/lib/signatureSets/indexedAttestation.js.map +1 -1
  95. package/lib/signatureSets/indexedPayloadAttestation.d.ts +6 -0
  96. package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -0
  97. package/lib/signatureSets/indexedPayloadAttestation.js +11 -0
  98. package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -0
  99. package/lib/signatureSets/proposer.d.ts +5 -4
  100. package/lib/signatureSets/proposer.d.ts.map +1 -1
  101. package/lib/signatureSets/proposer.js +12 -12
  102. package/lib/signatureSets/proposer.js.map +1 -1
  103. package/lib/signatureSets/proposerSlashings.d.ts +3 -2
  104. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -1
  105. package/lib/signatureSets/proposerSlashings.js +4 -5
  106. package/lib/signatureSets/proposerSlashings.js.map +1 -1
  107. package/lib/signatureSets/randao.d.ts +3 -2
  108. package/lib/signatureSets/randao.d.ts.map +1 -1
  109. package/lib/signatureSets/randao.js +4 -5
  110. package/lib/signatureSets/randao.js.map +1 -1
  111. package/lib/signatureSets/voluntaryExits.d.ts +4 -3
  112. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  113. package/lib/signatureSets/voluntaryExits.js +6 -7
  114. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  115. package/lib/slot/index.d.ts +2 -1
  116. package/lib/slot/index.d.ts.map +1 -1
  117. package/lib/slot/index.js +6 -2
  118. package/lib/slot/index.js.map +1 -1
  119. package/lib/slot/upgradeStateToAltair.js +1 -1
  120. package/lib/slot/upgradeStateToAltair.js.map +1 -1
  121. package/lib/slot/upgradeStateToGloas.d.ts +0 -1
  122. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  123. package/lib/slot/upgradeStateToGloas.js +47 -5
  124. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  125. package/lib/stateTransition.js +5 -4
  126. package/lib/stateTransition.js.map +1 -1
  127. package/lib/util/electra.d.ts +5 -5
  128. package/lib/util/electra.d.ts.map +1 -1
  129. package/lib/util/electra.js +2 -1
  130. package/lib/util/electra.js.map +1 -1
  131. package/lib/util/epoch.d.ts +3 -3
  132. package/lib/util/epoch.d.ts.map +1 -1
  133. package/lib/util/epoch.js.map +1 -1
  134. package/lib/util/execution.d.ts +1 -16
  135. package/lib/util/execution.d.ts.map +1 -1
  136. package/lib/util/execution.js +1 -44
  137. package/lib/util/execution.js.map +1 -1
  138. package/lib/util/gloas.d.ts +11 -0
  139. package/lib/util/gloas.d.ts.map +1 -0
  140. package/lib/util/gloas.js +35 -0
  141. package/lib/util/gloas.js.map +1 -0
  142. package/lib/util/seed.d.ts +5 -1
  143. package/lib/util/seed.d.ts.map +1 -1
  144. package/lib/util/seed.js +33 -1
  145. package/lib/util/seed.js.map +1 -1
  146. package/lib/util/validator.d.ts +2 -2
  147. package/lib/util/validator.d.ts.map +1 -1
  148. package/lib/util/validator.js +14 -1
  149. package/lib/util/validator.js.map +1 -1
  150. package/package.json +6 -6
  151. package/src/block/index.ts +32 -15
  152. package/src/block/isValidIndexedAttestation.ts +5 -2
  153. package/src/block/isValidIndexedPayloadAttestation.ts +23 -0
  154. package/src/block/processAttestationPhase0.ts +13 -2
  155. package/src/block/processAttestationsAltair.ts +63 -6
  156. package/src/block/processAttesterSlashing.ts +6 -3
  157. package/src/block/processConsolidationRequest.ts +6 -5
  158. package/src/block/processDepositRequest.ts +5 -2
  159. package/src/block/processExecutionPayload.ts +8 -14
  160. package/src/block/processExecutionPayloadBid.ts +120 -0
  161. package/src/block/processExecutionPayloadEnvelope.ts +181 -0
  162. package/src/block/processOperations.ts +16 -4
  163. package/src/block/processPayloadAttestation.ts +25 -0
  164. package/src/block/processProposerSlashing.ts +25 -4
  165. package/src/block/processRandao.ts +1 -1
  166. package/src/block/processSyncCommittee.ts +4 -3
  167. package/src/block/processVoluntaryExit.ts +2 -2
  168. package/src/block/processWithdrawalRequest.ts +4 -4
  169. package/src/block/processWithdrawals.ts +118 -27
  170. package/src/cache/epochCache.ts +58 -1
  171. package/src/epoch/index.ts +12 -0
  172. package/src/epoch/processBuilderPendingPayments.ts +31 -0
  173. package/src/index.ts +2 -0
  174. package/src/signatureSets/attesterSlashings.ts +7 -3
  175. package/src/signatureSets/index.ts +10 -6
  176. package/src/signatureSets/indexedAttestation.ts +14 -3
  177. package/src/signatureSets/indexedPayloadAttestation.ts +24 -0
  178. package/src/signatureSets/proposer.ts +13 -7
  179. package/src/signatureSets/proposerSlashings.ts +5 -3
  180. package/src/signatureSets/randao.ts +13 -5
  181. package/src/signatureSets/voluntaryExits.ts +7 -4
  182. package/src/slot/index.ts +11 -3
  183. package/src/slot/upgradeStateToAltair.ts +2 -1
  184. package/src/slot/upgradeStateToGloas.ts +49 -5
  185. package/src/stateTransition.ts +5 -5
  186. package/src/util/electra.ts +15 -6
  187. package/src/util/epoch.ts +6 -3
  188. package/src/util/execution.ts +0 -60
  189. package/src/util/gloas.ts +58 -0
  190. package/src/util/seed.ts +57 -1
  191. package/src/util/validator.ts +21 -2
@@ -0,0 +1,120 @@
1
+ import {PublicKey, Signature, verify} from "@chainsafe/blst";
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";
10
+ import {BeaconBlock, gloas, ssz} from "@lodestar/types";
11
+ import {toHex, toRootHex} from "@lodestar/utils";
12
+ import {G2_POINT_AT_INFINITY} from "../constants/constants.ts";
13
+ import {CachedBeaconStateGloas} from "../types.ts";
14
+ import {hasBuilderWithdrawalCredential} from "../util/gloas.ts";
15
+ import {computeSigningRoot, getCurrentEpoch, getRandaoMix, isActiveValidator} from "../util/index.ts";
16
+
17
+ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block: BeaconBlock<ForkPostGloas>): void {
18
+ const signedBid = block.body.signedExecutionPayloadBid;
19
+ const bid = signedBid.message;
20
+ const {builderIndex, value: amount} = bid;
21
+ const builder = state.validators.getReadonly(builderIndex);
22
+
23
+ // For self-builds, amount must be zero regardless of withdrawal credential prefix
24
+ if (builderIndex === block.proposerIndex) {
25
+ if (amount !== 0) {
26
+ throw Error(`Invalid execution payload bid: self-build with non-zero amount ${amount}`);
27
+ }
28
+ if (!byteArrayEquals(signedBid.signature, G2_POINT_AT_INFINITY)) {
29
+ throw Error("Invalid execution payload bid: self-build with non-zero signature");
30
+ }
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`);
35
+ }
36
+
37
+ if (!verifyExecutionPayloadBidSignature(state, builder.pubkey, signedBid)) {
38
+ throw Error(`Invalid execution payload bid: invalid signature for builder ${builderIndex}`);
39
+ }
40
+ }
41
+
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
+ if (bid.slot !== block.slot) {
67
+ throw Error(`Bid slot ${bid.slot} does not match block slot ${block.slot}`);
68
+ }
69
+
70
+ if (!byteArrayEquals(bid.parentBlockHash, state.latestBlockHash)) {
71
+ throw Error(
72
+ `Parent block hash ${toRootHex(bid.parentBlockHash)} of bid does not match state's latest block hash ${toRootHex(state.latestBlockHash)}`
73
+ );
74
+ }
75
+
76
+ if (!byteArrayEquals(bid.parentBlockRoot, block.parentRoot)) {
77
+ throw Error(
78
+ `Parent block root ${toRootHex(bid.parentBlockRoot)} of bid does not match block's parent root ${toRootHex(block.parentRoot)}`
79
+ );
80
+ }
81
+
82
+ const stateRandao = getRandaoMix(state, getCurrentEpoch(state));
83
+ if (!byteArrayEquals(bid.prevRandao, stateRandao)) {
84
+ throw Error(`Prev randao ${toHex(bid.prevRandao)} of bid does not match state's randao mix ${toHex(stateRandao)}`);
85
+ }
86
+
87
+ if (amount > 0) {
88
+ const pendingPaymentView = ssz.gloas.BuilderPendingPayment.toViewDU({
89
+ weight: 0,
90
+ withdrawal: ssz.gloas.BuilderPendingWithdrawal.toViewDU({
91
+ feeRecipient: bid.feeRecipient,
92
+ amount,
93
+ builderIndex,
94
+ withdrawableEpoch: FAR_FUTURE_EPOCH,
95
+ }),
96
+ });
97
+
98
+ state.builderPendingPayments.set(SLOTS_PER_EPOCH + (bid.slot % SLOTS_PER_EPOCH), pendingPaymentView);
99
+ }
100
+
101
+ state.latestExecutionPayloadBid = ssz.gloas.ExecutionPayloadBid.toViewDU(bid);
102
+ }
103
+
104
+ function verifyExecutionPayloadBidSignature(
105
+ state: CachedBeaconStateGloas,
106
+ pubkey: Uint8Array,
107
+ signedBid: gloas.SignedExecutionPayloadBid
108
+ ): boolean {
109
+ const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_BUILDER);
110
+ const signingRoot = computeSigningRoot(ssz.gloas.ExecutionPayloadBid, signedBid.message, domain);
111
+
112
+ try {
113
+ const publicKey = PublicKey.fromBytes(pubkey);
114
+ const signature = Signature.fromBytes(signedBid.signature, true);
115
+
116
+ return verify(signingRoot, publicKey, signature);
117
+ } catch (_e) {
118
+ return false; // Catch all BLS errors: failed key validation, failed signature validation, invalid signature
119
+ }
120
+ }
@@ -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 {CachedBeaconStateAllForks, CachedBeaconStateCapella, CachedBeaconStateElectra} from "../types.js";
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) => epochCtx.index2pubkey[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));