@lodestar/beacon-node 1.45.0-dev.e10fb5e991 → 1.45.0-dev.e6c77a6488
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/index.d.ts +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +6 -42
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +48 -5
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +13 -3
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +48 -4
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +2 -2
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +2 -2
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/proof/index.d.ts +1 -1
- package/lib/api/impl/proof/index.d.ts.map +1 -1
- package/lib/api/impl/proof/index.js +2 -2
- package/lib/api/impl/proof/index.js.map +1 -1
- package/lib/api/impl/utils.d.ts +23 -0
- package/lib/api/impl/utils.d.ts.map +1 -1
- package/lib/api/impl/utils.js +44 -1
- package/lib/api/impl/utils.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +0 -14
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +53 -58
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +1 -4
- package/lib/chain/blocks/importBlock.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/validation/executionPayloadBid.js +11 -10
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +3 -2
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/network/core/metrics.d.ts +65 -0
- package/lib/network/core/metrics.d.ts.map +1 -1
- package/lib/network/core/metrics.js +94 -0
- package/lib/network/core/metrics.js.map +1 -1
- package/lib/network/discv5/utils.d.ts.map +1 -1
- package/lib/network/discv5/utils.js +12 -3
- package/lib/network/discv5/utils.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +7 -0
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +2 -1
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +765 -3
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +23 -3
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/peers/peerManager.d.ts.map +1 -1
- package/lib/network/peers/peerManager.js +10 -0
- package/lib/network/peers/peerManager.js.map +1 -1
- package/lib/network/peers/score/score.d.ts +3 -1
- package/lib/network/peers/score/score.d.ts.map +1 -1
- package/lib/network/peers/score/score.js +6 -1
- package/lib/network/peers/score/score.js.map +1 -1
- package/lib/network/peers/score/store.d.ts.map +1 -1
- package/lib/network/peers/score/store.js +6 -1
- package/lib/network/peers/score/store.js.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.js +14 -0
- package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +6 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/package.json +18 -18
- package/src/api/impl/beacon/index.ts +1 -1
- package/src/api/impl/beacon/pool/index.ts +5 -52
- package/src/api/impl/beacon/state/index.ts +55 -3
- package/src/api/impl/beacon/state/utils.ts +68 -4
- package/src/api/impl/debug/index.ts +3 -2
- package/src/api/impl/lodestar/index.ts +2 -2
- package/src/api/impl/proof/index.ts +2 -2
- package/src/api/impl/utils.ts +51 -1
- package/src/api/impl/validator/index.ts +61 -63
- package/src/chain/blocks/importBlock.ts +1 -6
- package/src/chain/produceBlock/produceBlockBody.ts +9 -17
- package/src/chain/validation/executionPayloadBid.ts +14 -9
- package/src/chain/validation/proposerPreferences.ts +3 -2
- package/src/network/core/metrics.ts +94 -0
- package/src/network/discv5/utils.ts +11 -3
- package/src/network/gossip/encoding.ts +7 -0
- package/src/network/gossip/gossipsub.ts +2 -1
- package/src/network/gossip/topic.ts +28 -4
- package/src/network/peers/peerManager.ts +11 -0
- package/src/network/peers/score/score.ts +8 -1
- package/src/network/peers/score/store.ts +8 -1
- package/src/network/peers/utils/prioritizePeers.ts +15 -0
- package/src/network/processor/gossipHandlers.ts +6 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/impl/proof/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AACrC,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAEvC,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,EAChB,EAAC,KAAK,EAAE,MAAM,EAAC,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/impl/proof/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AACrC,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAEvC,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,EAChB,EAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAC,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC,GAC1E,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAwC5C"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ProofType, createProof } from "@chainsafe/persistent-merkle-tree";
|
|
2
2
|
import { getBlockResponse } from "../beacon/blocks/utils.js";
|
|
3
3
|
import { getStateResponseWithRegen } from "../beacon/state/utils.js";
|
|
4
|
-
export function getProofApi(opts, { chain, config }) {
|
|
4
|
+
export function getProofApi(opts, { chain, config, sync }) {
|
|
5
5
|
// It's currently possible to request gigantic proofs (eg: a proof of the entire beacon state)
|
|
6
6
|
// We want some some sort of resistance against this DoS vector.
|
|
7
7
|
const maxGindicesInProof = opts.maxGindicesInProof ?? 512;
|
|
@@ -11,7 +11,7 @@ export function getProofApi(opts, { chain, config }) {
|
|
|
11
11
|
if (descriptor.length / 2 > maxGindicesInProof) {
|
|
12
12
|
throw new Error("Requested proof is too large.");
|
|
13
13
|
}
|
|
14
|
-
const res = await getStateResponseWithRegen(chain, stateId);
|
|
14
|
+
const res = await getStateResponseWithRegen(chain, sync, stateId);
|
|
15
15
|
const state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
|
|
16
16
|
return {
|
|
17
17
|
data: state.createMultiProof(descriptor),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/impl/proof/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,WAAW,EAAC,MAAM,mCAAmC,CAAC;AAI5F,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAGnE,MAAM,UAAU,WAAW,CACzB,IAAgB,EAChB,EAAC,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/impl/proof/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,SAAS,EAAE,WAAW,EAAC,MAAM,mCAAmC,CAAC;AAI5F,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAC,yBAAyB,EAAC,MAAM,0BAA0B,CAAC;AAGnE,MAAM,UAAU,WAAW,CACzB,IAAgB,EAChB,EAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAuD,EAC/B;IAC5C,8FAA8F;IAC9F,gEAAgE;IAChE,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,IAAI,GAAG,CAAC;IAE1D,OAAO;QACL,KAAK,CAAC,aAAa,CAAC,EAAC,OAAO,EAAE,UAAU,EAAC,EAAE;YACzC,kEAAkE;YAClE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,GAAG,kBAAkB,EAAE,CAAC;gBAC/C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,yBAAyB,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YAElE,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;YAE3G,OAAO;gBACL,IAAI,EAAE,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC;gBACxC,IAAI,EAAE,EAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,EAAC;aAChD,CAAC;QAAA,CACH;QACD,KAAK,CAAC,aAAa,CAAC,EAAC,OAAO,EAAE,UAAU,EAAC,EAAE;YACzC,kEAAkE;YAClE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,GAAG,kBAAkB,EAAE,CAAC;gBAC/C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACnD,CAAC;YAED,MAAM,EAAC,KAAK,EAAC,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAEvD,4GAA4G;YAC5G,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;YAEjG,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,EAAE,EAAC,IAAI,EAAE,SAAS,CAAC,YAAY,EAAE,UAAU,EAAC,CAAC,CAAC;YAEjF,OAAO;gBACL,IAAI,EAAE,KAA0B;gBAChC,IAAI,EAAE,EAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC;aACxD,CAAC;QAAA,CACH;KACF,CAAC;AAAA,CACH"}
|
package/lib/api/impl/utils.d.ts
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
import type { IBeaconChain } from "../../chain/index.js";
|
|
2
|
+
import { SyncState } from "../../sync/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
5
|
+
* the network sync state.
|
|
6
|
+
*
|
|
7
|
+
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
8
|
+
* finalized head.
|
|
9
|
+
*
|
|
10
|
+
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
11
|
+
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
12
|
+
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
13
|
+
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
14
|
+
* caches are better.
|
|
15
|
+
*/
|
|
16
|
+
export declare const SYNC_TOLERANCE_EPOCHS = 1;
|
|
17
|
+
/**
|
|
18
|
+
* Reject any request while the node is syncing. Used by endpoints that must not serve while the
|
|
19
|
+
* node is behind — validator duties, and beacon state lookups whose regen could otherwise walk
|
|
20
|
+
* back past the block-root window (`SLOTS_PER_HISTORICAL_ROOT`) and wedge the node. Throws
|
|
21
|
+
* {@link NodeIsSyncing} (503).
|
|
22
|
+
*/
|
|
23
|
+
export declare function notWhileSyncing(chain: IBeaconChain, syncState: SyncState): void;
|
|
1
24
|
/**
|
|
2
25
|
* Ensures that the array contains unique values, and throws an ApiError
|
|
3
26
|
* otherwise.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/api/impl/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/api/impl/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAG9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAwB/E;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAerF"}
|
package/lib/api/impl/utils.js
CHANGED
|
@@ -1,4 +1,47 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SLOTS_PER_EPOCH } from "@lodestar/params";
|
|
2
|
+
import { SyncState } from "../../sync/index.js";
|
|
3
|
+
import { ApiError, NodeIsSyncing } from "./errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
6
|
+
* the network sync state.
|
|
7
|
+
*
|
|
8
|
+
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
9
|
+
* finalized head.
|
|
10
|
+
*
|
|
11
|
+
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
12
|
+
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
13
|
+
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
14
|
+
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
15
|
+
* caches are better.
|
|
16
|
+
*/
|
|
17
|
+
export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
18
|
+
/**
|
|
19
|
+
* Reject any request while the node is syncing. Used by endpoints that must not serve while the
|
|
20
|
+
* node is behind — validator duties, and beacon state lookups whose regen could otherwise walk
|
|
21
|
+
* back past the block-root window (`SLOTS_PER_HISTORICAL_ROOT`) and wedge the node. Throws
|
|
22
|
+
* {@link NodeIsSyncing} (503).
|
|
23
|
+
*/
|
|
24
|
+
export function notWhileSyncing(chain, syncState) {
|
|
25
|
+
// Consider node synced before or close to genesis
|
|
26
|
+
if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
switch (syncState) {
|
|
30
|
+
case SyncState.SyncingFinalized:
|
|
31
|
+
case SyncState.SyncingHead: {
|
|
32
|
+
const currentSlot = chain.clock.currentSlot;
|
|
33
|
+
const headSlot = chain.forkChoice.getHead().slot;
|
|
34
|
+
if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
|
|
35
|
+
throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
case SyncState.Synced:
|
|
40
|
+
return;
|
|
41
|
+
case SyncState.Stalled:
|
|
42
|
+
throw new NodeIsSyncing("waiting for peers");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
2
45
|
/**
|
|
3
46
|
* Ensures that the array contains unique values, and throws an ApiError
|
|
4
47
|
* otherwise.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/api/impl/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/api/impl/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAC,QAAQ,EAAE,aAAa,EAAC,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAmB,EAAE,SAAoB,EAAQ;IAC/E,kDAAkD;IAClD,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,eAAe,EAAE,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,SAAS,CAAC,gBAAgB,CAAC;QAChC,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC;YAC3B,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;YAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC;YACjD,IAAI,WAAW,GAAG,QAAQ,GAAG,qBAAqB,GAAG,eAAe,EAAE,CAAC;gBACrE,MAAM,IAAI,aAAa,CAAC,YAAY,QAAQ,gBAAgB,WAAW,EAAE,CAAC,CAAC;YAC7E,CAAC;YAED,OAAO;QACT,CAAC;QAED,KAAK,SAAS,CAAC,MAAM;YACnB,OAAO;QAET,KAAK,SAAS,CAAC,OAAO;YACpB,MAAM,IAAI,aAAa,CAAC,mBAAmB,CAAC,CAAC;IACjD,CAAC;AAAA,CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA4B,EAAE,OAAe,EAAQ;IACrF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,qBAAgC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrF,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3E,OAAO,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,qBAAqB,CAAC;IAAA,CAC9B,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,cAAc,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,GAAG,OAAO,KAAK,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;AAAA,CACF"}
|
|
@@ -3,20 +3,6 @@ import { ApplicationMethods } from "@lodestar/api/server";
|
|
|
3
3
|
import { ProducedBlockSource } from "@lodestar/types";
|
|
4
4
|
import { ApiOptions } from "../../options.js";
|
|
5
5
|
import { ApiModules } from "../types.js";
|
|
6
|
-
/**
|
|
7
|
-
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
8
|
-
* the network sync state.
|
|
9
|
-
*
|
|
10
|
-
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
11
|
-
* finalized head.
|
|
12
|
-
*
|
|
13
|
-
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
14
|
-
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
15
|
-
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
16
|
-
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
17
|
-
* caches are better.
|
|
18
|
-
*/
|
|
19
|
-
export declare const SYNC_TOLERANCE_EPOCHS = 1;
|
|
20
6
|
/**
|
|
21
7
|
* Engine block selection reasons tracked in metrics / logs
|
|
22
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/impl/validator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AACrC,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AA+BxD,OAAO,EAQL,mBAAmB,EAUpB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/impl/validator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AACrC,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AA+BxD,OAAO,EAQL,mBAAmB,EAUpB,MAAM,iBAAiB,CAAC;AAsCzB,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAgCvC;;GAEG;AACH,oBAAY,0BAA0B;IACpC,eAAe,qBAAqB;IACpC,YAAY,kBAAkB;IAC9B,cAAc,oBAAoB;IAClC,cAAc,oBAAoB;IAClC,YAAY,mBAAmB;IAC/B,iBAAiB,uBAAuB;IACxC,UAAU,gBAAgB;IAC1B,eAAe,qBAAqB;CACrC;AAED;;GAEG;AACH,oBAAY,2BAA2B;IACrC,cAAc,oBAAoB;IAClC,WAAW,iBAAiB;IAC5B,aAAa,mBAAmB;IAChC,UAAU,gBAAgB;IAC1B,gBAAgB,sBAAsB;CACvC;AAED,MAAM,MAAM,oBAAoB,GAC5B;IACE,MAAM,EAAE,mBAAmB,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,0BAA0B,CAAC;CACpC,GACD;IACE,MAAM,EAAE,mBAAmB,CAAC,OAAO,CAAC;IACpC,MAAM,EAAE,2BAA2B,CAAC;CACrC,CAAC;AAEN;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,EAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAC,EAAE,UAAU,GAC1D,kBAAkB,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAsnDhD"}
|
|
@@ -6,38 +6,25 @@ import { ProducedBlockSource, getValidatorStatus, ssz, } from "@lodestar/types";
|
|
|
6
6
|
import { TimeoutError, defer, formatWeiToEth, fromHex, prettyWeiToEth, resolveOrRacePromises, toHex, toRootHex, } from "@lodestar/utils";
|
|
7
7
|
import { MAX_BUILDER_BOOST_FACTOR } from "@lodestar/validator";
|
|
8
8
|
import { BlockInputSource } from "../../../chain/blocks/blockInput/types.js";
|
|
9
|
-
import { AttestationError, AttestationErrorCode, GossipAction, SyncCommitteeError, SyncCommitteeErrorCode, } from "../../../chain/errors/index.js";
|
|
9
|
+
import { AttestationError, AttestationErrorCode, GossipAction, ProposerPreferencesError, ProposerPreferencesErrorCode, SyncCommitteeError, SyncCommitteeErrorCode, } from "../../../chain/errors/index.js";
|
|
10
10
|
import { ChainEvent } from "../../../chain/index.js";
|
|
11
11
|
import { PREPARE_NEXT_SLOT_BPS } from "../../../chain/prepareNextSlot.js";
|
|
12
12
|
import { BlockType } from "../../../chain/produceBlock/index.js";
|
|
13
13
|
import { RegenCaller } from "../../../chain/regen/index.js";
|
|
14
14
|
import { validateApiAggregateAndProof } from "../../../chain/validation/index.js";
|
|
15
|
+
import { validateGossipProposerPreferences } from "../../../chain/validation/proposerPreferences.js";
|
|
15
16
|
import { validateSyncCommitteeGossipContributionAndProof } from "../../../chain/validation/syncCommitteeContributionAndProof.js";
|
|
16
17
|
import { ZERO_HASH } from "../../../constants/index.js";
|
|
17
18
|
import { BuilderStatus, NoBidReceived } from "../../../execution/builder/http.js";
|
|
18
19
|
import { validateGossipFnRetryUnknownRoot } from "../../../network/processor/gossipHandlers.js";
|
|
19
|
-
import { SyncState } from "../../../sync/index.js";
|
|
20
20
|
import { callInNextEventLoop } from "../../../util/eventLoop.js";
|
|
21
21
|
import { isOptimisticBlock } from "../../../util/forkChoice.js";
|
|
22
22
|
import { getBlockGraffiti, toGraffitiBytes } from "../../../util/graffiti.js";
|
|
23
23
|
import { getLodestarClientVersion } from "../../../util/metadata.js";
|
|
24
24
|
import { getStateResponseWithRegen } from "../beacon/state/utils.js";
|
|
25
25
|
import { ApiError, IndexedError, NodeIsSyncing, OnlySupportedByDVT } from "../errors.js";
|
|
26
|
+
import { notWhileSyncing } from "../utils.js";
|
|
26
27
|
import { computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource } from "./utils.js";
|
|
27
|
-
/**
|
|
28
|
-
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
29
|
-
* the network sync state.
|
|
30
|
-
*
|
|
31
|
-
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
32
|
-
* finalized head.
|
|
33
|
-
*
|
|
34
|
-
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
35
|
-
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
36
|
-
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
37
|
-
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
38
|
-
* caches are better.
|
|
39
|
-
*/
|
|
40
|
-
export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
41
28
|
/**
|
|
42
29
|
* Cutoff time to wait from start of the slot for execution and builder block production apis to resolve.
|
|
43
30
|
* Post this time, race execution and builder to pick whatever resolves first.
|
|
@@ -221,31 +208,6 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
221
208
|
}
|
|
222
209
|
return null;
|
|
223
210
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Reject any request while the node is syncing
|
|
226
|
-
*/
|
|
227
|
-
function notWhileSyncing() {
|
|
228
|
-
// Consider node synced before or close to genesis
|
|
229
|
-
if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
const syncState = sync.state;
|
|
233
|
-
switch (syncState) {
|
|
234
|
-
case SyncState.SyncingFinalized:
|
|
235
|
-
case SyncState.SyncingHead: {
|
|
236
|
-
const currentSlot = chain.clock.currentSlot;
|
|
237
|
-
const headSlot = chain.forkChoice.getHead().slot;
|
|
238
|
-
if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
|
|
239
|
-
throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
|
|
240
|
-
}
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
case SyncState.Synced:
|
|
244
|
-
return;
|
|
245
|
-
case SyncState.Stalled:
|
|
246
|
-
throw new NodeIsSyncing("waiting for peers");
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
211
|
/**
|
|
250
212
|
* Post merge, the CL and EL could be out of step in the sync, and could result in
|
|
251
213
|
* Syncing status of the chain head. To be precise:
|
|
@@ -402,7 +364,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
402
364
|
async function produceEngineOrBuilderBlock(slot, randaoReveal, graffiti,
|
|
403
365
|
// TODO deneb: skip randao verification
|
|
404
366
|
_skipRandaoVerification, builderBoostFactor, { feeRecipient, builderSelection, strictFeeRecipientCheck } = {}) {
|
|
405
|
-
notWhileSyncing();
|
|
367
|
+
notWhileSyncing(chain, sync.state);
|
|
406
368
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
407
369
|
const parentBlock = chain.getProposerHead(slot);
|
|
408
370
|
const { blockRoot: parentBlockRootHex, slot: parentSlot } = parentBlock;
|
|
@@ -675,7 +637,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
675
637
|
if (!isForkPostGloas(fork)) {
|
|
676
638
|
throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
|
|
677
639
|
}
|
|
678
|
-
notWhileSyncing();
|
|
640
|
+
notWhileSyncing(chain, sync.state);
|
|
679
641
|
await waitForSlot(slot);
|
|
680
642
|
const parentBlock = chain.getProposerHead(slot);
|
|
681
643
|
const { blockRoot: parentBlockRootHex, slot: parentSlot } = parentBlock;
|
|
@@ -776,7 +738,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
776
738
|
};
|
|
777
739
|
},
|
|
778
740
|
async produceAttestationData({ committeeIndex, slot }) {
|
|
779
|
-
notWhileSyncing();
|
|
741
|
+
notWhileSyncing(chain, sync.state);
|
|
780
742
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
781
743
|
// This needs a state in the same epoch as `slot` such that state.currentJustifiedCheckpoint is correct.
|
|
782
744
|
// Note: This may trigger an epoch transition if there skipped slots at the beginning of the epoch.
|
|
@@ -847,12 +809,12 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
847
809
|
if (!isForkPostGloas(fork)) {
|
|
848
810
|
throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
|
|
849
811
|
}
|
|
850
|
-
notWhileSyncing();
|
|
812
|
+
notWhileSyncing(chain, sync.state);
|
|
851
813
|
await waitForSlot(slot);
|
|
852
814
|
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
853
815
|
if (!block) {
|
|
854
|
-
// No block is seen at slot. Return
|
|
855
|
-
|
|
816
|
+
// No canonical block is seen at slot. Return 204 so vc can skip casting payload attestation.
|
|
817
|
+
return { data: undefined, meta: { version: fork }, status: 204 };
|
|
856
818
|
}
|
|
857
819
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
858
820
|
// Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
|
|
@@ -916,7 +878,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
916
878
|
return { data: contribution };
|
|
917
879
|
},
|
|
918
880
|
async getProposerDuties({ epoch }, _context, opts) {
|
|
919
|
-
notWhileSyncing();
|
|
881
|
+
notWhileSyncing(chain, sync.state);
|
|
920
882
|
const currentEpoch = currentEpochWithDisparity();
|
|
921
883
|
const nextEpoch = currentEpoch + 1;
|
|
922
884
|
const startSlot = computeStartSlotAtEpoch(epoch);
|
|
@@ -958,7 +920,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
958
920
|
state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
|
|
959
921
|
}
|
|
960
922
|
else {
|
|
961
|
-
const res = await getStateResponseWithRegen(chain, startSlot);
|
|
923
|
+
const res = await getStateResponseWithRegen(chain, sync, startSlot);
|
|
962
924
|
state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
|
|
963
925
|
if (state.epoch !== epoch) {
|
|
964
926
|
throw Error(`Loaded state epoch ${state.epoch} does not match requested epoch ${epoch}`);
|
|
@@ -1034,7 +996,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1034
996
|
return this.getProposerDuties(args, context, { v2: true });
|
|
1035
997
|
},
|
|
1036
998
|
async getAttesterDuties({ epoch, indices }) {
|
|
1037
|
-
notWhileSyncing();
|
|
999
|
+
notWhileSyncing(chain, sync.state);
|
|
1038
1000
|
if (indices.length === 0) {
|
|
1039
1001
|
throw new ApiError(400, "No validator to get attester duties");
|
|
1040
1002
|
}
|
|
@@ -1082,7 +1044,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1082
1044
|
};
|
|
1083
1045
|
},
|
|
1084
1046
|
async getPtcDuties({ epoch, indices }) {
|
|
1085
|
-
notWhileSyncing();
|
|
1047
|
+
notWhileSyncing(chain, sync.state);
|
|
1086
1048
|
if (indices.length === 0) {
|
|
1087
1049
|
throw new ApiError(400, "No validator to get PTC duties");
|
|
1088
1050
|
}
|
|
@@ -1135,7 +1097,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1135
1097
|
* @param validatorIndices an array of the validator indices for which to obtain the duties.
|
|
1136
1098
|
*/
|
|
1137
1099
|
async getSyncCommitteeDuties({ epoch, indices }) {
|
|
1138
|
-
notWhileSyncing();
|
|
1100
|
+
notWhileSyncing(chain, sync.state);
|
|
1139
1101
|
if (indices.length === 0) {
|
|
1140
1102
|
throw new ApiError(400, "No validator to get attester duties");
|
|
1141
1103
|
}
|
|
@@ -1175,7 +1137,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1175
1137
|
};
|
|
1176
1138
|
},
|
|
1177
1139
|
async getAggregatedAttestationV2({ attestationDataRoot, slot, committeeIndex }) {
|
|
1178
|
-
notWhileSyncing();
|
|
1140
|
+
notWhileSyncing(chain, sync.state);
|
|
1179
1141
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1180
1142
|
const dataRootHex = toRootHex(attestationDataRoot);
|
|
1181
1143
|
const aggregate = chain.attestationPool.getAggregate(slot, dataRootHex, committeeIndex);
|
|
@@ -1189,7 +1151,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1189
1151
|
};
|
|
1190
1152
|
},
|
|
1191
1153
|
async publishAggregateAndProofsV2({ signedAggregateAndProofs }) {
|
|
1192
|
-
notWhileSyncing();
|
|
1154
|
+
notWhileSyncing(chain, sync.state);
|
|
1193
1155
|
const seenTimestampSec = Date.now() / 1000;
|
|
1194
1156
|
const failures = [];
|
|
1195
1157
|
const fork = chain.config.getForkName(chain.clock.currentSlot);
|
|
@@ -1235,7 +1197,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1235
1197
|
* https://github.com/ethereum/beacon-APIs/pull/137
|
|
1236
1198
|
*/
|
|
1237
1199
|
async publishContributionAndProofs({ contributionAndProofs }) {
|
|
1238
|
-
notWhileSyncing();
|
|
1200
|
+
notWhileSyncing(chain, sync.state);
|
|
1239
1201
|
const failures = [];
|
|
1240
1202
|
await Promise.all(contributionAndProofs.map(async (contributionAndProof, i) => {
|
|
1241
1203
|
try {
|
|
@@ -1268,7 +1230,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1268
1230
|
}
|
|
1269
1231
|
},
|
|
1270
1232
|
async prepareBeaconCommitteeSubnet({ subscriptions }) {
|
|
1271
|
-
notWhileSyncing();
|
|
1233
|
+
notWhileSyncing(chain, sync.state);
|
|
1272
1234
|
await network.prepareBeaconCommitteeSubnets(subscriptions.map(({ validatorIndex, slot, isAggregator, committeesAtSlot, committeeIndex }) => ({
|
|
1273
1235
|
validatorIndex: validatorIndex,
|
|
1274
1236
|
subnet: computeSubnetForCommitteesAtSlot(slot, committeesAtSlot, committeeIndex),
|
|
@@ -1295,7 +1257,7 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1295
1257
|
* https://github.com/ethereum/beacon-APIs/pull/136
|
|
1296
1258
|
*/
|
|
1297
1259
|
async prepareSyncCommitteeSubnets({ subscriptions }) {
|
|
1298
|
-
notWhileSyncing();
|
|
1260
|
+
notWhileSyncing(chain, sync.state);
|
|
1299
1261
|
// A `validatorIndex` can be in multiple subnets, so compute the CommitteeSubscription with double for loop
|
|
1300
1262
|
const subs = [];
|
|
1301
1263
|
for (const sub of subscriptions) {
|
|
@@ -1370,12 +1332,45 @@ export function getValidatorApi(opts, { chain, config, logger, metrics, network,
|
|
|
1370
1332
|
count: filteredRegistrations.length,
|
|
1371
1333
|
});
|
|
1372
1334
|
},
|
|
1335
|
+
async submitProposerPreferences({ signedProposerPreferences }) {
|
|
1336
|
+
const failures = [];
|
|
1337
|
+
await Promise.all(signedProposerPreferences.map(async (signed, i) => {
|
|
1338
|
+
try {
|
|
1339
|
+
await validateGossipProposerPreferences(chain, signed);
|
|
1340
|
+
chain.proposerPreferencesPool.add(signed);
|
|
1341
|
+
await network.publishProposerPreferences(signed);
|
|
1342
|
+
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
|
|
1343
|
+
version: config.getForkName(signed.message.proposalSlot),
|
|
1344
|
+
data: signed,
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
catch (e) {
|
|
1348
|
+
const logCtx = {
|
|
1349
|
+
slot: signed.message.proposalSlot,
|
|
1350
|
+
validatorIndex: signed.message.validatorIndex,
|
|
1351
|
+
dependentRoot: toRootHex(signed.message.dependentRoot),
|
|
1352
|
+
};
|
|
1353
|
+
if (e instanceof ProposerPreferencesError && e.type.code === ProposerPreferencesErrorCode.ALREADY_KNOWN) {
|
|
1354
|
+
logger.debug("Ignoring known signed proposer preferences", logCtx);
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
failures.push({ index: i, message: e.message });
|
|
1358
|
+
logger.verbose(`Error on submitProposerPreferences [${i}]`, logCtx, e);
|
|
1359
|
+
if (e instanceof ProposerPreferencesError && e.action === GossipAction.REJECT) {
|
|
1360
|
+
chain.persistInvalidSszValue(ssz.gloas.SignedProposerPreferences, signed, "api_reject");
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}));
|
|
1364
|
+
if (failures.length > 0) {
|
|
1365
|
+
throw new IndexedError("Error processing signed proposer preferences", failures);
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1373
1368
|
async getExecutionPayloadEnvelope({ slot, beaconBlockRoot }) {
|
|
1374
1369
|
const fork = config.getForkName(slot);
|
|
1375
1370
|
if (!isForkPostGloas(fork)) {
|
|
1376
1371
|
throw new ApiError(400, `getExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
|
|
1377
1372
|
}
|
|
1378
|
-
notWhileSyncing();
|
|
1373
|
+
notWhileSyncing(chain, sync.state);
|
|
1379
1374
|
await waitForSlot(slot);
|
|
1380
1375
|
const blockRootHex = toRootHex(beaconBlockRoot);
|
|
1381
1376
|
const produceResult = chain.blockProductionCache.get(blockRootHex);
|