@lodestar/prover 1.35.0-dev.b42a298a7c → 1.35.0-dev.ba92bd8a88
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/bin/lodestar-prover.js +3 -0
- package/lib/browser/index.d.ts.map +1 -0
- package/lib/cli/applyPreset.d.ts.map +1 -0
- package/lib/cli/cli.d.ts.map +1 -0
- package/lib/cli/cmds/index.d.ts.map +1 -0
- package/lib/cli/cmds/start/handler.d.ts.map +1 -0
- package/lib/cli/cmds/start/index.d.ts.map +1 -0
- package/lib/cli/cmds/start/options.d.ts.map +1 -0
- package/lib/cli/index.d.ts.map +1 -0
- package/lib/cli/options.d.ts.map +1 -0
- package/lib/constants.d.ts.map +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/interfaces.d.ts.map +1 -0
- package/lib/proof_provider/index.d.ts.map +1 -0
- package/lib/proof_provider/ordered_map.d.ts.map +1 -0
- package/lib/proof_provider/payload_store.d.ts.map +1 -0
- package/lib/proof_provider/proof_provider.d.ts.map +1 -0
- package/lib/provider_types/eip1193_provider_type.d.ts.map +1 -0
- package/lib/provider_types/ethers_provider_type.d.ts.map +1 -0
- package/lib/provider_types/legacy_provider_type.d.ts.map +1 -0
- package/lib/provider_types/web3_js_provider_type.d.ts.map +1 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils/assertion.d.ts.map +1 -0
- package/lib/utils/consensus.d.ts.map +1 -0
- package/lib/utils/conversion.d.ts.map +1 -0
- package/lib/utils/errors.d.ts.map +1 -0
- package/lib/utils/evm.d.ts.map +1 -0
- package/lib/utils/execution.d.ts.map +1 -0
- package/lib/utils/file.d.ts.map +1 -0
- package/lib/utils/gitData/gitDataPath.d.ts.map +1 -0
- package/lib/utils/gitData/index.d.ts.map +1 -0
- package/lib/utils/gitData/writeGitData.d.ts.map +1 -0
- package/lib/utils/json_rpc.d.ts.map +1 -0
- package/lib/utils/process.d.ts.map +1 -0
- package/lib/utils/req_resp.d.ts.map +1 -0
- package/lib/utils/rpc_provider.d.ts.map +1 -0
- package/lib/utils/validation.d.ts.map +1 -0
- package/lib/utils/verification.d.ts.map +1 -0
- package/lib/utils/version.d.ts.map +1 -0
- package/lib/verified_requests/eth_call.d.ts.map +1 -0
- package/lib/verified_requests/eth_estimateGas.d.ts.map +1 -0
- package/lib/verified_requests/eth_getBalance.d.ts.map +1 -0
- package/lib/verified_requests/eth_getBlockByHash.d.ts.map +1 -0
- package/lib/verified_requests/eth_getBlockByNumber.d.ts.map +1 -0
- package/lib/verified_requests/eth_getCode.d.ts.map +1 -0
- package/lib/verified_requests/eth_getTransactionCount.d.ts.map +1 -0
- package/lib/web3_provider.d.ts.map +1 -0
- package/lib/web3_provider_inspector.d.ts.map +1 -0
- package/lib/web3_proxy.d.ts.map +1 -0
- package/package.json +14 -15
- package/src/browser/index.ts +3 -0
- package/src/cli/applyPreset.ts +83 -0
- package/src/cli/cli.ts +58 -0
- package/src/cli/cmds/index.ts +7 -0
- package/src/cli/cmds/start/handler.ts +27 -0
- package/src/cli/cmds/start/index.ts +18 -0
- package/src/cli/cmds/start/options.ts +85 -0
- package/src/cli/index.ts +30 -0
- package/src/cli/options.ts +73 -0
- package/src/constants.ts +6 -0
- package/src/index.ts +5 -0
- package/src/interfaces.ts +90 -0
- package/src/proof_provider/index.ts +1 -0
- package/src/proof_provider/ordered_map.ts +25 -0
- package/src/proof_provider/payload_store.ts +223 -0
- package/src/proof_provider/proof_provider.ts +210 -0
- package/src/provider_types/eip1193_provider_type.ts +32 -0
- package/src/provider_types/ethers_provider_type.ts +44 -0
- package/src/provider_types/legacy_provider_type.ts +123 -0
- package/src/provider_types/web3_js_provider_type.ts +35 -0
- package/src/types.ts +163 -0
- package/src/utils/assertion.ts +11 -0
- package/src/utils/consensus.ts +122 -0
- package/src/utils/conversion.ts +107 -0
- package/src/utils/errors.ts +4 -0
- package/src/utils/evm.ts +284 -0
- package/src/utils/execution.ts +76 -0
- package/src/utils/file.ts +51 -0
- package/src/utils/gitData/gitDataPath.ts +48 -0
- package/src/utils/gitData/index.ts +70 -0
- package/src/utils/gitData/writeGitData.ts +10 -0
- package/src/utils/json_rpc.ts +170 -0
- package/src/utils/process.ts +111 -0
- package/src/utils/req_resp.ts +34 -0
- package/src/utils/rpc_provider.ts +117 -0
- package/src/utils/validation.ts +161 -0
- package/src/utils/verification.ts +112 -0
- package/src/utils/version.ts +74 -0
- package/src/verified_requests/eth_call.ts +50 -0
- package/src/verified_requests/eth_estimateGas.ts +49 -0
- package/src/verified_requests/eth_getBalance.ts +26 -0
- package/src/verified_requests/eth_getBlockByHash.ts +24 -0
- package/src/verified_requests/eth_getBlockByNumber.ts +25 -0
- package/src/verified_requests/eth_getCode.ts +50 -0
- package/src/verified_requests/eth_getTransactionCount.ts +26 -0
- package/src/web3_provider.ts +58 -0
- package/src/web3_provider_inspector.ts +88 -0
- package/src/web3_proxy.ts +175 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {fileURLToPath} from "node:url";
|
|
4
|
+
import {findUpSync} from "find-up";
|
|
5
|
+
import {readAndGetGitData} from "./gitData/index.js";
|
|
6
|
+
|
|
7
|
+
// Global variable __dirname no longer available in ES6 modules.
|
|
8
|
+
// Solutions: https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
type VersionJson = {
|
|
12
|
+
/** "0.28.2" */
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const BRANCH_IGNORE = /^(HEAD|master|unstable|main)$/;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Gathers all information on package version including Git data.
|
|
20
|
+
* @returns a version string, e.g.
|
|
21
|
+
* - Stable release: `v0.36.0/80c248bb`
|
|
22
|
+
* - Dev release: `v0.36.0-dev.80c248bb/80c248bb`
|
|
23
|
+
* - Test branch: `v0.36.0/developer-feature/80c248bb`
|
|
24
|
+
*/
|
|
25
|
+
export function getVersionData(): {
|
|
26
|
+
version: string;
|
|
27
|
+
commit: string;
|
|
28
|
+
} {
|
|
29
|
+
const parts: string[] = [];
|
|
30
|
+
|
|
31
|
+
/** Returns local version from `lerna.json` or `package.json` as `"0.28.2"` */
|
|
32
|
+
const localVersion = readCliPackageJson() || readVersionFromLernaJson();
|
|
33
|
+
if (localVersion) {
|
|
34
|
+
parts.push(`v${localVersion}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const {branch, commit} = readAndGetGitData();
|
|
38
|
+
|
|
39
|
+
// Add branch only if not present and not an ignore value
|
|
40
|
+
if (branch && !BRANCH_IGNORE.test(branch)) parts.push(branch);
|
|
41
|
+
|
|
42
|
+
// Add commit only if present. 7 characters to be consistent with Github
|
|
43
|
+
if (commit) {
|
|
44
|
+
const commitShort = commit.slice(0, 7);
|
|
45
|
+
// Don't add commit if it's already in the version string (dev versions)
|
|
46
|
+
if (!localVersion || !localVersion.includes(commitShort)) {
|
|
47
|
+
parts.push(commitShort);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
// Guard against empty parts array
|
|
53
|
+
version: parts.length > 0 ? parts.join("/") : "unknown",
|
|
54
|
+
commit,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Read version information from lerna.json */
|
|
59
|
+
function readVersionFromLernaJson(): string | undefined {
|
|
60
|
+
const filePath = findUpSync("lerna.json", {cwd: __dirname});
|
|
61
|
+
if (!filePath) return undefined;
|
|
62
|
+
|
|
63
|
+
const lernaJson = JSON.parse(fs.readFileSync(filePath, "utf8")) as VersionJson;
|
|
64
|
+
return lernaJson.version;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Read version information from package.json */
|
|
68
|
+
function readCliPackageJson(): string | undefined {
|
|
69
|
+
const filePath = findUpSync("package.json", {cwd: __dirname});
|
|
70
|
+
if (!filePath) return undefined;
|
|
71
|
+
|
|
72
|
+
const packageJson = JSON.parse(fs.readFileSync(filePath, "utf8")) as VersionJson;
|
|
73
|
+
return packageJson.version;
|
|
74
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {ELVerifiedRequestHandler} from "../interfaces.js";
|
|
2
|
+
import {ELApiParams, ELApiReturn} from "../types.js";
|
|
3
|
+
import {bufferToHex} from "../utils/conversion.js";
|
|
4
|
+
import {createVM, executeVMCall, getVMWithState} from "../utils/evm.js";
|
|
5
|
+
import {
|
|
6
|
+
getErrorResponseForRequestWithFailedVerification,
|
|
7
|
+
getResponseForRequest,
|
|
8
|
+
getVerificationFailedMessage,
|
|
9
|
+
} from "../utils/json_rpc.js";
|
|
10
|
+
|
|
11
|
+
export const eth_call: ELVerifiedRequestHandler<ELApiParams["eth_call"], ELApiReturn["eth_call"]> = async ({
|
|
12
|
+
rpc,
|
|
13
|
+
payload,
|
|
14
|
+
logger,
|
|
15
|
+
proofProvider,
|
|
16
|
+
}) => {
|
|
17
|
+
const {
|
|
18
|
+
params: [tx, block],
|
|
19
|
+
} = payload;
|
|
20
|
+
// We assume that standard tx validation already been done by the caller (web3, ethers.js, etc.)
|
|
21
|
+
const executionPayload = await proofProvider.getExecutionPayload(block ?? "latest");
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
// TODO: Optimize the creation of the evm
|
|
25
|
+
const vm = await createVM({proofProvider});
|
|
26
|
+
const vmWithState = await getVMWithState({
|
|
27
|
+
rpc,
|
|
28
|
+
executionPayload,
|
|
29
|
+
tx,
|
|
30
|
+
vm,
|
|
31
|
+
logger,
|
|
32
|
+
});
|
|
33
|
+
const result = await executeVMCall({
|
|
34
|
+
vm: vmWithState,
|
|
35
|
+
tx,
|
|
36
|
+
rpc,
|
|
37
|
+
executionPayload,
|
|
38
|
+
network: proofProvider.network,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return getResponseForRequest(payload, bufferToHex(result.returnValue));
|
|
42
|
+
} catch (err) {
|
|
43
|
+
logger.error(
|
|
44
|
+
"Request could not be verified.",
|
|
45
|
+
{method: payload.method, params: JSON.stringify(payload.params)},
|
|
46
|
+
err as Error
|
|
47
|
+
);
|
|
48
|
+
return getErrorResponseForRequestWithFailedVerification(payload, getVerificationFailedMessage("eth_call"));
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {ELVerifiedRequestHandler} from "../interfaces.js";
|
|
2
|
+
import {ELApiParams, ELApiReturn} from "../types.js";
|
|
3
|
+
import {bigIntToHex} from "../utils/conversion.js";
|
|
4
|
+
import {createVM, executeVMTx, getVMWithState} from "../utils/evm.js";
|
|
5
|
+
import {
|
|
6
|
+
getErrorResponseForRequestWithFailedVerification,
|
|
7
|
+
getResponseForRequest,
|
|
8
|
+
getVerificationFailedMessage,
|
|
9
|
+
} from "../utils/json_rpc.js";
|
|
10
|
+
|
|
11
|
+
export const eth_estimateGas: ELVerifiedRequestHandler<
|
|
12
|
+
ELApiParams["eth_estimateGas"],
|
|
13
|
+
ELApiReturn["eth_estimateGas"]
|
|
14
|
+
> = async ({rpc, payload, logger, proofProvider}) => {
|
|
15
|
+
const {
|
|
16
|
+
params: [tx, block],
|
|
17
|
+
} = payload;
|
|
18
|
+
// We assume that standard tx validation already been done by the caller (web3, ethers.js, etc.)
|
|
19
|
+
const executionPayload = await proofProvider.getExecutionPayload(block ?? "latest");
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// TODO: Optimize the creation of the evm
|
|
23
|
+
const vm = await createVM({proofProvider});
|
|
24
|
+
const vmWithState = await getVMWithState({
|
|
25
|
+
rpc,
|
|
26
|
+
executionPayload,
|
|
27
|
+
tx,
|
|
28
|
+
vm,
|
|
29
|
+
logger,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const result = await executeVMTx({
|
|
33
|
+
vm: vmWithState,
|
|
34
|
+
tx,
|
|
35
|
+
rpc,
|
|
36
|
+
executionPayload,
|
|
37
|
+
network: proofProvider.network,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return getResponseForRequest(payload, bigIntToHex(result.totalGasSpent));
|
|
41
|
+
} catch (err) {
|
|
42
|
+
logger.error(
|
|
43
|
+
"Request could not be verified.",
|
|
44
|
+
{method: payload.method, params: JSON.stringify(payload.params)},
|
|
45
|
+
err as Error
|
|
46
|
+
);
|
|
47
|
+
return getErrorResponseForRequestWithFailedVerification(payload, getVerificationFailedMessage("eth_estimateGas"));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {ELVerifiedRequestHandler} from "../interfaces.js";
|
|
2
|
+
import {
|
|
3
|
+
getErrorResponseForRequestWithFailedVerification,
|
|
4
|
+
getResponseForRequest,
|
|
5
|
+
getVerificationFailedMessage,
|
|
6
|
+
} from "../utils/json_rpc.js";
|
|
7
|
+
import {verifyAccount} from "../utils/verification.js";
|
|
8
|
+
|
|
9
|
+
export const eth_getBalance: ELVerifiedRequestHandler<[address: string, block?: number | string], string> = async ({
|
|
10
|
+
rpc,
|
|
11
|
+
payload,
|
|
12
|
+
logger,
|
|
13
|
+
proofProvider,
|
|
14
|
+
}) => {
|
|
15
|
+
const {
|
|
16
|
+
params: [address, block],
|
|
17
|
+
} = payload;
|
|
18
|
+
const result = await verifyAccount({proofProvider, logger, rpc, address, block});
|
|
19
|
+
|
|
20
|
+
if (result.valid) {
|
|
21
|
+
return getResponseForRequest(payload, result.data.balance);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.error("Request could not be verified.", {method: payload.method, params: JSON.stringify(payload.params)});
|
|
25
|
+
return getErrorResponseForRequestWithFailedVerification(payload, getVerificationFailedMessage("eth_getBalance"));
|
|
26
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {ELVerifiedRequestHandler} from "../interfaces.js";
|
|
2
|
+
import {ELBlock} from "../types.js";
|
|
3
|
+
import {
|
|
4
|
+
getErrorResponseForRequestWithFailedVerification,
|
|
5
|
+
getResponseForRequest,
|
|
6
|
+
getVerificationFailedMessage,
|
|
7
|
+
} from "../utils/json_rpc.js";
|
|
8
|
+
import {verifyBlock} from "../utils/verification.js";
|
|
9
|
+
|
|
10
|
+
export const eth_getBlockByHash: ELVerifiedRequestHandler<[block: string, hydrated: boolean], ELBlock> = async ({
|
|
11
|
+
rpc,
|
|
12
|
+
payload,
|
|
13
|
+
logger,
|
|
14
|
+
proofProvider,
|
|
15
|
+
}) => {
|
|
16
|
+
const result = await verifyBlock({payload, proofProvider, logger, rpc});
|
|
17
|
+
|
|
18
|
+
if (result.valid) {
|
|
19
|
+
return getResponseForRequest(payload, result.data);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
logger.error("Request could not be verified.", {method: payload.method, params: JSON.stringify(payload.params)});
|
|
23
|
+
return getErrorResponseForRequestWithFailedVerification(payload, getVerificationFailedMessage("eth_getBlockByHash"));
|
|
24
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {ELVerifiedRequestHandler} from "../interfaces.js";
|
|
2
|
+
import {ELBlock} from "../types.js";
|
|
3
|
+
import {
|
|
4
|
+
getErrorResponseForRequestWithFailedVerification,
|
|
5
|
+
getResponseForRequest,
|
|
6
|
+
getVerificationFailedMessage,
|
|
7
|
+
} from "../utils/json_rpc.js";
|
|
8
|
+
import {verifyBlock} from "../utils/verification.js";
|
|
9
|
+
|
|
10
|
+
export const eth_getBlockByNumber: ELVerifiedRequestHandler<
|
|
11
|
+
[block: string | number, hydrated: boolean],
|
|
12
|
+
ELBlock
|
|
13
|
+
> = async ({rpc, payload, logger, proofProvider}) => {
|
|
14
|
+
const result = await verifyBlock({payload, proofProvider, logger, rpc});
|
|
15
|
+
|
|
16
|
+
if (result.valid) {
|
|
17
|
+
return getResponseForRequest(payload, result.data);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
logger.error("Request could not be verified.", {method: payload.method, params: JSON.stringify(payload.params)});
|
|
21
|
+
return getErrorResponseForRequestWithFailedVerification(
|
|
22
|
+
payload,
|
|
23
|
+
getVerificationFailedMessage("eth_getBlockByNumber")
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {ELVerifiedRequestHandler} from "../interfaces.js";
|
|
2
|
+
import {
|
|
3
|
+
getErrorResponseForRequestWithFailedVerification,
|
|
4
|
+
getResponseForRequest,
|
|
5
|
+
getVerificationFailedMessage,
|
|
6
|
+
} from "../utils/json_rpc.js";
|
|
7
|
+
import {verifyAccount, verifyCode} from "../utils/verification.js";
|
|
8
|
+
|
|
9
|
+
export const eth_getCode: ELVerifiedRequestHandler<[address: string, block?: number | string], string> = async ({
|
|
10
|
+
rpc,
|
|
11
|
+
payload,
|
|
12
|
+
logger,
|
|
13
|
+
proofProvider,
|
|
14
|
+
}) => {
|
|
15
|
+
const {
|
|
16
|
+
params: [address, block],
|
|
17
|
+
} = payload;
|
|
18
|
+
// TODO: When batch requests are supported merged these two requests into one
|
|
19
|
+
const accountProof = await verifyAccount({
|
|
20
|
+
proofProvider,
|
|
21
|
+
logger,
|
|
22
|
+
rpc,
|
|
23
|
+
address,
|
|
24
|
+
block,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!accountProof.valid) {
|
|
28
|
+
logger.error("Request could not be verified.", {method: payload.method, params: JSON.stringify(payload.params)});
|
|
29
|
+
return getErrorResponseForRequestWithFailedVerification(
|
|
30
|
+
payload,
|
|
31
|
+
"account for eth_getCode request can not be verified."
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const codeProof = await verifyCode({
|
|
36
|
+
proofProvider,
|
|
37
|
+
logger,
|
|
38
|
+
rpc,
|
|
39
|
+
address,
|
|
40
|
+
block,
|
|
41
|
+
codeHash: accountProof.data.codeHash,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (codeProof.valid) {
|
|
45
|
+
return getResponseForRequest(payload, codeProof.data);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
logger.error("Request could not be verified.", {method: payload.method, params: JSON.stringify(payload.params)});
|
|
49
|
+
return getErrorResponseForRequestWithFailedVerification(payload, getVerificationFailedMessage("eth_getCode"));
|
|
50
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {ELVerifiedRequestHandler} from "../interfaces.js";
|
|
2
|
+
import {
|
|
3
|
+
getErrorResponseForRequestWithFailedVerification,
|
|
4
|
+
getResponseForRequest,
|
|
5
|
+
getVerificationFailedMessage,
|
|
6
|
+
} from "../utils/json_rpc.js";
|
|
7
|
+
import {verifyAccount} from "../utils/verification.js";
|
|
8
|
+
|
|
9
|
+
export const eth_getTransactionCount: ELVerifiedRequestHandler<
|
|
10
|
+
[address: string, block?: number | string],
|
|
11
|
+
string
|
|
12
|
+
> = async ({rpc, payload, logger, proofProvider}) => {
|
|
13
|
+
const {
|
|
14
|
+
params: [address, block],
|
|
15
|
+
} = payload;
|
|
16
|
+
const result = await verifyAccount({proofProvider, logger, rpc, address, block});
|
|
17
|
+
if (result.valid) {
|
|
18
|
+
return getResponseForRequest(payload, result.data.nonce);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
logger.error("Request could not be verified.", {method: payload.method, params: JSON.stringify(payload.params)});
|
|
22
|
+
return getErrorResponseForRequestWithFailedVerification(
|
|
23
|
+
payload,
|
|
24
|
+
getVerificationFailedMessage("eth_getTransactionCount")
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {LogLevel} from "@lodestar/logger";
|
|
2
|
+
import {getBrowserLogger} from "@lodestar/logger/browser";
|
|
3
|
+
import {Logger} from "@lodestar/utils";
|
|
4
|
+
import {AnyWeb3Provider, ELRequestHandler, VerifiedExecutionInitOptions} from "./interfaces.js";
|
|
5
|
+
import {ProofProvider} from "./proof_provider/proof_provider.js";
|
|
6
|
+
import {processAndVerifyRequest} from "./utils/process.js";
|
|
7
|
+
import {ELRpcProvider} from "./utils/rpc_provider.js";
|
|
8
|
+
import {Web3ProviderInspector} from "./web3_provider_inspector.js";
|
|
9
|
+
|
|
10
|
+
export type Web3ProviderTypeHandler<T extends AnyWeb3Provider> = (
|
|
11
|
+
provider: T,
|
|
12
|
+
proofProvider: ProofProvider,
|
|
13
|
+
logger: Logger
|
|
14
|
+
) => {provider: T; handler: ELRpcProvider["handler"]};
|
|
15
|
+
|
|
16
|
+
export function createVerifiedExecutionProvider<
|
|
17
|
+
T extends AnyWeb3Provider,
|
|
18
|
+
Mutate extends undefined | boolean = true,
|
|
19
|
+
Return = {provider: Mutate extends undefined | true ? T : ELRpcProvider; proofProvider: ProofProvider},
|
|
20
|
+
>(provider: T, opts: VerifiedExecutionInitOptions<Mutate>): Return {
|
|
21
|
+
const signal = opts.signal ?? new AbortController().signal;
|
|
22
|
+
const logger = opts.logger ?? getBrowserLogger({level: opts.logLevel ?? LogLevel.info});
|
|
23
|
+
const mutateProvider = opts.mutateProvider === undefined;
|
|
24
|
+
const customProviderTypes = opts.providerTypes ?? [];
|
|
25
|
+
|
|
26
|
+
const providerInspector = Web3ProviderInspector.initWithDefault({logger});
|
|
27
|
+
for (const providerType of customProviderTypes.reverse()) {
|
|
28
|
+
providerInspector.register(providerType, {index: 0});
|
|
29
|
+
}
|
|
30
|
+
const providerType = providerInspector.detect(provider);
|
|
31
|
+
logger.debug(`Provider is detected as '${providerType.name}' provider.`);
|
|
32
|
+
|
|
33
|
+
const proofProvider = ProofProvider.init({
|
|
34
|
+
...opts,
|
|
35
|
+
signal,
|
|
36
|
+
logger,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const nonVerifiedHandler = providerType.handler(provider);
|
|
40
|
+
const nonVerifiedRpc = new ELRpcProvider(nonVerifiedHandler, logger);
|
|
41
|
+
|
|
42
|
+
nonVerifiedRpc.verifyCompatibility().catch((err) => {
|
|
43
|
+
logger.error(err);
|
|
44
|
+
logger.error("Due to compatibility issues, verified execution may not work properly.");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const verifiedHandler: ELRequestHandler = function newVerifiedHandler(payload) {
|
|
48
|
+
return processAndVerifyRequest({payload, rpc: nonVerifiedRpc, logger, proofProvider});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (mutateProvider) {
|
|
52
|
+
providerType.mutateProvider(provider, verifiedHandler);
|
|
53
|
+
return {provider, proofProvider} as Return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Verified RPC
|
|
57
|
+
return {provider: new ELRpcProvider(verifiedHandler, logger), proofProvider} as Return;
|
|
58
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {Logger} from "@lodestar/logger";
|
|
2
|
+
import {AnyWeb3Provider, Web3ProviderType} from "./interfaces.js";
|
|
3
|
+
import eip1193ProviderType from "./provider_types/eip1193_provider_type.js";
|
|
4
|
+
import ethersProviderType from "./provider_types/ethers_provider_type.js";
|
|
5
|
+
import legacyProviderType from "./provider_types/legacy_provider_type.js";
|
|
6
|
+
import web3jsProviderType from "./provider_types/web3_js_provider_type.js";
|
|
7
|
+
|
|
8
|
+
export class Web3ProviderInspector {
|
|
9
|
+
protected providerTypes: Web3ProviderType<AnyWeb3Provider>[] = [];
|
|
10
|
+
logger: Logger;
|
|
11
|
+
|
|
12
|
+
protected constructor(opts: {logger: Logger}) {
|
|
13
|
+
this.logger = opts.logger;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static initWithDefault(opts: {logger: Logger}): Web3ProviderInspector {
|
|
17
|
+
const inspector = new Web3ProviderInspector(opts);
|
|
18
|
+
inspector.register(web3jsProviderType, {index: 0});
|
|
19
|
+
inspector.register(ethersProviderType, {index: 1});
|
|
20
|
+
inspector.register(eip1193ProviderType, {index: 2});
|
|
21
|
+
inspector.register(legacyProviderType, {index: 3});
|
|
22
|
+
|
|
23
|
+
return inspector;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getProviderTypes(): Web3ProviderType<AnyWeb3Provider>[] {
|
|
27
|
+
// Destruct so user can not mutate the output
|
|
28
|
+
return [...this.providerTypes];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
register(providerType: Web3ProviderType<AnyWeb3Provider>, opts?: {index?: number}): void {
|
|
32
|
+
// If user does not provider index, we will register the provider type to last
|
|
33
|
+
let index = opts?.index ?? this.providerTypes.length;
|
|
34
|
+
|
|
35
|
+
// If index is larger, let's add type at the end
|
|
36
|
+
if (index > this.providerTypes.length) {
|
|
37
|
+
index = this.providerTypes.length;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// If a lower index is provided let's add type at the start
|
|
41
|
+
if (index < 0) {
|
|
42
|
+
index = 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.providerTypes.map((p) => p.name).includes(providerType.name)) {
|
|
46
|
+
throw new Error(`Provider type '${providerType.name}' is already registered.`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// If some provider type is already register on that index, we will make space for new
|
|
50
|
+
if (this.providerTypes.at(index)) {
|
|
51
|
+
this.logger.debug(
|
|
52
|
+
`A provider type '${this.providerTypes[index].name}' already existed at index '${index}', now moved down.`
|
|
53
|
+
);
|
|
54
|
+
this.providerTypes.splice(index, 0, providerType);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.logger.debug(`Registered provider type "${providerType.name}" at index ${index}`);
|
|
58
|
+
this.providerTypes[index] = providerType;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
unregister(indexOrName: string | number): void {
|
|
62
|
+
if (typeof indexOrName === "number") {
|
|
63
|
+
if (indexOrName > this.providerTypes.length || indexOrName < 0) {
|
|
64
|
+
throw new Error(`Provider type at index '${indexOrName}' is not registered.`);
|
|
65
|
+
}
|
|
66
|
+
this.providerTypes.splice(indexOrName, 1);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const index = this.providerTypes.findIndex((p) => p.name === indexOrName);
|
|
71
|
+
if (index < 0) {
|
|
72
|
+
throw Error(`Provider type '${indexOrName}' is not registered.`);
|
|
73
|
+
}
|
|
74
|
+
this.providerTypes.splice(index, 1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
detect(provider: AnyWeb3Provider): Web3ProviderType<AnyWeb3Provider> {
|
|
78
|
+
for (const providerType of Object.values(this.providerTypes)) {
|
|
79
|
+
if (providerType.matched(provider)) {
|
|
80
|
+
return providerType;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Given provider could not be detected of any type. Supported types are ${Object.keys(this.providerTypes).join()}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import https from "node:https";
|
|
3
|
+
import url from "node:url";
|
|
4
|
+
import httpProxy from "http-proxy";
|
|
5
|
+
import {LogLevel} from "@lodestar/logger";
|
|
6
|
+
import {getNodeLogger} from "@lodestar/logger/node";
|
|
7
|
+
import {ELRequestHandler, VerifiedExecutionInitOptions} from "./interfaces.js";
|
|
8
|
+
import {ProofProvider} from "./proof_provider/proof_provider.js";
|
|
9
|
+
import {JsonRpcRequestOrBatch, JsonRpcRequestPayload, JsonRpcResponseOrBatch} from "./types.js";
|
|
10
|
+
import {getResponseForRequest, isBatchRequest} from "./utils/json_rpc.js";
|
|
11
|
+
import {processAndVerifyRequest} from "./utils/process.js";
|
|
12
|
+
import {fetchRequestPayload, fetchResponseBody} from "./utils/req_resp.js";
|
|
13
|
+
import {ELRpcProvider} from "./utils/rpc_provider.js";
|
|
14
|
+
|
|
15
|
+
export type VerifiedProxyOptions = Exclude<VerifiedExecutionInitOptions<false>, "mutateProvider" | "providerTypes"> & {
|
|
16
|
+
executionRpcUrl: string;
|
|
17
|
+
requestTimeout: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function createHttpHandler({
|
|
21
|
+
info,
|
|
22
|
+
signal,
|
|
23
|
+
}: {
|
|
24
|
+
signal: AbortSignal;
|
|
25
|
+
info: () => {port: number; host: string; timeout: number} | string;
|
|
26
|
+
}): ELRequestHandler {
|
|
27
|
+
return function handler(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const serverInfo = info();
|
|
30
|
+
if (typeof serverInfo === "string") {
|
|
31
|
+
return reject(new Error(serverInfo));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const req = http.request(
|
|
35
|
+
{
|
|
36
|
+
method: "POST",
|
|
37
|
+
path: "/proxy",
|
|
38
|
+
port: serverInfo.port,
|
|
39
|
+
host: serverInfo.host,
|
|
40
|
+
timeout: serverInfo.timeout,
|
|
41
|
+
signal,
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
(res) => {
|
|
47
|
+
fetchResponseBody(res)
|
|
48
|
+
.then((response) => {
|
|
49
|
+
resolve(response);
|
|
50
|
+
})
|
|
51
|
+
.catch(reject);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
req.on("timeout", () => {
|
|
55
|
+
req.destroy();
|
|
56
|
+
reject(new Error("Request timeout"));
|
|
57
|
+
});
|
|
58
|
+
req.write(JSON.stringify(payload));
|
|
59
|
+
req.end();
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function createVerifiedExecutionProxy(opts: VerifiedProxyOptions): {
|
|
65
|
+
server: http.Server;
|
|
66
|
+
proofProvider: ProofProvider;
|
|
67
|
+
} {
|
|
68
|
+
const {executionRpcUrl, requestTimeout} = opts;
|
|
69
|
+
const signal = opts.signal ?? new AbortController().signal;
|
|
70
|
+
const logger = opts.logger ?? getNodeLogger({level: opts.logLevel ?? LogLevel.info, module: "prover"});
|
|
71
|
+
|
|
72
|
+
const proofProvider = ProofProvider.init({
|
|
73
|
+
...opts,
|
|
74
|
+
signal,
|
|
75
|
+
logger,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
logger.info("Creating http proxy", {url: executionRpcUrl});
|
|
79
|
+
const proxy = httpProxy.createProxy({
|
|
80
|
+
target: executionRpcUrl,
|
|
81
|
+
ws: executionRpcUrl.startsWith("ws"),
|
|
82
|
+
agent: executionRpcUrl.startsWith("https") ? https.globalAgent : http.globalAgent,
|
|
83
|
+
xfwd: true,
|
|
84
|
+
ignorePath: true,
|
|
85
|
+
changeOrigin: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let proxyServerListeningAddress: {host: string; port: number} | undefined;
|
|
89
|
+
const rpc = new ELRpcProvider(
|
|
90
|
+
createHttpHandler({
|
|
91
|
+
signal,
|
|
92
|
+
info: () => {
|
|
93
|
+
if (!proxyServerListeningAddress) {
|
|
94
|
+
return "Proxy server not listening";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
port: proxyServerListeningAddress.port,
|
|
99
|
+
host: proxyServerListeningAddress.host,
|
|
100
|
+
timeout: requestTimeout,
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
logger
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
logger.info("Creating http server");
|
|
108
|
+
const proxyServer = http.createServer(function proxyRequestHandler(req, res) {
|
|
109
|
+
if (req.url === "/proxy") {
|
|
110
|
+
logger.debug("Forwarding request to execution layer");
|
|
111
|
+
proxy.web(req, res);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let payload: JsonRpcRequestPayload;
|
|
116
|
+
fetchRequestPayload(req)
|
|
117
|
+
.then((data) => {
|
|
118
|
+
payload = data;
|
|
119
|
+
return processAndVerifyRequest({payload, proofProvider, rpc, logger});
|
|
120
|
+
})
|
|
121
|
+
.then((response) => {
|
|
122
|
+
res.write(JSON.stringify(response));
|
|
123
|
+
res.end();
|
|
124
|
+
})
|
|
125
|
+
.catch((err) => {
|
|
126
|
+
logger.error("Error processing request", err);
|
|
127
|
+
const message = (err as Error).message;
|
|
128
|
+
if (isBatchRequest(payload)) {
|
|
129
|
+
res.write(JSON.stringify(payload.map((req) => getResponseForRequest(req, {message}))));
|
|
130
|
+
} else {
|
|
131
|
+
res.write(JSON.stringify(getResponseForRequest(payload, undefined, {message})));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
res.end();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
proxyServer.on("listening", () => {
|
|
139
|
+
const address = proxyServer.address();
|
|
140
|
+
|
|
141
|
+
if (address === null) {
|
|
142
|
+
throw new Error("Invalid proxy server address");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (typeof address === "string") {
|
|
146
|
+
const rawUrl = url.parse(address);
|
|
147
|
+
if (!rawUrl.host || !rawUrl.port || !rawUrl.protocol) {
|
|
148
|
+
throw new Error(`Invalid proxy server address: ${address}`);
|
|
149
|
+
}
|
|
150
|
+
proxyServerListeningAddress = {host: rawUrl.host, port: parseInt(rawUrl.port)};
|
|
151
|
+
} else {
|
|
152
|
+
proxyServerListeningAddress = {host: address.address, port: address.port};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
logger.info(
|
|
156
|
+
`Lodestar Prover Proxy listening on ${proxyServerListeningAddress.host}:${proxyServerListeningAddress.port}`
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
rpc.verifyCompatibility().catch((err) => {
|
|
160
|
+
logger.error(err);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
proxyServer.on("upgrade", function proxyRequestUpgrade(req, socket, head) {
|
|
166
|
+
logger.debug("Upgrading the ws connection");
|
|
167
|
+
proxy.ws(req, socket, head);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
signal.addEventListener("abort", () => {
|
|
171
|
+
proxyServer.close();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return {server: proxyServer, proofProvider};
|
|
175
|
+
}
|