Skip to content

Commit

Permalink
mock test produceblockv3 and fix issues
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Aug 23, 2023
1 parent c7a8f5a commit 3bd0fc6
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 99 deletions.
17 changes: 14 additions & 3 deletions packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,14 @@ export function getValidatorApi({

// Start calls for building execution and builder blocks
const blindedBlockPromise = chain.executionBuilder
? produceBlindedBlockOrContents(slot, randaoReveal, graffiti, feeRecipient, {strictFeeRecipientCheck})
? produceBlindedBlockOrContents(slot, randaoReveal, graffiti, feeRecipient, {strictFeeRecipientCheck}).catch(
(e) => {
logger.error("produceBlindedBlockOrContents failed to produce block", {slot}, e);
return null;
}
)
: null;

const fullBlockPromise =
// At any point either the builder or execution or both flows should be active.
//
Expand All @@ -412,15 +418,20 @@ export function getValidatorApi({
!isBuilderEnabled || builderSelection !== BuilderSelection.BuilderOnly
? // TODO deneb: builderSelection needs to be figured out if to be done beacon side
// || builderSelection !== BuilderSelection.BuilderOnly
produceFullBlockOrContents(slot, randaoReveal, graffiti, feeRecipient, {strictFeeRecipientCheck})
produceFullBlockOrContents(slot, randaoReveal, graffiti, feeRecipient, {strictFeeRecipientCheck}).catch(
(e) => {
logger.error("produceFullBlockOrContents failed to produce block", {slot}, e);
return null;
}
)
: null;

let blindedBlock, fullBlock;
if (blindedBlockPromise !== null && fullBlockPromise !== null) {
// reference index of promises in the race
const promisesOrder = [ProducedBlockSource.builder, ProducedBlockSource.engine];
[blindedBlock, fullBlock] = await racePromisesWithCutoff<
routes.validator.ProduceBlockOrContentsRes | routes.validator.ProduceBlindedBlockOrContentsRes
routes.validator.ProduceBlockOrContentsRes | routes.validator.ProduceBlindedBlockOrContentsRes | null
>(
[blindedBlockPromise, fullBlockPromise],
BLOCK_PRODUCTION_RACE_CUTOFF_MS,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import sinon, {SinonStubbedInstance} from "sinon";
import {use, expect} from "chai";
import chaiAsPromised from "chai-as-promised";
import {ssz} from "@lodestar/types";
import {ForkChoice} from "@lodestar/fork-choice";
import {ChainForkConfig} from "@lodestar/config";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {createBeaconConfig, createChainForkConfig, defaultChainConfig} from "@lodestar/config";
import {SyncState} from "../../../../../src/sync/interface.js";
import {ApiModules} from "../../../../../src/api/impl/types.js";
import {BuilderSelection, getValidatorApi} from "../../../../../src/api/impl/validator/index.js";
import {IClock} from "../../../../../src/util/clock.js";
import {testLogger} from "../../../../utils/logger.js";
import {BeaconChain} from "../../../../../src/chain/index.js";
import {ExecutionEngineHttp} from "../../../../../src/execution/engine/http.js";
import {StubbedBeaconDb, StubbedChainMutable} from "../../../../utils/stub/index.js";
import {Eth1ForBlockProduction, IEth1ForBlockProduction} from "../../../../../src/eth1/index.js";
import {BeaconProposerCache} from "../../../../../src/chain/beaconProposerCache.js";
import {Network} from "../../../../../src/network/index.js";
import {BeaconSync} from "../../../../../src/sync/index.js";
import {ExecutionBuilderHttp, IExecutionBuilder} from "../../../../../src/execution/index.js";

use(chaiAsPromised);

type StubbedChain = StubbedChainMutable<"clock" | "forkChoice" | "logger" | "config">;

/* eslint-disable @typescript-eslint/naming-convention */
describe("api/validator - produceBlockV3", function () {
const logger = testLogger();
const sandbox = sinon.createSandbox();

let modules: ApiModules;

let chainStub: StubbedChain;
let forkChoiceStub: SinonStubbedInstance<ForkChoice> & ForkChoice;
let eth1Stub: SinonStubbedInstance<Eth1ForBlockProduction>;
let syncStub: SinonStubbedInstance<BeaconSync>;
let beaconProposerCacheStub: SinonStubbedInstance<BeaconProposerCache> & BeaconProposerCache;
let dbStub: StubbedBeaconDb;
let networkStub: SinonStubbedInstance<Network>;
let executionBuilderStub: SinonStubbedInstance<ExecutionBuilderHttp> & ExecutionBuilderHttp;

const chainConfig = createChainForkConfig({
...defaultChainConfig,
ALTAIR_FORK_EPOCH: 0,
BELLATRIX_FORK_EPOCH: 1,
});
const genesisValidatorsRoot = Buffer.alloc(32, 0xaa);
const config = createBeaconConfig(chainConfig, genesisValidatorsRoot);

beforeEach(() => {
const chainConfig = createChainForkConfig({
...defaultChainConfig,
ALTAIR_FORK_EPOCH: 0,
BELLATRIX_FORK_EPOCH: 1,
});
const genesisValidatorsRoot = Buffer.alloc(32, 0xaa);
const config = createBeaconConfig(chainConfig, genesisValidatorsRoot);

chainStub = sandbox.createStubInstance(BeaconChain) as StubbedChain;

dbStub = new StubbedBeaconDb(config);
networkStub = sinon.createStubInstance(Network);
syncStub = sinon.createStubInstance(BeaconSync);

eth1Stub = sinon.createStubInstance(Eth1ForBlockProduction);
chainStub.logger = logger;
forkChoiceStub = sandbox.createStubInstance(ForkChoice) as SinonStubbedInstance<ForkChoice> & ForkChoice;
chainStub.forkChoice = forkChoiceStub;

(chainStub as unknown as {eth1: IEth1ForBlockProduction}).eth1 = eth1Stub;
(chainStub as unknown as {config: ChainForkConfig}).config = config as unknown as ChainForkConfig;

beaconProposerCacheStub = sandbox.createStubInstance(
BeaconProposerCache
) as SinonStubbedInstance<BeaconProposerCache> & BeaconProposerCache;
(chainStub as unknown as {beaconProposerCache: BeaconProposerCache})["beaconProposerCache"] =
beaconProposerCacheStub;

executionBuilderStub = sandbox.createStubInstance(
ExecutionBuilderHttp
) as SinonStubbedInstance<ExecutionBuilderHttp> & ExecutionEngineHttp;
(chainStub as unknown as {executionBuilder: IExecutionBuilder}).executionBuilder = executionBuilderStub;
executionBuilderStub.status = true;
});
afterEach(() => {
sandbox.restore();
});

const testCases: [BuilderSelection, number | null, number | null, string][] = [
[BuilderSelection.MaxProfit, 1, 0, "builder"],
[BuilderSelection.MaxProfit, 1, 2, "engine"],
[BuilderSelection.MaxProfit, null, 0, "engine"],
[BuilderSelection.MaxProfit, 0, null, "builder"],

[BuilderSelection.BuilderAlways, 1, 2, "builder"],
[BuilderSelection.BuilderAlways, 1, 0, "builder"],
[BuilderSelection.BuilderAlways, null, 0, "engine"],
[BuilderSelection.BuilderAlways, 0, null, "builder"],
[BuilderSelection.BuilderOnly, 0, 2, "builder"],
];

testCases.forEach(([builderSelection, builderPayloadValue, enginePayloadValue, finalSelection]) => {
it(`produceBlockV3 - ${finalSelection} produces block`, async () => {
modules = {
chain: chainStub,
config,
db: dbStub,
logger,
network: networkStub,
sync: syncStub,
metrics: null,
};

const fullBlock = ssz.bellatrix.BeaconBlock.defaultValue();
const blindedBlock = ssz.bellatrix.BlindedBeaconBlock.defaultValue();

const slot = 1 * SLOTS_PER_EPOCH;
const randaoReveal = fullBlock.body.randaoReveal;
const graffiti = "a".repeat(32);
const expectedFeeRecipient = "0xccccccccccccccccccccccccccccccccccccccaa";
const currentSlot = 1 * SLOTS_PER_EPOCH;
chainStub.clock = {currentSlot} as IClock;
sinon.replaceGetter(syncStub, "state", () => SyncState.Synced);

const api = getValidatorApi(modules);

if (enginePayloadValue !== null) {
chainStub.produceBlock.resolves({
block: fullBlock,
executionPayloadValue: BigInt(enginePayloadValue),
});
} else {
chainStub.produceBlock.throws(Error("not produced"));
}

if (builderPayloadValue !== null) {
chainStub.produceBlindedBlock.resolves({
block: blindedBlock,
executionPayloadValue: BigInt(builderPayloadValue),
});
} else {
chainStub.produceBlindedBlock.throws(Error("not produced"));
}

const _skipRandaoVerification = false;
const produceBlockOpts = {
strictFeeRecipientCheck: false,
// builderEnabled: true,
builderSelection,
};

const block = await api.produceBlockV3(
slot,
randaoReveal,
graffiti,
expectedFeeRecipient,
_skipRandaoVerification,
produceBlockOpts
);

const expectedBlock = finalSelection === "builder" ? blindedBlock : fullBlock;
const expectedExecution = finalSelection === "builder" ? true : false;

expect(block.data).to.be.deep.equal(expectedBlock);
expect(block.executionPayloadBlinded).to.be.equal(expectedExecution);

// check call counts
expect(chainStub.produceBlindedBlock.callCount).to.equal(1, "produceBlindedBlock should be called");

if (builderSelection === BuilderSelection.BuilderOnly) {
expect(chainStub.produceBlock.callCount).to.equal(0, "produceBlock should not be called");
} else {
expect(chainStub.produceBlock.callCount).to.equal(1, "produceBlock should be called");
}
});
});
});
10 changes: 8 additions & 2 deletions packages/validator/test/unit/services/block.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {config as mainnetConfig} from "@lodestar/config/default";
import {sleep} from "@lodestar/utils";
import {ssz} from "@lodestar/types";
import {HttpStatusCode} from "@lodestar/api";
import {ForkName} from "@lodestar/params";
import {BlockProposingService} from "../../../src/services/block.js";
import {ValidatorStore} from "../../../src/services/validatorStore.js";
import {getApiClientStub} from "../../utils/apiStub.js";
Expand Down Expand Up @@ -53,8 +54,13 @@ describe("BlockDutiesService", function () {
const signedBlock = ssz.phase0.SignedBeaconBlock.defaultValue();
validatorStore.signRandao.resolves(signedBlock.message.body.randaoReveal);
validatorStore.signBlock.callsFake(async (_, block) => ({message: block, signature: signedBlock.signature}));
api.validator.produceBlock.resolves({
response: {data: signedBlock.message},
api.validator.produceBlockV3.resolves({
response: {
data: signedBlock.message,
version: ForkName.bellatrix,
executionPayloadValue: BigInt(1),
executionPayloadBlinded: false
},
ok: true,
status: HttpStatusCode.OK,
});
Expand Down
94 changes: 0 additions & 94 deletions packages/validator/test/unit/services/produceBlockwrapper.test.ts

This file was deleted.

0 comments on commit 3bd0fc6

Please sign in to comment.