@lodestar/state-transition 1.40.0-dev.3be9500fa9 → 1.40.0-dev.63c5c3e7f7
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 +1 -0
- package/lib/block/index.d.ts.map +1 -1
- package/lib/block/index.js +1 -0
- package/lib/block/index.js.map +1 -1
- package/lib/block/processConsolidationRequest.d.ts +1 -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 +8 -2
- package/lib/block/processDepositRequest.d.ts.map +1 -1
- package/lib/block/processDepositRequest.js +81 -8
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
- package/lib/block/processExecutionPayloadBid.js +14 -27
- package/lib/block/processExecutionPayloadBid.js.map +1 -1
- package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/block/processExecutionPayloadEnvelope.js +26 -25
- package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
- package/lib/block/processOperations.js +2 -2
- package/lib/block/processOperations.js.map +1 -1
- package/lib/block/processVoluntaryExit.d.ts +1 -1
- package/lib/block/processVoluntaryExit.d.ts.map +1 -1
- package/lib/block/processVoluntaryExit.js +45 -3
- package/lib/block/processVoluntaryExit.js.map +1 -1
- package/lib/block/processWithdrawalRequest.js +1 -1
- package/lib/block/processWithdrawalRequest.js.map +1 -1
- package/lib/block/processWithdrawals.d.ts +1 -0
- package/lib/block/processWithdrawals.d.ts.map +1 -1
- package/lib/block/processWithdrawals.js +121 -66
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/epoch/processBuilderPendingPayments.d.ts.map +1 -1
- package/lib/epoch/processBuilderPendingPayments.js +1 -4
- package/lib/epoch/processBuilderPendingPayments.js.map +1 -1
- package/lib/util/electra.d.ts.map +1 -1
- package/lib/util/electra.js +1 -2
- package/lib/util/electra.js.map +1 -1
- package/lib/util/gloas.d.ts +43 -3
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +93 -5
- package/lib/util/gloas.js.map +1 -1
- package/lib/util/validator.d.ts +6 -1
- package/lib/util/validator.d.ts.map +1 -1
- package/lib/util/validator.js +26 -16
- package/lib/util/validator.js.map +1 -1
- package/package.json +7 -7
- package/src/block/index.ts +1 -0
- package/src/block/processConsolidationRequest.ts +2 -3
- package/src/block/processDepositRequest.ts +101 -8
- package/src/block/processExecutionPayloadBid.ts +18 -40
- package/src/block/processExecutionPayloadEnvelope.ts +34 -30
- package/src/block/processOperations.ts +2 -2
- package/src/block/processVoluntaryExit.ts +60 -5
- package/src/block/processWithdrawalRequest.ts +1 -1
- package/src/block/processWithdrawals.ts +168 -70
- package/src/epoch/processBuilderPendingPayments.ts +1 -5
- package/src/util/electra.ts +1 -4
- package/src/util/gloas.ts +109 -8
- package/src/util/validator.ts +31 -16
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import {PublicKey, Signature, verify} from "@chainsafe/blst";
|
|
2
2
|
import {byteArrayEquals} from "@chainsafe/ssz";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
BUILDER_INDEX_SELF_BUILD,
|
|
5
|
+
DOMAIN_BEACON_BUILDER,
|
|
6
|
+
SLOTS_PER_EPOCH,
|
|
7
|
+
SLOTS_PER_HISTORICAL_ROOT,
|
|
8
|
+
} from "@lodestar/params";
|
|
4
9
|
import {gloas, ssz} from "@lodestar/types";
|
|
5
10
|
import {toHex, toRootHex} from "@lodestar/utils";
|
|
6
11
|
import {CachedBeaconStateGloas} from "../types.ts";
|
|
7
|
-
import {
|
|
12
|
+
import {computeSigningRoot, computeTimeAtSlot} from "../util/index.ts";
|
|
8
13
|
import {processConsolidationRequest} from "./processConsolidationRequest.ts";
|
|
9
14
|
import {processDepositRequest} from "./processDepositRequest.ts";
|
|
10
15
|
import {processWithdrawalRequest} from "./processWithdrawalRequest.ts";
|
|
@@ -19,13 +24,8 @@ export function processExecutionPayloadEnvelope(
|
|
|
19
24
|
const payload = envelope.payload;
|
|
20
25
|
const fork = state.config.getForkSeq(envelope.slot);
|
|
21
26
|
|
|
22
|
-
if (verify) {
|
|
23
|
-
|
|
24
|
-
const pubkey = state.validators.getReadonly(builderIndex).pubkey;
|
|
25
|
-
|
|
26
|
-
if (!verifyExecutionPayloadEnvelopeSignature(state, pubkey, signedEnvelope)) {
|
|
27
|
-
throw new Error("Payload Envelope has invalid signature");
|
|
28
|
-
}
|
|
27
|
+
if (verify && !verifyExecutionPayloadEnvelopeSignature(state, signedEnvelope)) {
|
|
28
|
+
throw Error(`Execution payload envelope has invalid signature builderIndex=${envelope.builderIndex}`);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
validateExecutionPayloadEnvelope(state, envelope);
|
|
@@ -33,7 +33,7 @@ export function processExecutionPayloadEnvelope(
|
|
|
33
33
|
const requests = envelope.executionRequests;
|
|
34
34
|
|
|
35
35
|
for (const deposit of requests.deposits) {
|
|
36
|
-
processDepositRequest(state, deposit);
|
|
36
|
+
processDepositRequest(fork, state, deposit);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
for (const withdrawal of requests.withdrawals) {
|
|
@@ -41,7 +41,7 @@ export function processExecutionPayloadEnvelope(
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
for (const consolidation of requests.consolidations) {
|
|
44
|
-
processConsolidationRequest(
|
|
44
|
+
processConsolidationRequest(state, consolidation);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// Queue the builder payment
|
|
@@ -50,9 +50,6 @@ export function processExecutionPayloadEnvelope(
|
|
|
50
50
|
const amount = payment.withdrawal.amount;
|
|
51
51
|
|
|
52
52
|
if (amount > 0) {
|
|
53
|
-
const exitQueueEpoch = computeExitEpochAndUpdateChurn(state, BigInt(amount));
|
|
54
|
-
|
|
55
|
-
payment.withdrawal.withdrawableEpoch = exitQueueEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
|
|
56
53
|
state.builderPendingWithdrawals.push(payment.withdrawal);
|
|
57
54
|
}
|
|
58
55
|
|
|
@@ -75,6 +72,7 @@ function validateExecutionPayloadEnvelope(
|
|
|
75
72
|
): void {
|
|
76
73
|
const payload = envelope.payload;
|
|
77
74
|
|
|
75
|
+
// Cache latest block header state root
|
|
78
76
|
if (byteArrayEquals(state.latestBlockHeader.stateRoot, ssz.Root.defaultValue())) {
|
|
79
77
|
const previousStateRoot = state.hashTreeRoot();
|
|
80
78
|
state.latestBlockHeader.stateRoot = previousStateRoot;
|
|
@@ -87,20 +85,18 @@ function validateExecutionPayloadEnvelope(
|
|
|
87
85
|
);
|
|
88
86
|
}
|
|
89
87
|
|
|
90
|
-
// Verify consistency with the beacon block
|
|
91
88
|
if (envelope.slot !== state.slot) {
|
|
92
89
|
throw new Error(`Slot mismatch between envelope and state envelope=${envelope.slot} state=${state.slot}`);
|
|
93
90
|
}
|
|
94
91
|
|
|
95
|
-
const committedBid = state.latestExecutionPayloadBid;
|
|
96
92
|
// Verify consistency with the committed bid
|
|
93
|
+
const committedBid = state.latestExecutionPayloadBid;
|
|
97
94
|
if (envelope.builderIndex !== committedBid.builderIndex) {
|
|
98
95
|
throw new Error(
|
|
99
96
|
`Builder index mismatch between envelope and committed bid envelope=${envelope.builderIndex} committedBid=${committedBid.builderIndex}`
|
|
100
97
|
);
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
// Verify consistency with the committed bid
|
|
104
100
|
const envelopeKzgRoot = ssz.deneb.BlobKzgCommitments.hashTreeRoot(envelope.blobKzgCommitments);
|
|
105
101
|
if (!byteArrayEquals(committedBid.blobKzgCommitmentsRoot, envelopeKzgRoot)) {
|
|
106
102
|
throw new Error(
|
|
@@ -108,11 +104,18 @@ function validateExecutionPayloadEnvelope(
|
|
|
108
104
|
);
|
|
109
105
|
}
|
|
110
106
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Verify consistency with expected withdrawals
|
|
114
|
+
const payloadWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(payload.withdrawals);
|
|
115
|
+
const expectedWithdrawalsRoot = state.payloadExpectedWithdrawals.hashTreeRoot();
|
|
116
|
+
if (!byteArrayEquals(payloadWithdrawalsRoot, expectedWithdrawalsRoot)) {
|
|
114
117
|
throw new Error(
|
|
115
|
-
`Withdrawals
|
|
118
|
+
`Withdrawals mismatch between payload and expected withdrawals payload=${toRootHex(payloadWithdrawalsRoot)} expected=${toRootHex(expectedWithdrawalsRoot)}`
|
|
116
119
|
);
|
|
117
120
|
}
|
|
118
121
|
|
|
@@ -137,13 +140,6 @@ function validateExecutionPayloadEnvelope(
|
|
|
137
140
|
);
|
|
138
141
|
}
|
|
139
142
|
|
|
140
|
-
// Verify prev_randao matches committed bid
|
|
141
|
-
if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
|
|
142
|
-
throw new Error(
|
|
143
|
-
`Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
143
|
// Verify timestamp
|
|
148
144
|
if (payload.timestamp !== computeTimeAtSlot(state.config, state.slot, state.genesisTime)) {
|
|
149
145
|
throw new Error(
|
|
@@ -164,14 +160,22 @@ function validateExecutionPayloadEnvelope(
|
|
|
164
160
|
|
|
165
161
|
function verifyExecutionPayloadEnvelopeSignature(
|
|
166
162
|
state: CachedBeaconStateGloas,
|
|
167
|
-
pubkey: Uint8Array,
|
|
168
163
|
signedEnvelope: gloas.SignedExecutionPayloadEnvelope
|
|
169
164
|
): boolean {
|
|
165
|
+
const builderIndex = signedEnvelope.message.builderIndex;
|
|
166
|
+
|
|
170
167
|
const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_BUILDER);
|
|
171
168
|
const signingRoot = computeSigningRoot(ssz.gloas.ExecutionPayloadEnvelope, signedEnvelope.message, domain);
|
|
172
169
|
|
|
173
170
|
try {
|
|
174
|
-
|
|
171
|
+
let publicKey: PublicKey;
|
|
172
|
+
|
|
173
|
+
if (builderIndex === BUILDER_INDEX_SELF_BUILD) {
|
|
174
|
+
const validatorIndex = state.latestBlockHeader.proposerIndex;
|
|
175
|
+
publicKey = state.epochCtx.index2pubkey[validatorIndex];
|
|
176
|
+
} else {
|
|
177
|
+
publicKey = PublicKey.fromBytes(state.builders.getReadonly(builderIndex).pubkey);
|
|
178
|
+
}
|
|
175
179
|
const signature = Signature.fromBytes(signedEnvelope.signature, true);
|
|
176
180
|
|
|
177
181
|
return verify(signingRoot, publicKey, signature);
|
|
@@ -75,7 +75,7 @@ export function processOperations(
|
|
|
75
75
|
const bodyElectra = body as electra.BeaconBlockBody;
|
|
76
76
|
|
|
77
77
|
for (const depositRequest of bodyElectra.executionRequests.deposits) {
|
|
78
|
-
processDepositRequest(stateElectra, depositRequest);
|
|
78
|
+
processDepositRequest(fork, stateElectra, depositRequest);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
for (const elWithdrawalRequest of bodyElectra.executionRequests.withdrawals) {
|
|
@@ -83,7 +83,7 @@ export function processOperations(
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
for (const elConsolidationRequest of bodyElectra.executionRequests.consolidations) {
|
|
86
|
-
processConsolidationRequest(
|
|
86
|
+
processConsolidationRequest(stateElectra, elConsolidationRequest);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
import {PublicKey, Signature, verify} from "@chainsafe/blst";
|
|
1
2
|
import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params";
|
|
2
|
-
import {phase0} from "@lodestar/types";
|
|
3
|
+
import {phase0, ssz} from "@lodestar/types";
|
|
3
4
|
import {verifyVoluntaryExitSignature} from "../signatureSets/index.js";
|
|
4
|
-
import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
|
|
5
|
-
import {
|
|
5
|
+
import {CachedBeaconStateAllForks, CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
|
|
6
|
+
import {
|
|
7
|
+
convertValidatorIndexToBuilderIndex,
|
|
8
|
+
getPendingBalanceToWithdrawForBuilder,
|
|
9
|
+
initiateBuilderExit,
|
|
10
|
+
isActiveBuilder,
|
|
11
|
+
isBuilderIndex,
|
|
12
|
+
} from "../util/gloas.js";
|
|
13
|
+
import {computeSigningRoot, getCurrentEpoch, getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js";
|
|
6
14
|
import {initiateValidatorExit} from "./index.js";
|
|
7
15
|
|
|
8
16
|
export enum VoluntaryExitValidity {
|
|
@@ -16,7 +24,7 @@ export enum VoluntaryExitValidity {
|
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
/**
|
|
19
|
-
* Process a VoluntaryExit operation. Initiates the exit of a validator.
|
|
27
|
+
* Process a VoluntaryExit operation. Initiates the exit of a validator or builder.
|
|
20
28
|
*
|
|
21
29
|
* PERF: Work depends on number of VoluntaryExit per block. On regular networks the average is 0 / block.
|
|
22
30
|
*/
|
|
@@ -26,6 +34,53 @@ export function processVoluntaryExit(
|
|
|
26
34
|
signedVoluntaryExit: phase0.SignedVoluntaryExit,
|
|
27
35
|
verifySignature = true
|
|
28
36
|
): void {
|
|
37
|
+
const voluntaryExit = signedVoluntaryExit.message;
|
|
38
|
+
const currentEpoch = getCurrentEpoch(state);
|
|
39
|
+
|
|
40
|
+
// Exits must specify an epoch when they become valid; they are not valid before then
|
|
41
|
+
if (currentEpoch < voluntaryExit.epoch) {
|
|
42
|
+
throw Error(`Voluntary exit epoch ${voluntaryExit.epoch} is after current epoch ${currentEpoch}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check if this is a builder exit
|
|
46
|
+
if (fork >= ForkSeq.gloas && isBuilderIndex(voluntaryExit.validatorIndex)) {
|
|
47
|
+
const stateGloas = state as CachedBeaconStateGloas;
|
|
48
|
+
const builderIndex = convertValidatorIndexToBuilderIndex(voluntaryExit.validatorIndex);
|
|
49
|
+
const builder = stateGloas.builders.getReadonly(builderIndex);
|
|
50
|
+
|
|
51
|
+
// Verify the builder is active
|
|
52
|
+
if (!isActiveBuilder(stateGloas, builderIndex)) {
|
|
53
|
+
throw Error(`Builder ${builderIndex} is not active`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Only exit builder if it has no pending withdrawals in the queue
|
|
57
|
+
if (getPendingBalanceToWithdrawForBuilder(stateGloas, builderIndex) !== 0) {
|
|
58
|
+
throw Error(`Builder ${builderIndex} has pending withdrawals`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Verify signature
|
|
62
|
+
if (verifySignature) {
|
|
63
|
+
const domain = state.config.getDomainForVoluntaryExit(state.slot);
|
|
64
|
+
const signingRoot = computeSigningRoot(ssz.phase0.VoluntaryExit, voluntaryExit, domain);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const publicKey = PublicKey.fromBytes(builder.pubkey);
|
|
68
|
+
const signature = Signature.fromBytes(signedVoluntaryExit.signature, true);
|
|
69
|
+
|
|
70
|
+
if (!verify(signingRoot, publicKey, signature)) {
|
|
71
|
+
throw Error("BLS verify failed");
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
throw Error(`Builder ${builderIndex} invalid exit signature reason=${(e as Error).message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Initiate builder exit
|
|
79
|
+
initiateBuilderExit(stateGloas, builderIndex);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Handle validator exit
|
|
29
84
|
const validity = getVoluntaryExitValidity(fork, state, signedVoluntaryExit, verifySignature);
|
|
30
85
|
if (validity !== VoluntaryExitValidity.valid) {
|
|
31
86
|
throw Error(`Invalid voluntary exit at forkSeq=${fork} reason=${validity}`);
|
|
@@ -69,7 +124,7 @@ export function getVoluntaryExitValidity(
|
|
|
69
124
|
// only exit validator if it has no pending withdrawals in the queue
|
|
70
125
|
if (
|
|
71
126
|
fork >= ForkSeq.electra &&
|
|
72
|
-
getPendingBalanceToWithdraw(
|
|
127
|
+
getPendingBalanceToWithdraw(state as CachedBeaconStateElectra, voluntaryExit.validatorIndex) !== 0
|
|
73
128
|
) {
|
|
74
129
|
return VoluntaryExitValidity.pendingWithdrawals;
|
|
75
130
|
}
|
|
@@ -42,7 +42,7 @@ export function processWithdrawalRequest(
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// TODO Electra: Consider caching pendingPartialWithdrawals
|
|
45
|
-
const pendingBalanceToWithdraw = getPendingBalanceToWithdraw(
|
|
45
|
+
const pendingBalanceToWithdraw = getPendingBalanceToWithdraw(state, validatorIndex);
|
|
46
46
|
const validatorBalance = state.balances.get(validatorIndex);
|
|
47
47
|
|
|
48
48
|
if (isFullExitRequest) {
|