@lodestar/state-transition 1.37.0 → 1.38.0-dev.1ddbe5d870

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) 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 +19 -8
  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/processExecutionPayloadBid.d.ts +5 -0
  32. package/lib/block/processExecutionPayloadBid.d.ts.map +1 -0
  33. package/lib/block/processExecutionPayloadBid.js +89 -0
  34. package/lib/block/processExecutionPayloadBid.js.map +1 -0
  35. package/lib/block/processExecutionPayloadEnvelope.d.ts +4 -0
  36. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -0
  37. package/lib/block/processExecutionPayloadEnvelope.js +118 -0
  38. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -0
  39. package/lib/block/processOperations.d.ts.map +1 -1
  40. package/lib/block/processOperations.js +8 -2
  41. package/lib/block/processOperations.js.map +1 -1
  42. package/lib/block/processPayloadAttestation.d.ts +4 -0
  43. package/lib/block/processPayloadAttestation.d.ts.map +1 -0
  44. package/lib/block/processPayloadAttestation.js +16 -0
  45. package/lib/block/processPayloadAttestation.js.map +1 -0
  46. package/lib/block/processProposerSlashing.d.ts.map +1 -1
  47. package/lib/block/processProposerSlashing.js +17 -2
  48. package/lib/block/processProposerSlashing.js.map +1 -1
  49. package/lib/block/processRandao.js +1 -1
  50. package/lib/block/processRandao.js.map +1 -1
  51. package/lib/block/processSyncCommittee.d.ts +2 -1
  52. package/lib/block/processSyncCommittee.d.ts.map +1 -1
  53. package/lib/block/processSyncCommittee.js +3 -4
  54. package/lib/block/processSyncCommittee.js.map +1 -1
  55. package/lib/block/processVoluntaryExit.js +2 -2
  56. package/lib/block/processVoluntaryExit.js.map +1 -1
  57. package/lib/block/processWithdrawalRequest.d.ts +2 -2
  58. package/lib/block/processWithdrawalRequest.d.ts.map +1 -1
  59. package/lib/block/processWithdrawalRequest.js +1 -1
  60. package/lib/block/processWithdrawalRequest.js.map +1 -1
  61. package/lib/block/processWithdrawals.d.ts +4 -3
  62. package/lib/block/processWithdrawals.d.ts.map +1 -1
  63. package/lib/block/processWithdrawals.js +89 -19
  64. package/lib/block/processWithdrawals.js.map +1 -1
  65. package/lib/cache/epochCache.d.ts +5 -1
  66. package/lib/cache/epochCache.d.ts.map +1 -1
  67. package/lib/cache/epochCache.js +34 -1
  68. package/lib/cache/epochCache.js.map +1 -1
  69. package/lib/epoch/index.d.ts +4 -2
  70. package/lib/epoch/index.d.ts.map +1 -1
  71. package/lib/epoch/index.js +10 -1
  72. package/lib/epoch/index.js.map +1 -1
  73. package/lib/epoch/processBuilderPendingPayments.d.ts +6 -0
  74. package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -0
  75. package/lib/epoch/processBuilderPendingPayments.js +28 -0
  76. package/lib/epoch/processBuilderPendingPayments.js.map +1 -0
  77. package/lib/index.d.ts +1 -1
  78. package/lib/index.d.ts.map +1 -1
  79. package/lib/index.js.map +1 -1
  80. package/lib/signatureSets/attesterSlashings.d.ts +4 -3
  81. package/lib/signatureSets/attesterSlashings.d.ts.map +1 -1
  82. package/lib/signatureSets/attesterSlashings.js +6 -6
  83. package/lib/signatureSets/attesterSlashings.js.map +1 -1
  84. package/lib/signatureSets/index.d.ts +4 -2
  85. package/lib/signatureSets/index.d.ts.map +1 -1
  86. package/lib/signatureSets/index.js +9 -8
  87. package/lib/signatureSets/index.js.map +1 -1
  88. package/lib/signatureSets/indexedAttestation.d.ts +4 -3
  89. package/lib/signatureSets/indexedAttestation.d.ts.map +1 -1
  90. package/lib/signatureSets/indexedAttestation.js +9 -7
  91. package/lib/signatureSets/indexedAttestation.js.map +1 -1
  92. package/lib/signatureSets/indexedPayloadAttestation.d.ts +6 -0
  93. package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -0
  94. package/lib/signatureSets/indexedPayloadAttestation.js +11 -0
  95. package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -0
  96. package/lib/signatureSets/proposer.d.ts +5 -4
  97. package/lib/signatureSets/proposer.d.ts.map +1 -1
  98. package/lib/signatureSets/proposer.js +12 -12
  99. package/lib/signatureSets/proposer.js.map +1 -1
  100. package/lib/signatureSets/proposerSlashings.d.ts +3 -2
  101. package/lib/signatureSets/proposerSlashings.d.ts.map +1 -1
  102. package/lib/signatureSets/proposerSlashings.js +4 -5
  103. package/lib/signatureSets/proposerSlashings.js.map +1 -1
  104. package/lib/signatureSets/randao.d.ts +3 -2
  105. package/lib/signatureSets/randao.d.ts.map +1 -1
  106. package/lib/signatureSets/randao.js +4 -5
  107. package/lib/signatureSets/randao.js.map +1 -1
  108. package/lib/signatureSets/voluntaryExits.d.ts +4 -3
  109. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  110. package/lib/signatureSets/voluntaryExits.js +6 -7
  111. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  112. package/lib/slot/index.d.ts +2 -1
  113. package/lib/slot/index.d.ts.map +1 -1
  114. package/lib/slot/index.js +6 -2
  115. package/lib/slot/index.js.map +1 -1
  116. package/lib/slot/upgradeStateToAltair.js +1 -1
  117. package/lib/slot/upgradeStateToAltair.js.map +1 -1
  118. package/lib/slot/upgradeStateToGloas.d.ts +0 -1
  119. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  120. package/lib/slot/upgradeStateToGloas.js +47 -5
  121. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  122. package/lib/stateTransition.js +5 -4
  123. package/lib/stateTransition.js.map +1 -1
  124. package/lib/util/electra.d.ts +5 -5
  125. package/lib/util/electra.d.ts.map +1 -1
  126. package/lib/util/electra.js +2 -1
  127. package/lib/util/electra.js.map +1 -1
  128. package/lib/util/epoch.d.ts +3 -3
  129. package/lib/util/epoch.d.ts.map +1 -1
  130. package/lib/util/epoch.js.map +1 -1
  131. package/lib/util/gloas.d.ts +11 -0
  132. package/lib/util/gloas.d.ts.map +1 -0
  133. package/lib/util/gloas.js +35 -0
  134. package/lib/util/gloas.js.map +1 -0
  135. package/lib/util/seed.d.ts +5 -1
  136. package/lib/util/seed.d.ts.map +1 -1
  137. package/lib/util/seed.js +33 -1
  138. package/lib/util/seed.js.map +1 -1
  139. package/lib/util/validator.d.ts +2 -2
  140. package/lib/util/validator.d.ts.map +1 -1
  141. package/lib/util/validator.js +14 -1
  142. package/lib/util/validator.js.map +1 -1
  143. package/package.json +6 -6
  144. package/src/block/index.ts +35 -14
  145. package/src/block/isValidIndexedAttestation.ts +5 -2
  146. package/src/block/isValidIndexedPayloadAttestation.ts +23 -0
  147. package/src/block/processAttestationPhase0.ts +13 -2
  148. package/src/block/processAttestationsAltair.ts +63 -6
  149. package/src/block/processAttesterSlashing.ts +6 -3
  150. package/src/block/processConsolidationRequest.ts +6 -5
  151. package/src/block/processDepositRequest.ts +5 -2
  152. package/src/block/processExecutionPayloadBid.ts +120 -0
  153. package/src/block/processExecutionPayloadEnvelope.ts +181 -0
  154. package/src/block/processOperations.ts +16 -4
  155. package/src/block/processPayloadAttestation.ts +25 -0
  156. package/src/block/processProposerSlashing.ts +25 -4
  157. package/src/block/processRandao.ts +1 -1
  158. package/src/block/processSyncCommittee.ts +4 -3
  159. package/src/block/processVoluntaryExit.ts +2 -2
  160. package/src/block/processWithdrawalRequest.ts +4 -4
  161. package/src/block/processWithdrawals.ts +118 -27
  162. package/src/cache/epochCache.ts +58 -1
  163. package/src/epoch/index.ts +12 -0
  164. package/src/epoch/processBuilderPendingPayments.ts +31 -0
  165. package/src/index.ts +2 -0
  166. package/src/signatureSets/attesterSlashings.ts +7 -3
  167. package/src/signatureSets/index.ts +12 -7
  168. package/src/signatureSets/indexedAttestation.ts +20 -9
  169. package/src/signatureSets/indexedPayloadAttestation.ts +24 -0
  170. package/src/signatureSets/proposer.ts +13 -7
  171. package/src/signatureSets/proposerSlashings.ts +5 -3
  172. package/src/signatureSets/randao.ts +13 -5
  173. package/src/signatureSets/voluntaryExits.ts +7 -4
  174. package/src/slot/index.ts +11 -3
  175. package/src/slot/upgradeStateToAltair.ts +2 -1
  176. package/src/slot/upgradeStateToGloas.ts +49 -5
  177. package/src/stateTransition.ts +5 -5
  178. package/src/util/electra.ts +15 -6
  179. package/src/util/epoch.ts +6 -3
  180. package/src/util/gloas.ts +58 -0
  181. package/src/util/seed.ts +57 -1
  182. package/src/util/validator.ts +21 -2
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.37.0",
14
+ "version": "1.38.0-dev.1ddbe5d870",
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
@@ -62,10 +62,10 @@
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.37.0",
66
- "@lodestar/params": "^1.37.0",
67
- "@lodestar/types": "^1.37.0",
68
- "@lodestar/utils": "^1.37.0",
65
+ "@lodestar/config": "1.38.0-dev.1ddbe5d870",
66
+ "@lodestar/params": "1.38.0-dev.1ddbe5d870",
67
+ "@lodestar/types": "1.38.0-dev.1ddbe5d870",
68
+ "@lodestar/utils": "1.38.0-dev.1ddbe5d870",
69
69
  "bigint-buffer": "^1.1.5"
70
70
  },
71
71
  "keywords": [
@@ -74,5 +74,5 @@
74
74
  "beacon",
75
75
  "blockchain"
76
76
  ],
77
- "gitHead": "eaf5bc974a483104665b6ed57b1a15e2fd48f730"
77
+ "gitHead": "6971d287b75663e80ed5f5635d26e81892a53476"
78
78
  }
@@ -1,14 +1,22 @@
1
- import {ForkSeq} from "@lodestar/params";
1
+ import {ForkPostGloas, ForkSeq} from "@lodestar/params";
2
2
  import {BeaconBlock, BlindedBeaconBlock, altair, capella} from "@lodestar/types";
3
3
  import {BeaconStateTransitionMetrics} from "../metrics.js";
4
- import {CachedBeaconStateAllForks, CachedBeaconStateBellatrix, CachedBeaconStateCapella} from "../types.js";
4
+ import {
5
+ CachedBeaconStateAllForks,
6
+ CachedBeaconStateBellatrix,
7
+ CachedBeaconStateCapella,
8
+ CachedBeaconStateGloas,
9
+ } from "../types.js";
5
10
  import {getFullOrBlindedPayload, isExecutionEnabled} from "../util/execution.js";
6
11
  import {BlockExternalData, DataAvailabilityStatus} from "./externalData.js";
7
12
  import {processBlobKzgCommitments} from "./processBlobKzgCommitments.js";
8
13
  import {processBlockHeader} from "./processBlockHeader.js";
9
14
  import {processEth1Data} from "./processEth1Data.js";
10
15
  import {processExecutionPayload} from "./processExecutionPayload.js";
16
+ import {processExecutionPayloadBid} from "./processExecutionPayloadBid.ts";
17
+ import {processExecutionPayloadEnvelope} from "./processExecutionPayloadEnvelope.ts";
11
18
  import {processOperations} from "./processOperations.js";
19
+ import {processPayloadAttestation} from "./processPayloadAttestation.ts";
12
20
  import {processRandao} from "./processRandao.js";
13
21
  import {processSyncAggregate} from "./processSyncCommittee.js";
14
22
  import {processWithdrawals} from "./processWithdrawals.js";
@@ -22,6 +30,9 @@ export {
22
30
  processEth1Data,
23
31
  processSyncAggregate,
24
32
  processWithdrawals,
33
+ processExecutionPayloadBid,
34
+ processPayloadAttestation,
35
+ processExecutionPayloadEnvelope,
25
36
  };
26
37
 
27
38
  export * from "./externalData.js";
@@ -41,23 +52,33 @@ export function processBlock(
41
52
 
42
53
  processBlockHeader(state, block);
43
54
 
44
- // The call to the process_execution_payload must happen before the call to the process_randao as the former depends
45
- // on the randao_mix computed with the reveal of the previous block.
46
- if (fork >= ForkSeq.bellatrix && isExecutionEnabled(state as CachedBeaconStateBellatrix, block)) {
55
+ if (fork >= ForkSeq.gloas) {
56
+ // After gloas, processWithdrawals does not take a payload parameter
57
+ processWithdrawals(fork, state as CachedBeaconStateGloas);
58
+ } else if (fork >= ForkSeq.capella) {
47
59
  const fullOrBlindedPayload = getFullOrBlindedPayload(block);
48
- // TODO Deneb: Allow to disable withdrawals for interop testing
49
- // https://github.com/ethereum/consensus-specs/blob/b62c9e877990242d63aa17a2a59a49bc649a2f2e/specs/eip4844/beacon-chain.md#disabling-withdrawals
50
- if (fork >= ForkSeq.capella) {
51
- processWithdrawals(
52
- fork,
53
- state as CachedBeaconStateCapella,
54
- fullOrBlindedPayload as capella.FullOrBlindedExecutionPayload
55
- );
56
- }
60
+ processWithdrawals(
61
+ fork,
62
+ state as CachedBeaconStateCapella,
63
+ fullOrBlindedPayload as capella.FullOrBlindedExecutionPayload
64
+ );
65
+ }
57
66
 
67
+ // The call to the process_execution_payload must happen before the call to the process_randao as the former depends
68
+ // on the randao_mix computed with the reveal of the previous block.
69
+ // TODO GLOAS: We call processExecutionPayload somewhere else post-gloas
70
+ if (
71
+ fork >= ForkSeq.bellatrix &&
72
+ fork < ForkSeq.gloas &&
73
+ isExecutionEnabled(state as CachedBeaconStateBellatrix, block)
74
+ ) {
58
75
  processExecutionPayload(fork, state as CachedBeaconStateBellatrix, block.body, externalData);
59
76
  }
60
77
 
78
+ if (fork >= ForkSeq.gloas) {
79
+ processExecutionPayloadBid(state as CachedBeaconStateGloas, block as BeaconBlock<ForkPostGloas>);
80
+ }
81
+
61
82
  processRandao(state, block, verifySignatures);
62
83
  processEth1Data(state, block.body.eth1Data);
63
84
  processOperations(fork, state, block.body, opts, metrics);
@@ -1,5 +1,6 @@
1
1
  import {ForkSeq, MAX_COMMITTEES_PER_SLOT, MAX_VALIDATORS_PER_COMMITTEE} from "@lodestar/params";
2
2
  import {IndexedAttestation, IndexedAttestationBigint} from "@lodestar/types";
3
+ import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
3
4
  import {getIndexedAttestationBigintSignatureSet, getIndexedAttestationSignatureSet} from "../signatureSets/index.js";
4
5
  import {CachedBeaconStateAllForks} from "../types.js";
5
6
  import {verifySignatureSet} from "../util/index.js";
@@ -8,6 +9,7 @@ import {verifySignatureSet} from "../util/index.js";
8
9
  * Check if `indexedAttestation` has sorted and unique indices and a valid aggregate signature.
9
10
  */
10
11
  export function isValidIndexedAttestation(
12
+ index2pubkey: Index2PubkeyCache,
11
13
  state: CachedBeaconStateAllForks,
12
14
  indexedAttestation: IndexedAttestation,
13
15
  verifySignature: boolean
@@ -17,12 +19,13 @@ export function isValidIndexedAttestation(
17
19
  }
18
20
 
19
21
  if (verifySignature) {
20
- return verifySignatureSet(getIndexedAttestationSignatureSet(state, indexedAttestation));
22
+ return verifySignatureSet(getIndexedAttestationSignatureSet(index2pubkey, state, indexedAttestation));
21
23
  }
22
24
  return true;
23
25
  }
24
26
 
25
27
  export function isValidIndexedAttestationBigint(
28
+ index2pubkey: Index2PubkeyCache,
26
29
  state: CachedBeaconStateAllForks,
27
30
  indexedAttestation: IndexedAttestationBigint,
28
31
  verifySignature: boolean
@@ -32,7 +35,7 @@ export function isValidIndexedAttestationBigint(
32
35
  }
33
36
 
34
37
  if (verifySignature) {
35
- return verifySignatureSet(getIndexedAttestationBigintSignatureSet(state, indexedAttestation));
38
+ return verifySignatureSet(getIndexedAttestationBigintSignatureSet(index2pubkey, state, indexedAttestation));
36
39
  }
37
40
  return true;
38
41
  }
@@ -0,0 +1,23 @@
1
+ import {gloas} from "@lodestar/types";
2
+ import {getIndexedPayloadAttestationSignatureSet} from "../signatureSets/index.ts";
3
+ import {CachedBeaconStateGloas} from "../types.js";
4
+ import {verifySignatureSet} from "../util/index.ts";
5
+
6
+ export function isValidIndexedPayloadAttestation(
7
+ state: CachedBeaconStateGloas,
8
+ indexedPayloadAttestation: gloas.IndexedPayloadAttestation,
9
+ verifySignature: boolean
10
+ ): boolean {
11
+ const indices = indexedPayloadAttestation.attestingIndices;
12
+ const isSorted = indices.every((val, i, arr) => i === 0 || arr[i - 1] <= val);
13
+
14
+ if (indices.length === 0 || !isSorted) {
15
+ return false;
16
+ }
17
+
18
+ if (verifySignature) {
19
+ return verifySignatureSet(getIndexedPayloadAttestationSignatureSet(state, indexedPayloadAttestation));
20
+ }
21
+
22
+ return true;
23
+ }
@@ -50,7 +50,14 @@ export function processAttestationPhase0(
50
50
  state.previousEpochAttestations.push(pendingAttestation);
51
51
  }
52
52
 
53
- if (!isValidIndexedAttestation(state, epochCtx.getIndexedAttestation(ForkSeq.phase0, attestation), verifySignature)) {
53
+ if (
54
+ !isValidIndexedAttestation(
55
+ epochCtx.index2pubkey,
56
+ state,
57
+ epochCtx.getIndexedAttestation(ForkSeq.phase0, attestation),
58
+ verifySignature
59
+ )
60
+ ) {
54
61
  throw new Error("Attestation is not valid");
55
62
  }
56
63
  }
@@ -86,7 +93,11 @@ export function validateAttestation(fork: ForkSeq, state: CachedBeaconStateAllFo
86
93
  }
87
94
 
88
95
  if (fork >= ForkSeq.electra) {
89
- assert.equal(data.index, 0, `AttestationData.index must be zero: index=${data.index}`);
96
+ if (fork >= ForkSeq.gloas) {
97
+ assert.lt(data.index, 2, `AttestationData.index must be 0 or 1: index=${data.index}`);
98
+ } else {
99
+ assert.equal(data.index, 0, `AttestationData.index must be 0: index=${data.index}`);
100
+ }
90
101
  const attestationElectra = attestation as electra.Attestation;
91
102
  const committeeIndices = attestationElectra.committeeBits.getTrueBitIndexes();
92
103
 
@@ -1,9 +1,11 @@
1
1
  import {byteArrayEquals} from "@chainsafe/ssz";
2
2
  import {
3
+ EFFECTIVE_BALANCE_INCREMENT,
3
4
  ForkSeq,
4
5
  MIN_ATTESTATION_INCLUSION_DELAY,
5
6
  PROPOSER_WEIGHT,
6
7
  SLOTS_PER_EPOCH,
8
+ SLOTS_PER_HISTORICAL_ROOT,
7
9
  TIMELY_HEAD_FLAG_INDEX,
8
10
  TIMELY_HEAD_WEIGHT,
9
11
  TIMELY_SOURCE_FLAG_INDEX,
@@ -16,7 +18,8 @@ import {Attestation, Epoch, phase0} from "@lodestar/types";
16
18
  import {intSqrt} from "@lodestar/utils";
17
19
  import {BeaconStateTransitionMetrics} from "../metrics.js";
18
20
  import {getAttestationWithIndicesSignatureSet} from "../signatureSets/indexedAttestation.js";
19
- import {CachedBeaconStateAltair} from "../types.js";
21
+ import {CachedBeaconStateAltair, CachedBeaconStateGloas} from "../types.js";
22
+ import {isAttestationSameSlot, isAttestationSameSlotRootCache} from "../util/gloas.ts";
20
23
  import {increaseBalance, verifySignatureSet} from "../util/index.js";
21
24
  import {RootCache} from "../util/rootCache.js";
22
25
  import {checkpointToStr, isTimelyTarget, validateAttestation} from "./processAttestationPhase0.js";
@@ -31,7 +34,7 @@ const SLOTS_PER_EPOCH_SQRT = intSqrt(SLOTS_PER_EPOCH);
31
34
 
32
35
  export function processAttestationsAltair(
33
36
  fork: ForkSeq,
34
- state: CachedBeaconStateAltair,
37
+ state: CachedBeaconStateAltair | CachedBeaconStateGloas,
35
38
  attestations: Attestation[],
36
39
  verifySignature = true,
37
40
  metrics?: BeaconStateTransitionMetrics | null
@@ -46,6 +49,9 @@ export function processAttestationsAltair(
46
49
  let proposerReward = 0;
47
50
  let newSeenAttesters = 0;
48
51
  let newSeenAttestersEffectiveBalance = 0;
52
+
53
+ const builderWeightMap: Map<number, number> = new Map();
54
+
49
55
  for (const attestation of attestations) {
50
56
  const data = attestation.data;
51
57
 
@@ -58,7 +64,7 @@ export function processAttestationsAltair(
58
64
  // TODO: Why should we verify an indexed attestation that we just created? If it's just for the signature
59
65
  // we can verify only that and nothing else.
60
66
  if (verifySignature) {
61
- const sigSet = getAttestationWithIndicesSignatureSet(state, attestation, attestingIndices);
67
+ const sigSet = getAttestationWithIndicesSignatureSet(epochCtx.index2pubkey, state, attestation, attestingIndices);
62
68
  if (!verifySignatureSet(sigSet)) {
63
69
  throw new Error("Attestation signature is not valid");
64
70
  }
@@ -66,13 +72,16 @@ export function processAttestationsAltair(
66
72
 
67
73
  const inCurrentEpoch = data.target.epoch === currentEpoch;
68
74
  const epochParticipation = inCurrentEpoch ? state.currentEpochParticipation : state.previousEpochParticipation;
75
+ // Count how much additional weight added to current or previous epoch's builder pending payment (in ETH increment)
76
+ let paymentWeightToAdd = 0;
69
77
 
70
78
  const flagsAttestation = getAttestationParticipationStatus(
71
79
  fork,
72
80
  data,
73
81
  stateSlot - data.slot,
74
82
  epochCtx.epoch,
75
- rootCache
83
+ rootCache,
84
+ fork >= ForkSeq.gloas ? (state as CachedBeaconStateGloas).executionPayloadAvailability.toBoolArray() : null
76
85
  );
77
86
 
78
87
  // For each participant, update their participation
@@ -121,12 +130,35 @@ export function processAttestationsAltair(
121
130
  }
122
131
  }
123
132
  }
133
+
134
+ if (fork >= ForkSeq.gloas && flagsNewSet !== 0 && isAttestationSameSlot(state as CachedBeaconStateGloas, data)) {
135
+ paymentWeightToAdd += effectiveBalanceIncrements[validatorIndex];
136
+ }
124
137
  }
125
138
 
126
139
  // Do the discrete math inside the loop to ensure a deterministic result
127
140
  const totalIncrements = totalBalanceIncrementsWithWeight;
128
141
  const proposerRewardNumerator = totalIncrements * state.epochCtx.baseRewardPerIncrement;
129
142
  proposerReward += Math.floor(proposerRewardNumerator / PROPOSER_REWARD_DOMINATOR);
143
+
144
+ if (fork >= ForkSeq.gloas) {
145
+ const builderPendingPaymentIndex = inCurrentEpoch
146
+ ? SLOTS_PER_EPOCH + (data.slot % SLOTS_PER_EPOCH)
147
+ : data.slot % SLOTS_PER_EPOCH;
148
+
149
+ const existingWeight =
150
+ builderWeightMap.get(builderPendingPaymentIndex) ??
151
+ (state as CachedBeaconStateGloas).builderPendingPayments.get(builderPendingPaymentIndex).weight;
152
+ const updatedWeight = existingWeight + paymentWeightToAdd * EFFECTIVE_BALANCE_INCREMENT;
153
+ builderWeightMap.set(builderPendingPaymentIndex, updatedWeight);
154
+ }
155
+ }
156
+
157
+ for (const [index, weight] of builderWeightMap) {
158
+ const payment = (state as CachedBeaconStateGloas).builderPendingPayments.get(index);
159
+ if (payment.withdrawal.amount > 0) {
160
+ payment.weight = weight;
161
+ }
130
162
  }
131
163
 
132
164
  metrics?.newSeenAttestersPerBlock.set(newSeenAttesters);
@@ -145,7 +177,8 @@ export function getAttestationParticipationStatus(
145
177
  data: phase0.AttestationData,
146
178
  inclusionDelay: number,
147
179
  currentEpoch: Epoch,
148
- rootCache: RootCache
180
+ rootCache: RootCache,
181
+ executionPayloadAvailability: boolean[] | null
149
182
  ): number {
150
183
  const justifiedCheckpoint =
151
184
  data.target.epoch === currentEpoch ? rootCache.currentJustifiedCheckpoint : rootCache.previousJustifiedCheckpoint;
@@ -168,9 +201,33 @@ export function getAttestationParticipationStatus(
168
201
  const isMatchingTarget = byteArrayEquals(data.target.root, rootCache.getBlockRoot(data.target.epoch));
169
202
 
170
203
  // a timely head is only be set if the target is _also_ matching
171
- const isMatchingHead =
204
+ // In gloas, this is called `head_root_matches`
205
+ let isMatchingHead =
172
206
  isMatchingTarget && byteArrayEquals(data.beaconBlockRoot, rootCache.getBlockRootAtSlot(data.slot));
173
207
 
208
+ if (fork >= ForkSeq.gloas) {
209
+ let isMatchingPayload = false;
210
+
211
+ if (isAttestationSameSlotRootCache(rootCache, data)) {
212
+ if (data.index !== 0) {
213
+ throw new Error("Attesting same slot must indicate empty payload");
214
+ }
215
+ isMatchingPayload = true;
216
+ } else {
217
+ if (executionPayloadAvailability === null) {
218
+ throw new Error("Must supply executionPayloadAvailability post-gloas");
219
+ }
220
+
221
+ if (data.index !== 0 && data.index !== 1) {
222
+ throw new Error(`data index must be 0 or 1 index=${data.index}`);
223
+ }
224
+
225
+ isMatchingPayload = Boolean(data.index) === executionPayloadAvailability[data.slot % SLOTS_PER_HISTORICAL_ROOT];
226
+ }
227
+
228
+ isMatchingHead = isMatchingHead && isMatchingPayload;
229
+ }
230
+
174
231
  let flags = 0;
175
232
  if (isMatchingSource && inclusionDelay <= SLOTS_PER_EPOCH_SQRT) flags |= TIMELY_SOURCE;
176
233
  if (isMatchingTarget && isTimelyTarget(fork, inclusionDelay)) flags |= TIMELY_TARGET;
@@ -1,5 +1,6 @@
1
1
  import {ForkSeq} from "@lodestar/params";
2
2
  import {AttesterSlashing} from "@lodestar/types";
3
+ import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
3
4
  import {CachedBeaconStateAllForks} from "../types.js";
4
5
  import {getAttesterSlashableIndices, isSlashableAttestationData, isSlashableValidator} from "../util/index.js";
5
6
  import {isValidIndexedAttestationBigint} from "./isValidIndexedAttestation.js";
@@ -17,7 +18,8 @@ export function processAttesterSlashing(
17
18
  attesterSlashing: AttesterSlashing,
18
19
  verifySignatures = true
19
20
  ): void {
20
- assertValidAttesterSlashing(state, attesterSlashing, verifySignatures);
21
+ const {epochCtx} = state;
22
+ assertValidAttesterSlashing(epochCtx.index2pubkey, state, attesterSlashing, verifySignatures);
21
23
 
22
24
  const intersectingIndices = getAttesterSlashableIndices(attesterSlashing);
23
25
 
@@ -25,7 +27,7 @@ export function processAttesterSlashing(
25
27
  const validators = state.validators; // Get the validators sub tree once for all indices
26
28
  // Spec requires to sort indexes beforehand
27
29
  for (const index of intersectingIndices.sort((a, b) => a - b)) {
28
- if (isSlashableValidator(validators.getReadonly(index), state.epochCtx.epoch)) {
30
+ if (isSlashableValidator(validators.getReadonly(index), epochCtx.epoch)) {
29
31
  slashValidator(fork, state, index);
30
32
  slashedAny = true;
31
33
  }
@@ -37,6 +39,7 @@ export function processAttesterSlashing(
37
39
  }
38
40
 
39
41
  export function assertValidAttesterSlashing(
42
+ index2pubkey: Index2PubkeyCache,
40
43
  state: CachedBeaconStateAllForks,
41
44
  attesterSlashing: AttesterSlashing,
42
45
  verifySignatures = true
@@ -52,7 +55,7 @@ export function assertValidAttesterSlashing(
52
55
  // be higher than the clock and the slashing would still be valid. Same applies to attestation data index, which
53
56
  // can be any arbitrary value. Must use bigint variants to hash correctly to all possible values
54
57
  for (const [i, attestation] of [attestation1, attestation2].entries()) {
55
- if (!isValidIndexedAttestationBigint(state, attestation, verifySignatures)) {
58
+ if (!isValidIndexedAttestationBigint(index2pubkey, state, attestation, verifySignatures)) {
56
59
  throw new Error(`AttesterSlashing attestation${i} is invalid`);
57
60
  }
58
61
  }
@@ -1,6 +1,6 @@
1
- import {FAR_FUTURE_EPOCH, MIN_ACTIVATION_BALANCE, PENDING_CONSOLIDATIONS_LIMIT} from "@lodestar/params";
1
+ import {FAR_FUTURE_EPOCH, ForkSeq, MIN_ACTIVATION_BALANCE, PENDING_CONSOLIDATIONS_LIMIT} from "@lodestar/params";
2
2
  import {electra, ssz} from "@lodestar/types";
3
- import {CachedBeaconStateElectra} from "../types.js";
3
+ import {CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
4
4
  import {hasEth1WithdrawalCredential} from "../util/capella.js";
5
5
  import {
6
6
  hasCompoundingWithdrawalCredential,
@@ -13,7 +13,8 @@ import {getConsolidationChurnLimit, getPendingBalanceToWithdraw, isActiveValidat
13
13
 
14
14
  // TODO Electra: Clean up necessary as there is a lot of overlap with isValidSwitchToCompoundRequest
15
15
  export function processConsolidationRequest(
16
- state: CachedBeaconStateElectra,
16
+ fork: ForkSeq,
17
+ state: CachedBeaconStateElectra | CachedBeaconStateGloas,
17
18
  consolidationRequest: electra.ConsolidationRequest
18
19
  ): void {
19
20
  const {sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest;
@@ -82,7 +83,7 @@ export function processConsolidationRequest(
82
83
  }
83
84
 
84
85
  // Verify the source has no pending withdrawals in the queue
85
- if (getPendingBalanceToWithdraw(state, sourceIndex) > 0) {
86
+ if (getPendingBalanceToWithdraw(fork, state, sourceIndex) > 0) {
86
87
  return;
87
88
  }
88
89
 
@@ -103,7 +104,7 @@ export function processConsolidationRequest(
103
104
  * Determine if we should set consolidation target validator to compounding credential
104
105
  */
105
106
  function isValidSwitchToCompoundRequest(
106
- state: CachedBeaconStateElectra,
107
+ state: CachedBeaconStateElectra | CachedBeaconStateGloas,
107
108
  consolidationRequest: electra.ConsolidationRequest
108
109
  ): boolean {
109
110
  const {sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest;
@@ -1,8 +1,11 @@
1
1
  import {UNSET_DEPOSIT_REQUESTS_START_INDEX} from "@lodestar/params";
2
2
  import {electra, ssz} from "@lodestar/types";
3
- import {CachedBeaconStateElectra} from "../types.js";
3
+ import {CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
4
4
 
5
- export function processDepositRequest(state: CachedBeaconStateElectra, depositRequest: electra.DepositRequest): void {
5
+ export function processDepositRequest(
6
+ state: CachedBeaconStateElectra | CachedBeaconStateGloas,
7
+ depositRequest: electra.DepositRequest
8
+ ): void {
6
9
  if (state.depositRequestsStartIndex === UNSET_DEPOSIT_REQUESTS_START_INDEX) {
7
10
  state.depositRequestsStartIndex = depositRequest.index;
8
11
  }
@@ -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
+ }