Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 0 additions & 72 deletions packages/api/src/beacon/routes/beacon/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ForkPreElectra,
MAX_PAYLOAD_ATTESTATIONS,
PTC_SIZE,
SLOTS_PER_EPOCH,
isForkPostElectra,
} from "@lodestar/params";
import {
Expand Down Expand Up @@ -47,12 +46,6 @@ const SignedBLSToExecutionChangeListType = ArrayOf(ssz.capella.SignedBLSToExecut
const SyncCommitteeMessageListType = ArrayOf(ssz.altair.SyncCommitteeMessage);
const PayloadAttestationListType = ArrayOf(ssz.gloas.PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS);
const PayloadAttestationMessageListType = ArrayOf(ssz.gloas.PayloadAttestationMessage, PTC_SIZE);
// 2 * SLOTS_PER_EPOCH max normally; getAll() may return up to 3 * SLOTS_PER_EPOCH during non-finality
const MAX_PROPOSER_PREFERENCES_PER_REQUEST = 3 * SLOTS_PER_EPOCH;
const SignedProposerPreferencesListType = ArrayOf(
ssz.gloas.SignedProposerPreferences,
MAX_PROPOSER_PREFERENCES_PER_REQUEST
);

type AttestationListPhase0 = ValueOf<typeof AttestationListTypePhase0>;
type AttestationListElectra = ValueOf<typeof AttestationListTypeElectra>;
Expand All @@ -68,7 +61,6 @@ type SignedBLSToExecutionChangeList = ValueOf<typeof SignedBLSToExecutionChangeL
type SyncCommitteeMessageList = ValueOf<typeof SyncCommitteeMessageListType>;
type PayloadAttestationList = ValueOf<typeof PayloadAttestationListType>;
type PayloadAttestationMessageList = ValueOf<typeof PayloadAttestationMessageListType>;
type SignedProposerPreferencesList = ValueOf<typeof SignedProposerPreferencesListType>;

export type Endpoints = {
/**
Expand All @@ -95,18 +87,6 @@ export type Endpoints = {
VersionMeta
>;

/**
* Get signed proposer preferences from operations pool
* Retrieves proposer preferences known by the node but not necessarily incorporated into any block.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incorporated into any block

what? since when are proposer preferences incorporated into a block? 😂

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*/
getPoolProposerPreferences: Endpoint<
"GET",
{slot?: Slot},
{query: {slot?: number}},
SignedProposerPreferencesList,
VersionMeta
>;

/**
* Get AttesterSlashings from operations pool
* Retrieves attester slashings known by the node but not necessarily incorporated into any block
Expand Down Expand Up @@ -246,18 +226,6 @@ export type Endpoints = {
EmptyResponseData,
EmptyMeta
>;

/**
* Submit signed proposer preferences
* Submits signed proposer preferences to the beacon node.
*/
submitSignedProposerPreferences: Endpoint<
"POST",
{signedProposerPreferences: SignedProposerPreferencesList},
{body: unknown; headers: {[MetaHeader.Version]: string}},
EmptyResponseData,
EmptyMeta
>;
};

export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> {
Expand Down Expand Up @@ -288,19 +256,6 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
meta: VersionCodec,
},
},
getPoolProposerPreferences: {
url: "/eth/v1/beacon/pool/proposer_preferences",
method: "GET",
req: {
writeReq: ({slot}) => ({query: {slot}}),
parseReq: ({query}) => ({slot: query.slot}),
schema: {query: {slot: Schema.Uint}},
},
resp: {
data: SignedProposerPreferencesListType,
meta: VersionCodec,
},
},
getPoolAttesterSlashingsV2: {
url: "/eth/v2/beacon/pool/attester_slashings",
method: "GET",
Expand Down Expand Up @@ -516,32 +471,5 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
},
resp: EmptyResponseCodec,
},
submitSignedProposerPreferences: {
url: "/eth/v1/beacon/pool/proposer_preferences",
method: "POST",
req: {
writeReqJson: ({signedProposerPreferences}) => ({
body: SignedProposerPreferencesListType.toJson(signedProposerPreferences),
headers: {[MetaHeader.Version]: ForkName.gloas},
}),
parseReqJson: ({body, headers}) => {
toForkName(fromHeaders(headers, MetaHeader.Version));
return {signedProposerPreferences: SignedProposerPreferencesListType.fromJson(body)};
},
writeReqSsz: ({signedProposerPreferences}) => ({
body: SignedProposerPreferencesListType.serialize(signedProposerPreferences),
headers: {[MetaHeader.Version]: ForkName.gloas},
}),
parseReqSsz: ({body, headers}) => {
toForkName(fromHeaders(headers, MetaHeader.Version));
return {signedProposerPreferences: SignedProposerPreferencesListType.deserialize(body)};
},
schema: {
body: Schema.ObjectArray,
headers: {[MetaHeader.Version]: Schema.String},
},
},
resp: EmptyResponseCodec,
},
};
}
48 changes: 48 additions & 0 deletions packages/api/src/beacon/routes/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
ForkPostDeneb,
ForkPostGloas,
ForkPreDeneb,
MIN_SEED_LOOKAHEAD,
SLOTS_PER_EPOCH,
VALIDATOR_REGISTRY_LIMIT,
isForkPostDeneb,
isForkPostElectra,
Expand Down Expand Up @@ -246,6 +248,10 @@ export const SignedValidatorRegistrationV1ListType = ArrayOf(
ssz.bellatrix.SignedValidatorRegistrationV1,
VALIDATOR_REGISTRY_LIMIT
);
export const SignedProposerPreferencesListType = ArrayOf(
ssz.gloas.SignedProposerPreferences,
(MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
);

export type ValidatorIndices = ValueOf<typeof ValidatorIndicesType>;
export type AttesterDuty = ValueOf<typeof AttesterDutyType>;
Expand Down Expand Up @@ -273,6 +279,7 @@ export type SyncCommitteeSelectionList = ValueOf<typeof SyncCommitteeSelectionLi
export type LivenessResponseData = ValueOf<typeof LivenessResponseDataType>;
export type LivenessResponseDataList = ValueOf<typeof LivenessResponseDataListType>;
export type SignedValidatorRegistrationV1List = ValueOf<typeof SignedValidatorRegistrationV1ListType>;
export type SignedProposerPreferencesList = ValueOf<typeof SignedProposerPreferencesListType>;

export type Endpoints = {
/**
Expand Down Expand Up @@ -646,6 +653,20 @@ export type Endpoints = {
EmptyResponseData,
EmptyMeta
>;

/**
* Submit signed proposer preferences
*
* Verifies given signed proposer preferences and publishes them on the `proposer_preferences`
* gossipsub topic. Supersedes `prepareBeaconProposer` and `registerValidator` from Gloas onwards.
*/
submitProposerPreferences: Endpoint<
"POST",
{signedProposerPreferences: SignedProposerPreferencesList},
{body: unknown; headers: {[MetaHeader.Version]: string}},
EmptyResponseData,
EmptyMeta
>;
};

export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> {
Expand Down Expand Up @@ -1210,6 +1231,33 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
requestWireFormat: WireFormat.ssz,
},
},
submitProposerPreferences: {
url: "/eth/v1/validator/proposer_preferences",
method: "POST",
req: {
writeReqJson: ({signedProposerPreferences}) => ({
body: SignedProposerPreferencesListType.toJson(signedProposerPreferences),
headers: {[MetaHeader.Version]: config.getForkName(signedProposerPreferences[0]?.message.proposalSlot ?? 0)},
}),
parseReqJson: ({body, headers}) => {
toForkName(fromHeaders(headers, MetaHeader.Version));
return {signedProposerPreferences: SignedProposerPreferencesListType.fromJson(body)};
},
writeReqSsz: ({signedProposerPreferences}) => ({
body: SignedProposerPreferencesListType.serialize(signedProposerPreferences),
headers: {[MetaHeader.Version]: config.getForkName(signedProposerPreferences[0]?.message.proposalSlot ?? 0)},
}),
parseReqSsz: ({body, headers}) => {
toForkName(fromHeaders(headers, MetaHeader.Version));
return {signedProposerPreferences: SignedProposerPreferencesListType.deserialize(body)};
},
schema: {
body: Schema.ObjectArray,
headers: {[MetaHeader.Version]: Schema.String},
},
},
resp: EmptyResponseCodec,
},
};
}

Expand Down
8 changes: 0 additions & 8 deletions packages/api/test/unit/beacon/testData/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,6 @@ export const testData: GenericServerTestCases<Endpoints> = {
args: {slot: 1},
res: {data: [ssz.gloas.PayloadAttestation.defaultValue()], meta: {version: ForkName.gloas}},
},
getPoolProposerPreferences: {
args: {slot: 1},
res: {data: [ssz.gloas.SignedProposerPreferences.defaultValue()], meta: {version: ForkName.gloas}},
},
getPoolAttesterSlashingsV2: {
args: undefined,
res: {data: [ssz.electra.AttesterSlashing.defaultValue()], meta: {version: ForkName.electra}},
Expand Down Expand Up @@ -167,10 +163,6 @@ export const testData: GenericServerTestCases<Endpoints> = {
args: {payloadAttestationMessages: [ssz.gloas.PayloadAttestationMessage.defaultValue()]},
res: undefined,
},
submitSignedProposerPreferences: {
args: {signedProposerPreferences: [ssz.gloas.SignedProposerPreferences.defaultValue()]},
res: undefined,
},

// state

Expand Down
6 changes: 3 additions & 3 deletions packages/api/test/unit/beacon/testData/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,10 @@ export const eventTestData: EventData = {
data: ssz.gloas.SignedProposerPreferences.fromJson({
message: {
dependent_root: "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
proposal_slot: "10",
validator_index: "42",
proposal_slot: "32",
validator_index: "123",
fee_recipient: "0x0000000000000000000000000000000000000000",
target_gas_limit: "30000000",
target_gas_limit: "60000000",
},
signature:
"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
Expand Down
4 changes: 4 additions & 0 deletions packages/api/test/unit/beacon/testData/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,8 @@ export const testData: GenericServerTestCases<Endpoints> = {
args: {registrations: [ssz.bellatrix.SignedValidatorRegistrationV1.defaultValue()]},
res: undefined,
},
submitProposerPreferences: {
args: {signedProposerPreferences: [ssz.gloas.SignedProposerPreferences.defaultValue()]},
res: undefined,
},
};
53 changes: 0 additions & 53 deletions packages/beacon-node/src/api/impl/beacon/pool/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {routes} from "@lodestar/api";
import {ApplicationMethods} from "@lodestar/api/server";
import {
ForkName,
ForkPostElectra,
ForkPreElectra,
SYNC_COMMITTEE_SUBNET_SIZE,
Expand All @@ -17,15 +16,12 @@ import {
GossipAction,
PayloadAttestationError,
PayloadAttestationErrorCode,
ProposerPreferencesError,
ProposerPreferencesErrorCode,
SyncCommitteeError,
} from "../../../../chain/errors/index.js";
import {validateApiAttesterSlashing} from "../../../../chain/validation/attesterSlashing.js";
import {validateApiBlsToExecutionChange} from "../../../../chain/validation/blsToExecutionChange.js";
import {toElectraSingleAttestation, validateApiAttestation} from "../../../../chain/validation/index.js";
import {validateApiPayloadAttestationMessage} from "../../../../chain/validation/payloadAttestationMessage.js";
import {validateGossipProposerPreferences} from "../../../../chain/validation/proposerPreferences.js";
import {validateApiProposerSlashing} from "../../../../chain/validation/proposerSlashing.js";
import {validateApiSyncCommittee} from "../../../../chain/validation/syncCommittee.js";
import {validateApiVoluntaryExit} from "../../../../chain/validation/voluntaryExit.js";
Expand Down Expand Up @@ -66,55 +62,6 @@ export function getBeaconPoolApi({
return {data: chain.payloadAttestationPool.getAll(slot), meta: {version: fork}};
},

async getPoolProposerPreferences({slot}) {
const fork = chain.config.getForkName(slot ?? chain.clock.currentSlot);
if (!isForkPostGloas(fork)) {
throw new ApiError(400, `Proposer preferences pool is not supported before Gloas fork=${fork}`);
}

return {data: chain.proposerPreferencesPool.getAll(slot), meta: {version: fork}};
},

async submitSignedProposerPreferences({signedProposerPreferences}) {
const failures: FailureList = [];

await Promise.all(
signedProposerPreferences.map(async (signed, i) => {
try {
await validateGossipProposerPreferences(chain, signed);

chain.proposerPreferencesPool.add(signed);
await network.publishProposerPreferences(signed);
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
version: ForkName.gloas,
data: signed,
});
} catch (e) {
const logCtx = {
slot: signed.message.proposalSlot,
validatorIndex: signed.message.validatorIndex,
dependentRoot: toRootHex(signed.message.dependentRoot),
};

if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
logger.debug("Ignoring known signed proposer preferences", logCtx);
return;
}

failures.push({index: i, message: (e as Error).message});
logger.verbose(`Error on submitSignedProposerPreferences [${i}]`, logCtx, e as Error);
if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
}
}
})
);

if (failures.length > 0) {
throw new IndexedError("Error processing signed proposer preferences", failures);
}
},

async getPoolAttesterSlashingsV2() {
const fork = chain.config.getForkName(chain.clock.currentSlot);
return {data: chain.opPool.getAllAttesterSlashings(), meta: {version: fork}};
Expand Down
43 changes: 43 additions & 0 deletions packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import {
AttestationError,
AttestationErrorCode,
GossipAction,
ProposerPreferencesError,
ProposerPreferencesErrorCode,
SyncCommitteeError,
SyncCommitteeErrorCode,
} from "../../../chain/errors/index.js";
Expand All @@ -74,6 +76,7 @@ import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/prod
import {RegenCaller} from "../../../chain/regen/index.js";
import {CheckpointHex} from "../../../chain/stateCache/types.js";
import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
import {ZERO_HASH} from "../../../constants/index.js";
import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
Expand Down Expand Up @@ -1765,6 +1768,46 @@ export function getValidatorApi(
});
},

async submitProposerPreferences({signedProposerPreferences}) {
const failures: FailureList = [];

await Promise.all(
signedProposerPreferences.map(async (signed, i) => {
try {
await validateGossipProposerPreferences(chain, signed);

chain.proposerPreferencesPool.add(signed);
await network.publishProposerPreferences(signed);
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
version: config.getForkName(signed.message.proposalSlot),
data: signed,
});
} catch (e) {
const logCtx = {
slot: signed.message.proposalSlot,
validatorIndex: signed.message.validatorIndex,
dependentRoot: toRootHex(signed.message.dependentRoot),
};

if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
logger.debug("Ignoring known signed proposer preferences", logCtx);
return;
}

failures.push({index: i, message: (e as Error).message});
logger.verbose(`Error on submitProposerPreferences [${i}]`, logCtx, e as Error);
if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
}
}
})
);

if (failures.length > 0) {
throw new IndexedError("Error processing signed proposer preferences", failures);
}
},

async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
const fork = config.getForkName(slot);

Expand Down
Loading
Loading