@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.
- package/lib/block/index.d.ts +4 -1
- package/lib/block/index.d.ts.map +1 -1
- package/lib/block/index.js +18 -9
- package/lib/block/index.js.map +1 -1
- package/lib/block/isValidIndexedAttestation.d.ts +3 -2
- package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
- package/lib/block/isValidIndexedAttestation.js +4 -4
- package/lib/block/isValidIndexedAttestation.js.map +1 -1
- package/lib/block/isValidIndexedPayloadAttestation.d.ts +4 -0
- package/lib/block/isValidIndexedPayloadAttestation.d.ts.map +1 -0
- package/lib/block/isValidIndexedPayloadAttestation.js +14 -0
- package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -0
- package/lib/block/processAttestationPhase0.d.ts.map +1 -1
- package/lib/block/processAttestationPhase0.js +7 -2
- package/lib/block/processAttestationPhase0.js.map +1 -1
- package/lib/block/processAttestationsAltair.d.ts +3 -3
- package/lib/block/processAttestationsAltair.d.ts.map +1 -1
- package/lib/block/processAttestationsAltair.js +47 -5
- package/lib/block/processAttestationsAltair.js.map +1 -1
- package/lib/block/processAttesterSlashing.d.ts +2 -1
- package/lib/block/processAttesterSlashing.d.ts.map +1 -1
- package/lib/block/processAttesterSlashing.js +5 -4
- package/lib/block/processAttesterSlashing.js.map +1 -1
- package/lib/block/processConsolidationRequest.d.ts +3 -2
- package/lib/block/processConsolidationRequest.d.ts.map +1 -1
- package/lib/block/processConsolidationRequest.js +2 -2
- package/lib/block/processConsolidationRequest.js.map +1 -1
- package/lib/block/processDepositRequest.d.ts +2 -2
- package/lib/block/processDepositRequest.d.ts.map +1 -1
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processExecutionPayload.d.ts.map +1 -1
- package/lib/block/processExecutionPayload.js +4 -6
- package/lib/block/processExecutionPayload.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts +5 -0
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -0
- package/lib/block/processExecutionPayloadBid.js +89 -0
- package/lib/block/processExecutionPayloadBid.js.map +1 -0
- package/lib/block/processExecutionPayloadEnvelope.d.ts +4 -0
- package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -0
- package/lib/block/processExecutionPayloadEnvelope.js +118 -0
- package/lib/block/processExecutionPayloadEnvelope.js.map +1 -0
- package/lib/block/processOperations.d.ts.map +1 -1
- package/lib/block/processOperations.js +8 -2
- package/lib/block/processOperations.js.map +1 -1
- package/lib/block/processPayloadAttestation.d.ts +4 -0
- package/lib/block/processPayloadAttestation.d.ts.map +1 -0
- package/lib/block/processPayloadAttestation.js +16 -0
- package/lib/block/processPayloadAttestation.js.map +1 -0
- package/lib/block/processProposerSlashing.d.ts.map +1 -1
- package/lib/block/processProposerSlashing.js +17 -2
- package/lib/block/processProposerSlashing.js.map +1 -1
- package/lib/block/processRandao.js +1 -1
- package/lib/block/processRandao.js.map +1 -1
- package/lib/block/processSyncCommittee.d.ts +2 -1
- package/lib/block/processSyncCommittee.d.ts.map +1 -1
- package/lib/block/processSyncCommittee.js +3 -4
- package/lib/block/processSyncCommittee.js.map +1 -1
- package/lib/block/processVoluntaryExit.js +2 -2
- package/lib/block/processVoluntaryExit.js.map +1 -1
- package/lib/block/processWithdrawalRequest.d.ts +2 -2
- package/lib/block/processWithdrawalRequest.d.ts.map +1 -1
- package/lib/block/processWithdrawalRequest.js +1 -1
- package/lib/block/processWithdrawalRequest.js.map +1 -1
- package/lib/block/processWithdrawals.d.ts +4 -3
- package/lib/block/processWithdrawals.d.ts.map +1 -1
- package/lib/block/processWithdrawals.js +89 -19
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/cache/epochCache.d.ts +5 -1
- package/lib/cache/epochCache.d.ts.map +1 -1
- package/lib/cache/epochCache.js +34 -1
- package/lib/cache/epochCache.js.map +1 -1
- package/lib/epoch/index.d.ts +4 -2
- package/lib/epoch/index.d.ts.map +1 -1
- package/lib/epoch/index.js +10 -1
- package/lib/epoch/index.js.map +1 -1
- package/lib/epoch/processBuilderPendingPayments.d.ts +6 -0
- package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -0
- package/lib/epoch/processBuilderPendingPayments.js +28 -0
- package/lib/epoch/processBuilderPendingPayments.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/signatureSets/attesterSlashings.d.ts +4 -3
- package/lib/signatureSets/attesterSlashings.d.ts.map +1 -1
- package/lib/signatureSets/attesterSlashings.js +6 -6
- package/lib/signatureSets/attesterSlashings.js.map +1 -1
- package/lib/signatureSets/index.d.ts +3 -1
- package/lib/signatureSets/index.d.ts.map +1 -1
- package/lib/signatureSets/index.js +9 -8
- package/lib/signatureSets/index.js.map +1 -1
- package/lib/signatureSets/indexedAttestation.d.ts +4 -3
- package/lib/signatureSets/indexedAttestation.d.ts.map +1 -1
- package/lib/signatureSets/indexedAttestation.js +6 -6
- package/lib/signatureSets/indexedAttestation.js.map +1 -1
- package/lib/signatureSets/indexedPayloadAttestation.d.ts +6 -0
- package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -0
- package/lib/signatureSets/indexedPayloadAttestation.js +11 -0
- package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -0
- package/lib/signatureSets/proposer.d.ts +5 -4
- package/lib/signatureSets/proposer.d.ts.map +1 -1
- package/lib/signatureSets/proposer.js +12 -12
- package/lib/signatureSets/proposer.js.map +1 -1
- package/lib/signatureSets/proposerSlashings.d.ts +3 -2
- package/lib/signatureSets/proposerSlashings.d.ts.map +1 -1
- package/lib/signatureSets/proposerSlashings.js +4 -5
- package/lib/signatureSets/proposerSlashings.js.map +1 -1
- package/lib/signatureSets/randao.d.ts +3 -2
- package/lib/signatureSets/randao.d.ts.map +1 -1
- package/lib/signatureSets/randao.js +4 -5
- package/lib/signatureSets/randao.js.map +1 -1
- package/lib/signatureSets/voluntaryExits.d.ts +4 -3
- package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
- package/lib/signatureSets/voluntaryExits.js +6 -7
- package/lib/signatureSets/voluntaryExits.js.map +1 -1
- package/lib/slot/index.d.ts +2 -1
- package/lib/slot/index.d.ts.map +1 -1
- package/lib/slot/index.js +6 -2
- package/lib/slot/index.js.map +1 -1
- package/lib/slot/upgradeStateToAltair.js +1 -1
- package/lib/slot/upgradeStateToAltair.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.d.ts +0 -1
- package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +47 -5
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateTransition.js +5 -4
- package/lib/stateTransition.js.map +1 -1
- package/lib/util/electra.d.ts +5 -5
- package/lib/util/electra.d.ts.map +1 -1
- package/lib/util/electra.js +2 -1
- package/lib/util/electra.js.map +1 -1
- package/lib/util/epoch.d.ts +3 -3
- package/lib/util/epoch.d.ts.map +1 -1
- package/lib/util/epoch.js.map +1 -1
- package/lib/util/execution.d.ts +1 -16
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +1 -44
- package/lib/util/execution.js.map +1 -1
- package/lib/util/gloas.d.ts +11 -0
- package/lib/util/gloas.d.ts.map +1 -0
- package/lib/util/gloas.js +35 -0
- package/lib/util/gloas.js.map +1 -0
- package/lib/util/seed.d.ts +5 -1
- package/lib/util/seed.d.ts.map +1 -1
- package/lib/util/seed.js +33 -1
- package/lib/util/seed.js.map +1 -1
- package/lib/util/validator.d.ts +2 -2
- package/lib/util/validator.d.ts.map +1 -1
- package/lib/util/validator.js +14 -1
- package/lib/util/validator.js.map +1 -1
- package/package.json +6 -6
- package/src/block/index.ts +32 -15
- package/src/block/isValidIndexedAttestation.ts +5 -2
- package/src/block/isValidIndexedPayloadAttestation.ts +23 -0
- package/src/block/processAttestationPhase0.ts +13 -2
- package/src/block/processAttestationsAltair.ts +63 -6
- package/src/block/processAttesterSlashing.ts +6 -3
- package/src/block/processConsolidationRequest.ts +6 -5
- package/src/block/processDepositRequest.ts +5 -2
- package/src/block/processExecutionPayload.ts +8 -14
- package/src/block/processExecutionPayloadBid.ts +120 -0
- package/src/block/processExecutionPayloadEnvelope.ts +181 -0
- package/src/block/processOperations.ts +16 -4
- package/src/block/processPayloadAttestation.ts +25 -0
- package/src/block/processProposerSlashing.ts +25 -4
- package/src/block/processRandao.ts +1 -1
- package/src/block/processSyncCommittee.ts +4 -3
- package/src/block/processVoluntaryExit.ts +2 -2
- package/src/block/processWithdrawalRequest.ts +4 -4
- package/src/block/processWithdrawals.ts +118 -27
- package/src/cache/epochCache.ts +58 -1
- package/src/epoch/index.ts +12 -0
- package/src/epoch/processBuilderPendingPayments.ts +31 -0
- package/src/index.ts +2 -0
- package/src/signatureSets/attesterSlashings.ts +7 -3
- package/src/signatureSets/index.ts +10 -6
- package/src/signatureSets/indexedAttestation.ts +14 -3
- package/src/signatureSets/indexedPayloadAttestation.ts +24 -0
- package/src/signatureSets/proposer.ts +13 -7
- package/src/signatureSets/proposerSlashings.ts +5 -3
- package/src/signatureSets/randao.ts +13 -5
- package/src/signatureSets/voluntaryExits.ts +7 -4
- package/src/slot/index.ts +11 -3
- package/src/slot/upgradeStateToAltair.ts +2 -1
- package/src/slot/upgradeStateToGloas.ts +49 -5
- package/src/stateTransition.ts +5 -5
- package/src/util/electra.ts +15 -6
- package/src/util/epoch.ts +6 -3
- package/src/util/execution.ts +0 -60
- package/src/util/gloas.ts +58 -0
- package/src/util/seed.ts +57 -1
- 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 {
|
|
4
|
+
import {
|
|
5
|
+
CachedBeaconStateAllForks,
|
|
6
|
+
CachedBeaconStateCapella,
|
|
7
|
+
CachedBeaconStateElectra,
|
|
8
|
+
CachedBeaconStateGloas,
|
|
9
|
+
} from "../types.js";
|
|
5
10
|
import {getEth1DepositCount} from "../util/deposit.js";
|
|
6
11
|
import {processAttestations} from "./processAttestations.js";
|
|
7
12
|
import {processAttesterSlashing} from "./processAttesterSlashing.js";
|
|
@@ -9,6 +14,7 @@ import {processBlsToExecutionChange} from "./processBlsToExecutionChange.js";
|
|
|
9
14
|
import {processConsolidationRequest} from "./processConsolidationRequest.js";
|
|
10
15
|
import {processDeposit} from "./processDeposit.js";
|
|
11
16
|
import {processDepositRequest} from "./processDepositRequest.js";
|
|
17
|
+
import {processPayloadAttestation} from "./processPayloadAttestation.ts";
|
|
12
18
|
import {processProposerSlashing} from "./processProposerSlashing.js";
|
|
13
19
|
import {processVoluntaryExit} from "./processVoluntaryExit.js";
|
|
14
20
|
import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
|
|
@@ -64,7 +70,7 @@ export function processOperations(
|
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
if (fork >= ForkSeq.electra) {
|
|
73
|
+
if (fork >= ForkSeq.electra && fork < ForkSeq.gloas) {
|
|
68
74
|
const stateElectra = state as CachedBeaconStateElectra;
|
|
69
75
|
const bodyElectra = body as electra.BeaconBlockBody;
|
|
70
76
|
|
|
@@ -77,7 +83,13 @@ export function processOperations(
|
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
for (const elConsolidationRequest of bodyElectra.executionRequests.consolidations) {
|
|
80
|
-
processConsolidationRequest(stateElectra, elConsolidationRequest);
|
|
86
|
+
processConsolidationRequest(fork, stateElectra, elConsolidationRequest);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (fork >= ForkSeq.gloas) {
|
|
91
|
+
for (const payloadAttestation of (body as gloas.BeaconBlockBody).payloadAttestations) {
|
|
92
|
+
processPayloadAttestation(state as CachedBeaconStateGloas, payloadAttestation);
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {byteArrayEquals} from "@chainsafe/ssz";
|
|
2
|
+
import {gloas} from "@lodestar/types";
|
|
3
|
+
import {CachedBeaconStateGloas} from "../types.ts";
|
|
4
|
+
import {isValidIndexedPayloadAttestation} from "./isValidIndexedPayloadAttestation.ts";
|
|
5
|
+
|
|
6
|
+
export function processPayloadAttestation(
|
|
7
|
+
state: CachedBeaconStateGloas,
|
|
8
|
+
payloadAttestation: gloas.PayloadAttestation
|
|
9
|
+
): void {
|
|
10
|
+
const data = payloadAttestation.data;
|
|
11
|
+
|
|
12
|
+
if (!byteArrayEquals(data.beaconBlockRoot, state.latestBlockHeader.parentRoot)) {
|
|
13
|
+
throw Error("Payload attestation is referring to the wrong block");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (data.slot + 1 !== state.slot) {
|
|
17
|
+
throw Error("Payload attestation is not from previous slot");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const indexedPayloadAttestation = state.epochCtx.getIndexedPayloadAttestation(data.slot, payloadAttestation);
|
|
21
|
+
|
|
22
|
+
if (!isValidIndexedPayloadAttestation(state, indexedPayloadAttestation, true)) {
|
|
23
|
+
throw Error("Invalid payload attestation");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {ForkSeq} from "@lodestar/params";
|
|
1
|
+
import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
2
2
|
import {phase0, ssz} from "@lodestar/types";
|
|
3
3
|
import {getProposerSlashingSignatureSets} from "../signatureSets/index.js";
|
|
4
|
-
import {CachedBeaconStateAllForks} from "../types.js";
|
|
5
|
-
import {isSlashableValidator} from "../util/index.js";
|
|
4
|
+
import {CachedBeaconStateAllForks, CachedBeaconStateGloas} from "../types.js";
|
|
5
|
+
import {computeEpochAtSlot, isSlashableValidator} from "../util/index.js";
|
|
6
6
|
import {verifySignatureSet} from "../util/signatureSets.js";
|
|
7
7
|
import {slashValidator} from "./slashValidator.js";
|
|
8
8
|
|
|
@@ -20,6 +20,27 @@ export function processProposerSlashing(
|
|
|
20
20
|
): void {
|
|
21
21
|
assertValidProposerSlashing(state, proposerSlashing, verifySignatures);
|
|
22
22
|
|
|
23
|
+
if (fork >= ForkSeq.gloas) {
|
|
24
|
+
const slot = Number(proposerSlashing.signedHeader1.message.slot);
|
|
25
|
+
const proposalEpoch = computeEpochAtSlot(slot);
|
|
26
|
+
const currentEpoch = state.epochCtx.epoch;
|
|
27
|
+
const previousEpoch = currentEpoch - 1;
|
|
28
|
+
|
|
29
|
+
const paymentIndex =
|
|
30
|
+
proposalEpoch === currentEpoch
|
|
31
|
+
? SLOTS_PER_EPOCH + (slot % SLOTS_PER_EPOCH)
|
|
32
|
+
: proposalEpoch === previousEpoch
|
|
33
|
+
? slot % SLOTS_PER_EPOCH
|
|
34
|
+
: undefined;
|
|
35
|
+
|
|
36
|
+
if (paymentIndex !== undefined) {
|
|
37
|
+
(state as CachedBeaconStateGloas).builderPendingPayments.set(
|
|
38
|
+
paymentIndex,
|
|
39
|
+
ssz.gloas.BuilderPendingPayment.defaultViewDU()
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
23
44
|
slashValidator(fork, state, proposerSlashing.signedHeader1.message.proposerIndex);
|
|
24
45
|
}
|
|
25
46
|
|
|
@@ -56,7 +77,7 @@ export function assertValidProposerSlashing(
|
|
|
56
77
|
|
|
57
78
|
// verify signatures
|
|
58
79
|
if (verifySignatures) {
|
|
59
|
-
const signatureSets = getProposerSlashingSignatureSets(state, proposerSlashing);
|
|
80
|
+
const signatureSets = getProposerSlashingSignatureSets(state.epochCtx.index2pubkey, state, proposerSlashing);
|
|
60
81
|
for (let i = 0; i < signatureSets.length; i++) {
|
|
61
82
|
if (!verifySignatureSet(signatureSets[i])) {
|
|
62
83
|
throw new Error(`ProposerSlashing header${i + 1} signature invalid`);
|
|
@@ -17,7 +17,7 @@ export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlo
|
|
|
17
17
|
const randaoReveal = block.body.randaoReveal;
|
|
18
18
|
|
|
19
19
|
// verify RANDAO reveal
|
|
20
|
-
if (verifySignature && !verifyRandaoSignature(state, block)) {
|
|
20
|
+
if (verifySignature && !verifyRandaoSignature(epochCtx.index2pubkey, state, block)) {
|
|
21
21
|
throw new Error("RANDAO reveal is an invalid signature");
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {byteArrayEquals} from "@chainsafe/ssz";
|
|
2
2
|
import {DOMAIN_SYNC_COMMITTEE, SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
|
3
3
|
import {altair, ssz} from "@lodestar/types";
|
|
4
|
+
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
|
4
5
|
import {G2_POINT_AT_INFINITY} from "../constants/index.js";
|
|
5
6
|
import {CachedBeaconStateAllForks} from "../types.js";
|
|
6
7
|
import {
|
|
@@ -23,7 +24,7 @@ export function processSyncAggregate(
|
|
|
23
24
|
if (verifySignatures) {
|
|
24
25
|
// This is to conform to the spec - we want the signature to be verified
|
|
25
26
|
const participantIndices = block.body.syncAggregate.syncCommitteeBits.intersectValues(committeeIndices);
|
|
26
|
-
const signatureSet = getSyncCommitteeSignatureSet(state, block, participantIndices);
|
|
27
|
+
const signatureSet = getSyncCommitteeSignatureSet(state.epochCtx.index2pubkey, state, block, participantIndices);
|
|
27
28
|
// When there's no participation we consider the signature valid and just ignore i
|
|
28
29
|
if (signatureSet !== null && !verifySignatureSet(signatureSet)) {
|
|
29
30
|
throw Error("Sync committee signature invalid");
|
|
@@ -63,12 +64,12 @@ export function processSyncAggregate(
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
export function getSyncCommitteeSignatureSet(
|
|
67
|
+
index2pubkey: Index2PubkeyCache,
|
|
66
68
|
state: CachedBeaconStateAllForks,
|
|
67
69
|
block: altair.BeaconBlock,
|
|
68
70
|
/** Optional parameter to prevent computing it twice */
|
|
69
71
|
participantIndices?: number[]
|
|
70
72
|
): ISignatureSet | null {
|
|
71
|
-
const {epochCtx} = state;
|
|
72
73
|
const {syncAggregate} = block.body;
|
|
73
74
|
const signature = syncAggregate.syncCommitteeSignature;
|
|
74
75
|
|
|
@@ -110,7 +111,7 @@ export function getSyncCommitteeSignatureSet(
|
|
|
110
111
|
|
|
111
112
|
return {
|
|
112
113
|
type: SignatureSetType.aggregate,
|
|
113
|
-
pubkeys: participantIndices.map((i) =>
|
|
114
|
+
pubkeys: participantIndices.map((i) => index2pubkey[i]),
|
|
114
115
|
signingRoot: computeSigningRoot(ssz.Root, rootSigned, domain),
|
|
115
116
|
signature,
|
|
116
117
|
};
|
|
@@ -69,12 +69,12 @@ export function getVoluntaryExitValidity(
|
|
|
69
69
|
// only exit validator if it has no pending withdrawals in the queue
|
|
70
70
|
if (
|
|
71
71
|
fork >= ForkSeq.electra &&
|
|
72
|
-
getPendingBalanceToWithdraw(state as CachedBeaconStateElectra, voluntaryExit.validatorIndex) !== 0
|
|
72
|
+
getPendingBalanceToWithdraw(fork, state as CachedBeaconStateElectra, voluntaryExit.validatorIndex) !== 0
|
|
73
73
|
) {
|
|
74
74
|
return VoluntaryExitValidity.pendingWithdrawals;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
if (verifySignature && !verifyVoluntaryExitSignature(state, signedVoluntaryExit)) {
|
|
77
|
+
if (verifySignature && !verifyVoluntaryExitSignature(epochCtx.index2pubkey, state, signedVoluntaryExit)) {
|
|
78
78
|
return VoluntaryExitValidity.invalidSignature;
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "@lodestar/params";
|
|
8
8
|
import {electra, phase0, ssz} from "@lodestar/types";
|
|
9
9
|
import {toHex} from "@lodestar/utils";
|
|
10
|
-
import {CachedBeaconStateElectra} from "../types.js";
|
|
10
|
+
import {CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
|
|
11
11
|
import {hasCompoundingWithdrawalCredential, hasExecutionWithdrawalCredential} from "../util/electra.js";
|
|
12
12
|
import {computeExitEpochAndUpdateChurn} from "../util/epoch.js";
|
|
13
13
|
import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/validator.js";
|
|
@@ -15,7 +15,7 @@ import {initiateValidatorExit} from "./initiateValidatorExit.js";
|
|
|
15
15
|
|
|
16
16
|
export function processWithdrawalRequest(
|
|
17
17
|
fork: ForkSeq,
|
|
18
|
-
state: CachedBeaconStateElectra,
|
|
18
|
+
state: CachedBeaconStateElectra | CachedBeaconStateGloas,
|
|
19
19
|
withdrawalRequest: electra.WithdrawalRequest
|
|
20
20
|
): void {
|
|
21
21
|
const amount = Number(withdrawalRequest.amount);
|
|
@@ -42,7 +42,7 @@ export function processWithdrawalRequest(
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// TODO Electra: Consider caching pendingPartialWithdrawals
|
|
45
|
-
const pendingBalanceToWithdraw = getPendingBalanceToWithdraw(state, validatorIndex);
|
|
45
|
+
const pendingBalanceToWithdraw = getPendingBalanceToWithdraw(fork, state, validatorIndex);
|
|
46
46
|
const validatorBalance = state.balances.get(validatorIndex);
|
|
47
47
|
|
|
48
48
|
if (isFullExitRequest) {
|
|
@@ -81,7 +81,7 @@ export function processWithdrawalRequest(
|
|
|
81
81
|
function isValidatorEligibleForWithdrawOrExit(
|
|
82
82
|
validator: phase0.Validator,
|
|
83
83
|
sourceAddress: Uint8Array,
|
|
84
|
-
state: CachedBeaconStateElectra
|
|
84
|
+
state: CachedBeaconStateElectra | CachedBeaconStateGloas
|
|
85
85
|
): boolean {
|
|
86
86
|
const {withdrawalCredentials} = validator;
|
|
87
87
|
const addressStr = toHex(withdrawalCredentials.subarray(12));
|