Skip to content

Commit

Permalink
fix: update builder mergemock sim with builder block assertions (#5432)
Browse files Browse the repository at this point in the history
fix: Update builder mergemock sim with builder block assertions
  • Loading branch information
g11tech committed May 4, 2023
1 parent 0ed1cd1 commit 597996a
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,15 @@ export async function produceBlockBody<T extends BlockType>(
// This path will not be used in the production, but is here just for merge mock
// tests because merge-mock requires an fcU to be issued prior to fetch payload
// header.
if (this.executionBuilder.issueLocalFcUForBlockProduction) {
if (this.executionBuilder.issueLocalFcUWithFeeRecipient !== undefined) {
await prepareExecutionPayload(
this,
this.logger,
fork,
safeBlockHash,
finalizedBlockHash ?? ZERO_HASH_HEX,
currentState as CachedBeaconStateBellatrix,
feeRecipient
this.executionBuilder.issueLocalFcUWithFeeRecipient
);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/beacon-node/src/execution/builder/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type ExecutionBuilderHttpOpts = {
allowedFaults?: number;

// Only required for merge-mock runs, no need to expose it to cli
issueLocalFcUForBlockProduction?: boolean;
issueLocalFcUWithFeeRecipient?: string;
// Add User-Agent header to all requests
userAgent?: string;
};
Expand All @@ -31,7 +31,7 @@ export const defaultExecutionBuilderHttpOpts: ExecutionBuilderHttpOpts = {
export class ExecutionBuilderHttp implements IExecutionBuilder {
readonly api: BuilderApi;
readonly config: ChainForkConfig;
readonly issueLocalFcUForBlockProduction?: boolean;
readonly issueLocalFcUWithFeeRecipient?: string;
// Builder needs to be explicity enabled using updateStatus
status = false;
faultInspectionWindow: number;
Expand All @@ -49,7 +49,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {
{config, metrics: metrics?.builderHttpClient}
);
this.config = config;
this.issueLocalFcUForBlockProduction = opts.issueLocalFcUForBlockProduction;
this.issueLocalFcUWithFeeRecipient = opts.issueLocalFcUWithFeeRecipient;

/**
* Beacon clients select randomized values from the following ranges when initializing
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/execution/builder/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface IExecutionBuilder {
* an advance fcU to be issued to the engine port before payload header
* fetch
*/
readonly issueLocalFcUForBlockProduction?: boolean;
readonly issueLocalFcUWithFeeRecipient?: string;
status: boolean;
/** Window to inspect missed slots for enabling/disabling builder circuit breaker */
faultInspectionWindow: number;
Expand Down
58 changes: 51 additions & 7 deletions packages/beacon-node/test/sim/mergemock.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import fs from "node:fs";
import {Context} from "mocha";
import {fromHexString} from "@chainsafe/ssz";
import {fromHexString, toHexString} from "@chainsafe/ssz";
import {LogLevel, sleep, TimestampFormatCode} from "@lodestar/utils";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {ChainConfig} from "@lodestar/config";
import {Epoch} from "@lodestar/types";
import {Epoch, bellatrix} from "@lodestar/types";
import {ValidatorProposerConfig, BuilderSelection} from "@lodestar/validator";
import {routes} from "@lodestar/api";

import {ClockEvent} from "../../src/util/clock.js";
import {testLogger, TestLoggerOpts} from "../utils/logger.js";
Expand Down Expand Up @@ -82,7 +83,7 @@ describe("executionEngine / ExecutionEngineHttp", function () {
this: Context,
{elClient, bellatrixEpoch, testName}: {elClient: ELClient; bellatrixEpoch: Epoch; testName: string}
): Promise<void> {
const {genesisBlockHash, ttd, engineRpcUrl} = elClient;
const {genesisBlockHash, ttd, engineRpcUrl, ethRpcUrl} = elClient;
const validatorClientCount = 1;
const validatorsPerClient = 32;

Expand All @@ -97,6 +98,18 @@ describe("executionEngine / ExecutionEngineHttp", function () {
const epochsOfMargin = 1;
const timeoutSetupMargin = 30 * 1000; // Give extra 30 seconds of margin

// The builder gets activated post middle of epoch because of circuit breaker
// In a perfect run expected builder = 16, expected engine = 16
// keeping 4 missed slots margin for both
const expectedBuilderBlocks = 12;
const expectedEngineBlocks = 12;

// All assertions are tracked w.r.t. fee recipient by attaching different fee recipient to
// execution and builder
const feeRecipientLocal = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const feeRecipientEngine = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
const feeRecipientMevBoost = "0xcccccccccccccccccccccccccccccccccccccccc";

// delay a bit so regular sync sees it's up to date and sync is completed from the beginning
const genesisSlotsDelay = 8;

Expand Down Expand Up @@ -135,8 +148,14 @@ describe("executionEngine / ExecutionEngineHttp", function () {
// Now eth deposit/merge tracker methods directly available on engine endpoints
eth1: {enabled: false, providerUrls: [engineRpcUrl], jwtSecretHex},
executionEngine: {urls: [engineRpcUrl], jwtSecretHex},
executionBuilder: {enabled: true, issueLocalFcUForBlockProduction: true},
chain: {suggestedFeeRecipient: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},
executionBuilder: {
urls: [ethRpcUrl],
enabled: true,
issueLocalFcUWithFeeRecipient: feeRecipientMevBoost,
allowedFaults: 16,
faultInspectionWindow: 32,
},
chain: {suggestedFeeRecipient: feeRecipientLocal},
},
validatorCount: validatorClientCount * validatorsPerClient,
logger: loggerNodeA,
Expand All @@ -159,7 +178,7 @@ describe("executionEngine / ExecutionEngineHttp", function () {
defaultConfig: {
graffiti: "default graffiti",
strictFeeRecipientCheck: true,
feeRecipient: "0xcccccccccccccccccccccccccccccccccccccccc",
feeRecipient: feeRecipientEngine,
builder: {
enabled: true,
gasLimit: 30000000,
Expand All @@ -183,7 +202,22 @@ describe("executionEngine / ExecutionEngineHttp", function () {
await Promise.all(validators.map((v) => v.close()));
});

let engineBlocks = 0;
let builderBlocks = 0;
await new Promise<void>((resolve, _reject) => {
bn.chain.emitter.on(routes.events.EventType.block, async (blockData) => {
const {data: fullOrBlindedBlock} = await bn.api.beacon.getBlockV2(blockData.block);
if (fullOrBlindedBlock !== undefined) {
const blockFeeRecipient = toHexString(
(fullOrBlindedBlock as bellatrix.SignedBeaconBlock).message.body.executionPayload.feeRecipient
);
if (blockFeeRecipient === feeRecipientMevBoost) {
builderBlocks++;
} else {
engineBlocks++;
}
}
});
bn.chain.clock.on(ClockEvent.epoch, (epoch) => {
// Resolve only if the finalized checkpoint includes execution payload
if (epoch >= expectedEpochsToFinish) {
Expand All @@ -199,7 +233,7 @@ describe("executionEngine / ExecutionEngineHttp", function () {
await bn.close();
await sleep(500);

if (bn.chain.beaconProposerCache.get(1) !== "0xcccccccccccccccccccccccccccccccccccccccc") {
if (bn.chain.beaconProposerCache.get(1) !== feeRecipientEngine) {
throw Error("Invalid feeRecipient set at BN");
}

Expand All @@ -221,6 +255,16 @@ describe("executionEngine / ExecutionEngineHttp", function () {
);
}

// 2. builder blocks are as expected
if (builderBlocks < expectedBuilderBlocks) {
throw Error(`Incorrect builderBlocks=${builderBlocks} (expected=${expectedBuilderBlocks})`);
}

// 3. engine blocks are as expected
if (engineBlocks < expectedEngineBlocks) {
throw Error(`Incorrect engineBlocks=${engineBlocks} (expected=${expectedEngineBlocks})`);
}

// wait for 1 slot to print current epoch stats
await sleep(1 * bn.config.SECONDS_PER_SLOT * 1000);
stopInfoTracker();
Expand Down

0 comments on commit 597996a

Please sign in to comment.