@lodestar/state-transition 1.43.0 → 1.44.0-dev.055b83cb3d
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/block/processDepositRequest.d.ts +3 -11
- package/lib/block/processDepositRequest.d.ts.map +1 -1
- package/lib/block/processDepositRequest.js +27 -35
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processParentExecutionPayload.d.ts.map +1 -1
- package/lib/block/processParentExecutionPayload.js +4 -3
- package/lib/block/processParentExecutionPayload.js.map +1 -1
- package/lib/cache/epochCache.d.ts.map +1 -1
- package/lib/cache/epochCache.js +10 -7
- package/lib/cache/epochCache.js.map +1 -1
- package/lib/lightClient/spec/index.d.ts +22 -0
- package/lib/lightClient/spec/index.d.ts.map +1 -0
- package/lib/lightClient/spec/index.js +58 -0
- package/lib/lightClient/spec/index.js.map +1 -0
- package/lib/lightClient/spec/isBetterUpdate.d.ts +23 -0
- package/lib/lightClient/spec/isBetterUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/isBetterUpdate.js +66 -0
- package/lib/lightClient/spec/isBetterUpdate.js.map +1 -0
- package/lib/lightClient/spec/processLightClientUpdate.d.ts +12 -0
- package/lib/lightClient/spec/processLightClientUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/processLightClientUpdate.js +80 -0
- package/lib/lightClient/spec/processLightClientUpdate.js.map +1 -0
- package/lib/lightClient/spec/store.d.ts +45 -0
- package/lib/lightClient/spec/store.d.ts.map +1 -0
- package/lib/lightClient/spec/store.js +56 -0
- package/lib/lightClient/spec/store.js.map +1 -0
- package/lib/lightClient/spec/utils.d.ts +47 -0
- package/lib/lightClient/spec/utils.d.ts.map +1 -0
- package/lib/lightClient/spec/utils.js +197 -0
- package/lib/lightClient/spec/utils.js.map +1 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.d.ts +4 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.d.ts.map +1 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.js +22 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.js.map +1 -0
- package/lib/lightClient/spec/validateLightClientUpdate.d.ts +5 -0
- package/lib/lightClient/spec/validateLightClientUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/validateLightClientUpdate.js +88 -0
- package/lib/lightClient/spec/validateLightClientUpdate.js.map +1 -0
- package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +35 -29
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +9 -3
- package/lib/stateView/beaconStateView.d.ts.map +1 -1
- package/lib/stateView/beaconStateView.js +23 -4
- package/lib/stateView/beaconStateView.js.map +1 -1
- package/lib/stateView/interface.d.ts +2 -1
- package/lib/stateView/interface.d.ts.map +1 -1
- package/lib/stateView/interface.js.map +1 -1
- package/lib/util/gloas.d.ts +14 -0
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +24 -0
- package/lib/util/gloas.js.map +1 -1
- package/lib/util/index.d.ts +1 -0
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +1 -0
- package/lib/util/index.js.map +1 -1
- package/lib/util/pendingDepositsLookup.d.ts +40 -0
- package/lib/util/pendingDepositsLookup.d.ts.map +1 -0
- package/lib/util/pendingDepositsLookup.js +84 -0
- package/lib/util/pendingDepositsLookup.js.map +1 -0
- package/lib/util/shuffling.d.ts +6 -5
- package/lib/util/shuffling.d.ts.map +1 -1
- package/lib/util/shuffling.js +13 -15
- package/lib/util/shuffling.js.map +1 -1
- package/package.json +12 -7
- package/src/block/processDepositRequest.ts +29 -47
- package/src/block/processParentExecutionPayload.ts +4 -3
- package/src/cache/epochCache.ts +10 -7
- package/src/lightClient/spec/index.ts +101 -0
- package/src/lightClient/spec/isBetterUpdate.ts +94 -0
- package/src/lightClient/spec/processLightClientUpdate.ts +119 -0
- package/src/lightClient/spec/store.ts +106 -0
- package/src/lightClient/spec/utils.ts +317 -0
- package/src/lightClient/spec/validateLightClientBootstrap.ts +39 -0
- package/src/lightClient/spec/validateLightClientUpdate.ts +145 -0
- package/src/slot/upgradeStateToGloas.ts +43 -45
- package/src/stateView/beaconStateView.ts +23 -4
- package/src/stateView/interface.ts +2 -1
- package/src/util/gloas.ts +28 -0
- package/src/util/index.ts +1 -0
- package/src/util/pendingDepositsLookup.ts +105 -0
- package/src/util/shuffling.ts +17 -15
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { toPubkeyHex } from "@lodestar/utils";
|
|
2
|
+
import { isValidDepositSignature } from "../block/processDeposit.js";
|
|
3
|
+
/**
|
|
4
|
+
* Mutable lookup for the pending-deposit sequence used by builder-routing logic.
|
|
5
|
+
*
|
|
6
|
+
* This is to implement the spec's `is_pending_validator(pending_deposits, pubkey)` lazily:
|
|
7
|
+
* deposits are grouped by pubkey without verifying signatures, and BLS verification is
|
|
8
|
+
* deferred until a builder deposit needs to know whether the same pubkey already has a
|
|
9
|
+
* valid pending validator deposit.
|
|
10
|
+
*
|
|
11
|
+
* Call `add()` whenever a deposit is appended to the represented sequence. A cached `true`
|
|
12
|
+
* result short-circuits all subsequent checks for that pubkey; a cached `false` records
|
|
13
|
+
* how many deposits were already verified, so appending a new deposit only verifies the
|
|
14
|
+
* newly-appended tail rather than re-running BLS on previously-invalid entries.
|
|
15
|
+
*/
|
|
16
|
+
export class PendingDepositsLookup {
|
|
17
|
+
depositsByPubkey;
|
|
18
|
+
validationCache;
|
|
19
|
+
constructor(depositsByPubkey, validationCache) {
|
|
20
|
+
this.depositsByPubkey = depositsByPubkey;
|
|
21
|
+
this.validationCache = validationCache;
|
|
22
|
+
}
|
|
23
|
+
/** Build an empty lookup for a sequence that will be populated incrementally. */
|
|
24
|
+
static buildEmpty() {
|
|
25
|
+
return new PendingDepositsLookup(new Map(), new Map());
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a pubkey -> pending-deposits lookup from `state.pendingDeposits`.
|
|
29
|
+
* No BLS work is done here; signature verification happens lazily in `hasPendingValidator`.
|
|
30
|
+
*/
|
|
31
|
+
static build(state) {
|
|
32
|
+
const lookup = PendingDepositsLookup.buildEmpty();
|
|
33
|
+
for (const pendingDeposit of state.pendingDeposits.getAllReadonly()) {
|
|
34
|
+
lookup.add(pendingDeposit);
|
|
35
|
+
}
|
|
36
|
+
return lookup;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Append a pending deposit to the represented sequence.
|
|
40
|
+
* Pass `pubkeyHex` if the caller has already computed it.
|
|
41
|
+
*/
|
|
42
|
+
add(pendingDeposit, pubkeyHex) {
|
|
43
|
+
const key = pubkeyHex ?? toPubkeyHex(pendingDeposit.pubkey);
|
|
44
|
+
const existing = this.depositsByPubkey.get(key);
|
|
45
|
+
if (existing) {
|
|
46
|
+
existing.push(pendingDeposit);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
this.depositsByPubkey.set(key, [pendingDeposit]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Returns true if any pending deposit for `pubkeyHex` has a valid BLS deposit signature.
|
|
54
|
+
* Memoizes the result in `validationCache` so repeated checks for the same pubkey
|
|
55
|
+
* within a block only verify deposits that have not already been checked.
|
|
56
|
+
*/
|
|
57
|
+
hasPendingValidator(config, pubkeyHex) {
|
|
58
|
+
const validation = this.validationCache.get(pubkeyHex);
|
|
59
|
+
if (validation?.hasValidSignature === true) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const deposits = this.depositsByPubkey.get(pubkeyHex);
|
|
63
|
+
if (deposits === undefined) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
// hasValidSignature is false or undefined; resume from the last validatedCount so
|
|
67
|
+
// previously-checked invalid deposits are not re-verified.
|
|
68
|
+
const startIndex = validation?.validatedCount ?? 0;
|
|
69
|
+
if (startIndex === deposits.length) {
|
|
70
|
+
// Nothing new to check; the cached false result still holds.
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
for (let i = startIndex; i < deposits.length; i++) {
|
|
74
|
+
const deposit = deposits[i];
|
|
75
|
+
if (isValidDepositSignature(config, deposit.pubkey, deposit.withdrawalCredentials, deposit.amount, deposit.signature)) {
|
|
76
|
+
this.validationCache.set(pubkeyHex, { hasValidSignature: true, validatedCount: i + 1 });
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
this.validationCache.set(pubkeyHex, { hasValidSignature: false, validatedCount: deposits.length });
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=pendingDepositsLookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pendingDepositsLookup.js","sourceRoot":"","sources":["../../src/util/pendingDepositsLookup.ts"],"names":[],"mappings":"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"}
|
package/lib/util/shuffling.d.ts
CHANGED
|
@@ -5,13 +5,14 @@ import { CachedBeaconStateAllForks } from "../cache/stateCache.js";
|
|
|
5
5
|
import { IBeaconStateView } from "../stateView/interface.js";
|
|
6
6
|
import { EpochShuffling } from "./epochShuffling.js";
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Block root that decided the proposer shuffling for `proposalEpoch` (keys that shuffling).
|
|
9
|
+
* Computed for the requested epoch, not `state.epoch`, so it is correct when `state` is one
|
|
10
|
+
* epoch off the requested epoch (e.g. serving next-epoch duties from the current state).
|
|
10
11
|
*
|
|
11
|
-
* Returns `null`
|
|
12
|
-
*
|
|
12
|
+
* Returns `null` when the genesis block decides its own shuffling (caller falls back to the
|
|
13
|
+
* genesis block root).
|
|
13
14
|
*/
|
|
14
|
-
export declare function proposerShufflingDecisionRoot(fork: ForkName, state: IBeaconStateView): Root | null;
|
|
15
|
+
export declare function proposerShufflingDecisionRoot(fork: ForkName, state: IBeaconStateView, proposalEpoch: Epoch): Root | null;
|
|
15
16
|
/**
|
|
16
17
|
* Returns the block root which decided the attester shuffling for the given `requestedEpoch`.
|
|
17
18
|
* This root can be used to key that attester shuffling.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shuffling.d.ts","sourceRoot":"","sources":["../../src/util/shuffling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAE,OAAO,EAAkC,MAAM,kBAAkB,CAAC;AACpF,OAAO,EACL,WAAW,EACX,cAAc,EACd,KAAK,EACL,kBAAkB,EAClB,IAAI,EACJ,IAAI,EACJ,cAAc,EAEf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,yBAAyB,EAAC,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAEnD
|
|
1
|
+
{"version":3,"file":"shuffling.d.ts","sourceRoot":"","sources":["../../src/util/shuffling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAE,OAAO,EAAkC,MAAM,kBAAkB,CAAC;AACpF,OAAO,EACL,WAAW,EACX,cAAc,EACd,KAAK,EACL,kBAAkB,EAClB,IAAI,EACJ,IAAI,EACJ,cAAc,EAEf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,yBAAyB,EAAC,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAEnD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,gBAAgB,EACvB,aAAa,EAAE,KAAK,GACnB,IAAI,GAAG,IAAI,CAMb;AAUD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,yBAAyB,EAAE,cAAc,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CAMlH;AAsCD,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,CAAC;IAChC,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,cAAc,EAC9B,yBAAyB,EAAE,cAAc,EAAE,GAC1C,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,CAyBnC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,WAAW,GACvB,kBAAkB,CAWpB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,EAAE,CA6BrH;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,cAAc,GAAG,WAAW,CAEjH;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,cAAc,EAAE,GACxB,WAAW,EAAE,CAoBf;AAED,oBAAY,kBAAkB;IAC5B,4BAA4B,iDAAiD;CAC9E;AAED,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,kBAAkB,CAAC,4BAA4B,CAAC;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,qBAAa,cAAe,SAAQ,aAAa,CAAC,kBAAkB,CAAC;CAAG"}
|
package/lib/util/shuffling.js
CHANGED
|
@@ -3,28 +3,26 @@ import { LodestarError } from "@lodestar/utils";
|
|
|
3
3
|
import { getBlockRootAtSlot } from "./blockRoot.js";
|
|
4
4
|
import { computeStartSlotAtEpoch } from "./epoch.js";
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Block root that decided the proposer shuffling for `proposalEpoch` (keys that shuffling).
|
|
7
|
+
* Computed for the requested epoch, not `state.epoch`, so it is correct when `state` is one
|
|
8
|
+
* epoch off the requested epoch (e.g. serving next-epoch duties from the current state).
|
|
8
9
|
*
|
|
9
|
-
* Returns `null`
|
|
10
|
-
*
|
|
10
|
+
* Returns `null` when the genesis block decides its own shuffling (caller falls back to the
|
|
11
|
+
* genesis block root).
|
|
11
12
|
*/
|
|
12
|
-
export function proposerShufflingDecisionRoot(fork, state) {
|
|
13
|
-
const decisionSlot = proposerShufflingDecisionSlot(fork,
|
|
13
|
+
export function proposerShufflingDecisionRoot(fork, state, proposalEpoch) {
|
|
14
|
+
const decisionSlot = proposerShufflingDecisionSlot(fork, proposalEpoch);
|
|
14
15
|
if (state.slot === decisionSlot) {
|
|
15
16
|
return null;
|
|
16
17
|
}
|
|
17
18
|
return state.getBlockRootAtSlot(decisionSlot);
|
|
18
19
|
}
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const epoch = isForkPostFulu(fork) ? state.epoch - 1 : state.epoch;
|
|
26
|
-
const startSlot = computeStartSlotAtEpoch(epoch);
|
|
27
|
-
return Math.max(startSlot - 1, 0);
|
|
20
|
+
/** Slot whose block root keys the proposer shuffling for `proposalEpoch`. */
|
|
21
|
+
function proposerShufflingDecisionSlot(fork, proposalEpoch) {
|
|
22
|
+
// Post-Fulu the shuffling is decided one epoch earlier (deterministic proposer lookahead,
|
|
23
|
+
// MIN_SEED_LOOKAHEAD = 1); pre-Fulu it is the last block before `proposalEpoch`.
|
|
24
|
+
const decisionEpoch = isForkPostFulu(fork) ? proposalEpoch - 1 : proposalEpoch;
|
|
25
|
+
return Math.max(computeStartSlotAtEpoch(decisionEpoch) - 1, 0);
|
|
28
26
|
}
|
|
29
27
|
/**
|
|
30
28
|
* Returns the block root which decided the attester shuffling for the given `requestedEpoch`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shuffling.js","sourceRoot":"","sources":["../../src/util/shuffling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,OAAO,EAAE,eAAe,EAAE,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAWpF,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAG9C,OAAO,EAAC,kBAAkB,EAAC,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAC,uBAAuB,EAAC,MAAM,YAAY,CAAC;AAGnD
|
|
1
|
+
{"version":3,"file":"shuffling.js","sourceRoot":"","sources":["../../src/util/shuffling.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/ChainSafe/lodestar/issues"
|
|
13
13
|
},
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.44.0-dev.055b83cb3d",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"types": "./lib/slot/index.d.ts",
|
|
35
35
|
"import": "./lib/slot/index.js"
|
|
36
36
|
},
|
|
37
|
+
"./light-client": {
|
|
38
|
+
"bun": "./src/lightClient/spec/index.ts",
|
|
39
|
+
"types": "./lib/lightClient/spec/index.d.ts",
|
|
40
|
+
"import": "./lib/lightClient/spec/index.js"
|
|
41
|
+
},
|
|
37
42
|
"./test-utils": {
|
|
38
43
|
"bun": "./src/testUtils/index.ts",
|
|
39
44
|
"types": "./lib/testUtils/index.d.ts",
|
|
@@ -67,14 +72,14 @@
|
|
|
67
72
|
"@chainsafe/pubkey-index-map": "^3.0.0",
|
|
68
73
|
"@chainsafe/ssz": "^1.4.0",
|
|
69
74
|
"@chainsafe/swap-or-not-shuffle": "^1.2.1",
|
|
70
|
-
"@lodestar/config": "^1.
|
|
71
|
-
"@lodestar/params": "^1.
|
|
72
|
-
"@lodestar/types": "^1.
|
|
73
|
-
"@lodestar/utils": "^1.
|
|
75
|
+
"@lodestar/config": "^1.44.0-dev.055b83cb3d",
|
|
76
|
+
"@lodestar/params": "^1.44.0-dev.055b83cb3d",
|
|
77
|
+
"@lodestar/types": "^1.44.0-dev.055b83cb3d",
|
|
78
|
+
"@lodestar/utils": "^1.44.0-dev.055b83cb3d",
|
|
74
79
|
"@vekexasia/bigint-buffer2": "^1.1.1"
|
|
75
80
|
},
|
|
76
81
|
"devDependencies": {
|
|
77
|
-
"@lodestar/api": "^1.
|
|
82
|
+
"@lodestar/api": "^1.44.0-dev.055b83cb3d"
|
|
78
83
|
},
|
|
79
84
|
"keywords": [
|
|
80
85
|
"ethereum",
|
|
@@ -82,5 +87,5 @@
|
|
|
82
87
|
"beacon",
|
|
83
88
|
"blockchain"
|
|
84
89
|
],
|
|
85
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "4038b49cad701b00bf8287e48f87df9fcb5542a0"
|
|
86
91
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {BeaconConfig} from "@lodestar/config";
|
|
2
1
|
import {FAR_FUTURE_EPOCH, ForkSeq, UNSET_DEPOSIT_REQUESTS_START_INDEX} from "@lodestar/params";
|
|
3
|
-
import {BLSPubkey, Bytes32,
|
|
2
|
+
import {BLSPubkey, Bytes32, UintNum64, electra, ssz} from "@lodestar/types";
|
|
4
3
|
import {toPubkeyHex} from "@lodestar/utils";
|
|
5
4
|
import {CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
|
|
6
5
|
import {findBuilderIndexByPubkey, isBuilderWithdrawalCredential} from "../util/gloas.js";
|
|
7
6
|
import {computeEpochAtSlot, isValidatorKnown} from "../util/index.js";
|
|
7
|
+
import {PendingDepositsLookup} from "../util/pendingDepositsLookup.js";
|
|
8
8
|
import {isValidDepositSignature} from "./processDeposit.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -76,53 +76,60 @@ function addBuilderToRegistry(
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// TODO GLOAS:
|
|
80
|
-
//
|
|
81
|
-
//
|
|
79
|
+
// TODO GLOAS: the PendingDepositsLookup is currently scoped to a single envelope of
|
|
80
|
+
// deposit-requests. We can track it as ephemeral within EpochCache and transfer to the next block
|
|
81
|
+
// transition to reuse cached signature verifications.
|
|
82
82
|
// See https://github.com/ChainSafe/lodestar/issues/9181
|
|
83
83
|
export function processDepositRequest(
|
|
84
84
|
fork: ForkSeq,
|
|
85
85
|
state: CachedBeaconStateElectra | CachedBeaconStateGloas,
|
|
86
86
|
depositRequest: electra.DepositRequest,
|
|
87
|
-
|
|
87
|
+
pendingDepositsLookup?: PendingDepositsLookup
|
|
88
88
|
): void {
|
|
89
89
|
const {pubkey, withdrawalCredentials, amount, signature} = depositRequest;
|
|
90
90
|
|
|
91
|
-
// Check if this is a builder or validator deposit
|
|
92
91
|
if (fork >= ForkSeq.gloas) {
|
|
93
92
|
const stateGloas = state as CachedBeaconStateGloas;
|
|
94
|
-
const
|
|
95
|
-
pendingValidatorPubkeysCache ?? getPendingValidatorPubkeys(state.config, stateGloas);
|
|
93
|
+
const lookup = pendingDepositsLookup ?? PendingDepositsLookup.build(stateGloas);
|
|
96
94
|
const pubkeyHex = toPubkeyHex(pubkey);
|
|
97
95
|
const builderIndex = findBuilderIndexByPubkey(stateGloas, pubkey);
|
|
98
96
|
const validatorIndex = state.epochCtx.getValidatorIndex(pubkey);
|
|
99
97
|
|
|
100
|
-
// Regardless of the withdrawal credentials prefix, if a builder/validator
|
|
101
|
-
// already exists with this pubkey, apply the deposit to their balance
|
|
102
98
|
const isBuilder = builderIndex !== null;
|
|
103
99
|
const isValidator = isValidatorKnown(state, validatorIndex);
|
|
104
|
-
const isPendingValidator = pendingValidatorPubkeys.has(pubkeyHex);
|
|
105
100
|
|
|
106
|
-
if (isBuilder
|
|
107
|
-
//
|
|
101
|
+
if (isBuilder) {
|
|
102
|
+
// Top up an existing builder regardless of withdrawal credential prefix
|
|
108
103
|
applyDepositForBuilder(stateGloas, pubkey, withdrawalCredentials, amount, signature, state.slot);
|
|
109
104
|
return;
|
|
110
105
|
}
|
|
111
106
|
|
|
112
|
-
//
|
|
113
|
-
// deposit requests for the same pubkey in this envelope must see it as a pending validator
|
|
107
|
+
// Only check the (expensive) "pending validator" condition when needed
|
|
114
108
|
if (
|
|
115
|
-
|
|
109
|
+
isBuilderWithdrawalCredential(withdrawalCredentials) &&
|
|
116
110
|
!isValidator &&
|
|
117
|
-
!
|
|
118
|
-
isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)
|
|
111
|
+
!lookup.hasPendingValidator(state.config, pubkeyHex)
|
|
119
112
|
) {
|
|
120
|
-
|
|
113
|
+
applyDepositForBuilder(stateGloas, pubkey, withdrawalCredentials, amount, signature, state.slot);
|
|
114
|
+
return;
|
|
121
115
|
}
|
|
116
|
+
|
|
117
|
+
const pendingDeposit = ssz.electra.PendingDeposit.toViewDU({
|
|
118
|
+
pubkey,
|
|
119
|
+
withdrawalCredentials,
|
|
120
|
+
amount,
|
|
121
|
+
signature,
|
|
122
|
+
slot: state.slot,
|
|
123
|
+
});
|
|
124
|
+
// Keep the lookup in sync with state.pendingDeposits so later deposit-requests
|
|
125
|
+
// in the same envelope see this deposit
|
|
126
|
+
lookup.add(pendingDeposit, pubkeyHex);
|
|
127
|
+
state.pendingDeposits.push(pendingDeposit);
|
|
128
|
+
return;
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
//
|
|
125
|
-
if (
|
|
131
|
+
// Pre-Gloas (Electra) path
|
|
132
|
+
if (state.depositRequestsStartIndex === UNSET_DEPOSIT_REQUESTS_START_INDEX) {
|
|
126
133
|
state.depositRequestsStartIndex = depositRequest.index;
|
|
127
134
|
}
|
|
128
135
|
|
|
@@ -136,28 +143,3 @@ export function processDepositRequest(
|
|
|
136
143
|
});
|
|
137
144
|
state.pendingDeposits.push(pendingDeposit);
|
|
138
145
|
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Build a set of pubkeys (hex-encoded) from pending deposits that have valid signatures.
|
|
142
|
-
* This is computed once and passed to each processDepositRequest call to avoid
|
|
143
|
-
* repeatedly iterating state.pendingDeposits.
|
|
144
|
-
*
|
|
145
|
-
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.3/specs/gloas/beacon-chain.md#new-is_pending_validator
|
|
146
|
-
*/
|
|
147
|
-
export function getPendingValidatorPubkeys(config: BeaconConfig, state: CachedBeaconStateGloas): Set<PubkeyHex> {
|
|
148
|
-
const result = new Set<PubkeyHex>();
|
|
149
|
-
for (const pendingDeposit of state.pendingDeposits.getAllReadonly()) {
|
|
150
|
-
if (
|
|
151
|
-
isValidDepositSignature(
|
|
152
|
-
config,
|
|
153
|
-
pendingDeposit.pubkey,
|
|
154
|
-
pendingDeposit.withdrawalCredentials,
|
|
155
|
-
pendingDeposit.amount,
|
|
156
|
-
pendingDeposit.signature
|
|
157
|
-
)
|
|
158
|
-
) {
|
|
159
|
-
result.add(toPubkeyHex(pendingDeposit.pubkey));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
@@ -3,8 +3,9 @@ import {BeaconBlock, electra, ssz} from "@lodestar/types";
|
|
|
3
3
|
import {byteArrayEquals, toRootHex} from "@lodestar/utils";
|
|
4
4
|
import {CachedBeaconStateGloas} from "../types.js";
|
|
5
5
|
import {computeEpochAtSlot} from "../util/epoch.js";
|
|
6
|
+
import {PendingDepositsLookup} from "../util/pendingDepositsLookup.js";
|
|
6
7
|
import {processConsolidationRequest} from "./processConsolidationRequest.js";
|
|
7
|
-
import {
|
|
8
|
+
import {processDepositRequest} from "./processDepositRequest.js";
|
|
8
9
|
import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -54,9 +55,9 @@ export function applyParentExecutionPayload(state: CachedBeaconStateGloas, reque
|
|
|
54
55
|
// Process execution requests from parent's payload. The execution
|
|
55
56
|
// requests are processed at state.slot (child's slot), not the parent's slot.
|
|
56
57
|
if (requests.deposits.length > 0) {
|
|
57
|
-
const
|
|
58
|
+
const pendingDepositsLookup = PendingDepositsLookup.build(state);
|
|
58
59
|
for (const deposit of requests.deposits) {
|
|
59
|
-
processDepositRequest(fork, state, deposit,
|
|
60
|
+
processDepositRequest(fork, state, deposit, pendingDepositsLookup);
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
package/src/cache/epochCache.ts
CHANGED
|
@@ -782,14 +782,17 @@ export class EpochCache {
|
|
|
782
782
|
*/
|
|
783
783
|
getBeaconProposer(slot: Slot): ValidatorIndex {
|
|
784
784
|
const epoch = computeEpochAtSlot(slot);
|
|
785
|
-
if (epoch
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
});
|
|
785
|
+
if (epoch === this.currentShuffling.epoch) {
|
|
786
|
+
return this.proposers[slot % SLOTS_PER_EPOCH];
|
|
787
|
+
}
|
|
788
|
+
if (epoch === this.nextEpoch) {
|
|
789
|
+
return this.getBeaconProposersNextEpoch()[slot % SLOTS_PER_EPOCH];
|
|
791
790
|
}
|
|
792
|
-
|
|
791
|
+
throw new EpochCacheError({
|
|
792
|
+
code: EpochCacheErrorCode.PROPOSER_EPOCH_MISMATCH,
|
|
793
|
+
currentEpoch: this.currentShuffling.epoch,
|
|
794
|
+
requestedEpoch: epoch,
|
|
795
|
+
});
|
|
793
796
|
}
|
|
794
797
|
|
|
795
798
|
getBeaconProposers(): ValidatorIndex[] {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
+
import {UPDATE_TIMEOUT} from "@lodestar/params";
|
|
3
|
+
import {
|
|
4
|
+
LightClientBootstrap,
|
|
5
|
+
LightClientFinalityUpdate,
|
|
6
|
+
LightClientOptimisticUpdate,
|
|
7
|
+
LightClientUpdate,
|
|
8
|
+
Slot,
|
|
9
|
+
} from "@lodestar/types";
|
|
10
|
+
import {computeSyncPeriodAtSlot} from "../../util/epoch.js";
|
|
11
|
+
import {
|
|
12
|
+
type ProcessUpdateOpts,
|
|
13
|
+
getSyncCommitteeAtPeriod,
|
|
14
|
+
processLightClientUpdate,
|
|
15
|
+
} from "./processLightClientUpdate.js";
|
|
16
|
+
import {type ILightClientStore, LightClientStore, type LightClientStoreEvents} from "./store.js";
|
|
17
|
+
import {ZERO_HEADER, ZERO_SYNC_COMMITTEE, getZeroFinalityBranch, getZeroSyncCommitteeBranch} from "./utils.js";
|
|
18
|
+
|
|
19
|
+
export type {LightClientUpdateSummary} from "./isBetterUpdate.js";
|
|
20
|
+
export {isBetterUpdate, toLightClientUpdateSummary} from "./isBetterUpdate.js";
|
|
21
|
+
export {
|
|
22
|
+
type ProcessUpdateOpts,
|
|
23
|
+
getSyncCommitteeAtPeriod,
|
|
24
|
+
isSafeLightClientUpdate,
|
|
25
|
+
processLightClientUpdate,
|
|
26
|
+
} from "./processLightClientUpdate.js";
|
|
27
|
+
export {
|
|
28
|
+
type ILightClientStore,
|
|
29
|
+
LightClientStore,
|
|
30
|
+
type LightClientStoreEvents,
|
|
31
|
+
type LightClientUpdateWithSummary,
|
|
32
|
+
type SyncCommitteeFast,
|
|
33
|
+
} from "./store.js";
|
|
34
|
+
export {
|
|
35
|
+
getSafetyThreshold,
|
|
36
|
+
isFinalityUpdate,
|
|
37
|
+
isSyncCommitteeUpdate,
|
|
38
|
+
isValidLightClientHeader,
|
|
39
|
+
normalizeMerkleBranch,
|
|
40
|
+
upgradeLightClientFinalityUpdate,
|
|
41
|
+
upgradeLightClientHeader,
|
|
42
|
+
upgradeLightClientOptimisticUpdate,
|
|
43
|
+
upgradeLightClientStore,
|
|
44
|
+
upgradeLightClientUpdate,
|
|
45
|
+
} from "./utils.js";
|
|
46
|
+
export {validateLightClientBootstrap} from "./validateLightClientBootstrap.js";
|
|
47
|
+
export {validateLightClientUpdate} from "./validateLightClientUpdate.js";
|
|
48
|
+
|
|
49
|
+
export class LightclientSpec {
|
|
50
|
+
readonly store: ILightClientStore;
|
|
51
|
+
readonly config: BeaconConfig;
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
config: BeaconConfig,
|
|
55
|
+
private readonly opts: ProcessUpdateOpts & LightClientStoreEvents,
|
|
56
|
+
bootstrap: LightClientBootstrap
|
|
57
|
+
) {
|
|
58
|
+
this.store = new LightClientStore(config, bootstrap, opts);
|
|
59
|
+
this.config = config;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onUpdate(currentSlot: Slot, update: LightClientUpdate): void {
|
|
63
|
+
processLightClientUpdate(this.config, this.store, currentSlot, this.opts, update);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onFinalityUpdate(currentSlot: Slot, finalityUpdate: LightClientFinalityUpdate): void {
|
|
67
|
+
this.onUpdate(currentSlot, {
|
|
68
|
+
attestedHeader: finalityUpdate.attestedHeader,
|
|
69
|
+
nextSyncCommittee: ZERO_SYNC_COMMITTEE,
|
|
70
|
+
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(this.config.getForkName(finalityUpdate.signatureSlot)),
|
|
71
|
+
finalizedHeader: finalityUpdate.finalizedHeader,
|
|
72
|
+
finalityBranch: finalityUpdate.finalityBranch,
|
|
73
|
+
syncAggregate: finalityUpdate.syncAggregate,
|
|
74
|
+
signatureSlot: finalityUpdate.signatureSlot,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onOptimisticUpdate(currentSlot: Slot, optimisticUpdate: LightClientOptimisticUpdate): void {
|
|
79
|
+
this.onUpdate(currentSlot, {
|
|
80
|
+
attestedHeader: optimisticUpdate.attestedHeader,
|
|
81
|
+
nextSyncCommittee: ZERO_SYNC_COMMITTEE,
|
|
82
|
+
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(this.config.getForkName(optimisticUpdate.signatureSlot)),
|
|
83
|
+
finalizedHeader: {beacon: ZERO_HEADER},
|
|
84
|
+
finalityBranch: getZeroFinalityBranch(this.config.getForkName(optimisticUpdate.signatureSlot)),
|
|
85
|
+
syncAggregate: optimisticUpdate.syncAggregate,
|
|
86
|
+
signatureSlot: optimisticUpdate.signatureSlot,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
forceUpdate(currentSlot: Slot): void {
|
|
91
|
+
for (const bestValidUpdate of this.store.bestValidUpdates.values()) {
|
|
92
|
+
if (currentSlot > bestValidUpdate.update.finalizedHeader.beacon.slot + UPDATE_TIMEOUT) {
|
|
93
|
+
const updatePeriod = computeSyncPeriodAtSlot(bestValidUpdate.update.signatureSlot);
|
|
94
|
+
// Simulate process_light_client_store_force_update() by forcing to apply a bestValidUpdate
|
|
95
|
+
// https://github.com/ethereum/consensus-specs/blob/a57e15636013eeba3610ff3ade41781dba1bb0cd/specs/altair/light-client/sync-protocol.md?plain=1#L394
|
|
96
|
+
// Call for `updatePeriod + 1` to force the update at `update.signatureSlot` to be applied
|
|
97
|
+
getSyncCommitteeAtPeriod(this.store, updatePeriod + 1, this.opts);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
|
2
|
+
import {LightClientUpdate, Slot} from "@lodestar/types";
|
|
3
|
+
import {computeSyncPeriodAtSlot} from "../../util/epoch.js";
|
|
4
|
+
import {isFinalityUpdate, isSyncCommitteeUpdate, sumBits} from "./utils.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wrapper type for `isBetterUpdate()` so we can apply its logic without requiring the full LightClientUpdate type.
|
|
8
|
+
*/
|
|
9
|
+
export type LightClientUpdateSummary = {
|
|
10
|
+
activeParticipants: number;
|
|
11
|
+
attestedHeaderSlot: Slot;
|
|
12
|
+
signatureSlot: Slot;
|
|
13
|
+
finalizedHeaderSlot: Slot;
|
|
14
|
+
/** `if update.next_sync_committee_branch != [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]` */
|
|
15
|
+
isSyncCommitteeUpdate: boolean;
|
|
16
|
+
/** `if update.finality_branch != [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]` */
|
|
17
|
+
isFinalityUpdate: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns the update with more bits. On ties, prevUpdate is the better
|
|
22
|
+
*
|
|
23
|
+
* https://github.com/ethereum/consensus-specs/blob/be3c774069e16e89145660be511c1b183056017e/specs/altair/light-client/sync-protocol.md#is_better_update
|
|
24
|
+
*/
|
|
25
|
+
export function isBetterUpdate(newUpdate: LightClientUpdateSummary, oldUpdate: LightClientUpdateSummary): boolean {
|
|
26
|
+
// Compare supermajority (> 2/3) sync committee participation
|
|
27
|
+
const newNumActiveParticipants = newUpdate.activeParticipants;
|
|
28
|
+
const oldNumActiveParticipants = oldUpdate.activeParticipants;
|
|
29
|
+
const newHasSupermajority = newNumActiveParticipants * 3 >= SYNC_COMMITTEE_SIZE * 2;
|
|
30
|
+
const oldHasSupermajority = oldNumActiveParticipants * 3 >= SYNC_COMMITTEE_SIZE * 2;
|
|
31
|
+
if (newHasSupermajority !== oldHasSupermajority) {
|
|
32
|
+
return newHasSupermajority;
|
|
33
|
+
}
|
|
34
|
+
if (!newHasSupermajority && newNumActiveParticipants !== oldNumActiveParticipants) {
|
|
35
|
+
return newNumActiveParticipants > oldNumActiveParticipants;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Compare presence of relevant sync committee
|
|
39
|
+
const newHasRelevantSyncCommittee =
|
|
40
|
+
newUpdate.isSyncCommitteeUpdate &&
|
|
41
|
+
computeSyncPeriodAtSlot(newUpdate.attestedHeaderSlot) === computeSyncPeriodAtSlot(newUpdate.signatureSlot);
|
|
42
|
+
const oldHasRelevantSyncCommittee =
|
|
43
|
+
oldUpdate.isSyncCommitteeUpdate &&
|
|
44
|
+
computeSyncPeriodAtSlot(oldUpdate.attestedHeaderSlot) === computeSyncPeriodAtSlot(oldUpdate.signatureSlot);
|
|
45
|
+
if (newHasRelevantSyncCommittee !== oldHasRelevantSyncCommittee) {
|
|
46
|
+
return newHasRelevantSyncCommittee;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Compare indication of any finality
|
|
50
|
+
const newHasFinality = newUpdate.isFinalityUpdate;
|
|
51
|
+
const oldHasFinality = oldUpdate.isFinalityUpdate;
|
|
52
|
+
if (newHasFinality !== oldHasFinality) {
|
|
53
|
+
return newHasFinality;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Compare sync committee finality
|
|
57
|
+
if (newHasFinality) {
|
|
58
|
+
const newHasSyncCommitteeFinality =
|
|
59
|
+
computeSyncPeriodAtSlot(newUpdate.finalizedHeaderSlot) === computeSyncPeriodAtSlot(newUpdate.attestedHeaderSlot);
|
|
60
|
+
const oldHasSyncCommitteeFinality =
|
|
61
|
+
computeSyncPeriodAtSlot(oldUpdate.finalizedHeaderSlot) === computeSyncPeriodAtSlot(oldUpdate.attestedHeaderSlot);
|
|
62
|
+
if (newHasSyncCommitteeFinality !== oldHasSyncCommitteeFinality) {
|
|
63
|
+
return newHasSyncCommitteeFinality;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Tiebreaker 1: Sync committee participation beyond supermajority
|
|
68
|
+
if (newNumActiveParticipants !== oldNumActiveParticipants) {
|
|
69
|
+
return newNumActiveParticipants > oldNumActiveParticipants;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Tiebreaker 2: Prefer older data (fewer changes to best)
|
|
73
|
+
if (newUpdate.attestedHeaderSlot !== oldUpdate.attestedHeaderSlot) {
|
|
74
|
+
return newUpdate.attestedHeaderSlot < oldUpdate.attestedHeaderSlot;
|
|
75
|
+
}
|
|
76
|
+
return newUpdate.signatureSlot < oldUpdate.signatureSlot;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function isSafeLightClientUpdate(update: LightClientUpdateSummary): boolean {
|
|
80
|
+
return (
|
|
81
|
+
update.activeParticipants * 3 >= SYNC_COMMITTEE_SIZE * 2 && update.isFinalityUpdate && update.isSyncCommitteeUpdate
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function toLightClientUpdateSummary(update: LightClientUpdate): LightClientUpdateSummary {
|
|
86
|
+
return {
|
|
87
|
+
activeParticipants: sumBits(update.syncAggregate.syncCommitteeBits),
|
|
88
|
+
attestedHeaderSlot: update.attestedHeader.beacon.slot,
|
|
89
|
+
signatureSlot: update.signatureSlot,
|
|
90
|
+
finalizedHeaderSlot: update.finalizedHeader.beacon.slot,
|
|
91
|
+
isSyncCommitteeUpdate: isSyncCommitteeUpdate(update),
|
|
92
|
+
isFinalityUpdate: isFinalityUpdate(update),
|
|
93
|
+
};
|
|
94
|
+
}
|