@lodestar/beacon-node 1.44.0-dev.b506aab66d → 1.44.0-dev.c116c26c3d
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/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +30 -0
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +1 -1
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +82 -44
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +5 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +1 -1
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +2 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +6 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
- package/lib/chain/errors/payloadAttestation.js +1 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +55 -15
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +34 -7
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +24 -4
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +5 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +2 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +5 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +8 -0
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/blocks/index.ts +36 -0
- package/src/api/impl/beacon/pool/index.ts +1 -0
- package/src/api/impl/validator/index.ts +95 -45
- package/src/chain/blocks/importBlock.ts +8 -1
- package/src/chain/blocks/importExecutionPayload.ts +1 -1
- package/src/chain/chain.ts +2 -0
- package/src/chain/errors/executionPayloadBid.ts +2 -0
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
- package/src/chain/prepareNextSlot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +79 -17
- package/src/chain/regen/interface.ts +2 -0
- package/src/chain/validation/executionPayloadBid.ts +36 -7
- package/src/chain/validation/payloadAttestationMessage.ts +26 -4
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +2 -1
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +6 -0
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +8 -0
|
@@ -914,20 +914,40 @@ export function getValidatorApi(
|
|
|
914
914
|
notWhileSyncing();
|
|
915
915
|
await waitForSlot(slot);
|
|
916
916
|
|
|
917
|
-
// TODO GLOAS: support producing blocks from builder bids
|
|
918
|
-
const source = ProducedBlockSource.engine;
|
|
919
|
-
|
|
920
|
-
// TODO GLOAS: needs to be updated after fork choice changes are merged
|
|
921
917
|
const parentBlock = chain.getProposerHead(slot);
|
|
922
918
|
const {blockRoot: parentBlockRootHex, slot: parentSlot} = parentBlock;
|
|
923
919
|
const parentBlockRoot = fromHex(parentBlockRootHex);
|
|
924
920
|
notOnOutOfRangeData(parentBlockRoot);
|
|
925
921
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
926
|
-
metrics?.blockProductionRequests.inc({source});
|
|
927
922
|
|
|
928
923
|
const graffitiBytes = toGraffitiBytes(
|
|
929
924
|
graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
|
|
930
925
|
);
|
|
926
|
+
|
|
927
|
+
// TODO GLOAS: respect builderSelection (MaxProfit, BuilderAlways, ExecutionAlways, etc.) to let
|
|
928
|
+
// the user control bid source preferences and value comparison. Also add external builder api
|
|
929
|
+
// support when it is implemented.
|
|
930
|
+
const builderBid = chain.executionPayloadBidPool.getBestBid(
|
|
931
|
+
slot,
|
|
932
|
+
parentBlock.executionPayloadBlockHash,
|
|
933
|
+
parentBlockRootHex
|
|
934
|
+
);
|
|
935
|
+
|
|
936
|
+
const logCtx = {
|
|
937
|
+
slot,
|
|
938
|
+
parentSlot,
|
|
939
|
+
parentBlockRoot: parentBlockRootHex,
|
|
940
|
+
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
941
|
+
fork,
|
|
942
|
+
...(builderBid !== null
|
|
943
|
+
? {
|
|
944
|
+
bidValue: builderBid.message.value,
|
|
945
|
+
builderIndex: builderBid.message.builderIndex,
|
|
946
|
+
bidBlockHash: toRootHex(builderBid.message.blockHash),
|
|
947
|
+
}
|
|
948
|
+
: {}),
|
|
949
|
+
};
|
|
950
|
+
|
|
931
951
|
const commonBlockBodyPromise = chain.produceCommonBlockBody({
|
|
932
952
|
slot,
|
|
933
953
|
parentBlock,
|
|
@@ -935,44 +955,76 @@ export function getValidatorApi(
|
|
|
935
955
|
graffiti: graffitiBytes,
|
|
936
956
|
});
|
|
937
957
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
feeRecipient,
|
|
947
|
-
commonBlockBodyPromise,
|
|
948
|
-
});
|
|
958
|
+
const baseAttrs = {
|
|
959
|
+
slot,
|
|
960
|
+
parentBlock,
|
|
961
|
+
randaoReveal,
|
|
962
|
+
graffiti: graffitiBytes,
|
|
963
|
+
feeRecipient,
|
|
964
|
+
commonBlockBodyPromise,
|
|
965
|
+
};
|
|
949
966
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
metrics?.
|
|
953
|
-
|
|
967
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
|
|
968
|
+
if (builderBid !== null) {
|
|
969
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
|
|
970
|
+
}
|
|
954
971
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
972
|
+
const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
|
|
973
|
+
const t = metrics?.blockProductionTime.startTimer();
|
|
974
|
+
return fn().finally(() => t?.({source}));
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
// Always build local block. If builder bid available, also build with it in parallel and prefer it.
|
|
978
|
+
const [engineResult, bidResult] = await Promise.allSettled([
|
|
979
|
+
timed(ProducedBlockSource.engine, () => chain.produceBlock(baseAttrs)),
|
|
980
|
+
builderBid !== null
|
|
981
|
+
? timed(ProducedBlockSource.builder, () => chain.produceBlock({...baseAttrs, builderBid}))
|
|
982
|
+
: Promise.reject(),
|
|
983
|
+
]);
|
|
984
|
+
|
|
985
|
+
let bestResult: typeof engineResult | null = null;
|
|
986
|
+
let source: ProducedBlockSource = ProducedBlockSource.engine;
|
|
987
|
+
if (builderBid !== null && bidResult.status === "fulfilled") {
|
|
988
|
+
source = ProducedBlockSource.builder;
|
|
989
|
+
bestResult = bidResult;
|
|
990
|
+
logger.info("Selected builder bid block", logCtx);
|
|
991
|
+
} else if (engineResult.status === "fulfilled") {
|
|
992
|
+
source = ProducedBlockSource.engine;
|
|
993
|
+
bestResult = engineResult;
|
|
994
|
+
if (builderBid !== null) {
|
|
995
|
+
logger.warn("Builder bid block production failed, using local block", logCtx);
|
|
964
996
|
}
|
|
997
|
+
}
|
|
965
998
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
|
|
999
|
+
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
1000
|
+
const engineReason = engineResult.status === "rejected" ? engineResult.reason : undefined;
|
|
1001
|
+
const bidReason = builderBid !== null && bidResult.status === "rejected" ? bidResult.reason : undefined;
|
|
1002
|
+
logger.error("Block production failed", {...logCtx, engineReason, bidReason});
|
|
1003
|
+
throw Error(`Block production failed: engine=${engineReason ?? "n/a"} builder=${bidReason ?? "n/a"}`);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const {block, executionPayloadValue, consensusBlockValue} = bestResult.value;
|
|
1007
|
+
|
|
1008
|
+
metrics?.blockProductionSuccess.inc({source});
|
|
1009
|
+
metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length);
|
|
1010
|
+
metrics?.blockProductionConsensusBlockValue.observe({source}, Number(formatWeiToEth(consensusBlockValue)));
|
|
1011
|
+
metrics?.blockProductionExecutionPayloadValue.observe({source}, Number(formatWeiToEth(executionPayloadValue)));
|
|
1012
|
+
|
|
1013
|
+
const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block));
|
|
1014
|
+
logger.verbose("Produced block", {
|
|
1015
|
+
...logCtx,
|
|
1016
|
+
executionPayloadValue,
|
|
1017
|
+
consensusBlockValue,
|
|
1018
|
+
root: blockRoot,
|
|
1019
|
+
});
|
|
1020
|
+
if (chain.opts.persistProducedBlocks) {
|
|
1021
|
+
void chain.persistBlock(block, "produced_engine_block");
|
|
975
1022
|
}
|
|
1023
|
+
|
|
1024
|
+
return {
|
|
1025
|
+
data: block as gloas.BeaconBlock,
|
|
1026
|
+
meta: {version: fork, consensusBlockValue},
|
|
1027
|
+
};
|
|
976
1028
|
},
|
|
977
1029
|
|
|
978
1030
|
async produceAttestationData({committeeIndex, slot}) {
|
|
@@ -1060,23 +1112,21 @@ export function getValidatorApi(
|
|
|
1060
1112
|
notWhileSyncing();
|
|
1061
1113
|
await waitForSlot(slot);
|
|
1062
1114
|
|
|
1063
|
-
const block = chain.forkChoice.
|
|
1115
|
+
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
1064
1116
|
if (!block) {
|
|
1065
|
-
|
|
1117
|
+
// No block is seen at slot. Return 404 so vc can skip casting payload attestation.
|
|
1118
|
+
throw new ApiError(404, `No canonical block found at slot=${slot}`);
|
|
1066
1119
|
}
|
|
1067
1120
|
|
|
1068
|
-
const blockIsForSlot = block.slot === slot;
|
|
1069
1121
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
1070
1122
|
// Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
|
|
1071
1123
|
// into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
|
|
1072
1124
|
// the input's creation time.
|
|
1073
1125
|
const payloadDueSec = config.getPayloadDueMs() / 1000;
|
|
1074
1126
|
const payloadPresent =
|
|
1075
|
-
|
|
1076
|
-
payloadInput !== undefined &&
|
|
1077
|
-
payloadInput.hasPayloadEnvelope() &&
|
|
1127
|
+
payloadInput?.hasPayloadEnvelope() === true &&
|
|
1078
1128
|
chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec) < payloadDueSec;
|
|
1079
|
-
const blobDataAvailable =
|
|
1129
|
+
const blobDataAvailable = payloadInput?.hasAllData() === true;
|
|
1080
1130
|
|
|
1081
1131
|
return {
|
|
1082
1132
|
data: {
|
|
@@ -116,13 +116,19 @@ export async function importBlock(
|
|
|
116
116
|
}
|
|
117
117
|
executionStatus = parentBlock.executionStatus;
|
|
118
118
|
}
|
|
119
|
+
|
|
120
|
+
// getBeaconProposerOrNull will return null if head state is more than one epoch away
|
|
121
|
+
// from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
|
|
122
|
+
const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);
|
|
123
|
+
|
|
119
124
|
const blockSummary = this.forkChoice.onBlock(
|
|
120
125
|
block.message,
|
|
121
126
|
postState,
|
|
122
127
|
blockDelaySec,
|
|
123
128
|
currentSlot,
|
|
124
129
|
executionStatus,
|
|
125
|
-
dataAvailabilityStatus
|
|
130
|
+
dataAvailabilityStatus,
|
|
131
|
+
expectedProposerIndex
|
|
126
132
|
);
|
|
127
133
|
|
|
128
134
|
// This adds the state necessary to process the next block
|
|
@@ -257,6 +263,7 @@ export async function importBlock(
|
|
|
257
263
|
if (ptcIndices.length > 0) {
|
|
258
264
|
this.forkChoice.notifyPtcMessages(
|
|
259
265
|
toRootHex(payloadAttestation.data.beaconBlockRoot),
|
|
266
|
+
payloadAttestation.data.slot,
|
|
260
267
|
ptcIndices,
|
|
261
268
|
payloadAttestation.data.payloadPresent,
|
|
262
269
|
payloadAttestation.data.blobDataAvailable
|
|
@@ -129,7 +129,7 @@ export async function importExecutionPayload(
|
|
|
129
129
|
|
|
130
130
|
// 3. Regenerate state for envelope verification
|
|
131
131
|
const blockState = await this.regen
|
|
132
|
-
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.
|
|
132
|
+
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.importExecutionPayload)
|
|
133
133
|
.catch(() =>
|
|
134
134
|
// only happen at the 1st batch of skipped slot checkpoint sync
|
|
135
135
|
this.regen.getClosestHeadState(protoBlock)
|
package/src/chain/chain.ts
CHANGED
|
@@ -1049,6 +1049,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1049
1049
|
feeRecipient,
|
|
1050
1050
|
commonBlockBodyPromise,
|
|
1051
1051
|
parentBlock,
|
|
1052
|
+
builderBid,
|
|
1052
1053
|
}: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}
|
|
1053
1054
|
): Promise<{
|
|
1054
1055
|
block: AssembledBlockType<T>;
|
|
@@ -1078,6 +1079,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1078
1079
|
proposerIndex,
|
|
1079
1080
|
proposerPubKey,
|
|
1080
1081
|
commonBlockBodyPromise,
|
|
1082
|
+
builderBid,
|
|
1081
1083
|
}
|
|
1082
1084
|
);
|
|
1083
1085
|
|
|
@@ -11,6 +11,7 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
11
11
|
UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
12
12
|
UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
|
|
13
13
|
INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
|
|
14
|
+
NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
|
|
14
15
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
|
|
15
16
|
NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
|
|
16
17
|
PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
|
|
@@ -41,6 +42,7 @@ export type ExecutionPayloadBidErrorType =
|
|
|
41
42
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
|
|
42
43
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
|
|
43
44
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
|
|
45
|
+
| {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
44
46
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
|
|
45
47
|
| {
|
|
46
48
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
|
|
@@ -5,6 +5,7 @@ export enum PayloadAttestationErrorCode {
|
|
|
5
5
|
NOT_CURRENT_SLOT = "PAYLOAD_ATTESTATION_ERROR_NOT_CURRENT_SLOT",
|
|
6
6
|
PAYLOAD_ATTESTATION_ALREADY_KNOWN = "PAYLOAD_ATTESTATION_ERROR_PAYLOAD_ATTESTATION_ALREADY_KNOWN",
|
|
7
7
|
UNKNOWN_BLOCK_ROOT = "PAYLOAD_ATTESTATION_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
8
|
+
INVALID_BLOCK_SLOT = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK_SLOT",
|
|
8
9
|
INVALID_BLOCK = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK",
|
|
9
10
|
INVALID_ATTESTER = "PAYLOAD_ATTESTATION_ERROR_INVALID_ATTESTER",
|
|
10
11
|
INVALID_SIGNATURE = "PAYLOAD_ATTESTATION_ERROR_INVALID_SIGNATURE",
|
|
@@ -18,6 +19,7 @@ export type PayloadAttestationErrorType =
|
|
|
18
19
|
blockRoot: RootHex;
|
|
19
20
|
}
|
|
20
21
|
| {code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT; blockRoot: RootHex}
|
|
22
|
+
| {code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT; blockRoot: RootHex; blockSlot: Slot; slot: Slot}
|
|
21
23
|
| {code: PayloadAttestationErrorCode.INVALID_BLOCK; blockRoot: RootHex}
|
|
22
24
|
| {code: PayloadAttestationErrorCode.INVALID_ATTESTER; attesterIndex: ValidatorIndex}
|
|
23
25
|
| {code: PayloadAttestationErrorCode.INVALID_SIGNATURE};
|
|
@@ -12,13 +12,13 @@ type BlockRootHex = string;
|
|
|
12
12
|
type BlockHashHex = string;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Store the best execution payload bid per slot / (parent block root, parent block hash).
|
|
15
|
+
* Store the best signed execution payload bid per slot / (parent block root, parent block hash).
|
|
16
16
|
*/
|
|
17
17
|
export class ExecutionPayloadBidPool {
|
|
18
18
|
private readonly bidByParentHashByParentRootBySlot = new MapDef<
|
|
19
19
|
Slot,
|
|
20
|
-
MapDef<BlockRootHex, Map<BlockHashHex, gloas.
|
|
21
|
-
>(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.
|
|
20
|
+
MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>
|
|
21
|
+
>(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>(() => new Map()));
|
|
22
22
|
private lowestPermissibleSlot = 0;
|
|
23
23
|
|
|
24
24
|
get size(): number {
|
|
@@ -31,8 +31,8 @@ export class ExecutionPayloadBidPool {
|
|
|
31
31
|
return count;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
add(bid: gloas.
|
|
35
|
-
const {slot, parentBlockRoot, parentBlockHash, value} = bid;
|
|
34
|
+
add(bid: gloas.SignedExecutionPayloadBid): InsertOutcome {
|
|
35
|
+
const {slot, parentBlockRoot, parentBlockHash, value} = bid.message;
|
|
36
36
|
const lowestPermissibleSlot = this.lowestPermissibleSlot;
|
|
37
37
|
|
|
38
38
|
if (slot < lowestPermissibleSlot) {
|
|
@@ -45,7 +45,7 @@ export class ExecutionPayloadBidPool {
|
|
|
45
45
|
const existing = bidByParentHash.get(parentHashHex);
|
|
46
46
|
|
|
47
47
|
if (existing) {
|
|
48
|
-
const existingValue = existing.value;
|
|
48
|
+
const existingValue = existing.message.value;
|
|
49
49
|
const newValue = value;
|
|
50
50
|
if (newValue > existingValue) {
|
|
51
51
|
bidByParentHash.set(parentHashHex, bid);
|
|
@@ -59,14 +59,15 @@ export class ExecutionPayloadBidPool {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Return the highest-value bid matching slot, parent block hash, and parent block root.
|
|
62
|
+
* Return the highest-value signed bid matching slot, parent block hash, and parent block root.
|
|
63
63
|
* Used for gossip validation and block production.
|
|
64
64
|
*/
|
|
65
65
|
getBestBid(
|
|
66
66
|
slot: Slot,
|
|
67
|
-
parentBlockHash: BlockHashHex,
|
|
67
|
+
parentBlockHash: BlockHashHex | null,
|
|
68
68
|
parentBlockRoot: BlockRootHex
|
|
69
|
-
): gloas.
|
|
69
|
+
): gloas.SignedExecutionPayloadBid | null {
|
|
70
|
+
if (parentBlockHash === null) return null;
|
|
70
71
|
const bidByParentHash = this.bidByParentHashByParentRootBySlot.get(slot)?.get(parentBlockRoot);
|
|
71
72
|
return bidByParentHash?.get(parentBlockHash) ?? null;
|
|
72
73
|
}
|
|
@@ -170,7 +170,7 @@ export class PrepareNextSlotScheduler {
|
|
|
170
170
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
|
|
171
171
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
172
172
|
// Spec: should_build_on_full(store, head) — see produceBlockBody.ts for context.
|
|
173
|
-
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead)) {
|
|
173
|
+
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead, prepareSlot)) {
|
|
174
174
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
|
|
175
175
|
// Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
|
|
176
176
|
if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
gloas,
|
|
51
51
|
ssz,
|
|
52
52
|
} from "@lodestar/types";
|
|
53
|
-
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
53
|
+
import {GWEI_TO_WEI, Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
54
54
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
55
55
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
56
56
|
import {IExecutionBuilder, IExecutionEngine, PayloadAttributes, PayloadId} from "../../execution/index.js";
|
|
@@ -91,6 +91,8 @@ export type BlockAttributes = {
|
|
|
91
91
|
slot: Slot;
|
|
92
92
|
parentBlock: ProtoBlock;
|
|
93
93
|
feeRecipient?: string;
|
|
94
|
+
/** When provided, build block with this builder bid instead of a self-build bid */
|
|
95
|
+
builderBid?: gloas.SignedExecutionPayloadBid;
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
export enum BlockType {
|
|
@@ -150,6 +152,28 @@ export type ProduceResult =
|
|
|
150
152
|
| ProduceFullPhase0
|
|
151
153
|
| ProduceBlinded;
|
|
152
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Drop voluntary exits that `parent_execution_requests` have invalidated (e.g. a withdrawal
|
|
157
|
+
* request initiating an exit on the same validator). Op pool selected against the unapplied
|
|
158
|
+
* state, so re-validate against the post-apply state to avoid producing an invalid block.
|
|
159
|
+
*
|
|
160
|
+
* `getStateAfterParentPayload` is a thunk so the post-apply state is only materialized when
|
|
161
|
+
* actually needed (i.e. when extending the parent payload and there are exits to filter).
|
|
162
|
+
*/
|
|
163
|
+
function maybeFilterInvalidatedVoluntaryExits(
|
|
164
|
+
commonBlockBody: CommonBlockBody,
|
|
165
|
+
isExtendingPayload: boolean,
|
|
166
|
+
getStateAfterParentPayload: () => IBeaconStateViewBellatrix
|
|
167
|
+
): CommonBlockBody["voluntaryExits"] {
|
|
168
|
+
if (!isExtendingPayload || commonBlockBody.voluntaryExits.length === 0) {
|
|
169
|
+
return commonBlockBody.voluntaryExits;
|
|
170
|
+
}
|
|
171
|
+
const state = getStateAfterParentPayload();
|
|
172
|
+
return commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
|
|
173
|
+
state.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
153
177
|
export async function produceBlockBody<T extends BlockType>(
|
|
154
178
|
this: BeaconChain,
|
|
155
179
|
blockType: T,
|
|
@@ -172,6 +196,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
172
196
|
proposerIndex,
|
|
173
197
|
proposerPubKey,
|
|
174
198
|
commonBlockBodyPromise,
|
|
199
|
+
builderBid,
|
|
175
200
|
} = blockAttr;
|
|
176
201
|
let executionPayloadValue: Wei;
|
|
177
202
|
let blockBody: AssembledBodyType<T>;
|
|
@@ -192,7 +217,43 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
192
217
|
};
|
|
193
218
|
this.logger.verbose("Producing beacon block body", logMeta);
|
|
194
219
|
|
|
195
|
-
if (
|
|
220
|
+
if (builderBid !== undefined) {
|
|
221
|
+
if (!isStatePostGloas(currentState)) {
|
|
222
|
+
throw new Error("Expected Gloas state for builder bid block production");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const isExtendingPayload = byteArrayEquals(
|
|
226
|
+
builderBid.message.parentBlockHash,
|
|
227
|
+
currentState.latestExecutionPayloadBid.blockHash
|
|
228
|
+
);
|
|
229
|
+
const parentExecutionRequests = isExtendingPayload
|
|
230
|
+
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
231
|
+
: ssz.electra.ExecutionRequests.defaultValue();
|
|
232
|
+
executionPayloadValue = BigInt(builderBid.message.value) * GWEI_TO_WEI;
|
|
233
|
+
|
|
234
|
+
const commonBlockBody = await commonBlockBodyPromise;
|
|
235
|
+
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
236
|
+
gloasBody.signedExecutionPayloadBid = builderBid;
|
|
237
|
+
gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
|
|
238
|
+
parentBlock.blockRoot,
|
|
239
|
+
blockSlot - 1
|
|
240
|
+
);
|
|
241
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
242
|
+
gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(commonBlockBody, isExtendingPayload, () =>
|
|
243
|
+
currentState.withParentPayloadApplied(parentExecutionRequests)
|
|
244
|
+
);
|
|
245
|
+
blockBody = gloasBody as AssembledBodyType<T>;
|
|
246
|
+
|
|
247
|
+
this.logger.verbose("Produced block with builder bid", {
|
|
248
|
+
slot: blockSlot,
|
|
249
|
+
builderIndex: builderBid.message.builderIndex,
|
|
250
|
+
bidValue: builderBid.message.value,
|
|
251
|
+
parentBlockHash: toRootHex(builderBid.message.parentBlockHash),
|
|
252
|
+
parentBlockRoot: toRootHex(builderBid.message.parentBlockRoot),
|
|
253
|
+
blockHash: toRootHex(builderBid.message.blockHash),
|
|
254
|
+
isExtendingPayload,
|
|
255
|
+
});
|
|
256
|
+
} else if (isForkPostGloas(fork)) {
|
|
196
257
|
if (!isStatePostGloas(currentState)) {
|
|
197
258
|
throw new Error("Expected Gloas state for Gloas block production");
|
|
198
259
|
}
|
|
@@ -209,12 +270,6 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
209
270
|
|
|
210
271
|
const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
|
|
211
272
|
|
|
212
|
-
this.logger.verbose("Preparing execution payload from engine", {
|
|
213
|
-
slot: blockSlot,
|
|
214
|
-
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
215
|
-
feeRecipient,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
273
|
// Get execution payload from EL
|
|
219
274
|
let parentBlockHash: Bytes32;
|
|
220
275
|
let parentExecutionRequests: electra.ExecutionRequests;
|
|
@@ -223,7 +278,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
223
278
|
// Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
|
|
224
279
|
// (set by chain.getProposerHead(slot)). Returns false when the PTC majority
|
|
225
280
|
// signalled the blob data is not available, forcing a build on EMPTY (reorg).
|
|
226
|
-
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock);
|
|
281
|
+
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock, blockSlot);
|
|
227
282
|
if (isBuildingOnFull) {
|
|
228
283
|
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
229
284
|
parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
|
|
@@ -247,6 +302,16 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
247
302
|
const {prepType, payloadId} = prepareRes;
|
|
248
303
|
Object.assign(logMeta, {executionPayloadPrepType: prepType});
|
|
249
304
|
|
|
305
|
+
this.logger.verbose("Prepared execution payload from engine", {
|
|
306
|
+
slot: blockSlot,
|
|
307
|
+
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
308
|
+
parentBlockHash: toRootHex(parentBlockHash),
|
|
309
|
+
feeRecipient,
|
|
310
|
+
prepType,
|
|
311
|
+
payloadId,
|
|
312
|
+
isBuildingOnFull,
|
|
313
|
+
});
|
|
314
|
+
|
|
250
315
|
if (prepType !== PayloadPreparationType.Cached) {
|
|
251
316
|
await sleep(PAYLOAD_GENERATION_TIME_MS);
|
|
252
317
|
}
|
|
@@ -300,14 +365,11 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
300
365
|
blockSlot - 1
|
|
301
366
|
);
|
|
302
367
|
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
309
|
-
);
|
|
310
|
-
}
|
|
368
|
+
gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(
|
|
369
|
+
commonBlockBody,
|
|
370
|
+
isBuildingOnFull,
|
|
371
|
+
() => stateAfterParentPayload
|
|
372
|
+
);
|
|
311
373
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
312
374
|
|
|
313
375
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -17,10 +17,12 @@ export enum RegenCaller {
|
|
|
17
17
|
predictProposerHead = "predictProposerHead",
|
|
18
18
|
produceAttestationData = "produceAttestationData",
|
|
19
19
|
processBlocksInEpoch = "processBlocksInEpoch",
|
|
20
|
+
importExecutionPayload = "importExecutionPayload",
|
|
20
21
|
validateGossipAggregateAndProof = "validateGossipAggregateAndProof",
|
|
21
22
|
validateGossipAttestation = "validateGossipAttestation",
|
|
22
23
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
23
24
|
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
25
|
+
validateGossipPayloadAttestationMessage = "validateGossipPayloadAttestationMessage",
|
|
24
26
|
validateGossipProposerPreferences = "validateGossipProposerPreferences",
|
|
25
27
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
26
28
|
restApi = "restApi",
|
|
@@ -35,10 +35,6 @@ async function validateExecutionPayloadBid(
|
|
|
35
35
|
const bid = signedExecutionPayloadBid.message;
|
|
36
36
|
const parentBlockRootHex = toRootHex(bid.parentBlockRoot);
|
|
37
37
|
const parentBlockHashHex = toRootHex(bid.parentBlockHash);
|
|
38
|
-
const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.validateGossipExecutionPayloadBid);
|
|
39
|
-
if (!isStatePostGloas(state)) {
|
|
40
|
-
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
41
|
-
}
|
|
42
38
|
|
|
43
39
|
// [IGNORE] `bid.slot` is the current slot or the next slot.
|
|
44
40
|
const currentSlot = chain.clock.currentSlot;
|
|
@@ -61,6 +57,17 @@ async function validateExecutionPayloadBid(
|
|
|
61
57
|
});
|
|
62
58
|
}
|
|
63
59
|
|
|
60
|
+
// [REJECT] The bid is for a higher slot than its parent block -- i.e.
|
|
61
|
+
// validate that `bid.slot` is greater than the slot of the block with root
|
|
62
|
+
// `bid.parent_block_root`.
|
|
63
|
+
if (bid.slot <= parentBlock.slot) {
|
|
64
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
65
|
+
code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT,
|
|
66
|
+
parentSlot: parentBlock.slot,
|
|
67
|
+
slot: bid.slot,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
64
71
|
// [IGNORE] A `SignedProposerPreferences` matching `bid.slot` and the bid's branch has been
|
|
65
72
|
// seen — i.e. `proposal_slot == bid.slot` AND `dependent_root ==
|
|
66
73
|
// get_proposer_dependent_root(parent_state, compute_epoch_at_slot(bid.slot))`.
|
|
@@ -100,9 +107,31 @@ async function validateExecutionPayloadBid(
|
|
|
100
107
|
});
|
|
101
108
|
}
|
|
102
109
|
|
|
110
|
+
// Use the bid's parent branch state for builder checks
|
|
111
|
+
const state = await chain.regen
|
|
112
|
+
.getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateGossipExecutionPayloadBid)
|
|
113
|
+
.catch(() => {
|
|
114
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
115
|
+
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
116
|
+
parentBlockRoot: parentBlockRootHex,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!isStatePostGloas(state)) {
|
|
121
|
+
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
103
124
|
// [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
|
|
104
125
|
// `is_active_builder(state, bid.builder_index)` returns `True`.
|
|
105
|
-
|
|
126
|
+
let builder: gloas.Builder;
|
|
127
|
+
try {
|
|
128
|
+
builder = state.getBuilder(bid.builderIndex);
|
|
129
|
+
} catch {
|
|
130
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
131
|
+
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
132
|
+
builderIndex: bid.builderIndex,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
106
135
|
if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
|
|
107
136
|
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
108
137
|
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
@@ -186,11 +215,11 @@ async function validateExecutionPayloadBid(
|
|
|
186
215
|
// [IGNORE] this bid is the highest value bid seen for the tuple
|
|
187
216
|
// `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
|
|
188
217
|
const bestBid = chain.executionPayloadBidPool.getBestBid(bid.slot, parentBlockHashHex, parentBlockRootHex);
|
|
189
|
-
if (bestBid !== null && bestBid.value >= bid.value) {
|
|
218
|
+
if (bestBid !== null && bestBid.message.value >= bid.value) {
|
|
190
219
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
191
220
|
code: ExecutionPayloadBidErrorCode.BID_TOO_LOW,
|
|
192
221
|
bidValue: bid.value,
|
|
193
|
-
currentHighestBid: bestBid.value,
|
|
222
|
+
currentHighestBid: bestBid.message.value,
|
|
194
223
|
});
|
|
195
224
|
}
|
|
196
225
|
// [IGNORE] `bid.value` is less or equal than the builder's excess balance --
|
|
@@ -8,6 +8,7 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
|
|
|
8
8
|
import {toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
|
|
10
10
|
import {IBeaconChain} from "../index.js";
|
|
11
|
+
import {RegenCaller} from "../regen/index.js";
|
|
11
12
|
|
|
12
13
|
export type PayloadAttestationValidationResult = {
|
|
13
14
|
attDataRootHex: RootHex;
|
|
@@ -61,22 +62,43 @@ async function validatePayloadAttestationMessage(
|
|
|
61
62
|
// [IGNORE] The message's block `data.beacon_block_root` has been seen (via
|
|
62
63
|
// gossip or non-gossip sources) (a client MAY queue attestation for processing
|
|
63
64
|
// once the block is retrieved. Note a client might want to request payload after).
|
|
64
|
-
|
|
65
|
+
const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot);
|
|
66
|
+
if (!block) {
|
|
65
67
|
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
66
68
|
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
67
69
|
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// [IGNORE] The block referenced by `data.beacon_block_root` is at slot `data.slot`,
|
|
74
|
+
// i.e. the block has `block.slot == data.slot`.
|
|
75
|
+
if (block.slot !== data.slot) {
|
|
76
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
77
|
+
code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT,
|
|
78
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
79
|
+
blockSlot: block.slot,
|
|
80
|
+
slot: data.slot,
|
|
81
|
+
});
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
// [REJECT] The message's block `data.beacon_block_root` passes validation.
|
|
77
85
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
78
86
|
// it is possible that the block didn't pass the validation
|
|
79
87
|
|
|
88
|
+
// Use the referenced block's branch state for the PTC committee check
|
|
89
|
+
const state = await chain.regen
|
|
90
|
+
.getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
|
|
91
|
+
.catch(() => {
|
|
92
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
93
|
+
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
94
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!isStatePostGloas(state)) {
|
|
99
|
+
throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
// [REJECT] The message's validator index is within the payload committee in
|
|
81
103
|
// `get_ptc(state, data.slot)`. The `state` is the head state corresponding to
|
|
82
104
|
// processing the block up to the current slot as determined by the fork choice.
|
package/src/network/interface.ts
CHANGED
|
@@ -113,6 +113,7 @@ export interface INetwork extends INetworkCorePublic {
|
|
|
113
113
|
publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
|
|
114
114
|
publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
|
|
115
115
|
publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
|
|
116
|
+
publishSignedExecutionPayloadBid(signedBid: gloas.SignedExecutionPayloadBid): Promise<number>;
|
|
116
117
|
publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number>;
|
|
117
118
|
publishProposerPreferences(signedProposerPreferences: gloas.SignedProposerPreferences): Promise<number>;
|
|
118
119
|
|