From fa5c6cf5d9985a31d16fdb4caa75507913316b9a Mon Sep 17 00:00:00 2001 From: g11tech Date: Mon, 14 Aug 2023 20:02:33 +0530 Subject: [PATCH] feat: send and use validators fee recipient for engine block production (#5831) * feat: send and use validators fee recipient for block production * fix tests * mock tests that fee recipients are correctly passed * improve logging * add more info * add more info --- packages/api/src/beacon/routes/validator.ts | 26 ++- .../test/unit/beacon/testData/validator.ts | 7 +- .../src/api/impl/validator/index.ts | 6 +- packages/beacon-node/src/chain/chain.ts | 3 +- .../chain/produceBlock/produceBlockBody.ts | 47 +++- .../api/impl/validator/produceBlockV2.test.ts | 219 ++++++++++++++++++ packages/validator/src/services/block.ts | 10 +- 7 files changed, 293 insertions(+), 25 deletions(-) create mode 100644 packages/beacon-node/test/unit/api/impl/validator/produceBlockV2.test.ts diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index 1c399d71ff72..3b02946f29ef 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -201,7 +201,8 @@ export type Api = { produceBlock( slot: Slot, randaoReveal: BLSSignature, - graffiti: string + graffiti: string, + feeRecipient?: string ): Promise< ApiClientResponse< {[HttpStatusCode.OK]: {data: allForks.BeaconBlock; blockValue: Wei}}, @@ -222,7 +223,8 @@ export type Api = { produceBlockV2( slot: Slot, randaoReveal: BLSSignature, - graffiti: string + graffiti: string, + feeRecipient?: string ): Promise< ApiClientResponse< {[HttpStatusCode.OK]: {data: allForks.BeaconBlock | BlockContents; version: ForkName; blockValue: Wei}}, @@ -233,7 +235,8 @@ export type Api = { produceBlindedBlock( slot: Slot, randaoReveal: BLSSignature, - graffiti: string + graffiti: string, + feeRecipient?: string ): Promise< ApiClientResponse< { @@ -428,7 +431,7 @@ export type ReqTypes = { getProposerDuties: {params: {epoch: Epoch}}; getSyncCommitteeDuties: {params: {epoch: Epoch}; body: U64Str[]}; produceBlock: {params: {slot: number}; query: {randao_reveal: string; graffiti: string}}; - produceBlockV2: {params: {slot: number}; query: {randao_reveal: string; graffiti: string}}; + produceBlockV2: {params: {slot: number}; query: {randao_reveal: string; graffiti: string; fee_recipient?: string}}; produceBlindedBlock: {params: {slot: number}; query: {randao_reveal: string; graffiti: string}}; produceAttestationData: {query: {slot: number; committee_index: number}}; produceSyncCommitteeContribution: {query: {slot: number; subcommittee_index: number; beacon_block_root: string}}; @@ -484,15 +487,20 @@ export function getReqSerializers(): ReqSerializers { {jsonCase: "eth2"} ); - const produceBlock: ReqSerializers["produceBlock"] = { - writeReq: (slot, randaoReveal, graffiti) => ({ + const produceBlock: ReqSerializers["produceBlockV2"] = { + writeReq: (slot, randaoReveal, graffiti, feeRecipient) => ({ params: {slot}, - query: {randao_reveal: toHexString(randaoReveal), graffiti: toGraffitiHex(graffiti)}, + query: {randao_reveal: toHexString(randaoReveal), graffiti: toGraffitiHex(graffiti), fee_recipient: feeRecipient}, }), - parseReq: ({params, query}) => [params.slot, fromHexString(query.randao_reveal), fromGraffitiHex(query.graffiti)], + parseReq: ({params, query}) => [ + params.slot, + fromHexString(query.randao_reveal), + fromGraffitiHex(query.graffiti), + query.fee_recipient, + ], schema: { params: {slot: Schema.UintRequired}, - query: {randao_reveal: Schema.StringRequired, graffiti: Schema.String}, + query: {randao_reveal: Schema.StringRequired, graffiti: Schema.String, fee_recipient: Schema.String}, }, }; diff --git a/packages/api/test/unit/beacon/testData/validator.ts b/packages/api/test/unit/beacon/testData/validator.ts index 9cc92f5a8629..20ad9ef6a099 100644 --- a/packages/api/test/unit/beacon/testData/validator.ts +++ b/packages/api/test/unit/beacon/testData/validator.ts @@ -8,6 +8,7 @@ const ZERO_HASH_HEX = "0x" + ZERO_HASH.toString("hex"); const randaoReveal = Buffer.alloc(96, 1); const selectionProof = Buffer.alloc(96, 1); const graffiti = "a".repeat(32); +const feeRecipient = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; export const testData: GenericServerTestCases = { getAttesterDuties: { @@ -44,15 +45,15 @@ export const testData: GenericServerTestCases = { }, }, produceBlock: { - args: [32000, randaoReveal, graffiti], + args: [32000, randaoReveal, graffiti, feeRecipient], res: {data: ssz.phase0.BeaconBlock.defaultValue(), blockValue: ssz.Wei.defaultValue()}, }, produceBlockV2: { - args: [32000, randaoReveal, graffiti], + args: [32000, randaoReveal, graffiti, feeRecipient], res: {data: ssz.altair.BeaconBlock.defaultValue(), version: ForkName.altair, blockValue: ssz.Wei.defaultValue()}, }, produceBlindedBlock: { - args: [32000, randaoReveal, graffiti], + args: [32000, randaoReveal, graffiti, feeRecipient], res: { data: ssz.bellatrix.BlindedBeaconBlock.defaultValue(), version: ForkName.bellatrix, diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index 00de92581476..2a29c4426eeb 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -277,7 +277,8 @@ export function getValidatorApi({ const produceBlockV2: ServerApi["produceBlockV2"] = async function produceBlockV2( slot, randaoReveal, - graffiti + graffiti, + feeRecipient ) { const source = ProducedBlockSource.engine; let timer; @@ -297,6 +298,7 @@ export function getValidatorApi({ slot, randaoReveal, graffiti: toGraffitiBuffer(graffiti || ""), + feeRecipient, }); metrics?.blockProductionSuccess.inc({source}); metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length); @@ -326,7 +328,7 @@ export function getValidatorApi({ randaoReveal, graffiti ) { - const {data, version, blockValue} = await produceBlockV2(slot, randaoReveal, graffiti); + const {data, version, blockValue} = await produceBlockV2(slot, randaoReveal, graffiti, undefined); if ((data as BlockContents).block !== undefined) { throw Error(`Invalid block contents for produceBlock at fork=${version}`); } else { diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 8dacf79c642b..694cb5495958 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -474,7 +474,7 @@ export class BeaconChain implements IBeaconChain { async produceBlockWrapper( blockType: T, - {randaoReveal, graffiti, slot}: BlockAttributes + {randaoReveal, graffiti, slot, feeRecipient}: BlockAttributes ): Promise<{block: AssembledBlockType; blockValue: Wei}> { const head = this.forkChoice.getHead(); const state = await this.regen.getBlockSlotState( @@ -491,6 +491,7 @@ export class BeaconChain implements IBeaconChain { randaoReveal, graffiti, slot, + feeRecipient, parentSlot: slot - 1, parentBlockRoot, proposerIndex, diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 04b5760a4a71..2df0f71afa87 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -49,11 +49,12 @@ export type BlockAttributes = { randaoReveal: BLSSignature; graffiti: Bytes32; slot: Slot; + feeRecipient?: string; }; export enum BlockType { - Full, - Blinded, + Full = "Full", + Blinded = "Blinded", } export type AssembledBodyType = T extends BlockType.Full ? allForks.BeaconBlockBody @@ -80,6 +81,7 @@ export async function produceBlockBody( randaoReveal, graffiti, slot: blockSlot, + feeRecipient: requestedFeeRecipient, parentSlot, parentBlockRoot, proposerIndex, @@ -96,6 +98,14 @@ export async function produceBlockBody( // TODO: Does not guarantee that preDeneb enum goes with a preDeneb block let blobsResult: BlobsResult; let blockValue: Wei; + const fork = currentState.config.getForkName(blockSlot); + + const logMeta: Record = { + fork, + blockType, + slot: blockSlot, + }; + this.logger.verbose("Producing beacon block body", logMeta); // TODO: // Iterate through the naive aggregation pool and ensure all the attestations from there @@ -125,8 +135,6 @@ export async function produceBlockBody( voluntaryExits, }; - this.logger.verbose("Produced phase0 beacon block body", {slot: blockSlot, numAttestations: attestations.length}); - const blockEpoch = computeEpochAtSlot(blockSlot); if (blockEpoch >= this.config.ALTAIR_FORK_EPOCH) { @@ -137,12 +145,25 @@ export async function produceBlockBody( (blockBody as altair.BeaconBlockBody).syncAggregate = syncAggregate; } - const fork = currentState.config.getForkName(blockSlot); + Object.assign(logMeta, { + attestations: attestations.length, + deposits: deposits.length, + voluntaryExits: voluntaryExits.length, + attesterSlashings: attesterSlashings.length, + proposerSlashings: proposerSlashings.length, + }); if (isForkExecution(fork)) { const safeBlockHash = this.forkChoice.getJustifiedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX; const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX; - const feeRecipient = this.beaconProposerCache.getOrDefault(proposerIndex); + const feeRecipient = requestedFeeRecipient ?? this.beaconProposerCache.getOrDefault(proposerIndex); + const feeRecipientType = requestedFeeRecipient + ? "requested" + : this.beaconProposerCache.get(proposerIndex) + ? "cached" + : "default"; + + Object.assign(logMeta, {feeRecipientType, feeRecipient}); if (blockType === BlockType.Blinded) { if (!this.executionBuilder) throw Error("Execution Builder not available"); @@ -182,6 +203,8 @@ export async function produceBlockBody( } (blockBody as deneb.BlindedBeaconBlockBody).blobKzgCommitments = blobKzgCommitments; blobsResult = {type: BlobsResultType.blinded}; + + Object.assign(logMeta, {blobs: blobKzgCommitments.length}); } else { blobsResult = {type: BlobsResultType.preDeneb}; } @@ -212,6 +235,8 @@ export async function produceBlockBody( blockValue = BigInt(0); } else { const {prepType, payloadId} = prepareRes; + Object.assign(logMeta, {executionPayloadPrepType: prepType}); + if (prepType !== PayloadPreparationType.Cached) { // Wait for 500ms to allow EL to add some txs to the payload // the pitfalls of this have been put forward here, but 500ms delay for block proposal @@ -225,6 +250,7 @@ export async function produceBlockBody( const {executionPayload, blobsBundle} = engineRes; (blockBody as allForks.ExecutionBlockBody).executionPayload = executionPayload; blockValue = engineRes.blockValue; + Object.assign(logMeta, {transactions: executionPayload.transactions.length}); const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime); this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime); @@ -265,6 +291,8 @@ export async function produceBlockBody( return blobSidecar; }) as deneb.BlobSidecars; blobsResult = {type: BlobsResultType.produced, blobSidecars, blockHash}; + + Object.assign(logMeta, {blobs: blobSidecars.length}); } else { blobsResult = {type: BlobsResultType.preDeneb}; } @@ -299,8 +327,15 @@ export async function produceBlockBody( if (ForkSeq[fork] >= ForkSeq.capella) { // TODO: blsToExecutionChanges should be passed in the produceBlock call (blockBody as capella.BeaconBlockBody).blsToExecutionChanges = blsToExecutionChanges; + Object.assign(logMeta, { + blsToExecutionChanges: blsToExecutionChanges.length, + withdrawals: (blockBody as capella.BeaconBlockBody).executionPayload.withdrawals.length, + }); } + Object.assign(logMeta, {blockValue}); + this.logger.verbose("Produced beacon block body", logMeta); + return {body: blockBody as AssembledBodyType, blobs: blobsResult, blockValue}; } diff --git a/packages/beacon-node/test/unit/api/impl/validator/produceBlockV2.test.ts b/packages/beacon-node/test/unit/api/impl/validator/produceBlockV2.test.ts new file mode 100644 index 000000000000..79cc49ca82c9 --- /dev/null +++ b/packages/beacon-node/test/unit/api/impl/validator/produceBlockV2.test.ts @@ -0,0 +1,219 @@ +import sinon, {SinonStubbedInstance} from "sinon"; +import {use, expect} from "chai"; +import chaiAsPromised from "chai-as-promised"; +import {fromHexString} from "@chainsafe/ssz"; +import {ssz} from "@lodestar/types"; +import {config} from "@lodestar/config/default"; +import {ForkChoice, ProtoBlock} from "@lodestar/fork-choice"; +import {ChainForkConfig} from "@lodestar/config"; +import {ForkName} from "@lodestar/params"; +import {computeTimeAtSlot, CachedBeaconStateBellatrix} from "@lodestar/state-transition"; +import {IBeaconSync, SyncState} from "../../../../../src/sync/interface.js"; +import {ApiModules} from "../../../../../src/api/impl/types.js"; +import {getValidatorApi} from "../../../../../src/api/impl/validator/index.js"; +import {IClock} from "../../../../../src/util/clock.js"; +import {testLogger} from "../../../../utils/logger.js"; +import {ApiImplTestModules, setupApiImplTestServer} from "../index.test.js"; +import {BeaconChain} from "../../../../../src/chain/index.js"; +import {generateCachedBellatrixState} from "../../../../utils/state.js"; +import {ExecutionEngineHttp} from "../../../../../src/execution/engine/http.js"; +import {IExecutionEngine} from "../../../../../src/execution/engine/interface.js"; +import {PayloadIdCache} from "../../../../../src/execution/engine/payloadIdCache.js"; +import {StubbedChainMutable} from "../../../../utils/stub/index.js"; +import {toGraffitiBuffer} from "../../../../../src/util/graffiti.js"; +import {BlockType, produceBlockBody} from "../../../../../src/chain/produceBlock/produceBlockBody.js"; +import {generateProtoBlock} from "../../../../utils/typeGenerator.js"; +import {ZERO_HASH_HEX} from "../../../../../src/constants/index.js"; +import {OpPool} from "../../../../../src/chain/opPools/opPool.js"; +import {AggregatedAttestationPool} from "../../../../../src/chain/opPools/index.js"; +import {Eth1ForBlockProduction, IEth1ForBlockProduction} from "../../../../../src/eth1/index.js"; +import {BeaconProposerCache} from "../../../../../src/chain/beaconProposerCache.js"; + +use(chaiAsPromised); + +type StubbedChain = StubbedChainMutable<"clock" | "forkChoice" | "logger">; + +describe("api/validator - produceBlockV2", function () { + const logger = testLogger(); + const sandbox = sinon.createSandbox(); + + let modules: ApiModules; + let server: ApiImplTestModules; + + let chainStub: StubbedChain; + let forkChoiceStub: SinonStubbedInstance & ForkChoice; + let executionEngineStub: SinonStubbedInstance & ExecutionEngineHttp; + let opPoolStub: SinonStubbedInstance & OpPool; + let aggregatedAttestationPoolStub: SinonStubbedInstance & AggregatedAttestationPool; + let eth1Stub: SinonStubbedInstance; + let syncStub: SinonStubbedInstance; + let state: CachedBeaconStateBellatrix; + let beaconProposerCacheStub: SinonStubbedInstance & BeaconProposerCache; + + beforeEach(() => { + chainStub = sandbox.createStubInstance(BeaconChain) as StubbedChain; + eth1Stub = sinon.createStubInstance(Eth1ForBlockProduction); + chainStub.logger = logger; + forkChoiceStub = sandbox.createStubInstance(ForkChoice) as SinonStubbedInstance & ForkChoice; + chainStub.forkChoice = forkChoiceStub; + + executionEngineStub = sandbox.createStubInstance(ExecutionEngineHttp) as SinonStubbedInstance & + ExecutionEngineHttp; + (chainStub as unknown as {executionEngine: IExecutionEngine}).executionEngine = executionEngineStub; + + opPoolStub = sandbox.createStubInstance(OpPool) as SinonStubbedInstance & OpPool; + (chainStub as unknown as {opPool: OpPool}).opPool = opPoolStub; + aggregatedAttestationPoolStub = sandbox.createStubInstance( + AggregatedAttestationPool + ) as SinonStubbedInstance & AggregatedAttestationPool; + (chainStub as unknown as {aggregatedAttestationPool: AggregatedAttestationPool}).aggregatedAttestationPool = + aggregatedAttestationPoolStub; + (chainStub as unknown as {eth1: IEth1ForBlockProduction}).eth1 = eth1Stub; + (chainStub as unknown as {config: ChainForkConfig}).config = config as unknown as ChainForkConfig; + + executionEngineStub = sandbox.createStubInstance(ExecutionEngineHttp) as SinonStubbedInstance & + ExecutionEngineHttp; + (chainStub as unknown as {executionEngine: IExecutionEngine}).executionEngine = executionEngineStub; + + beaconProposerCacheStub = sandbox.createStubInstance( + BeaconProposerCache + ) as SinonStubbedInstance & BeaconProposerCache; + (chainStub as unknown as {beaconProposerCache: BeaconProposerCache})["beaconProposerCache"] = + beaconProposerCacheStub; + + state = generateCachedBellatrixState(); + }); + afterEach(() => { + sandbox.restore(); + }); + + it("correctly pass feeRecipient to produceBlock", async function () { + server = setupApiImplTestServer(); + syncStub = server.syncStub; + modules = { + chain: server.chainStub, + config, + db: server.dbStub, + logger, + network: server.networkStub, + sync: syncStub, + metrics: null, + }; + + const fullBlock = ssz.bellatrix.BeaconBlock.defaultValue(); + const blockValue = ssz.Wei.defaultValue(); + + const currentSlot = 100000; + server.chainStub.clock = {currentSlot} as IClock; + sinon.replaceGetter(syncStub, "state", () => SyncState.Synced); + + // Set the node's state to way back from current slot + const slot = 100000; + const randaoReveal = fullBlock.body.randaoReveal; + const graffiti = "a".repeat(32); + const expectedFeeRecipient = "0xcccccccccccccccccccccccccccccccccccccccc"; + + const api = getValidatorApi(modules); + server.chainStub.produceBlock.resolves({block: fullBlock, blockValue}); + + // check if expectedFeeRecipient is passed to produceBlock + await api.produceBlockV2(slot, randaoReveal, graffiti, expectedFeeRecipient); + expect( + server.chainStub.produceBlock.calledWith({ + randaoReveal, + graffiti: toGraffitiBuffer(graffiti), + slot, + feeRecipient: expectedFeeRecipient, + }) + ).to.be.true; + + // check that no feeRecipient is passed to produceBlock so that produceBlockBody will + // pick it from beaconProposerCache + await api.produceBlockV2(slot, randaoReveal, graffiti); + expect( + server.chainStub.produceBlock.calledWith({ + randaoReveal, + graffiti: toGraffitiBuffer(graffiti), + slot, + feeRecipient: undefined, + }) + ).to.be.true; + }); + + it("correctly use passed feeRecipient in notifyForkchoiceUpdate", async () => { + const fullBlock = ssz.bellatrix.BeaconBlock.defaultValue(); + const blockValue = ssz.Wei.defaultValue(); + const slot = 100000; + const randaoReveal = fullBlock.body.randaoReveal; + const graffiti = "a".repeat(32); + const expectedFeeRecipient = "0xccccccccccccccccccccccccccccccccccccccaa"; + + const headSlot = 0; + forkChoiceStub.getHead.returns(generateProtoBlock({slot: headSlot})); + + opPoolStub.getSlashingsAndExits.returns([[], [], [], []]); + aggregatedAttestationPoolStub.getAttestationsForBlock.returns([]); + eth1Stub.getEth1DataAndDeposits.resolves({eth1Data: ssz.phase0.Eth1Data.defaultValue(), deposits: []}); + forkChoiceStub.getJustifiedBlock.returns({} as ProtoBlock); + forkChoiceStub.getFinalizedBlock.returns({} as ProtoBlock); + (executionEngineStub as unknown as {payloadIdCache: PayloadIdCache}).payloadIdCache = new PayloadIdCache(); + + executionEngineStub.notifyForkchoiceUpdate.resolves("0x"); + executionEngineStub.getPayload.resolves({ + executionPayload: ssz.bellatrix.ExecutionPayload.defaultValue(), + blockValue, + }); + + // use fee recipient passed in produceBlockBody call for payload gen in engine notifyForkchoiceUpdate + await produceBlockBody.call(chainStub as unknown as BeaconChain, BlockType.Full, state, { + randaoReveal, + graffiti: toGraffitiBuffer(graffiti), + slot, + feeRecipient: expectedFeeRecipient, + parentSlot: slot - 1, + parentBlockRoot: fromHexString(ZERO_HASH_HEX), + proposerIndex: 0, + proposerPubKey: Uint8Array.from(Buffer.alloc(32, 1)), + }); + + expect( + executionEngineStub.notifyForkchoiceUpdate.calledWith( + ForkName.bellatrix, + ZERO_HASH_HEX, + ZERO_HASH_HEX, + ZERO_HASH_HEX, + { + timestamp: computeTimeAtSlot(chainStub.config, state.slot, state.genesisTime), + prevRandao: Uint8Array.from(Buffer.alloc(32, 0)), + suggestedFeeRecipient: expectedFeeRecipient, + } + ) + ).to.be.true; + + // use fee recipient set in beaconProposerCacheStub if none passed + beaconProposerCacheStub.getOrDefault.returns("0x fee recipient address"); + await produceBlockBody.call(chainStub as unknown as BeaconChain, BlockType.Full, state, { + randaoReveal, + graffiti: toGraffitiBuffer(graffiti), + slot, + parentSlot: slot - 1, + parentBlockRoot: fromHexString(ZERO_HASH_HEX), + proposerIndex: 0, + proposerPubKey: Uint8Array.from(Buffer.alloc(32, 1)), + }); + + expect( + executionEngineStub.notifyForkchoiceUpdate.calledWith( + ForkName.bellatrix, + ZERO_HASH_HEX, + ZERO_HASH_HEX, + ZERO_HASH_HEX, + { + timestamp: computeTimeAtSlot(chainStub.config, state.slot, state.genesisTime), + prevRandao: Uint8Array.from(Buffer.alloc(32, 0)), + suggestedFeeRecipient: "0x fee recipient address", + } + ) + ).to.be.true; + }); +}); diff --git a/packages/validator/src/services/block.ts b/packages/validator/src/services/block.ts index c1385fce9a9c..81c749297f48 100644 --- a/packages/validator/src/services/block.ts +++ b/packages/validator/src/services/block.ts @@ -210,7 +210,7 @@ export class BlockProposingService { // // So if builder is disabled ignore builder selection of builderonly if caused by user mistake !isBuilderEnabled || builderSelection !== BuilderSelection.BuilderOnly - ? this.produceBlock(slot, randaoReveal, graffiti) + ? this.produceBlock(slot, randaoReveal, graffiti, expectedFeeRecipient) : null; let blindedBlock, fullBlock; @@ -357,7 +357,8 @@ export class BlockProposingService { private produceBlock = async ( slot: Slot, randaoReveal: BLSSignature, - graffiti: string + graffiti: string, + expectedFeeRecipient?: string ): Promise<{block: allForks.BeaconBlock; blobs?: deneb.BlobSidecars; blockValue: Wei}> => { const fork = this.config.getForkName(slot); switch (fork) { @@ -367,11 +368,12 @@ export class BlockProposingService { const {data: block, blockValue} = res.response; return {block, blockValue}; } + // All subsequent forks are expected to use v2 too case ForkName.altair: case ForkName.bellatrix: case ForkName.capella: { - const res = await this.api.validator.produceBlockV2(slot, randaoReveal, graffiti); + const res = await this.api.validator.produceBlockV2(slot, randaoReveal, graffiti, expectedFeeRecipient); ApiError.assert(res, "Failed to produce block: validator.produceBlockV2"); const {response} = res; @@ -384,7 +386,7 @@ export class BlockProposingService { case ForkName.deneb: default: { - const res = await this.api.validator.produceBlockV2(slot, randaoReveal, graffiti); + const res = await this.api.validator.produceBlockV2(slot, randaoReveal, graffiti, expectedFeeRecipient); ApiError.assert(res, "Failed to produce block: validator.produceBlockV2"); const {response} = res;