@lodestar/beacon-node 1.45.0-dev.f535421f29 → 1.45.0-dev.fadf0fbb1f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +2 -43
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +46 -3
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +11 -2
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +39 -3
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +46 -6
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.js +1 -1
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +1 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/interface.d.ts +1 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +2 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +3 -0
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/options.d.ts +1 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +4 -12
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +0 -1
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +0 -1
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +11 -3
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +759 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +2 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/util/graffiti.d.ts +15 -0
- package/lib/util/graffiti.d.ts.map +1 -1
- package/lib/util/graffiti.js +55 -0
- package/lib/util/graffiti.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/pool/index.ts +0 -53
- package/src/api/impl/beacon/state/index.ts +52 -1
- package/src/api/impl/beacon/state/utils.ts +57 -4
- package/src/api/impl/validator/index.ts +54 -5
- package/src/chain/blocks/importBlock.ts +1 -1
- package/src/chain/chain.ts +0 -3
- package/src/chain/interface.ts +0 -2
- package/src/chain/opPools/proposerPreferencesPool.ts +5 -1
- package/src/chain/options.ts +2 -0
- package/src/chain/produceBlock/produceBlockBody.ts +9 -17
- package/src/chain/seenCache/index.ts +0 -1
- package/src/chain/validation/proposerPreferences.ts +12 -3
- package/src/network/processor/gossipHandlers.ts +1 -2
- package/src/util/graffiti.ts +78 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +0 -16
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +0 -1
- package/lib/chain/seenCache/seenProposerPreferences.js +0 -26
- package/lib/chain/seenCache/seenProposerPreferences.js.map +0 -1
- package/src/chain/seenCache/seenProposerPreferences.ts +0 -32
|
@@ -65,6 +65,8 @@ import {
|
|
|
65
65
|
AttestationError,
|
|
66
66
|
AttestationErrorCode,
|
|
67
67
|
GossipAction,
|
|
68
|
+
ProposerPreferencesError,
|
|
69
|
+
ProposerPreferencesErrorCode,
|
|
68
70
|
SyncCommitteeError,
|
|
69
71
|
SyncCommitteeErrorCode,
|
|
70
72
|
} from "../../../chain/errors/index.js";
|
|
@@ -74,6 +76,7 @@ import {BlockType, ProduceFullDeneb, ProduceFullGloas} from "../../../chain/prod
|
|
|
74
76
|
import {RegenCaller} from "../../../chain/regen/index.js";
|
|
75
77
|
import {CheckpointHex} from "../../../chain/stateCache/types.js";
|
|
76
78
|
import {validateApiAggregateAndProof} from "../../../chain/validation/index.js";
|
|
79
|
+
import {validateGossipProposerPreferences} from "../../../chain/validation/proposerPreferences.js";
|
|
77
80
|
import {validateSyncCommitteeGossipContributionAndProof} from "../../../chain/validation/syncCommitteeContributionAndProof.js";
|
|
78
81
|
import {ZERO_HASH} from "../../../constants/index.js";
|
|
79
82
|
import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
|
|
@@ -82,7 +85,7 @@ import {CommitteeSubscription} from "../../../network/subnets/index.js";
|
|
|
82
85
|
import {SyncState} from "../../../sync/index.js";
|
|
83
86
|
import {callInNextEventLoop} from "../../../util/eventLoop.js";
|
|
84
87
|
import {isOptimisticBlock} from "../../../util/forkChoice.js";
|
|
85
|
-
import {
|
|
88
|
+
import {getBlockGraffiti, toGraffitiBytes} from "../../../util/graffiti.js";
|
|
86
89
|
import {getLodestarClientVersion} from "../../../util/metadata.js";
|
|
87
90
|
import {ApiOptions} from "../../options.js";
|
|
88
91
|
import {getStateResponseWithRegen} from "../beacon/state/utils.js";
|
|
@@ -611,7 +614,10 @@ export function getValidatorApi(
|
|
|
611
614
|
}
|
|
612
615
|
|
|
613
616
|
const graffitiBytes = toGraffitiBytes(
|
|
614
|
-
graffiti
|
|
617
|
+
getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
|
|
618
|
+
private: opts.private,
|
|
619
|
+
graffitiAppend: chain.opts.graffitiAppend,
|
|
620
|
+
})
|
|
615
621
|
);
|
|
616
622
|
|
|
617
623
|
const loggerContext = {
|
|
@@ -921,7 +927,10 @@ export function getValidatorApi(
|
|
|
921
927
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
922
928
|
|
|
923
929
|
const graffitiBytes = toGraffitiBytes(
|
|
924
|
-
graffiti
|
|
930
|
+
getBlockGraffiti(graffiti, getLodestarClientVersion(opts), chain.executionEngine.clientVersion, {
|
|
931
|
+
private: opts.private,
|
|
932
|
+
graffitiAppend: chain.opts.graffitiAppend,
|
|
933
|
+
})
|
|
925
934
|
);
|
|
926
935
|
|
|
927
936
|
// TODO GLOAS: respect builderSelection (MaxProfit, BuilderAlways, ExecutionAlways, etc.) to let
|
|
@@ -1112,8 +1121,8 @@ export function getValidatorApi(
|
|
|
1112
1121
|
|
|
1113
1122
|
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
1114
1123
|
if (!block) {
|
|
1115
|
-
// No block is seen at slot. Return
|
|
1116
|
-
|
|
1124
|
+
// No canonical block is seen at slot. Return 204 so vc can skip casting payload attestation.
|
|
1125
|
+
return {data: undefined, meta: {version: fork}, status: 204};
|
|
1117
1126
|
}
|
|
1118
1127
|
|
|
1119
1128
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
@@ -1765,6 +1774,46 @@ export function getValidatorApi(
|
|
|
1765
1774
|
});
|
|
1766
1775
|
},
|
|
1767
1776
|
|
|
1777
|
+
async submitProposerPreferences({signedProposerPreferences}) {
|
|
1778
|
+
const failures: FailureList = [];
|
|
1779
|
+
|
|
1780
|
+
await Promise.all(
|
|
1781
|
+
signedProposerPreferences.map(async (signed, i) => {
|
|
1782
|
+
try {
|
|
1783
|
+
await validateGossipProposerPreferences(chain, signed);
|
|
1784
|
+
|
|
1785
|
+
chain.proposerPreferencesPool.add(signed);
|
|
1786
|
+
await network.publishProposerPreferences(signed);
|
|
1787
|
+
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
|
|
1788
|
+
version: config.getForkName(signed.message.proposalSlot),
|
|
1789
|
+
data: signed,
|
|
1790
|
+
});
|
|
1791
|
+
} catch (e) {
|
|
1792
|
+
const logCtx = {
|
|
1793
|
+
slot: signed.message.proposalSlot,
|
|
1794
|
+
validatorIndex: signed.message.validatorIndex,
|
|
1795
|
+
dependentRoot: toRootHex(signed.message.dependentRoot),
|
|
1796
|
+
};
|
|
1797
|
+
|
|
1798
|
+
if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
|
|
1799
|
+
logger.debug("Ignoring known signed proposer preferences", logCtx);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
failures.push({index: i, message: (e as Error).message});
|
|
1804
|
+
logger.verbose(`Error on submitProposerPreferences [${i}]`, logCtx, e as Error);
|
|
1805
|
+
if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
|
|
1806
|
+
chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
})
|
|
1810
|
+
);
|
|
1811
|
+
|
|
1812
|
+
if (failures.length > 0) {
|
|
1813
|
+
throw new IndexedError("Error processing signed proposer preferences", failures);
|
|
1814
|
+
}
|
|
1815
|
+
},
|
|
1816
|
+
|
|
1768
1817
|
async getExecutionPayloadEnvelope({slot, beaconBlockRoot}) {
|
|
1769
1818
|
const fork = config.getForkName(slot);
|
|
1770
1819
|
|
|
@@ -426,7 +426,7 @@ export async function importBlock(
|
|
|
426
426
|
}
|
|
427
427
|
} catch (e) {
|
|
428
428
|
if (isStartSlotOfEpoch(proposalSlot)) {
|
|
429
|
-
notOverrideFcuReason = NotReorgedReason.
|
|
429
|
+
notOverrideFcuReason = NotReorgedReason.AtEpochBoundary;
|
|
430
430
|
} else {
|
|
431
431
|
this.logger.warn("Unable to get beacon proposer. Do not override fcu.", {proposalSlot}, e as Error);
|
|
432
432
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -107,7 +107,6 @@ import {
|
|
|
107
107
|
SeenExecutionPayloadBids,
|
|
108
108
|
SeenPayloadAttesters,
|
|
109
109
|
SeenPayloadEnvelopeInput,
|
|
110
|
-
SeenProposerPreferences,
|
|
111
110
|
SeenSyncCommitteeMessages,
|
|
112
111
|
} from "./seenCache/index.js";
|
|
113
112
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -189,7 +188,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
189
188
|
readonly seenPayloadAttesters = new SeenPayloadAttesters();
|
|
190
189
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
191
190
|
readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
|
|
192
|
-
readonly seenProposerPreferences = new SeenProposerPreferences();
|
|
193
191
|
readonly seenBlockProposers = new SeenBlockProposers();
|
|
194
192
|
readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
|
|
195
193
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -1473,7 +1471,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1473
1471
|
this.payloadAttestationPool.prune(slot);
|
|
1474
1472
|
this.executionPayloadBidPool.prune(slot);
|
|
1475
1473
|
this.seenExecutionPayloadBids.prune(slot);
|
|
1476
|
-
this.seenProposerPreferences.prune(slot);
|
|
1477
1474
|
this.proposerPreferencesPool.prune(slot);
|
|
1478
1475
|
this.seenAttestationDatas.onSlot(slot);
|
|
1479
1476
|
this.reprocessController.onSlot(slot);
|
package/src/chain/interface.ts
CHANGED
|
@@ -61,7 +61,6 @@ import {
|
|
|
61
61
|
SeenContributionAndProof,
|
|
62
62
|
SeenExecutionPayloadBids,
|
|
63
63
|
SeenPayloadAttesters,
|
|
64
|
-
SeenProposerPreferences,
|
|
65
64
|
SeenSyncCommitteeMessages,
|
|
66
65
|
} from "./seenCache/index.js";
|
|
67
66
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -133,7 +132,6 @@ export interface IBeaconChain {
|
|
|
133
132
|
readonly seenPayloadAttesters: SeenPayloadAttesters;
|
|
134
133
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
135
134
|
readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
|
|
136
|
-
readonly seenProposerPreferences: SeenProposerPreferences;
|
|
137
135
|
readonly seenBlockProposers: SeenBlockProposers;
|
|
138
136
|
readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
|
139
137
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
1
|
+
import {RootHex, Slot, ValidatorIndex, gloas} from "@lodestar/types";
|
|
2
2
|
import {toRootHex} from "@lodestar/utils";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -22,6 +22,10 @@ export class ProposerPreferencesPool {
|
|
|
22
22
|
return this.bySlot.get(slot)?.get(dependentRootHex) ?? null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
isKnown(proposalSlot: Slot, dependentRoot: RootHex, validatorIndex: ValidatorIndex): boolean {
|
|
26
|
+
return this.get(proposalSlot, dependentRoot)?.message.validatorIndex === validatorIndex;
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
add(signed: gloas.SignedProposerPreferences): void {
|
|
26
30
|
const {proposalSlot, dependentRoot} = signed.message;
|
|
27
31
|
const rootHex = toRootHex(dependentRoot);
|
package/src/chain/options.ts
CHANGED
|
@@ -35,6 +35,7 @@ export type IChainOptions = BlockProcessOpts &
|
|
|
35
35
|
persistOrphanedBlocksDir?: string;
|
|
36
36
|
skipCreateStateCacheIfAvailable?: boolean;
|
|
37
37
|
suggestedFeeRecipient: string;
|
|
38
|
+
graffitiAppend?: boolean;
|
|
38
39
|
maxSkipSlots?: number;
|
|
39
40
|
/** Ensure blobs returned by the execution engine are valid */
|
|
40
41
|
sanityCheckExecutionEngineBlobs?: boolean;
|
|
@@ -107,6 +108,7 @@ export const defaultChainOptions: IChainOptions = {
|
|
|
107
108
|
computeUnrealized: true,
|
|
108
109
|
fastConfirmation: false,
|
|
109
110
|
suggestedFeeRecipient: defaultValidatorOptions.suggestedFeeRecipient,
|
|
111
|
+
graffitiAppend: true,
|
|
110
112
|
serveHistoricalState: false,
|
|
111
113
|
assertCorrectProgressiveBalances: false,
|
|
112
114
|
archiveStateEpochFrequency: 1024,
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
ValidatorIndex,
|
|
44
44
|
Wei,
|
|
45
45
|
altair,
|
|
46
|
+
bellatrix,
|
|
46
47
|
capella,
|
|
47
48
|
deneb,
|
|
48
49
|
electra,
|
|
@@ -830,28 +831,19 @@ export function getPayloadAttributesForSSE(
|
|
|
830
831
|
feeRecipient,
|
|
831
832
|
});
|
|
832
833
|
|
|
833
|
-
|
|
834
|
-
if (isForkPostGloas(fork)) {
|
|
835
|
-
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
|
|
836
|
-
toRootHex(parentBlockRoot),
|
|
837
|
-
toRootHex(parentBlockHash)
|
|
838
|
-
);
|
|
839
|
-
if (parentBlock?.executionPayloadBlockHash == null) {
|
|
840
|
-
throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
|
|
841
|
-
}
|
|
842
|
-
parentBlockNumber = parentBlock.executionPayloadNumber;
|
|
843
|
-
} else {
|
|
844
|
-
parentBlockNumber = prepareState.payloadBlockNumber;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
const ssePayloadAttributes: SSEPayloadAttributes = {
|
|
834
|
+
const ssePayloadAttributes = {
|
|
848
835
|
proposerIndex: prepareState.getBeaconProposer(prepareSlot),
|
|
849
836
|
proposalSlot: prepareSlot,
|
|
850
|
-
parentBlockNumber,
|
|
851
837
|
parentBlockRoot,
|
|
852
838
|
parentBlockHash,
|
|
853
839
|
payloadAttributes,
|
|
854
|
-
};
|
|
840
|
+
} as SSEPayloadAttributes;
|
|
841
|
+
|
|
842
|
+
if (!isForkPostGloas(fork)) {
|
|
843
|
+
// Removed in Gloas, builders can get the block number from the EL via the block hash if required
|
|
844
|
+
(ssePayloadAttributes as bellatrix.SSEPayloadAttributes).parentBlockNumber = prepareState.payloadBlockNumber;
|
|
845
|
+
}
|
|
846
|
+
|
|
855
847
|
return ssePayloadAttributes;
|
|
856
848
|
}
|
|
857
849
|
|
|
@@ -5,4 +5,3 @@ export {SeenContributionAndProof} from "./seenCommitteeContribution.js";
|
|
|
5
5
|
export {SeenExecutionPayloadBids} from "./seenExecutionPayloadBids.js";
|
|
6
6
|
export {SeenBlockInput} from "./seenGossipBlockInput.js";
|
|
7
7
|
export {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenPayloadEnvelopeInput.js";
|
|
8
|
-
export {SeenProposerPreferences} from "./seenProposerPreferences.js";
|
|
@@ -81,7 +81,7 @@ export async function validateGossipProposerPreferences(
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// [IGNORE] First valid message for (dependent_root, proposal_slot, validator_index).
|
|
84
|
-
if (chain.
|
|
84
|
+
if (chain.proposerPreferencesPool.isKnown(proposalSlot, dependentRootHex, validatorIndex)) {
|
|
85
85
|
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
86
86
|
code: ProposerPreferencesErrorCode.ALREADY_KNOWN,
|
|
87
87
|
proposalSlot,
|
|
@@ -105,6 +105,15 @@ export async function validateGossipProposerPreferences(
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
chain.
|
|
108
|
+
// Repeated check - deals with race-condition between preferences submissions
|
|
109
|
+
if (chain.proposerPreferencesPool.isKnown(proposalSlot, dependentRootHex, validatorIndex)) {
|
|
110
|
+
throw new ProposerPreferencesError(GossipAction.IGNORE, {
|
|
111
|
+
code: ProposerPreferencesErrorCode.ALREADY_KNOWN,
|
|
112
|
+
proposalSlot,
|
|
113
|
+
validatorIndex,
|
|
114
|
+
dependentRoot: dependentRootHex,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
chain.proposerPreferencesPool.add(signedProposerPreferences);
|
|
110
119
|
}
|
|
@@ -1273,9 +1273,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1273
1273
|
const signedProposerPreferences = sszDeserialize(topic, serializedData);
|
|
1274
1274
|
await validateGossipProposerPreferences(chain, signedProposerPreferences);
|
|
1275
1275
|
|
|
1276
|
-
chain.proposerPreferencesPool.add(signedProposerPreferences);
|
|
1277
1276
|
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
|
|
1278
|
-
version:
|
|
1277
|
+
version: config.getForkName(signedProposerPreferences.message.proposalSlot),
|
|
1279
1278
|
data: signedProposerPreferences,
|
|
1280
1279
|
});
|
|
1281
1280
|
},
|
package/src/util/graffiti.ts
CHANGED
|
@@ -37,3 +37,81 @@ export function getDefaultGraffiti(
|
|
|
37
37
|
// No EL client info available. We still want to include CL info albeit not spec compliant
|
|
38
38
|
return `${consensusClientVersion.code}${consensusClientVersion.commit.slice(0, 4)}`;
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
function appendLongestFittingSuffix(userGraffiti: string, suffixes: string[]): string {
|
|
42
|
+
const userGraffitiBytes = Buffer.byteLength(userGraffiti, "utf8");
|
|
43
|
+
if (userGraffitiBytes >= GRAFFITI_SIZE) {
|
|
44
|
+
return userGraffiti;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const availableBytes = GRAFFITI_SIZE - userGraffitiBytes;
|
|
48
|
+
|
|
49
|
+
for (const suffix of suffixes) {
|
|
50
|
+
if (Buffer.byteLength(suffix, "utf8") <= availableBytes) {
|
|
51
|
+
return `${userGraffiti}${suffix}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return userGraffiti;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Appends the richest available client watermark that fits after user graffiti.
|
|
60
|
+
*
|
|
61
|
+
* Tiers are:
|
|
62
|
+
* - full EL/CL watermark, e.g. " BU9b0eLS80c2"
|
|
63
|
+
* - EL/CL client codes, e.g. " BULS"
|
|
64
|
+
* - CL client code, e.g. " LS"
|
|
65
|
+
*/
|
|
66
|
+
export function appendClientInfoToGraffiti(
|
|
67
|
+
userGraffiti: string,
|
|
68
|
+
consensusClientVersion: ClientVersion,
|
|
69
|
+
executionClientVersion: ClientVersion | null | undefined,
|
|
70
|
+
opts: {private?: boolean} = {}
|
|
71
|
+
): string {
|
|
72
|
+
if (opts.private) {
|
|
73
|
+
return userGraffiti;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Graffiti supplied via the beacon API is decoded from a fixed 32-byte field (see
|
|
77
|
+
// fromGraffitiHex) and arrives right-padded with NUL bytes. Trim only trailing padding
|
|
78
|
+
// NULs; a NUL that appears in the middle of the string is data, not padding.
|
|
79
|
+
let end = userGraffiti.length;
|
|
80
|
+
while (end > 0 && userGraffiti.charCodeAt(end - 1) === 0) {
|
|
81
|
+
end--;
|
|
82
|
+
}
|
|
83
|
+
const graffiti = userGraffiti.slice(0, end);
|
|
84
|
+
|
|
85
|
+
const fullClientInfo = getDefaultGraffiti(consensusClientVersion, executionClientVersion, {private: false});
|
|
86
|
+
if (graffiti.length === 0) {
|
|
87
|
+
return fullClientInfo;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const suffixes =
|
|
91
|
+
executionClientVersion != null
|
|
92
|
+
? [
|
|
93
|
+
` ${fullClientInfo}`,
|
|
94
|
+
` ${executionClientVersion.code}${consensusClientVersion.code}`,
|
|
95
|
+
` ${consensusClientVersion.code}`,
|
|
96
|
+
]
|
|
97
|
+
: [` ${fullClientInfo}`, ` ${consensusClientVersion.code}`];
|
|
98
|
+
|
|
99
|
+
return appendLongestFittingSuffix(graffiti, suffixes);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getBlockGraffiti(
|
|
103
|
+
userGraffiti: string | undefined,
|
|
104
|
+
consensusClientVersion: ClientVersion,
|
|
105
|
+
executionClientVersion: ClientVersion | null | undefined,
|
|
106
|
+
opts: {private?: boolean; graffitiAppend?: boolean}
|
|
107
|
+
): string {
|
|
108
|
+
if (userGraffiti === undefined) {
|
|
109
|
+
return getDefaultGraffiti(consensusClientVersion, executionClientVersion, opts);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (opts.graffitiAppend === false) {
|
|
113
|
+
return userGraffiti;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return appendClientInfoToGraffiti(userGraffiti, consensusClientVersion, executionClientVersion, opts);
|
|
117
|
+
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { RootHex, Slot, ValidatorIndex } from "@lodestar/types";
|
|
2
|
-
/**
|
|
3
|
-
* Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
|
|
4
|
-
*/
|
|
5
|
-
export declare class SeenProposerPreferences {
|
|
6
|
-
private readonly validatorByDependentRootBySlot;
|
|
7
|
-
isKnown(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean;
|
|
8
|
-
add(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): void;
|
|
9
|
-
/**
|
|
10
|
-
* Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
|
|
11
|
-
* passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
|
|
12
|
-
* on each slot tick.
|
|
13
|
-
*/
|
|
14
|
-
prune(currentSlot: Slot): void;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=seenProposerPreferences.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"seenProposerPreferences.d.ts","sourceRoot":"","sources":["../../../src/chain/seenCache/seenProposerPreferences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAG9D;;GAEG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAE7C;IAEF,OAAO,CAAC,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAE3F;IAED,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,GAAG,IAAI,CAEpF;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,EAAE,IAAI,GAAG,IAAI,CAM7B;CACF"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { MapDef } from "@lodestar/utils";
|
|
2
|
-
/**
|
|
3
|
-
* Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
|
|
4
|
-
*/
|
|
5
|
-
export class SeenProposerPreferences {
|
|
6
|
-
validatorByDependentRootBySlot = new MapDef(() => new Map());
|
|
7
|
-
isKnown(dependentRoot, proposalSlot, validatorIndex) {
|
|
8
|
-
return this.validatorByDependentRootBySlot.get(proposalSlot)?.get(dependentRoot) === validatorIndex;
|
|
9
|
-
}
|
|
10
|
-
add(dependentRoot, proposalSlot, validatorIndex) {
|
|
11
|
-
this.validatorByDependentRootBySlot.getOrDefault(proposalSlot).set(dependentRoot, validatorIndex);
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
|
|
15
|
-
* passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
|
|
16
|
-
* on each slot tick.
|
|
17
|
-
*/
|
|
18
|
-
prune(currentSlot) {
|
|
19
|
-
for (const slot of this.validatorByDependentRootBySlot.keys()) {
|
|
20
|
-
if (slot < currentSlot) {
|
|
21
|
-
this.validatorByDependentRootBySlot.delete(slot);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=seenProposerPreferences.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"seenProposerPreferences.js","sourceRoot":"","sources":["../../../src/chain/seenCache/seenProposerPreferences.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAEvC;;GAEG;AACH,MAAM,OAAO,uBAAuB;IACjB,8BAA8B,GAAG,IAAI,MAAM,CAC1D,GAAG,EAAE,CAAC,IAAI,GAAG,EAA2B,CACzC,CAAC;IAEF,OAAO,CAAC,aAAsB,EAAE,YAAkB,EAAE,cAA8B,EAAW;QAC3F,OAAO,IAAI,CAAC,8BAA8B,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,aAAa,CAAC,KAAK,cAAc,CAAC;IAAA,CACrG;IAED,GAAG,CAAC,aAAsB,EAAE,YAAkB,EAAE,cAA8B,EAAQ;QACpF,IAAI,CAAC,8BAA8B,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAAA,CACnG;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAiB,EAAQ;QAC7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,8BAA8B,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9D,IAAI,IAAI,GAAG,WAAW,EAAE,CAAC;gBACvB,IAAI,CAAC,8BAA8B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;IAAA,CACF;CACF"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {RootHex, Slot, ValidatorIndex} from "@lodestar/types";
|
|
2
|
-
import {MapDef} from "@lodestar/utils";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Tracks signed proposer preferences we've already seen per (dependent_root, proposal_slot, validator_index).
|
|
6
|
-
*/
|
|
7
|
-
export class SeenProposerPreferences {
|
|
8
|
-
private readonly validatorByDependentRootBySlot = new MapDef<Slot, Map<RootHex, ValidatorIndex>>(
|
|
9
|
-
() => new Map<RootHex, ValidatorIndex>()
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
isKnown(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): boolean {
|
|
13
|
-
return this.validatorByDependentRootBySlot.get(proposalSlot)?.get(dependentRoot) === validatorIndex;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
add(dependentRoot: RootHex, proposalSlot: Slot, validatorIndex: ValidatorIndex): void {
|
|
17
|
-
this.validatorByDependentRootBySlot.getOrDefault(proposalSlot).set(dependentRoot, validatorIndex);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Entries are only load-bearing while `proposal_slot > current_slot`. Once the slot has
|
|
22
|
-
* passed the `[IGNORE] proposal_slot > current_slot` gossip rule takes over, so drop them
|
|
23
|
-
* on each slot tick.
|
|
24
|
-
*/
|
|
25
|
-
prune(currentSlot: Slot): void {
|
|
26
|
-
for (const slot of this.validatorByDependentRootBySlot.keys()) {
|
|
27
|
-
if (slot < currentSlot) {
|
|
28
|
-
this.validatorByDependentRootBySlot.delete(slot);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|