From d6f0e7f1ce820665fb43479350fc87260e336de1 Mon Sep 17 00:00:00 2001 From: harkamal Date: Thu, 4 Jan 2024 17:27:27 +0530 Subject: [PATCH] add keymanager endpoint and update the test --- packages/api/src/keymanager/routes.ts | 40 +++++++++++++++++++ .../cli/src/cmds/validator/keymanager/impl.ts | 9 +++++ .../validator/src/services/validatorStore.ts | 8 ++++ .../test/unit/services/block.test.ts | 26 ++++++++++++ 4 files changed, 83 insertions(+) diff --git a/packages/api/src/keymanager/routes.ts b/packages/api/src/keymanager/routes.ts index 09f5e7610604..c9a16a52e343 100644 --- a/packages/api/src/keymanager/routes.ts +++ b/packages/api/src/keymanager/routes.ts @@ -247,6 +247,16 @@ export type Api = { > >; + updateBuilderBoostFactor( + pubkey: string, + builderBoostFactor: number + ): Promise< + ApiClientResponse< + {[HttpStatusCode.OK]: void; [HttpStatusCode.NO_CONTENT]: void}, + HttpStatusCode.UNAUTHORIZED | HttpStatusCode.FORBIDDEN | HttpStatusCode.NOT_FOUND + > + >; + /** * Create a signed voluntary exit message for an active validator, identified by a public key known to the validator * client. This endpoint returns a `SignedVoluntaryExit` object, which can be used to initiate voluntary exit via the @@ -290,6 +300,8 @@ export const routesData: RoutesData = { setGasLimit: {url: "/eth/v1/validator/{pubkey}/gas_limit", method: "POST", statusOk: 202}, deleteGasLimit: {url: "/eth/v1/validator/{pubkey}/gas_limit", method: "DELETE", statusOk: 204}, + updateBuilderBoostFactor: {url: "/eth/v1/validator/{pubkey}/builder_boost_factor", method: "POST", statusOk: 202}, + signVoluntaryExit: {url: "/eth/v1/validator/{pubkey}/voluntary_exit", method: "POST"}, }; @@ -326,6 +338,8 @@ export type ReqTypes = { setGasLimit: {params: {pubkey: string}; body: {gas_limit: string}}; deleteGasLimit: {params: {pubkey: string}}; + updateBuilderBoostFactor: {params: {pubkey: string}; body: {builder_boost_factor: string}}; + signVoluntaryExit: {params: {pubkey: string}; query: {epoch?: number}}; }; @@ -423,6 +437,17 @@ export function getReqSerializers(): ReqSerializers { params: {pubkey: Schema.StringRequired}, }, }, + updateBuilderBoostFactor: { + writeReq: (pubkey, builderBoostFactor) => ({ + params: {pubkey}, + body: {builder_boost_factor: builderBoostFactor.toString(10)}, + }), + parseReq: ({params: {pubkey}, body: {builder_boost_factor}}) => [pubkey, parseBoostFactor(builder_boost_factor)], + schema: { + params: {pubkey: Schema.StringRequired}, + body: Schema.Object, + }, + }, signVoluntaryExit: { writeReq: (pubkey, epoch) => ({params: {pubkey}, query: epoch !== undefined ? {epoch} : {}}), parseReq: ({params: {pubkey}, query: {epoch}}) => [pubkey, epoch], @@ -469,3 +494,18 @@ function parseGasLimit(gasLimitInput: string | number): number { } return gasLimit; } + +function parseBoostFactor(builderBoostFactorInput: string | number): number { + if ( + (typeof builderBoostFactorInput !== "string" && typeof builderBoostFactorInput !== "number") || + `${builderBoostFactorInput}`.trim() === "" + ) { + throw Error("Not valid Builder Boost Factor"); + } + + const builderBoostFactor = Number(builderBoostFactorInput); + if (Number.isNaN(builderBoostFactor)) { + throw Error(`Builder Boost Factor is not valid builderBoostFactor=${builderBoostFactor}`); + } + return builderBoostFactor; +} diff --git a/packages/cli/src/cmds/validator/keymanager/impl.ts b/packages/cli/src/cmds/validator/keymanager/impl.ts index 2abda3c9642e..fd57b624f50d 100644 --- a/packages/cli/src/cmds/validator/keymanager/impl.ts +++ b/packages/cli/src/cmds/validator/keymanager/impl.ts @@ -390,6 +390,15 @@ export class KeymanagerApi implements Api { }; } + async updateBuilderBoostFactor(pubkeyHex: string, builderBoostFactor: number): Promise { + this.checkIfProposerWriteEnabled(); + this.validator.validatorStore.updateBuilderBoostFactor(pubkeyHex, builderBoostFactor); + this.persistedKeysBackend.writeProposerConfig( + pubkeyHex, + this.validator.validatorStore.getProposerConfig(pubkeyHex) + ); + } + /** * Create and sign a voluntary exit message for an active validator */ diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index 61c794414303..18f9dba28529 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -311,6 +311,14 @@ export class ValidatorStore { delete validatorData.builder?.gasLimit; } + updateBuilderBoostFactor(pubkeyHex: PubkeyHex, boostFactor: number): void { + const validatorData = this.validators.get(pubkeyHex); + if (validatorData === undefined) { + throw Error(`Validator pubkey ${pubkeyHex} not known`); + } + validatorData.builder = {...validatorData.builder, boostFactor}; + } + /** Return true if `index` is active part of this validator client */ hasValidatorIndex(index: ValidatorIndex): boolean { return this.indicesService.index2pubkey.has(index); diff --git a/packages/validator/test/unit/services/block.test.ts b/packages/validator/test/unit/services/block.test.ts index f879017c95f1..7825208ee5a5 100644 --- a/packages/validator/test/unit/services/block.test.ts +++ b/packages/validator/test/unit/services/block.test.ts @@ -59,6 +59,14 @@ 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})); + validatorStore.getBuilderSelectionParams.returns({ + selection: routes.validator.BuilderSelection.MaxProfit, + boostFactor: 100, + }); + validatorStore.getGraffiti.returns("aaaa"); + validatorStore.getFeeRecipient.returns("0x00"); + validatorStore.strictFeeRecipientCheck.returns(false); + api.validator.produceBlockV3.resolves({ response: { data: signedBlock.message, @@ -86,6 +94,24 @@ describe("BlockDutiesService", function () { [signedBlock, {broadcastValidation: routes.beacon.BroadcastValidation.consensus}], "wrong publishBlock() args" ); + + // ProduceBlockV3 is called with all correct arguments + expect(api.validator.produceBlockV3.getCall(0).args).to.deep.equal( + [ + 1, + signedBlock.message.body.randaoReveal, + "aaaa", + false, + { + feeRecipient: "0x00", + builderSelection: routes.validator.BuilderSelection.MaxProfit, + strictFeeRecipientCheck: false, + blindedLocal: false, + builderBoostFactor: 100, + }, + ], + "wrong produceBlockV3() args" + ); }); it("Should produce, sign, and publish a blinded block", async function () {