@lodestar/prover 1.35.0-dev.f80d2d52da → 1.35.0-dev.fcf8d024ea

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.
Files changed (121) hide show
  1. package/bin/lodestar-prover.js +3 -0
  2. package/lib/browser/index.d.ts.map +1 -0
  3. package/lib/cli/applyPreset.d.ts.map +1 -0
  4. package/lib/cli/cli.d.ts +3 -3
  5. package/lib/cli/cli.d.ts.map +1 -0
  6. package/lib/cli/cli.js +1 -1
  7. package/lib/cli/cli.js.map +1 -1
  8. package/lib/cli/cmds/index.d.ts.map +1 -0
  9. package/lib/cli/cmds/index.js.map +1 -1
  10. package/lib/cli/cmds/start/handler.d.ts.map +1 -0
  11. package/lib/cli/cmds/start/index.d.ts.map +1 -0
  12. package/lib/cli/cmds/start/options.d.ts.map +1 -0
  13. package/lib/cli/index.d.ts.map +1 -0
  14. package/lib/cli/index.js.map +1 -1
  15. package/lib/cli/options.d.ts.map +1 -0
  16. package/lib/constants.d.ts.map +1 -0
  17. package/lib/index.d.ts +1 -1
  18. package/lib/index.d.ts.map +1 -0
  19. package/lib/index.js +1 -1
  20. package/lib/index.js.map +1 -1
  21. package/lib/interfaces.d.ts.map +1 -0
  22. package/lib/proof_provider/index.d.ts.map +1 -0
  23. package/lib/proof_provider/ordered_map.d.ts.map +1 -0
  24. package/lib/proof_provider/ordered_map.js +2 -0
  25. package/lib/proof_provider/ordered_map.js.map +1 -1
  26. package/lib/proof_provider/payload_store.d.ts.map +1 -0
  27. package/lib/proof_provider/payload_store.js +12 -11
  28. package/lib/proof_provider/payload_store.js.map +1 -1
  29. package/lib/proof_provider/proof_provider.d.ts.map +1 -0
  30. package/lib/proof_provider/proof_provider.js +9 -0
  31. package/lib/proof_provider/proof_provider.js.map +1 -1
  32. package/lib/provider_types/eip1193_provider_type.d.ts.map +1 -0
  33. package/lib/provider_types/ethers_provider_type.d.ts.map +1 -0
  34. package/lib/provider_types/legacy_provider_type.d.ts.map +1 -0
  35. package/lib/provider_types/web3_js_provider_type.d.ts.map +1 -0
  36. package/lib/types.d.ts.map +1 -0
  37. package/lib/utils/assertion.d.ts.map +1 -0
  38. package/lib/utils/consensus.d.ts.map +1 -0
  39. package/lib/utils/conversion.d.ts.map +1 -0
  40. package/lib/utils/errors.d.ts.map +1 -0
  41. package/lib/utils/evm.d.ts.map +1 -0
  42. package/lib/utils/execution.d.ts.map +1 -0
  43. package/lib/utils/file.d.ts.map +1 -0
  44. package/lib/utils/file.js.map +1 -1
  45. package/lib/utils/gitData/gitDataPath.d.ts.map +1 -0
  46. package/lib/utils/gitData/index.d.ts.map +1 -0
  47. package/lib/utils/gitData/index.js.map +1 -1
  48. package/lib/utils/gitData/writeGitData.d.ts.map +1 -0
  49. package/lib/utils/json_rpc.d.ts.map +1 -0
  50. package/lib/utils/process.d.ts.map +1 -0
  51. package/lib/utils/req_resp.d.ts.map +1 -0
  52. package/lib/utils/rpc_provider.d.ts.map +1 -0
  53. package/lib/utils/rpc_provider.js +3 -1
  54. package/lib/utils/rpc_provider.js.map +1 -1
  55. package/lib/utils/validation.d.ts.map +1 -0
  56. package/lib/utils/validation.js.map +1 -1
  57. package/lib/utils/verification.d.ts.map +1 -0
  58. package/lib/utils/version.d.ts.map +1 -0
  59. package/lib/verified_requests/eth_call.d.ts.map +1 -0
  60. package/lib/verified_requests/eth_estimateGas.d.ts.map +1 -0
  61. package/lib/verified_requests/eth_getBalance.d.ts.map +1 -0
  62. package/lib/verified_requests/eth_getBlockByHash.d.ts.map +1 -0
  63. package/lib/verified_requests/eth_getBlockByNumber.d.ts.map +1 -0
  64. package/lib/verified_requests/eth_getCode.d.ts.map +1 -0
  65. package/lib/verified_requests/eth_getTransactionCount.d.ts.map +1 -0
  66. package/lib/web3_provider.d.ts.map +1 -0
  67. package/lib/web3_provider_inspector.d.ts.map +1 -0
  68. package/lib/web3_provider_inspector.js +2 -1
  69. package/lib/web3_provider_inspector.js.map +1 -1
  70. package/lib/web3_proxy.d.ts.map +1 -0
  71. package/lib/web3_proxy.js +1 -1
  72. package/lib/web3_proxy.js.map +1 -1
  73. package/package.json +16 -15
  74. package/src/browser/index.ts +3 -0
  75. package/src/cli/applyPreset.ts +83 -0
  76. package/src/cli/cli.ts +58 -0
  77. package/src/cli/cmds/index.ts +7 -0
  78. package/src/cli/cmds/start/handler.ts +27 -0
  79. package/src/cli/cmds/start/index.ts +18 -0
  80. package/src/cli/cmds/start/options.ts +85 -0
  81. package/src/cli/index.ts +30 -0
  82. package/src/cli/options.ts +73 -0
  83. package/src/constants.ts +6 -0
  84. package/src/index.ts +5 -0
  85. package/src/interfaces.ts +90 -0
  86. package/src/proof_provider/index.ts +1 -0
  87. package/src/proof_provider/ordered_map.ts +25 -0
  88. package/src/proof_provider/payload_store.ts +223 -0
  89. package/src/proof_provider/proof_provider.ts +210 -0
  90. package/src/provider_types/eip1193_provider_type.ts +32 -0
  91. package/src/provider_types/ethers_provider_type.ts +44 -0
  92. package/src/provider_types/legacy_provider_type.ts +123 -0
  93. package/src/provider_types/web3_js_provider_type.ts +35 -0
  94. package/src/types.ts +163 -0
  95. package/src/utils/assertion.ts +11 -0
  96. package/src/utils/consensus.ts +122 -0
  97. package/src/utils/conversion.ts +107 -0
  98. package/src/utils/errors.ts +4 -0
  99. package/src/utils/evm.ts +284 -0
  100. package/src/utils/execution.ts +76 -0
  101. package/src/utils/file.ts +51 -0
  102. package/src/utils/gitData/gitDataPath.ts +48 -0
  103. package/src/utils/gitData/index.ts +70 -0
  104. package/src/utils/gitData/writeGitData.ts +10 -0
  105. package/src/utils/json_rpc.ts +170 -0
  106. package/src/utils/process.ts +111 -0
  107. package/src/utils/req_resp.ts +34 -0
  108. package/src/utils/rpc_provider.ts +117 -0
  109. package/src/utils/validation.ts +161 -0
  110. package/src/utils/verification.ts +112 -0
  111. package/src/utils/version.ts +74 -0
  112. package/src/verified_requests/eth_call.ts +50 -0
  113. package/src/verified_requests/eth_estimateGas.ts +49 -0
  114. package/src/verified_requests/eth_getBalance.ts +26 -0
  115. package/src/verified_requests/eth_getBlockByHash.ts +24 -0
  116. package/src/verified_requests/eth_getBlockByNumber.ts +25 -0
  117. package/src/verified_requests/eth_getCode.ts +50 -0
  118. package/src/verified_requests/eth_getTransactionCount.ts +26 -0
  119. package/src/web3_provider.ts +58 -0
  120. package/src/web3_provider_inspector.ts +88 -0
  121. package/src/web3_proxy.ts +175 -0
@@ -0,0 +1,223 @@
1
+ import {ApiClient} from "@lodestar/api";
2
+ import {ForkName} from "@lodestar/params";
3
+ import {ExecutionPayload, LightClientHeader} from "@lodestar/types";
4
+ import {Logger} from "@lodestar/utils";
5
+ import {MAX_PAYLOAD_HISTORY} from "../constants.js";
6
+ import {fetchBlock, getExecutionPayloadForBlockNumber} from "../utils/consensus.js";
7
+ import {bufferToHex, hexToNumber} from "../utils/conversion.js";
8
+ import {OrderedMap} from "./ordered_map.js";
9
+
10
+ type BlockELRoot = string;
11
+ type BlockELRootAndSlot = {
12
+ blockELRoot: BlockELRoot;
13
+ slot: number;
14
+ };
15
+ type BlockCLRoot = string;
16
+
17
+ /**
18
+ * The in-memory store for the execution payloads to be used to verify the proofs
19
+ */
20
+ export class PayloadStore {
21
+ // We store the block root from execution for finalized blocks
22
+ // As these blocks are finalized, so not to be worried about conflicting roots
23
+ private finalizedRoots = new OrderedMap<BlockELRootAndSlot>();
24
+
25
+ // Unfinalized blocks may change over time and may have conflicting roots
26
+ // We can receive multiple light-client headers for the same block of execution
27
+ // So we why store unfinalized payloads by their CL root, which is only used
28
+ // in processing the light-client headers
29
+ private unfinalizedRoots = new Map<BlockCLRoot, BlockELRoot>();
30
+
31
+ // Payloads store with BlockELRoot as key
32
+ private payloads = new Map<BlockELRoot, ExecutionPayload>();
33
+
34
+ private latestBlockRoot: BlockELRoot | null = null;
35
+
36
+ constructor(private opts: {api: ApiClient; logger: Logger}) {}
37
+
38
+ get finalized(): ExecutionPayload | undefined {
39
+ const maxBlockNumberForFinalized = this.finalizedRoots.max;
40
+
41
+ if (maxBlockNumberForFinalized === undefined) {
42
+ return undefined;
43
+ }
44
+
45
+ const finalizedMaxRoot = this.finalizedRoots.get(maxBlockNumberForFinalized);
46
+ if (finalizedMaxRoot) {
47
+ return this.payloads.get(finalizedMaxRoot.blockELRoot);
48
+ }
49
+
50
+ return undefined;
51
+ }
52
+
53
+ get latest(): ExecutionPayload | undefined {
54
+ if (this.latestBlockRoot) {
55
+ return this.payloads.get(this.latestBlockRoot);
56
+ }
57
+
58
+ return undefined;
59
+ }
60
+
61
+ async get(blockId: number | string): Promise<ExecutionPayload | undefined> {
62
+ // Given block id is a block hash in hex (32 bytes root takes 64 hex chars + 2 for 0x prefix)
63
+ if (typeof blockId === "string" && blockId.startsWith("0x") && blockId.length === 64 + 2) {
64
+ return this.payloads.get(blockId);
65
+ }
66
+
67
+ // Given block id is a block number in hex
68
+ if (typeof blockId === "string" && blockId.startsWith("0x")) {
69
+ return this.getOrFetchFinalizedPayload(hexToNumber(blockId));
70
+ }
71
+
72
+ // Given block id is a block number in decimal string
73
+ if (typeof blockId === "string" && !blockId.startsWith("0x")) {
74
+ return this.getOrFetchFinalizedPayload(parseInt(blockId, 10));
75
+ }
76
+
77
+ // Given block id is a block number in decimal
78
+ if (typeof blockId === "number") {
79
+ return this.getOrFetchFinalizedPayload(blockId);
80
+ }
81
+
82
+ return undefined;
83
+ }
84
+
85
+ protected async getOrFetchFinalizedPayload(blockNumber: number): Promise<ExecutionPayload | undefined> {
86
+ const maxBlockNumberForFinalized = this.finalizedRoots.max;
87
+ const minBlockNumberForFinalized = this.finalizedRoots.min;
88
+
89
+ if (maxBlockNumberForFinalized === undefined || minBlockNumberForFinalized === undefined) {
90
+ return;
91
+ }
92
+
93
+ if (blockNumber > maxBlockNumberForFinalized) {
94
+ throw new Error(
95
+ `Block number ${blockNumber} is higher than the latest finalized block number. We recommend to use block hash for unfinalized blocks.`
96
+ );
97
+ }
98
+
99
+ let blockELRoot = this.finalizedRoots.get(blockNumber);
100
+ // check if we have payload cached locally else fetch from api
101
+ if (!blockELRoot) {
102
+ const finalizedMaxRoot = this.finalizedRoots.get(maxBlockNumberForFinalized);
103
+ const slot = finalizedMaxRoot?.slot;
104
+ if (slot !== undefined) {
105
+ const payloads = await getExecutionPayloadForBlockNumber(this.opts.api, slot, blockNumber);
106
+ for (const [slot, payload] of payloads.entries()) {
107
+ this.set(payload, slot, true);
108
+ }
109
+ }
110
+ }
111
+
112
+ blockELRoot = this.finalizedRoots.get(blockNumber);
113
+ if (blockELRoot) {
114
+ return this.payloads.get(blockELRoot.blockELRoot);
115
+ }
116
+
117
+ return undefined;
118
+ }
119
+
120
+ set(payload: ExecutionPayload, slot: number, finalized: boolean): void {
121
+ const blockELRoot = bufferToHex(payload.blockHash);
122
+ this.payloads.set(blockELRoot, payload);
123
+
124
+ if (this.latestBlockRoot) {
125
+ const latestPayload = this.payloads.get(this.latestBlockRoot);
126
+ if (latestPayload && latestPayload.blockNumber < payload.blockNumber) {
127
+ this.latestBlockRoot = blockELRoot;
128
+ }
129
+ } else {
130
+ this.latestBlockRoot = blockELRoot;
131
+ }
132
+
133
+ if (finalized) {
134
+ this.finalizedRoots.set(payload.blockNumber, {blockELRoot, slot});
135
+ }
136
+ }
137
+
138
+ async processLCHeader(header: LightClientHeader<ForkName.capella>, finalized = false): Promise<void> {
139
+ const blockSlot = header.beacon.slot;
140
+ const blockNumber = header.execution.blockNumber;
141
+ const blockELRoot = bufferToHex(header.execution.blockHash);
142
+ const blockCLRoot = bufferToHex(header.beacon.stateRoot);
143
+ const existingELRoot = this.unfinalizedRoots.get(blockCLRoot);
144
+
145
+ // ==== Finalized blocks ====
146
+ // if the block is finalized, we need to update the finalizedRoots map
147
+ if (finalized) {
148
+ this.finalizedRoots.set(blockNumber, {blockELRoot, slot: blockSlot});
149
+
150
+ // If the block is finalized and we already have the payload
151
+ // We can remove it from the unfinalizedRoots map and do nothing else
152
+ if (existingELRoot) {
153
+ this.unfinalizedRoots.delete(blockCLRoot);
154
+ }
155
+
156
+ // If the block is finalized and we do not have the payload
157
+ // We need to fetch and set the payload
158
+ else {
159
+ const block = await fetchBlock(this.opts.api, blockSlot);
160
+ if (block) {
161
+ this.payloads.set(blockELRoot, block.message.body.executionPayload);
162
+ } else {
163
+ this.opts.logger.error("Failed to fetch block", blockSlot);
164
+ }
165
+ }
166
+
167
+ return;
168
+ }
169
+
170
+ // ==== Unfinalized blocks ====
171
+ // We already have the payload for this block
172
+ if (existingELRoot && existingELRoot === blockELRoot) {
173
+ return;
174
+ }
175
+
176
+ // Re-org happened, we need to update the payload
177
+ if (existingELRoot && existingELRoot !== blockELRoot) {
178
+ this.payloads.delete(existingELRoot);
179
+ }
180
+
181
+ // This is unfinalized header we need to store it's root related to cl root
182
+ this.unfinalizedRoots.set(blockCLRoot, blockELRoot);
183
+
184
+ // We do not have the payload for this block, we need to fetch it
185
+ const block = await fetchBlock(this.opts.api, blockSlot);
186
+ if (block) {
187
+ this.set(block.message.body.executionPayload, blockSlot, false);
188
+ } else {
189
+ this.opts.logger.error("Failed to fetch finalized block", blockSlot);
190
+ }
191
+ this.prune();
192
+ }
193
+
194
+ prune(): void {
195
+ if (this.finalizedRoots.size <= MAX_PAYLOAD_HISTORY) return;
196
+ // Store doe not have any finalized blocks means it's recently initialized
197
+ if (this.finalizedRoots.max === undefined || this.finalizedRoots.min === undefined) return;
198
+
199
+ for (
200
+ let blockNumber = this.finalizedRoots.max - MAX_PAYLOAD_HISTORY;
201
+ blockNumber >= this.finalizedRoots.min;
202
+ blockNumber--
203
+ ) {
204
+ const blockELRoot = this.finalizedRoots.get(blockNumber);
205
+ if (blockELRoot) {
206
+ this.payloads.delete(blockELRoot.blockELRoot);
207
+ this.finalizedRoots.delete(blockNumber);
208
+ }
209
+ }
210
+
211
+ for (const [clRoot, elRoot] of this.unfinalizedRoots) {
212
+ const payload = this.payloads.get(elRoot);
213
+ if (!payload) {
214
+ this.unfinalizedRoots.delete(clRoot);
215
+ continue;
216
+ }
217
+
218
+ if (payload.blockNumber < this.finalizedRoots.min) {
219
+ this.unfinalizedRoots.delete(clRoot);
220
+ }
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,210 @@
1
+ import {ApiClient, getClient} from "@lodestar/api/beacon";
2
+ import {ChainForkConfig, createChainForkConfig} from "@lodestar/config";
3
+ import {NetworkName, networksChainConfig} from "@lodestar/config/networks";
4
+ import {Lightclient, LightclientEvent, RunStatusCode} from "@lodestar/light-client";
5
+ import {LightClientRestTransport} from "@lodestar/light-client/transport";
6
+ import {ForkName, isForkPostCapella} from "@lodestar/params";
7
+ import {ExecutionPayload, LightClientHeader} from "@lodestar/types";
8
+ import {Logger} from "@lodestar/utils";
9
+ import {LCTransport, RootProviderInitOptions} from "../interfaces.js";
10
+ import {assertLightClient} from "../utils/assertion.js";
11
+ import {
12
+ fetchBlock,
13
+ getExecutionPayloads,
14
+ getGenesisData,
15
+ getSyncCheckpoint,
16
+ getUnFinalizedRangeForPayloads,
17
+ } from "../utils/consensus.js";
18
+ import {bufferToHex} from "../utils/conversion.js";
19
+ import {PayloadStore} from "./payload_store.js";
20
+
21
+ type RootProviderOptions = Omit<RootProviderInitOptions, "transport"> & {
22
+ transport: LightClientRestTransport;
23
+ api: ApiClient;
24
+ config: ChainForkConfig;
25
+ };
26
+
27
+ export class ProofProvider {
28
+ private store: PayloadStore;
29
+ private logger: Logger;
30
+ // Make sure readyPromise doesn't throw unhandled exceptions
31
+ private readyPromise?: Promise<void>;
32
+
33
+ readonly config: ChainForkConfig;
34
+ readonly network: NetworkName;
35
+ readonly api: ApiClient;
36
+
37
+ lightClient?: Lightclient;
38
+
39
+ constructor(readonly opts: RootProviderOptions) {
40
+ this.store = new PayloadStore({api: opts.api, logger: opts.logger});
41
+ this.logger = opts.logger;
42
+ this.config = opts.config;
43
+ this.api = opts.api;
44
+ this.network = opts.config.PRESET_BASE as NetworkName;
45
+ }
46
+
47
+ async waitToBeReady(): Promise<void> {
48
+ return this.readyPromise;
49
+ }
50
+
51
+ static init(opts: RootProviderInitOptions): ProofProvider {
52
+ if (opts.transport === LCTransport.P2P) {
53
+ throw new Error("P2P mode not supported yet");
54
+ }
55
+ opts.logger.info("Creating ProofProvider instance with REST APIs", {
56
+ network: opts.network,
57
+ urls: opts.urls.join(","),
58
+ });
59
+
60
+ const config = opts.network
61
+ ? createChainForkConfig(networksChainConfig[opts.network])
62
+ : createChainForkConfig(opts.config);
63
+
64
+ const api = getClient({urls: opts.urls}, {config});
65
+ const transport = new LightClientRestTransport(api);
66
+
67
+ const provider = new ProofProvider({
68
+ ...opts,
69
+ config,
70
+ api,
71
+ transport,
72
+ });
73
+
74
+ provider.readyPromise = provider.sync(opts.wsCheckpoint).catch((e) => {
75
+ opts.logger.error("Error while syncing", e);
76
+ return Promise.reject(e);
77
+ });
78
+
79
+ return provider;
80
+ }
81
+
82
+ private async sync(wsCheckpoint?: string): Promise<void> {
83
+ if (this.lightClient !== undefined) {
84
+ throw Error("Light client already initialized and syncing.");
85
+ }
86
+ this.logger.info("Starting sync for proof provider");
87
+ const {api, config, transport} = this.opts;
88
+ const checkpointRoot = await getSyncCheckpoint(api, wsCheckpoint);
89
+ const genesisData = await getGenesisData(api);
90
+
91
+ this.logger.info("Initializing lightclient", {checkpointRoot: bufferToHex(checkpointRoot)});
92
+ this.lightClient = await Lightclient.initializeFromCheckpointRoot({
93
+ checkpointRoot,
94
+ config,
95
+ transport,
96
+ genesisData,
97
+ });
98
+
99
+ assertLightClient(this.lightClient);
100
+
101
+ this.logger.info("Initiating lightclient");
102
+ // Wait for the lightclient to start
103
+ await this.lightClient?.start();
104
+ this.logger.info("Lightclient synced", this.getStatus());
105
+ this.registerEvents();
106
+
107
+ // Load the payloads from the CL
108
+ this.logger.info("Building EL payload history");
109
+ const {start, end} = await getUnFinalizedRangeForPayloads(this.lightClient);
110
+ const payloads = await getExecutionPayloads({
111
+ api: this.opts.api,
112
+ startSlot: start,
113
+ endSlot: end,
114
+ logger: this.logger,
115
+ });
116
+ for (const [slot, payload] of payloads.entries()) {
117
+ this.store.set(payload, slot, false);
118
+ }
119
+
120
+ // Load the finalized payload from the CL
121
+ const finalizedSlot = this.lightClient.getFinalized().beacon.slot;
122
+ this.logger.debug("Getting finalized slot from lightclient", {finalizedSlot});
123
+ const block = await fetchBlock(this.opts.api, finalizedSlot);
124
+ if (block) {
125
+ this.store.set(block.message.body.executionPayload, finalizedSlot, true);
126
+ } else {
127
+ this.logger.error("Failed to fetch finalized block", finalizedSlot);
128
+ }
129
+
130
+ this.logger.info("Proof provider ready");
131
+ }
132
+
133
+ getStatus(): {latest: number; finalized: number; status: RunStatusCode} {
134
+ if (!this.lightClient) {
135
+ return {
136
+ latest: 0,
137
+ finalized: 0,
138
+ status: RunStatusCode.uninitialized,
139
+ };
140
+ }
141
+
142
+ return {
143
+ latest: this.lightClient.getHead().beacon.slot,
144
+ finalized: this.lightClient.getFinalized().beacon.slot,
145
+ status: this.lightClient.status,
146
+ };
147
+ }
148
+
149
+ async getExecutionPayload(blockNumber: number | string | "finalized" | "latest"): Promise<ExecutionPayload> {
150
+ assertLightClient(this.lightClient);
151
+
152
+ if (typeof blockNumber === "string" && blockNumber === "finalized") {
153
+ const payload = this.store.finalized;
154
+ if (!payload) throw new Error("No finalized payload");
155
+ return payload;
156
+ }
157
+
158
+ if (typeof blockNumber === "string" && blockNumber === "latest") {
159
+ const payload = this.store.latest;
160
+ if (!payload) throw new Error("No latest payload");
161
+ return payload;
162
+ }
163
+
164
+ if ((typeof blockNumber === "string" && blockNumber.startsWith("0x")) || typeof blockNumber === "number") {
165
+ const payload = await this.store.get(blockNumber);
166
+ if (!payload) throw new Error(`No payload for blockNumber ${blockNumber}`);
167
+ return payload;
168
+ }
169
+
170
+ throw new Error(`Invalid blockNumber "${blockNumber}"`);
171
+ }
172
+
173
+ async processLCHeader(lcHeader: LightClientHeader, finalized = false): Promise<void> {
174
+ const fork = this.opts.config.getForkName(lcHeader.beacon.slot);
175
+
176
+ if (!isForkPostCapella(fork)) {
177
+ return;
178
+ }
179
+
180
+ const sszType = this.opts.config.getPostBellatrixForkTypes(lcHeader.beacon.slot).ExecutionPayloadHeader;
181
+ if (
182
+ isForkPostCapella(fork) &&
183
+ (!("execution" in lcHeader) || sszType.equals(lcHeader.execution, sszType.defaultValue()))
184
+ ) {
185
+ throw new Error("Execution payload is required for execution fork");
186
+ }
187
+
188
+ await this.store.processLCHeader(lcHeader as LightClientHeader<ForkName.capella>, finalized);
189
+ }
190
+
191
+ private registerEvents(): void {
192
+ assertLightClient(this.lightClient);
193
+
194
+ this.opts.signal.addEventListener("abort", () => {
195
+ this.lightClient?.stop();
196
+ });
197
+
198
+ this.lightClient.emitter.on(LightclientEvent.lightClientFinalityHeader, async (data) => {
199
+ await this.processLCHeader(data, true).catch((e) => {
200
+ this.logger.error("Error processing finality update", null, e);
201
+ });
202
+ });
203
+
204
+ this.lightClient.emitter.on(LightclientEvent.lightClientOptimisticHeader, async (data) => {
205
+ await this.processLCHeader(data).catch((e) => {
206
+ this.logger.error("Error processing optimistic update", null, e);
207
+ });
208
+ });
209
+ }
210
+ }
@@ -0,0 +1,32 @@
1
+ import {Web3ProviderType} from "../interfaces.js";
2
+ import {JsonRpcRequestOrBatch, JsonRpcResponseOrBatch} from "../types.js";
3
+
4
+ // Modern providers uses this structure e.g. Web3 4.x
5
+ export interface EIP1193Provider {
6
+ request: (payload: JsonRpcRequestOrBatch) => Promise<JsonRpcResponseOrBatch>;
7
+ }
8
+ export default {
9
+ name: "eip1193",
10
+ matched(provider): provider is EIP1193Provider {
11
+ return (
12
+ "request" in provider &&
13
+ typeof provider.request === "function" &&
14
+ provider.request.constructor.name === "AsyncFunction"
15
+ );
16
+ },
17
+ handler(provider) {
18
+ const request = provider.request.bind(provider);
19
+
20
+ return async (payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> => {
21
+ const response = await request(payload);
22
+ return response;
23
+ };
24
+ },
25
+ mutateProvider(provider, newHandler) {
26
+ Object.assign(provider, {
27
+ request: async function newRequest(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
28
+ return newHandler(payload);
29
+ },
30
+ });
31
+ },
32
+ } as Web3ProviderType<EIP1193Provider>;
@@ -0,0 +1,44 @@
1
+ import {Web3ProviderType} from "../interfaces.js";
2
+ import {JsonRpcRequestOrBatch, JsonRpcResponse, JsonRpcResponseOrBatch} from "../types.js";
3
+ import {isBatchRequest} from "../utils/json_rpc.js";
4
+ import web3JsProviderType from "./web3_js_provider_type.js";
5
+
6
+ export interface EthersProvider {
7
+ // Ethers provider does not have a public interface for batch requests
8
+ send(method: string, params: Array<unknown>): Promise<JsonRpcResponse>;
9
+ }
10
+ export default {
11
+ name: "ethers",
12
+ matched(provider): provider is EthersProvider {
13
+ return (
14
+ !web3JsProviderType.matched(provider) &&
15
+ "send" in provider &&
16
+ typeof provider.send === "function" &&
17
+ provider.send.length > 1 &&
18
+ provider.send.constructor.name === "AsyncFunction"
19
+ );
20
+ },
21
+ handler(provider) {
22
+ const send = provider.send.bind(provider);
23
+
24
+ return async (payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> => {
25
+ // Because ethers provider public interface does not support batch requests
26
+ // so we need to handle it manually
27
+ if (isBatchRequest(payload)) {
28
+ const responses = [];
29
+ for (const request of payload) {
30
+ responses.push(await send(request.method, request.params));
31
+ }
32
+ return responses;
33
+ }
34
+ return send(payload.method, payload.params);
35
+ };
36
+ },
37
+ mutateProvider(provider, newHandler) {
38
+ Object.assign(provider, {
39
+ send: function newSend(method: string, params: Array<unknown>): Promise<JsonRpcResponseOrBatch | undefined> {
40
+ return newHandler({jsonrpc: "2.0", id: 0, method, params});
41
+ },
42
+ });
43
+ },
44
+ } as Web3ProviderType<EthersProvider>;
@@ -0,0 +1,123 @@
1
+ import {AnyWeb3Provider, Web3ProviderType} from "../interfaces.js";
2
+ import {JsonRpcRequest, JsonRpcRequestOrBatch, JsonRpcResponse, JsonRpcResponseOrBatch} from "../types.js";
3
+ import web3JsProviderType from "./web3_js_provider_type.js";
4
+
5
+ // Some providers uses `request` instead of the `send`. e.g. Ganache
6
+ interface RequestProvider {
7
+ request(
8
+ payload: JsonRpcRequestOrBatch,
9
+ callback: (err: Error | undefined, response: JsonRpcResponseOrBatch) => void
10
+ ): void;
11
+ }
12
+ // The legacy Web3 1.x use this structure
13
+ interface SendProvider {
14
+ send(payload: JsonRpcRequest, callback: (err?: Error | null, response?: JsonRpcResponse) => void): void;
15
+ }
16
+ // Some legacy providers use this very old structure
17
+ interface SendAsyncProvider {
18
+ sendAsync(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch>;
19
+ }
20
+
21
+ type LegacyProvider = RequestProvider | SendProvider | SendAsyncProvider;
22
+
23
+ export default {
24
+ name: "legacy",
25
+ matched(provider): provider is LegacyProvider {
26
+ return isRequestProvider(provider) || isSendProvider(provider) || isSendAsyncProvider(provider);
27
+ },
28
+ handler(provider) {
29
+ if (isRequestProvider(provider)) {
30
+ const request = provider.request.bind(provider);
31
+ return function newHandler(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
32
+ return new Promise((resolve, reject) => {
33
+ request(payload, (err, response) => {
34
+ if (err) {
35
+ reject(err);
36
+ } else {
37
+ resolve(response);
38
+ }
39
+ });
40
+ });
41
+ };
42
+ }
43
+ if (isSendProvider(provider)) {
44
+ const send = provider.send.bind(provider);
45
+ return function newHandler(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
46
+ return new Promise((resolve, reject) => {
47
+ // web3 providers supports batch requests but don't have valid types
48
+ send(payload as JsonRpcRequest, (err, response) => {
49
+ if (err) {
50
+ reject(err);
51
+ } else {
52
+ resolve(response);
53
+ }
54
+ });
55
+ });
56
+ };
57
+ }
58
+
59
+ // sendAsync provider
60
+ const sendAsync = provider.sendAsync.bind(provider);
61
+ return async function newHandler(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
62
+ const response = await sendAsync(payload);
63
+ return response;
64
+ };
65
+ },
66
+ mutateProvider(provider, newHandler) {
67
+ if (isRequestProvider(provider)) {
68
+ const newRequest = function newRequest(
69
+ payload: JsonRpcRequestOrBatch,
70
+ callback: (err?: Error | null, response?: JsonRpcResponseOrBatch) => void
71
+ ): void {
72
+ newHandler(payload)
73
+ .then((response) => callback(undefined, response))
74
+ .catch((err) => callback(err, undefined));
75
+ };
76
+
77
+ Object.assign(provider, {request: newRequest});
78
+ }
79
+
80
+ if (isSendProvider(provider)) {
81
+ const newSend = function newSend(
82
+ payload: JsonRpcRequestOrBatch,
83
+ callback: (err?: Error | null, response?: JsonRpcResponseOrBatch) => void
84
+ ): void {
85
+ newHandler(payload)
86
+ .then((response) => callback(undefined, response))
87
+ .catch((err) => callback(err, undefined));
88
+ };
89
+
90
+ Object.assign(provider, {send: newSend});
91
+ }
92
+
93
+ // sendAsync provider
94
+ Object.assign(provider, {sendAsync: newHandler});
95
+ },
96
+ } as Web3ProviderType<LegacyProvider>;
97
+
98
+ function isSendProvider(provider: AnyWeb3Provider): provider is SendProvider {
99
+ return (
100
+ !web3JsProviderType.matched(provider) &&
101
+ "send" in provider &&
102
+ typeof provider.send === "function" &&
103
+ provider.send.length > 1 &&
104
+ provider.send.constructor.name !== "AsyncFunction"
105
+ );
106
+ }
107
+
108
+ function isRequestProvider(provider: AnyWeb3Provider): provider is RequestProvider {
109
+ return (
110
+ !web3JsProviderType.matched(provider) &&
111
+ "request" in provider &&
112
+ typeof provider.request === "function" &&
113
+ provider.request.length > 1
114
+ );
115
+ }
116
+
117
+ function isSendAsyncProvider(provider: AnyWeb3Provider): provider is SendAsyncProvider {
118
+ return (
119
+ "sendAsync" in provider &&
120
+ typeof provider.sendAsync === "function" &&
121
+ provider.sendAsync.constructor.name === "AsyncFunction"
122
+ );
123
+ }
@@ -0,0 +1,35 @@
1
+ import {AnyWeb3Provider, Web3ProviderType} from "../interfaces.js";
2
+ import {JsonRpcRequest, JsonRpcRequestOrBatch, JsonRpcResponse, JsonRpcResponseOrBatch} from "../types.js";
3
+ import {isBatchRequest} from "../utils/json_rpc.js";
4
+
5
+ export interface Web3jsProvider {
6
+ request: (payload: JsonRpcRequest) => Promise<JsonRpcResponse>;
7
+ }
8
+
9
+ export default {
10
+ name: "web3js",
11
+ matched(provider): provider is Web3jsProvider {
12
+ return (
13
+ "isWeb3Provider" in provider.constructor &&
14
+ (provider.constructor as {isWeb3Provider: (provider: AnyWeb3Provider) => boolean}).isWeb3Provider(provider)
15
+ );
16
+ },
17
+ handler(provider) {
18
+ const request = provider.request.bind(provider);
19
+
20
+ return async (payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> => {
21
+ if (isBatchRequest(payload)) {
22
+ return Promise.all(payload.map((p) => request(p)));
23
+ }
24
+
25
+ return request(payload);
26
+ };
27
+ },
28
+ mutateProvider(provider, newHandler) {
29
+ Object.assign(provider, {
30
+ request: async function newRequest(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
31
+ return newHandler(payload);
32
+ },
33
+ });
34
+ },
35
+ } as Web3ProviderType<Web3jsProvider>;