diff --git a/packages/api/src/routes/validator.ts b/packages/api/src/routes/validator.ts index 3e558c20fd20..398b9d3eff7f 100644 --- a/packages/api/src/routes/validator.ts +++ b/packages/api/src/routes/validator.ts @@ -13,7 +13,6 @@ import { ssz, UintNum64, ValidatorIndex, - ExecutionAddress, } from "@chainsafe/lodestar-types"; import { RoutesData, @@ -24,6 +23,7 @@ import { WithVersion, reqOnlyBody, ReqSerializers, + jsonType, } from "../utils"; // See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes @@ -46,8 +46,8 @@ export type SyncCommitteeSubscription = { }; export type ProposerPreparationData = { - validatorIndex: ValidatorIndex; - feeRecipient: ExecutionAddress; + validatorIndex: string; + feeRecipient: string; }; export type ProposerDuty = { @@ -264,14 +264,6 @@ export function getReqSerializers(): ReqSerializers { {jsonCase: "eth2"} ); - const ProposerPreparationData = new ContainerType( - { - validatorIndex: ssz.ValidatorIndex, - feeRecipient: ssz.ExecutionAddress, - }, - {jsonCase: "eth2"} - ); - const produceBlock: ReqSerializers["produceBlock"] = { writeReq: (slot, randaoReveal, grafitti) => ({ params: {slot}, @@ -348,7 +340,13 @@ export function getReqSerializers(): ReqSerializers { publishContributionAndProofs: reqOnlyBody(ArrayOf(ssz.altair.SignedContributionAndProof), Schema.ObjectArray), prepareBeaconCommitteeSubnet: reqOnlyBody(ArrayOf(BeaconCommitteeSubscription), Schema.ObjectArray), prepareSyncCommitteeSubnets: reqOnlyBody(ArrayOf(SyncCommitteeSubscription), Schema.ObjectArray), - prepareBeaconProposer: reqOnlyBody(ArrayOf(ProposerPreparationData), Schema.ObjectArray), + prepareBeaconProposer: { + writeReq: (items: ProposerPreparationData[]) => ({body: items.map((item) => jsonType("snake").toJson(item))}), + parseReq: ({body}) => [ + (body as Record[]).map((item) => jsonType("snake").fromJson(item) as ProposerPreparationData), + ], + schema: {body: Schema.ObjectArray}, + }, }; } diff --git a/packages/cli/src/util/feeRecipient.ts b/packages/cli/src/util/feeRecipient.ts index 4b3d147ce95f..2706bfafdfc5 100644 --- a/packages/cli/src/util/feeRecipient.ts +++ b/packages/cli/src/util/feeRecipient.ts @@ -1,9 +1,6 @@ -import {ExecutionAddress} from "@chainsafe/lodestar-types"; -import {fromHex} from "@chainsafe/lodestar-utils"; - -export function parseFeeRecipient(feeRecipientHex: string): ExecutionAddress { +export function parseFeeRecipient(feeRecipientHex: string): string { if (!/^0x[a-fA-F0-9]{40}$/i.test(feeRecipientHex)) { throw Error(`Invalid feeRecipient= ${feeRecipientHex}, expected format: ^0x[a-fA-F0-9]{40}$`); } - return fromHex(feeRecipientHex.toLowerCase()); + return feeRecipientHex.toLowerCase(); } diff --git a/packages/cli/test/unit/validator/options.test.ts b/packages/cli/test/unit/validator/options.test.ts index 5d20d22cc11a..f59f2f78bf59 100644 --- a/packages/cli/test/unit/validator/options.test.ts +++ b/packages/cli/test/unit/validator/options.test.ts @@ -8,7 +8,7 @@ describe("validator / parseFeeRecipient", () => { const testCases: string[] = [`0x${feeRecipientString}`, `0X${feeRecipientString}`]; for (const testCase of testCases) { it(`parse ${testCase}`, () => { - expect(feeRecipient).to.be.deep.equal(parseFeeRecipient(testCase)); + expect(`0x${feeRecipientString}`).to.be.deep.equal(parseFeeRecipient(testCase)); }); } }); diff --git a/packages/lodestar/src/chain/chain.ts b/packages/lodestar/src/chain/chain.ts index ca6c6ac4b80f..0910d36e8ed3 100644 --- a/packages/lodestar/src/chain/chain.ts +++ b/packages/lodestar/src/chain/chain.ts @@ -13,18 +13,8 @@ import { } from "@chainsafe/lodestar-beacon-state-transition"; import {IBeaconConfig} from "@chainsafe/lodestar-config"; import {IForkChoice} from "@chainsafe/lodestar-fork-choice"; -import { - allForks, - UintNum64, - Root, - phase0, - Slot, - RootHex, - ValidatorIndex, - ExecutionAddress, - Epoch, -} from "@chainsafe/lodestar-types"; -import {ILogger, fromHex} from "@chainsafe/lodestar-utils"; +import {allForks, UintNum64, Root, phase0, Slot, RootHex, Epoch} from "@chainsafe/lodestar-types"; +import {ILogger} from "@chainsafe/lodestar-utils"; import {fromHexString} from "@chainsafe/ssz"; import {AbortController} from "@chainsafe/abort-controller"; import {GENESIS_EPOCH, ZERO_HASH} from "../constants"; @@ -100,7 +90,7 @@ export class BeaconChain implements IBeaconChain { readonly pubkey2index: PubkeyIndexMap; readonly index2pubkey: Index2PubkeyCache; - readonly beaconProposerCache: MapDef; + readonly beaconProposerCache: MapDef; protected readonly blockProcessor: BlockProcessor; protected readonly db: IBeaconDb; @@ -156,9 +146,9 @@ export class BeaconChain implements IBeaconChain { this.pubkey2index = new PubkeyIndexMap(); this.index2pubkey = []; - this.beaconProposerCache = new MapDef(() => ({ + this.beaconProposerCache = new MapDef(() => ({ epoch: 0, - feeRecipient: fromHex(opts.defaultFeeRecipient), + feeRecipient: opts.defaultFeeRecipient, })); // Restore state caches diff --git a/packages/lodestar/src/chain/factory/block/body.ts b/packages/lodestar/src/chain/factory/block/body.ts index 37e09c6b5721..6c286b4751d2 100644 --- a/packages/lodestar/src/chain/factory/block/body.ts +++ b/packages/lodestar/src/chain/factory/block/body.ts @@ -12,7 +12,6 @@ import { RootHex, Slot, ssz, - ExecutionAddress, ValidatorIndex, } from "@chainsafe/lodestar-types"; import { @@ -90,7 +89,7 @@ export async function assembleBody( // - Call prepareExecutionPayload again if parameters change const finalizedBlockHash = chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash; - const feeRecipient = chain.beaconProposerCache.getOrDefault(proposerIndex).feeRecipient; + const feeRecipient = chain.beaconProposerCache.getOrDefault(`${proposerIndex}`).feeRecipient; // prepareExecutionPayload will throw error via notifyForkchoiceUpdate if // the EL returns Syncing on this request to prepare a payload @@ -134,7 +133,7 @@ async function prepareExecutionPayload( chain: IBeaconChain, finalizedBlockHash: RootHex, state: CachedBeaconStateBellatrix, - suggestedFeeRecipient: ExecutionAddress + suggestedFeeRecipient: string ): Promise { // Use different POW block hash parent for block production based on merge status. // Returned value of null == using an empty ExecutionPayload value diff --git a/packages/lodestar/src/chain/interface.ts b/packages/lodestar/src/chain/interface.ts index da61b1d3fa62..cff8cf4bed6e 100644 --- a/packages/lodestar/src/chain/interface.ts +++ b/packages/lodestar/src/chain/interface.ts @@ -1,14 +1,4 @@ -import { - allForks, - UintNum64, - Root, - phase0, - Slot, - RootHex, - ValidatorIndex, - ExecutionAddress, - Epoch, -} from "@chainsafe/lodestar-types"; +import {allForks, UintNum64, Root, phase0, Slot, RootHex, Epoch} from "@chainsafe/lodestar-types"; import {CachedBeaconStateAllForks} from "@chainsafe/lodestar-beacon-state-transition"; import {IForkChoice} from "@chainsafe/lodestar-fork-choice"; import {IBeaconConfig} from "@chainsafe/lodestar-config"; @@ -41,8 +31,8 @@ export type Eth2Context = { }; export type ProposerPreparationData = { - validatorIndex: ValidatorIndex; - feeRecipient: ExecutionAddress; + validatorIndex: string; + feeRecipient: string; }; /** @@ -84,7 +74,7 @@ export interface IBeaconChain { readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages; readonly seenContributionAndProof: SeenContributionAndProof; - readonly beaconProposerCache: MapDef; + readonly beaconProposerCache: MapDef; /** Stop beacon chain processing */ close(): void; diff --git a/packages/lodestar/src/executionEngine/http.ts b/packages/lodestar/src/executionEngine/http.ts index fa6ebd1ec973..e24bd18d05b2 100644 --- a/packages/lodestar/src/executionEngine/http.ts +++ b/packages/lodestar/src/executionEngine/http.ts @@ -200,7 +200,7 @@ export class ExecutionEngineHttp implements IExecutionEngine { ? { timestamp: numToQuantity(payloadAttributes.timestamp), prevRandao: bytesToData(payloadAttributes.prevRandao), - suggestedFeeRecipient: bytesToData(payloadAttributes.suggestedFeeRecipient), + suggestedFeeRecipient: payloadAttributes.suggestedFeeRecipient, } : undefined; diff --git a/packages/lodestar/src/executionEngine/interface.ts b/packages/lodestar/src/executionEngine/interface.ts index 8978fa12fb77..88a9b677e4ed 100644 --- a/packages/lodestar/src/executionEngine/interface.ts +++ b/packages/lodestar/src/executionEngine/interface.ts @@ -52,7 +52,9 @@ export type ForkChoiceUpdateStatus = export type PayloadAttributes = { timestamp: number; prevRandao: Uint8Array; - suggestedFeeRecipient: Uint8Array; + // DATA is anyway a hex string, so we can just track it as a hex string to + // avoid any conversions + suggestedFeeRecipient: string; }; export type ApiPayloadAttributes = { diff --git a/packages/lodestar/src/executionEngine/mock.ts b/packages/lodestar/src/executionEngine/mock.ts index 702fad9cdf63..72c3eba40ace 100644 --- a/packages/lodestar/src/executionEngine/mock.ts +++ b/packages/lodestar/src/executionEngine/mock.ts @@ -1,6 +1,6 @@ import crypto from "node:crypto"; import {bellatrix, RootHex, Root} from "@chainsafe/lodestar-types"; -import {toHexString} from "@chainsafe/ssz"; +import {toHexString, fromHexString} from "@chainsafe/ssz"; import {BYTES_PER_LOGS_BLOOM} from "@chainsafe/lodestar-params"; import {ZERO_HASH, ZERO_HASH_HEX} from "../constants"; @@ -107,7 +107,7 @@ export class ExecutionEngineMock implements IExecutionEngine { const payloadId = this.payloadId++; const payload: bellatrix.ExecutionPayload = { parentHash: headBlockHash, - feeRecipient: payloadAttributes.suggestedFeeRecipient, + feeRecipient: fromHexString(payloadAttributes.suggestedFeeRecipient), stateRoot: crypto.randomBytes(32), receiptsRoot: crypto.randomBytes(32), logsBloom: crypto.randomBytes(BYTES_PER_LOGS_BLOOM), diff --git a/packages/lodestar/test/utils/mocks/chain/chain.ts b/packages/lodestar/test/utils/mocks/chain/chain.ts index 9ff22102823a..e6b236a3964c 100644 --- a/packages/lodestar/test/utils/mocks/chain/chain.ts +++ b/packages/lodestar/test/utils/mocks/chain/chain.ts @@ -2,23 +2,11 @@ import {AbortController} from "@chainsafe/abort-controller"; import sinon from "sinon"; import {toHexString} from "@chainsafe/ssz"; -import { - allForks, - UintNum64, - Root, - Slot, - ssz, - Uint16, - UintBn64, - ValidatorIndex, - Epoch, - ExecutionAddress, -} from "@chainsafe/lodestar-types"; +import {allForks, UintNum64, Root, Slot, ssz, Uint16, UintBn64, Epoch} from "@chainsafe/lodestar-types"; import {IBeaconConfig} from "@chainsafe/lodestar-config"; import {BeaconStateAllForks, CachedBeaconStateAllForks} from "@chainsafe/lodestar-beacon-state-transition"; import {phase0} from "@chainsafe/lodestar-beacon-state-transition"; import {CheckpointWithHex, IForkChoice, IProtoBlock, ExecutionStatus} from "@chainsafe/lodestar-fork-choice"; -import {fromHex} from "@chainsafe/lodestar-utils"; import {ChainEventEmitter, IBeaconChain} from "../../../../src/chain"; import {IBeaconClock} from "../../../../src/chain/clock/interface"; @@ -96,9 +84,9 @@ export class MockBeaconChain implements IBeaconChain { readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages(); readonly seenContributionAndProof = new SeenContributionAndProof(); - readonly beaconProposerCache = new MapDef(() => ({ + readonly beaconProposerCache = new MapDef(() => ({ epoch: 0, - feeRecipient: fromHex(defaultDefaultFeeRecipient), + feeRecipient: defaultDefaultFeeRecipient, })); private state: BeaconStateAllForks; diff --git a/packages/validator/src/services/prepareBeaconProposer.ts b/packages/validator/src/services/prepareBeaconProposer.ts index f4ad7b9fce2d..9d70b6e7d8d2 100644 --- a/packages/validator/src/services/prepareBeaconProposer.ts +++ b/packages/validator/src/services/prepareBeaconProposer.ts @@ -1,4 +1,4 @@ -import {ValidatorIndex, ExecutionAddress, Epoch} from "@chainsafe/lodestar-types"; +import {Epoch} from "@chainsafe/lodestar-types"; import {Api} from "@chainsafe/lodestar-api"; import {ValidatorStore} from "./validatorStore"; @@ -7,8 +7,8 @@ import {IClock, ILoggerVc} from "../util"; import {Metrics} from "../metrics"; type ProposerPreparationData = { - validatorIndex: ValidatorIndex; - feeRecipient: ExecutionAddress; + validatorIndex: string; + feeRecipient: string; }; /** @@ -22,7 +22,7 @@ export class PrepareBeaconProposerService { private readonly api: Api, private clock: IClock, private readonly validatorStore: ValidatorStore, - private readonly defaultFeeRecipient: ExecutionAddress, + private readonly defaultFeeRecipient: string, private readonly indicesService: IndicesService, private readonly metrics: Metrics | null ) { @@ -49,6 +49,9 @@ export class PrepareBeaconProposerService { }; private getProposerData(indices: number[]): ProposerPreparationData[] { - return indices.map((validatorIndex) => ({validatorIndex, feeRecipient: this.defaultFeeRecipient})); + return indices.map((validatorIndex) => ({ + validatorIndex: validatorIndex.toString(), + feeRecipient: this.defaultFeeRecipient, + })); } } diff --git a/packages/validator/src/validator.ts b/packages/validator/src/validator.ts index 4f37366d3cf3..77259ec9f0ed 100644 --- a/packages/validator/src/validator.ts +++ b/packages/validator/src/validator.ts @@ -1,6 +1,6 @@ import {AbortController, AbortSignal} from "@chainsafe/abort-controller"; import {IDatabaseApiOptions} from "@chainsafe/lodestar-db"; -import {ssz, ExecutionAddress} from "@chainsafe/lodestar-types"; +import {ssz} from "@chainsafe/lodestar-types"; import {createIBeaconConfig, IBeaconConfig} from "@chainsafe/lodestar-config"; import {Genesis} from "@chainsafe/lodestar-types/phase0"; import {ILogger} from "@chainsafe/lodestar-utils"; @@ -31,7 +31,7 @@ export type ValidatorOptions = { logger: ILogger; afterBlockDelaySlotFraction?: number; graffiti?: string; - defaultFeeRecipient?: ExecutionAddress; + defaultFeeRecipient?: string; }; // TODO: Extend the timeout, and let it be customizable