@lodestar/beacon-node 1.47.0-dev.4546ac1229 → 1.47.0-dev.56250f4ab6
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 +39 -11
- 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 +13 -1
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/events/index.d.ts.map +1 -1
- package/lib/api/impl/events/index.js +2 -0
- package/lib/api/impl/events/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +232 -49
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/impl/validator/utils.js +1 -1
- package/lib/api/impl/validator/utils.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +7 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +6 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +14 -5
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +5 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -1
- package/lib/chain/chain.d.ts +6 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +14 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +4 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +4 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts +16 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.d.ts.map +1 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js +57 -0
- package/lib/chain/opPools/deferredVoluntaryExitPool.js.map +1 -0
- package/lib/chain/opPools/executionPayloadBidPool.d.ts +7 -2
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.js +4 -4
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
- package/lib/chain/opPools/index.d.ts +2 -1
- package/lib/chain/opPools/index.d.ts.map +1 -1
- package/lib/chain/opPools/index.js +1 -0
- package/lib/chain/opPools/index.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +23 -5
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +4 -2
- 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/regen/queued.d.ts +2 -2
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +5 -5
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +28 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +121 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +5 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +6 -0
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +47 -5
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +117 -7
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +3 -3
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +2 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/voluntaryExit.d.ts +9 -1
- package/lib/chain/validation/voluntaryExit.d.ts.map +1 -1
- package/lib/chain/validation/voluntaryExit.js +46 -1
- package/lib/chain/validation/voluntaryExit.js.map +1 -1
- package/lib/execution/builder/apiClient.d.ts +64 -0
- package/lib/execution/builder/apiClient.d.ts.map +1 -0
- package/lib/execution/builder/apiClient.js +184 -0
- package/lib/execution/builder/apiClient.js.map +1 -0
- package/lib/execution/builder/validateBid.d.ts +22 -0
- package/lib/execution/builder/validateBid.d.ts.map +1 -0
- package/lib/execution/builder/validateBid.js +111 -0
- package/lib/execution/builder/validateBid.js.map +1 -0
- package/lib/metrics/metrics/lodestar.d.ts +39 -5
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +63 -2
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/processor/constants.d.ts +7 -0
- package/lib/network/processor/constants.d.ts.map +1 -0
- package/lib/network/processor/constants.js +7 -0
- package/lib/network/processor/constants.js.map +1 -0
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +6 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/indexed.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/indexed.js +0 -1
- package/lib/network/processor/gossipQueues/indexed.js.map +1 -1
- package/lib/network/processor/index.d.ts +3 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +41 -17
- package/lib/network/processor/index.js.map +1 -1
- package/lib/node/deferredVoluntaryExitPublisher.d.ts +15 -0
- package/lib/node/deferredVoluntaryExitPublisher.d.ts.map +1 -0
- package/lib/node/deferredVoluntaryExitPublisher.js +38 -0
- package/lib/node/deferredVoluntaryExitPublisher.js.map +1 -0
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +7 -0
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/types.d.ts +32 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +33 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +6 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +143 -68
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +16 -11
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +3 -1
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/package.json +34 -34
- package/src/api/impl/beacon/blocks/index.ts +46 -14
- package/src/api/impl/beacon/pool/index.ts +15 -1
- package/src/api/impl/events/index.ts +3 -1
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +285 -49
- package/src/api/impl/validator/utils.ts +1 -1
- package/src/api/rest/base.ts +7 -0
- package/src/chain/archiveStore/archiveStore.ts +1 -1
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +8 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -4
- package/src/chain/chain.ts +16 -1
- package/src/chain/forkChoice/index.ts +4 -0
- package/src/chain/interface.ts +4 -0
- package/src/chain/opPools/deferredVoluntaryExitPool.ts +65 -0
- package/src/chain/opPools/executionPayloadBidPool.ts +13 -7
- package/src/chain/opPools/index.ts +2 -1
- package/src/chain/prepareNextSlot.ts +23 -5
- package/src/chain/produceBlock/produceBlockBody.ts +1 -1
- package/src/chain/regen/interface.ts +4 -2
- package/src/chain/regen/queued.ts +5 -5
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +143 -14
- package/src/chain/stateCache/fifoBlockStateCache.ts +8 -1
- package/src/chain/stateCache/persistentCheckpointsCache.ts +133 -8
- package/src/chain/stateCache/types.ts +3 -3
- package/src/chain/validation/executionPayloadBid.ts +2 -2
- package/src/chain/validation/voluntaryExit.ts +59 -2
- package/src/execution/builder/apiClient.ts +244 -0
- package/src/execution/builder/validateBid.ts +166 -0
- package/src/metrics/metrics/lodestar.ts +72 -7
- package/src/network/processor/constants.ts +6 -0
- package/src/network/processor/gossipHandlers.ts +6 -2
- package/src/network/processor/gossipQueues/indexed.ts +0 -1
- package/src/network/processor/index.ts +53 -21
- package/src/node/deferredVoluntaryExitPublisher.ts +54 -0
- package/src/node/nodejs.ts +8 -0
- package/src/sync/types.ts +33 -0
- package/src/sync/unknownBlock.ts +167 -73
- package/src/sync/utils/downloadByRange.ts +21 -12
- package/src/sync/utils/downloadByRoot.ts +3 -1
|
@@ -52,12 +52,16 @@ import {
|
|
|
52
52
|
import {
|
|
53
53
|
GWEI_TO_WEI,
|
|
54
54
|
TimeoutError,
|
|
55
|
+
byteArrayEquals,
|
|
55
56
|
defer,
|
|
56
57
|
formatWeiToEth,
|
|
57
58
|
fromHex,
|
|
59
|
+
prettyGweiToEth,
|
|
58
60
|
prettyWeiToEth,
|
|
59
61
|
resolveOrRacePromises,
|
|
62
|
+
sleep,
|
|
60
63
|
toHex,
|
|
64
|
+
toPrintableUrl,
|
|
61
65
|
toRootHex,
|
|
62
66
|
} from "@lodestar/utils";
|
|
63
67
|
import {MAX_BUILDER_BOOST_FACTOR} from "@lodestar/validator";
|
|
@@ -72,6 +76,7 @@ import {
|
|
|
72
76
|
SyncCommitteeErrorCode,
|
|
73
77
|
} from "../../../chain/errors/index.js";
|
|
74
78
|
import {ChainEvent, CommonBlockBody} from "../../../chain/index.js";
|
|
79
|
+
import {PooledExecutionPayloadBid} from "../../../chain/opPools/index.js";
|
|
75
80
|
import {PREPARE_NEXT_SLOT_BPS} from "../../../chain/prepareNextSlot.js";
|
|
76
81
|
import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/produceBlock/index.js";
|
|
77
82
|
import {RegenCaller} from "../../../chain/regen/index.js";
|
|
@@ -80,7 +85,9 @@ import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
|
|
|
80
85
|
import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
|
|
81
86
|
import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
|
|
82
87
|
import {ZERO_HASH} from "../../../constants/index.js";
|
|
88
|
+
import {BUILDER_BID_DEADLINE_MS, BuilderApiBid, decodeBuilderUrl} from "../../../execution/builder/apiClient.js";
|
|
83
89
|
import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
|
|
90
|
+
import {getBuilderBidTotalGwei, validateBuilderApiExecutionPayloadBid} from "../../../execution/builder/validateBid.js";
|
|
84
91
|
import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
|
|
85
92
|
import {CommitteeSubscription} from "../../../network/subnets/index.js";
|
|
86
93
|
import {callInNextEventLoop} from "../../../util/eventLoop.js";
|
|
@@ -112,6 +119,55 @@ import {
|
|
|
112
119
|
const BLOCK_PRODUCTION_RACE_CUTOFF_MS = 2_000;
|
|
113
120
|
/** Overall timeout for execution and block production apis */
|
|
114
121
|
const BLOCK_PRODUCTION_RACE_TIMEOUT_MS = 12_000;
|
|
122
|
+
/** Rejection message of the bid block branch when there is no viable bid to commit to */
|
|
123
|
+
const NO_BID_AVAILABLE = "No builder bid available";
|
|
124
|
+
|
|
125
|
+
type BidCandidate = {
|
|
126
|
+
signedBid: gloas.SignedExecutionPayloadBid;
|
|
127
|
+
/** Total payment in Gwei, counting the execution payment at most at the entry's cap */
|
|
128
|
+
totalGwei: bigint;
|
|
129
|
+
boostFactor: bigint;
|
|
130
|
+
url?: string;
|
|
131
|
+
/** Time in milliseconds from the slot start when the bid was received */
|
|
132
|
+
receivedMs: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Return the best bid by boosted total payment. A candidate with the max boost factor is
|
|
137
|
+
* preferred over any other regardless of value, ties prefer a builder api bid over the
|
|
138
|
+
* p2p bid, and the earlier received bid between builder api bids.
|
|
139
|
+
*/
|
|
140
|
+
function selectBestBid(candidates: BidCandidate[]): BidCandidate | null {
|
|
141
|
+
const boostedValue = ({totalGwei, boostFactor}: BidCandidate): bigint => boostFactor * totalGwei;
|
|
142
|
+
let best: BidCandidate | null = null;
|
|
143
|
+
for (const candidate of candidates) {
|
|
144
|
+
if (best === null) {
|
|
145
|
+
best = candidate;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
// Preserve max boost preference before comparing bid values
|
|
149
|
+
const candidateIsMaxBoost = candidate.boostFactor === MAX_BUILDER_BOOST_FACTOR;
|
|
150
|
+
const bestIsMaxBoost = best.boostFactor === MAX_BUILDER_BOOST_FACTOR;
|
|
151
|
+
if (candidateIsMaxBoost !== bestIsMaxBoost) {
|
|
152
|
+
if (candidateIsMaxBoost) {
|
|
153
|
+
best = candidate;
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const candidateValue = boostedValue(candidate);
|
|
158
|
+
const bestValue = boostedValue(best);
|
|
159
|
+
if (candidateValue > bestValue) {
|
|
160
|
+
best = candidate;
|
|
161
|
+
} else if (
|
|
162
|
+
candidateValue === bestValue &&
|
|
163
|
+
// A tie prefers a builder api bid over the p2p bid, and the earlier received bid otherwise
|
|
164
|
+
(best.url === undefined || (candidate.url !== undefined && candidate.receivedMs < best.receivedMs))
|
|
165
|
+
) {
|
|
166
|
+
best = candidate;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return best;
|
|
170
|
+
}
|
|
115
171
|
|
|
116
172
|
type ProduceBlockContentsRes = {executionPayloadValue: Wei; consensusBlockValue: Wei} & {
|
|
117
173
|
data: BlockContents;
|
|
@@ -857,7 +913,7 @@ export function getValidatorApi(
|
|
|
857
913
|
feeRecipient,
|
|
858
914
|
strictFeeRecipientCheck,
|
|
859
915
|
includePayload,
|
|
860
|
-
|
|
916
|
+
builderConfig,
|
|
861
917
|
}) {
|
|
862
918
|
const fork = config.getForkName(slot);
|
|
863
919
|
|
|
@@ -865,7 +921,7 @@ export function getValidatorApi(
|
|
|
865
921
|
throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
|
|
866
922
|
}
|
|
867
923
|
|
|
868
|
-
builderBoostFactor = builderBoostFactor
|
|
924
|
+
const builderBoostFactor = builderConfig.builderBoostFactor;
|
|
869
925
|
if (builderBoostFactor > MAX_BUILDER_BOOST_FACTOR) {
|
|
870
926
|
throw new ApiError(400, `Invalid builderBoostFactor=${builderBoostFactor} > MAX_BUILDER_BOOST_FACTOR`);
|
|
871
927
|
}
|
|
@@ -892,32 +948,117 @@ export function getValidatorApi(
|
|
|
892
948
|
})
|
|
893
949
|
);
|
|
894
950
|
|
|
895
|
-
// TODO GLOAS: add external builder api support when it is implemented
|
|
896
951
|
const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
|
|
897
952
|
const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
|
|
953
|
+
// Post-gloas every proposal parent has an execution payload hash
|
|
954
|
+
if (bidParentBlockHash === null) {
|
|
955
|
+
throw new ApiError(500, `Unknown parent block hash for proposal parent ${parentBlockRootHex}`);
|
|
956
|
+
}
|
|
898
957
|
const circuitBreakerActive = chain.builderCircuitBreaker.isActive(slot, parentBlock);
|
|
899
|
-
// Keep a builder bid as fallback unless the circuit breaker is active
|
|
900
|
-
const builderBid = circuitBreakerActive
|
|
901
|
-
? null
|
|
902
|
-
: chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
903
958
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
959
|
+
// Fire builder API bid requests while the local payload is built, one request per entry.
|
|
960
|
+
// Any entry failure yields no bid and never fails block production.
|
|
961
|
+
let builderApiBidsPromise: Promise<BuilderApiBid[]> = Promise.resolve([]);
|
|
962
|
+
if (builderConfig.builders.length > 0 && !circuitBreakerActive) {
|
|
963
|
+
try {
|
|
964
|
+
const proposerIndex = chain.getHeadState().getBeaconProposer(slot);
|
|
965
|
+
const proposerPubkey = chain.pubkeyCache.getOrThrow(proposerIndex).toBytes();
|
|
966
|
+
builderApiBidsPromise = chain.builderApiClient.getExecutionPayloadBids(
|
|
967
|
+
builderConfig.builders,
|
|
968
|
+
slot,
|
|
969
|
+
fromHex(bidParentBlockHash),
|
|
970
|
+
parentBlockRoot,
|
|
971
|
+
proposerPubkey
|
|
972
|
+
);
|
|
973
|
+
} catch (e) {
|
|
974
|
+
logger.warn("Unable to request builder API bids", {slot}, e as Error);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// A builder bid is expected if builders are configured or a p2p bid was already received.
|
|
979
|
+
// Used by the censorship override which may run before the bid deadline
|
|
980
|
+
const builderBidExpected =
|
|
981
|
+
builderConfig.builders.length > 0 ||
|
|
982
|
+
(!circuitBreakerActive &&
|
|
983
|
+
chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex) !== null);
|
|
984
|
+
|
|
985
|
+
// Select the p2p bid once builders had time to bid up, matching the deadline advertised
|
|
986
|
+
// on builder API bid requests, unless the circuit breaker is active
|
|
987
|
+
const p2pBidPromise: Promise<PooledExecutionPayloadBid | null> = circuitBreakerActive
|
|
988
|
+
? Promise.resolve(null)
|
|
989
|
+
: sleep(Math.max(0, BUILDER_BID_DEADLINE_MS - chain.clock.msFromSlot(slot))).then(() => {
|
|
990
|
+
const p2pBid = chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
991
|
+
// Discard p2p bids below the proposer's configured floor on the total payment.
|
|
992
|
+
// A p2p bid's total is just its value since gossip validation enforces executionPayment=0.
|
|
993
|
+
if (p2pBid !== null && BigInt(p2pBid.signedBid.message.value) < builderConfig.minBid) {
|
|
994
|
+
logger.info("Best p2p bid below configured minimum", {
|
|
995
|
+
slot,
|
|
996
|
+
bidValue: prettyGweiToEth(p2pBid.signedBid.message.value),
|
|
997
|
+
minBid: prettyGweiToEth(builderConfig.minBid),
|
|
998
|
+
});
|
|
999
|
+
return null;
|
|
918
1000
|
}
|
|
919
|
-
|
|
920
|
-
|
|
1001
|
+
return p2pBid;
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// Candidates are ranked by their boosted counted total payment, the p2p bid is governed
|
|
1005
|
+
// by the top-level factors and each builder API bid by its own entry. Ties prefer the
|
|
1006
|
+
// earliest received builder API bid, so the signed block can be routed back to its
|
|
1007
|
+
// builder directly.
|
|
1008
|
+
const bestBidPromise: Promise<BidCandidate | null> = (async () => {
|
|
1009
|
+
const [builderApiBids, p2pBid] = await Promise.all([builderApiBidsPromise, p2pBidPromise]);
|
|
1010
|
+
|
|
1011
|
+
const candidates = (
|
|
1012
|
+
await Promise.all(
|
|
1013
|
+
builderApiBids.map(async ({url, entry, signedBid, receivedMs}): Promise<BidCandidate | null> => {
|
|
1014
|
+
try {
|
|
1015
|
+
await validateBuilderApiExecutionPayloadBid(chain, signedBid, {
|
|
1016
|
+
slot,
|
|
1017
|
+
parentBlock,
|
|
1018
|
+
parentBlockHash: bidParentBlockHash,
|
|
1019
|
+
parentBlockRoot: parentBlockRootHex,
|
|
1020
|
+
entry,
|
|
1021
|
+
});
|
|
1022
|
+
return {
|
|
1023
|
+
signedBid,
|
|
1024
|
+
totalGwei: getBuilderBidTotalGwei(signedBid.message, entry.maxExecutionPayment),
|
|
1025
|
+
boostFactor: entry.builderBoostFactor,
|
|
1026
|
+
url,
|
|
1027
|
+
receivedMs,
|
|
1028
|
+
};
|
|
1029
|
+
} catch (e) {
|
|
1030
|
+
metrics?.builderApi.bidsDiscarded.inc();
|
|
1031
|
+
logger.warn("Ignoring invalid builder API bid", {slot, builder: toPrintableUrl(url)}, e as Error);
|
|
1032
|
+
return null;
|
|
1033
|
+
}
|
|
1034
|
+
})
|
|
1035
|
+
)
|
|
1036
|
+
).filter((candidate): candidate is BidCandidate => candidate !== null);
|
|
1037
|
+
|
|
1038
|
+
if (p2pBid !== null) {
|
|
1039
|
+
candidates.push({
|
|
1040
|
+
signedBid: p2pBid.signedBid,
|
|
1041
|
+
totalGwei: BigInt(p2pBid.signedBid.message.value),
|
|
1042
|
+
boostFactor: builderConfig.builderBoostFactor,
|
|
1043
|
+
receivedMs: p2pBid.receivedMs,
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const best = selectBestBid(candidates);
|
|
1048
|
+
if (candidates.length > 0) {
|
|
1049
|
+
logger.debug("Ranked builder bid candidates", {
|
|
1050
|
+
slot,
|
|
1051
|
+
candidates: candidates
|
|
1052
|
+
.map(
|
|
1053
|
+
(candidate) =>
|
|
1054
|
+
`${candidate.url ?? "p2p"}:total=${prettyGweiToEth(candidate.totalGwei)}:boost=${candidate.boostFactor}:received=${candidate.receivedMs}ms`
|
|
1055
|
+
)
|
|
1056
|
+
.join(","),
|
|
1057
|
+
bidSource: best?.url ?? "p2p",
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
return best;
|
|
1061
|
+
})();
|
|
921
1062
|
|
|
922
1063
|
const commonBlockBodyPromise = chain.produceCommonBlockBody({
|
|
923
1064
|
slot,
|
|
@@ -937,9 +1078,6 @@ export function getValidatorApi(
|
|
|
937
1078
|
};
|
|
938
1079
|
|
|
939
1080
|
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
|
|
940
|
-
if (builderBid !== null) {
|
|
941
|
-
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
|
|
942
|
-
}
|
|
943
1081
|
|
|
944
1082
|
const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
|
|
945
1083
|
const t = metrics?.blockProductionTime.startTimer();
|
|
@@ -956,19 +1094,25 @@ export function getValidatorApi(
|
|
|
956
1094
|
const enginePromise: ReturnType<typeof chain.produceBlock> = timed(ProducedBlockSource.engine, () =>
|
|
957
1095
|
chain.produceBlock(baseAttrs)
|
|
958
1096
|
).then((engineBlock) => {
|
|
959
|
-
// No need to wait for the bid block if the engine block will always be selected
|
|
960
|
-
// suspected builder censorship or
|
|
961
|
-
|
|
1097
|
+
// No need to wait for the bid block if the engine block will always be selected, either
|
|
1098
|
+
// due to suspected builder censorship, or because no builders are configured and the
|
|
1099
|
+
// boost factor of 0 always prefers the local block over p2p bids
|
|
1100
|
+
if (engineBlock.shouldOverrideBuilder || (builderConfig.builders.length === 0 && builderBoostFactor === 0n)) {
|
|
962
1101
|
controller.abort();
|
|
963
1102
|
}
|
|
964
1103
|
return engineBlock;
|
|
965
1104
|
});
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1105
|
+
const bidBlockPromise: ReturnType<typeof chain.produceBlock> = bestBidPromise.then((candidate) => {
|
|
1106
|
+
if (candidate === null) {
|
|
1107
|
+
throw new Error(NO_BID_AVAILABLE);
|
|
1108
|
+
}
|
|
1109
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
|
|
1110
|
+
return timed(ProducedBlockSource.builder, () =>
|
|
1111
|
+
chain.produceBlock({...baseAttrs, builderBid: candidate.signedBid})
|
|
1112
|
+
);
|
|
1113
|
+
});
|
|
970
1114
|
|
|
971
|
-
const [engineResult,
|
|
1115
|
+
const [engineResult, bidBlockResult] = await resolveOrRacePromises([enginePromise, bidBlockPromise], {
|
|
972
1116
|
resolveTimeoutMs: cutoffMs,
|
|
973
1117
|
raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS,
|
|
974
1118
|
signal: controller.signal,
|
|
@@ -977,29 +1121,74 @@ export function getValidatorApi(
|
|
|
977
1121
|
let bestResult: typeof engineResult | null = null;
|
|
978
1122
|
let source: ProducedBlockSource = ProducedBlockSource.engine;
|
|
979
1123
|
|
|
1124
|
+
// Resolved instantly whenever the bid branch produced a block
|
|
1125
|
+
const bestBid = bidBlockResult.status === "fulfilled" ? await bestBidPromise : null;
|
|
1126
|
+
|
|
1127
|
+
const logCtx = {
|
|
1128
|
+
slot,
|
|
1129
|
+
parentSlot,
|
|
1130
|
+
parentBlockRoot: parentBlockRootHex,
|
|
1131
|
+
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
1132
|
+
fork,
|
|
1133
|
+
builderBoostFactor,
|
|
1134
|
+
strictFeeRecipientCheck,
|
|
1135
|
+
circuitBreakerActive,
|
|
1136
|
+
builderEntries: builderConfig.builders.length,
|
|
1137
|
+
...(bestBid !== null
|
|
1138
|
+
? {
|
|
1139
|
+
bidSource: bestBid.url !== undefined ? toPrintableUrl(bestBid.url) : "p2p",
|
|
1140
|
+
bidValue: prettyGweiToEth(bestBid.signedBid.message.value),
|
|
1141
|
+
bidExecutionPayment: prettyGweiToEth(bestBid.signedBid.message.executionPayment),
|
|
1142
|
+
// The full bid total and the counted total used during bid selection
|
|
1143
|
+
bidTotal: prettyGweiToEth(
|
|
1144
|
+
BigInt(bestBid.signedBid.message.value) + bestBid.signedBid.message.executionPayment
|
|
1145
|
+
),
|
|
1146
|
+
bidCountedTotal: prettyGweiToEth(bestBid.totalGwei),
|
|
1147
|
+
bidBoostFactor: bestBid.boostFactor,
|
|
1148
|
+
builderIndex: bestBid.signedBid.message.builderIndex,
|
|
1149
|
+
bidBlockHash: toRootHex(bestBid.signedBid.message.blockHash),
|
|
1150
|
+
bidReceivedMs: bestBid.receivedMs,
|
|
1151
|
+
}
|
|
1152
|
+
: {}),
|
|
1153
|
+
};
|
|
1154
|
+
|
|
980
1155
|
// handle shouldOverrideBuilder separately
|
|
981
|
-
if (
|
|
1156
|
+
if (
|
|
1157
|
+
engineResult.status === "fulfilled" &&
|
|
1158
|
+
engineResult.value.shouldOverrideBuilder &&
|
|
1159
|
+
(builderBidExpected || bidBlockResult.status === "fulfilled")
|
|
1160
|
+
) {
|
|
982
1161
|
source = ProducedBlockSource.engine;
|
|
983
1162
|
bestResult = engineResult;
|
|
984
1163
|
metrics?.blockProductionSelectionResults.inc({
|
|
985
1164
|
source: ProducedBlockSource.engine,
|
|
986
1165
|
reason: EngineBlockSelectionReason.BuilderCensorship,
|
|
987
1166
|
});
|
|
988
|
-
logger.warn("Selected local block: censorship suspected in builder bid",
|
|
989
|
-
|
|
1167
|
+
logger.warn("Selected local block: censorship suspected in builder bid", {
|
|
1168
|
+
...logCtx,
|
|
1169
|
+
durationMs: engineResult.durationMs,
|
|
1170
|
+
...getBlockValueLogInfo(engineResult.value),
|
|
1171
|
+
});
|
|
1172
|
+
} else if (engineResult.status === "fulfilled" && bidBlockResult.status === "fulfilled") {
|
|
990
1173
|
const result = selectBlockProductionSourceByBoostFactor({
|
|
991
|
-
builderBoostFactor,
|
|
1174
|
+
builderBoostFactor: bestBid?.boostFactor ?? builderBoostFactor,
|
|
992
1175
|
engineExecutionPayloadValue: engineResult.value.executionPayloadValue,
|
|
993
|
-
// The bid
|
|
994
|
-
builderExecutionPayloadValue:
|
|
1176
|
+
// The bid total payment is its value plus its counted executionPayment, in Gwei
|
|
1177
|
+
builderExecutionPayloadValue: (bestBid?.totalGwei ?? 0n) * GWEI_TO_WEI,
|
|
995
1178
|
});
|
|
996
1179
|
source = result.source;
|
|
997
1180
|
metrics?.blockProductionSelectionResults.inc(result);
|
|
998
|
-
logger.info(`Selected ${source} block`, {
|
|
999
|
-
|
|
1000
|
-
|
|
1181
|
+
logger.info(`Selected ${source} block`, {
|
|
1182
|
+
reason: result.reason,
|
|
1183
|
+
...logCtx,
|
|
1184
|
+
engineDurationMs: engineResult.durationMs,
|
|
1185
|
+
...getBlockValueLogInfo(engineResult.value, ProducedBlockSource.engine),
|
|
1186
|
+
builderDurationMs: bidBlockResult.durationMs,
|
|
1187
|
+
});
|
|
1188
|
+
bestResult = source === ProducedBlockSource.builder ? bidBlockResult : engineResult;
|
|
1189
|
+
} else if (bidBlockResult.status === "fulfilled") {
|
|
1001
1190
|
source = ProducedBlockSource.builder;
|
|
1002
|
-
bestResult =
|
|
1191
|
+
bestResult = bidBlockResult;
|
|
1003
1192
|
const reason =
|
|
1004
1193
|
engineResult.status === "pending"
|
|
1005
1194
|
? BuilderBlockSelectionReason.EnginePending
|
|
@@ -1008,28 +1197,32 @@ export function getValidatorApi(
|
|
|
1008
1197
|
logger.info("Selected builder bid block: no local block produced", {
|
|
1009
1198
|
reason,
|
|
1010
1199
|
...logCtx,
|
|
1200
|
+
durationMs: bidBlockResult.durationMs,
|
|
1201
|
+
...getBlockValueLogInfo(bidBlockResult.value),
|
|
1011
1202
|
error: engineResult.status === "rejected" ? (engineResult.reason as Error).message : undefined,
|
|
1012
1203
|
});
|
|
1013
1204
|
} else if (engineResult.status === "fulfilled") {
|
|
1014
1205
|
source = ProducedBlockSource.engine;
|
|
1015
1206
|
bestResult = engineResult;
|
|
1016
1207
|
const reason =
|
|
1017
|
-
|
|
1208
|
+
bidBlockResult.status === "rejected" && (bidBlockResult.reason as Error).message === NO_BID_AVAILABLE
|
|
1018
1209
|
? EngineBlockSelectionReason.BuilderNoBid
|
|
1019
|
-
:
|
|
1210
|
+
: bidBlockResult.status === "pending"
|
|
1020
1211
|
? EngineBlockSelectionReason.BuilderPending
|
|
1021
1212
|
: EngineBlockSelectionReason.BuilderError;
|
|
1022
1213
|
metrics?.blockProductionSelectionResults.inc({source: ProducedBlockSource.engine, reason});
|
|
1023
1214
|
logger.info("Selected local block: no builder bid block produced", {
|
|
1024
1215
|
reason,
|
|
1025
1216
|
...logCtx,
|
|
1026
|
-
|
|
1217
|
+
durationMs: engineResult.durationMs,
|
|
1218
|
+
...getBlockValueLogInfo(engineResult.value),
|
|
1219
|
+
error: bidBlockResult.status === "rejected" ? (bidBlockResult.reason as Error).message : undefined,
|
|
1027
1220
|
});
|
|
1028
1221
|
}
|
|
1029
1222
|
|
|
1030
1223
|
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
1031
1224
|
const engineReason = engineResult.status === "rejected" ? engineResult.reason : engineResult.status;
|
|
1032
|
-
const bidReason =
|
|
1225
|
+
const bidReason = bidBlockResult.status === "rejected" ? bidBlockResult.reason : bidBlockResult.status;
|
|
1033
1226
|
logger.error("Block production failed", {
|
|
1034
1227
|
...logCtx,
|
|
1035
1228
|
engineReason: String(engineReason),
|
|
@@ -1093,7 +1286,13 @@ export function getValidatorApi(
|
|
|
1093
1286
|
|
|
1094
1287
|
return {
|
|
1095
1288
|
data: block as gloas.BeaconBlock,
|
|
1096
|
-
meta: {
|
|
1289
|
+
meta: {
|
|
1290
|
+
version: fork,
|
|
1291
|
+
consensusBlockValue,
|
|
1292
|
+
executionPayloadValue,
|
|
1293
|
+
executionPayloadIncluded: false,
|
|
1294
|
+
builderUrl: source === ProducedBlockSource.builder ? bestBid?.url : undefined,
|
|
1295
|
+
},
|
|
1097
1296
|
};
|
|
1098
1297
|
},
|
|
1099
1298
|
|
|
@@ -1877,6 +2076,43 @@ export function getValidatorApi(
|
|
|
1877
2076
|
}
|
|
1878
2077
|
},
|
|
1879
2078
|
|
|
2079
|
+
async submitBuilderPreferences({builderPreferences}) {
|
|
2080
|
+
const failures: FailureList = [];
|
|
2081
|
+
|
|
2082
|
+
await Promise.all(
|
|
2083
|
+
builderPreferences.map(async (entry, i) => {
|
|
2084
|
+
let builder = Buffer.from(entry.url).toString("utf8");
|
|
2085
|
+
try {
|
|
2086
|
+
const url = decodeBuilderUrl(entry.url);
|
|
2087
|
+
builder = toPrintableUrl(url);
|
|
2088
|
+
const proposerIndex = chain.getHeadState().getBeaconProposer(entry.auth.message.slot);
|
|
2089
|
+
const expectedProposerPubkey = chain.pubkeyCache.getOrThrow(proposerIndex).toBytes();
|
|
2090
|
+
if (!byteArrayEquals(entry.proposerPubkey, expectedProposerPubkey)) {
|
|
2091
|
+
throw new ApiError(
|
|
2092
|
+
400,
|
|
2093
|
+
`Invalid proposer pubkey for builder preferences slot=${entry.auth.message.slot}`
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
await chain.builderApiClient.submitBuilderPreferences(url, entry.proposerPubkey, {
|
|
2097
|
+
preferences: {maxExecutionPayment: entry.maxExecutionPayment},
|
|
2098
|
+
auth: entry.auth,
|
|
2099
|
+
});
|
|
2100
|
+
} catch (e) {
|
|
2101
|
+
failures.push({index: i, message: (e as Error).message});
|
|
2102
|
+
logger.verbose(
|
|
2103
|
+
`Error on submitBuilderPreferences [${i}]`,
|
|
2104
|
+
{slot: entry.auth.message.slot, builder},
|
|
2105
|
+
e as Error
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
})
|
|
2109
|
+
);
|
|
2110
|
+
|
|
2111
|
+
if (failures.length > 0) {
|
|
2112
|
+
throw new IndexedError("Error submitting builder preferences", failures);
|
|
2113
|
+
}
|
|
2114
|
+
},
|
|
2115
|
+
|
|
1880
2116
|
async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
|
|
1881
2117
|
const fork = config.getForkName(slot);
|
|
1882
2118
|
|
|
@@ -89,7 +89,7 @@ export function selectBlockProductionSourceByBoostFactor({
|
|
|
89
89
|
return {source: ProducedBlockSource.builder, reason: BuilderBlockSelectionReason.BuilderPreferred};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
if (engineExecutionPayloadValue >=
|
|
92
|
+
if (engineExecutionPayloadValue * 100n >= builderExecutionPayloadValue * builderBoostFactor) {
|
|
93
93
|
return {source: ProducedBlockSource.engine, reason: EngineBlockSelectionReason.BlockValue};
|
|
94
94
|
}
|
|
95
95
|
|
package/src/api/rest/base.ts
CHANGED
|
@@ -104,6 +104,13 @@ export class RestApiServer {
|
|
|
104
104
|
|
|
105
105
|
// To parse our ApiError -> statusCode
|
|
106
106
|
server.setErrorHandler<FastifyError | Error>((err, _req, res) => {
|
|
107
|
+
if (res.raw.headersSent) {
|
|
108
|
+
// A status code can no longer be written, e.g. if a long-lived SSE stream failed mid-flight.
|
|
109
|
+
// End the response instead, else the socket is left open and the client never notices.
|
|
110
|
+
if (!res.raw.writableEnded && !res.raw.destroyed) res.raw.end();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
const stacktraces = opts.stacktraces ? err.stack?.split("\n") : undefined;
|
|
108
115
|
if ("validation" in err && err.validation) {
|
|
109
116
|
const {instancePath = "unknown", message} = err.validation?.[0] ?? {};
|
|
@@ -192,7 +192,7 @@ export class ArchiveStore {
|
|
|
192
192
|
|
|
193
193
|
private onCheckpoint = (): void => {
|
|
194
194
|
const headStateRoot = this.chain.forkChoice.getHead().stateRoot;
|
|
195
|
-
this.chain.regen.
|
|
195
|
+
this.chain.regen.onCheckpoint(
|
|
196
196
|
this.chain.forkChoice.getFinalizedCheckpoint().epoch,
|
|
197
197
|
this.chain.forkChoice.getJustifiedCheckpoint().epoch,
|
|
198
198
|
headStateRoot
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ColumnWithSource,
|
|
10
10
|
CreateFromBidProps,
|
|
11
11
|
CreateFromBlockProps,
|
|
12
|
+
PayloadEnvelopeInputSource,
|
|
12
13
|
SourceMeta,
|
|
13
14
|
} from "./types.js";
|
|
14
15
|
|
|
@@ -71,6 +72,7 @@ export class PayloadEnvelopeInput {
|
|
|
71
72
|
readonly bid: gloas.ExecutionPayloadBid;
|
|
72
73
|
readonly versionedHashes: VersionedHashes;
|
|
73
74
|
readonly daOutOfRange: boolean;
|
|
75
|
+
readonly source: PayloadEnvelopeInputSource;
|
|
74
76
|
|
|
75
77
|
private columnsCache = new Map<ColumnIndex, ColumnWithSource>();
|
|
76
78
|
|
|
@@ -95,6 +97,7 @@ export class PayloadEnvelopeInput {
|
|
|
95
97
|
custodyColumns: ColumnIndex[];
|
|
96
98
|
timeCreatedSec: number;
|
|
97
99
|
daOutOfRange: boolean;
|
|
100
|
+
source: PayloadEnvelopeInputSource;
|
|
98
101
|
}) {
|
|
99
102
|
this.blockRootHex = props.blockRootHex;
|
|
100
103
|
this.slot = props.slot;
|
|
@@ -106,6 +109,7 @@ export class PayloadEnvelopeInput {
|
|
|
106
109
|
this.custodyColumns = props.custodyColumns;
|
|
107
110
|
this.timeCreatedSec = props.timeCreatedSec;
|
|
108
111
|
this.daOutOfRange = props.daOutOfRange;
|
|
112
|
+
this.source = props.source;
|
|
109
113
|
this.payloadEnvelopeDataPromise = createPromise();
|
|
110
114
|
this.allDataPromise = createPromise();
|
|
111
115
|
this.columnsDataPromise = createPromise();
|
|
@@ -133,8 +137,9 @@ export class PayloadEnvelopeInput {
|
|
|
133
137
|
bid,
|
|
134
138
|
sampledColumns: props.sampledColumns,
|
|
135
139
|
custodyColumns: props.custodyColumns,
|
|
136
|
-
timeCreatedSec: props.
|
|
140
|
+
timeCreatedSec: props.seenTimestampSec,
|
|
137
141
|
daOutOfRange: props.daOutOfRange,
|
|
142
|
+
source: props.source,
|
|
138
143
|
});
|
|
139
144
|
}
|
|
140
145
|
|
|
@@ -153,8 +158,9 @@ export class PayloadEnvelopeInput {
|
|
|
153
158
|
bid: props.bid,
|
|
154
159
|
sampledColumns: props.sampledColumns,
|
|
155
160
|
custodyColumns: props.custodyColumns,
|
|
156
|
-
timeCreatedSec: props.
|
|
161
|
+
timeCreatedSec: props.seenTimestampSec,
|
|
157
162
|
daOutOfRange: props.daOutOfRange,
|
|
163
|
+
source: props.source,
|
|
158
164
|
});
|
|
159
165
|
}
|
|
160
166
|
|
|
@@ -7,9 +7,24 @@ export enum PayloadEnvelopeInputSource {
|
|
|
7
7
|
engine = "engine",
|
|
8
8
|
byRange = "req_resp_by_range",
|
|
9
9
|
byRoot = "req_resp_by_root",
|
|
10
|
+
// Data-column reconstruction (KZG cell recovery), NOT a cache reload
|
|
10
11
|
recovery = "recovery",
|
|
12
|
+
// Entry reconstructed from the hot DB by SeenPayloadEnvelopeInput.getOrReload
|
|
13
|
+
reload = "reload",
|
|
14
|
+
// Entry seeded from a checkpoint anchor state's latestExecutionPayloadBid at chain init
|
|
15
|
+
anchorState = "anchor_state",
|
|
11
16
|
}
|
|
12
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Reason a PayloadEnvelopeInput is evicted from SeenPayloadEnvelopeInput. Used for the `pruned` metric
|
|
20
|
+
* label and the eviction log.
|
|
21
|
+
* - belowParent: pruned below the new head's parent (canonical, FULL, all-columns)
|
|
22
|
+
* - finalized: below the finalized slot
|
|
23
|
+
* - prune: explicit prune by root
|
|
24
|
+
* - cap: insertion-order backstop cap (MAX_PAYLOAD_ENVELOPE_INPUT_CACHE_SIZE)
|
|
25
|
+
*/
|
|
26
|
+
export type PayloadEnvelopeInputPruneReason = "belowParent" | "finalized" | "prune" | "cap";
|
|
27
|
+
|
|
13
28
|
export type SourceMeta = {
|
|
14
29
|
source: PayloadEnvelopeInputSource;
|
|
15
30
|
seenTimestampSec: number;
|
|
@@ -20,13 +35,12 @@ export type ColumnWithSource = SourceMeta & {
|
|
|
20
35
|
columnSidecar: gloas.DataColumnSidecar;
|
|
21
36
|
};
|
|
22
37
|
|
|
23
|
-
export type CreateFromBlockProps = {
|
|
38
|
+
export type CreateFromBlockProps = SourceMeta & {
|
|
24
39
|
blockRootHex: RootHex;
|
|
25
40
|
block: SignedBeaconBlock<ForkPostGloas>;
|
|
26
41
|
forkName: ForkName;
|
|
27
42
|
sampledColumns: ColumnIndex[];
|
|
28
43
|
custodyColumns: ColumnIndex[];
|
|
29
|
-
timeCreatedSec: number;
|
|
30
44
|
daOutOfRange: boolean;
|
|
31
45
|
};
|
|
32
46
|
|
|
@@ -35,7 +49,7 @@ export type CreateFromBlockProps = {
|
|
|
35
49
|
* the chain from a checkpoint anchor state — we have the bid via the state but not the
|
|
36
50
|
* full SignedBeaconBlock).
|
|
37
51
|
*/
|
|
38
|
-
export type CreateFromBidProps = {
|
|
52
|
+
export type CreateFromBidProps = SourceMeta & {
|
|
39
53
|
blockRootHex: RootHex;
|
|
40
54
|
slot: number;
|
|
41
55
|
forkName: ForkName;
|
|
@@ -43,7 +57,6 @@ export type CreateFromBidProps = {
|
|
|
43
57
|
bid: gloas.ExecutionPayloadBid;
|
|
44
58
|
sampledColumns: ColumnIndex[];
|
|
45
59
|
custodyColumns: ColumnIndex[];
|
|
46
|
-
timeCreatedSec: number;
|
|
47
60
|
daOutOfRange: boolean;
|
|
48
61
|
};
|
|
49
62
|
|