Compare commits

...

61 Commits

Author SHA1 Message Date
semantic-release-bot c0b38b9014 chore(release): 0.1.0-develop.1 [skip ci]
# [0.1.0-develop.1](https://git.lumeweb.com/LumeWeb/kernel-eth/compare/v0.0.1...v0.1.0-develop.1) (2023-10-12)

### Bug Fixes

* add synced listener on eth client to flag the synced status ([5f64664](5f64664676))
* createEthClient needs logging functions and sync delay passed ([0324700](032470029c))

### Features

* add caching of the last consensus update via leveldb ([9f35ea7](9f35ea7f9b))
* add name api ([9370003](9370003a59))
* add register method for network registry ([e7d85a2](e7d85a2af4))
* add status api ([2a079f3](2a079f340f))
* Update to capella fork ([e62eadd](e62eadd436))
2023-10-12 15:27:04 +00:00
Derrick Hammer 6d5522cb28
ci: update tsconfig 2023-10-12 11:25:33 -04:00
Derrick Hammer ac0627ed55
dep: update npm-shrinkwrap.json 2023-10-12 11:23:11 -04:00
Derrick Hammer 0178ed3323
dep: remove overrides 2023-10-12 11:22:30 -04:00
Derrick Hammer 8438dd48e3 dep: update kernel-network-registry-client and libkernel 2023-10-12 09:46:47 -04:00
Derrick Hammer 1709d73106 dep: update deps 2023-09-16 17:58:51 -04:00
Derrick Hammer 4b82995188 dep: update libethsync 2023-09-16 12:10:17 -04:00
Derrick Hammer 9f35ea7f9b feat: add caching of the last consensus update via leveldb 2023-09-16 11:38:59 -04:00
Derrick Hammer 41704501f8 dep: update libethsync 2023-09-16 11:38:19 -04:00
Derrick Hammer f5a8c31096 dep: update kernel-network-registry-client 2023-09-09 17:36:48 -04:00
Derrick Hammer f81336bbbb dep: update kernel-rpc-client 2023-09-04 06:34:50 -04:00
Derrick Hammer d53def6a69 dep: update libkernel 2023-09-04 06:21:50 -04:00
Derrick Hammer 6c163966c4 dep: update deps 2023-09-03 01:17:49 -04:00
Derrick Hammer 97e560491d dep: update deps 2023-09-02 13:47:23 -04:00
Derrick Hammer 1ddf717071 dep: update kernel-rpc-client 2023-07-29 12:36:36 -04:00
Derrick Hammer 9370003a59 feat: add name api 2023-07-28 23:23:42 -04:00
Derrick Hammer abecdfa925 dep: update libethsync 2023-07-24 14:27:24 -04:00
Derrick Hammer 255e658e32 dep: update kernel-rpc-client 2023-07-24 14:12:55 -04:00
Derrick Hammer 4ee39c33d5 dep: update kernel-rpc-client 2023-07-23 14:50:50 -04:00
Derrick Hammer 5f64664676 fix: add synced listener on eth client to flag the synced status 2023-07-23 12:12:01 -04:00
Derrick Hammer bb0d3f1bb2 dep: update libethsync 2023-07-23 12:11:20 -04:00
Derrick Hammer 2a079f340f feat: add status api 2023-07-23 11:44:29 -04:00
Derrick Hammer 4a7feb9a98 refactor: use defers and add clientInitDefer 2023-07-23 11:43:57 -04:00
Derrick Hammer f45bf1c43a dep: update kernel-rpc-client 2023-07-22 20:22:47 -04:00
Derrick Hammer ebf54c59d7 dep: update kernel-rpc-client 2023-07-22 10:45:21 -04:00
Derrick Hammer 69a1e69817 dep: update kernel-rpc-client 2023-07-22 10:37:27 -04:00
Derrick Hammer 07681982d4 dep: update kernel-rpc-client 2023-07-22 07:29:33 -04:00
Derrick Hammer 7238b854e3 dep: update kernel-rpc-client 2023-07-21 22:06:46 -04:00
Derrick Hammer e7853dda19 dep: update kernel-rpc-client 2023-07-21 20:53:16 -04:00
Derrick Hammer ac5e92cb04 dep: update kernel-network-registry-client 2023-07-21 13:31:36 -04:00
Derrick Hammer 9e5aef62c0 dep: update kernel-network-registry-client 2023-07-19 13:34:18 -04:00
Derrick Hammer e7d85a2af4 feat: add register method for network registry 2023-07-19 12:41:48 -04:00
Derrick Hammer 032470029c fix: createEthClient needs logging functions and sync delay passed 2023-07-15 05:49:43 -04:00
Derrick Hammer 1e654d0057 dep: update libethsync 2023-07-15 05:49:14 -04:00
Derrick Hammer ba77a905a9 dep: unneeded patch 2023-07-14 03:49:40 -04:00
Derrick Hammer 08f001b7ae ci: move to vite 2023-07-14 03:49:19 -04:00
Derrick Hammer be573687a7 refactor: move to @lumeweb/libethsync library 2023-07-14 03:49:03 -04:00
Derrick Hammer 3699fb48ed
dep: update bls patch 2023-07-14 03:47:31 -04:00
Derrick Hammer a67aa1cb78
ci: add patch for rollup-plugin-ignore-import 2023-07-05 22:38:24 -04:00
Derrick Hammer 6e46ea3170
ci: setup 2023-07-05 22:34:37 -04:00
Derrick Hammer d1dbc3d94b
ci: add npm-shrinkwrap.json 2023-07-05 22:34:10 -04:00
Derrick Hammer 9b7a67b7ae
ci: add custom build 2023-07-05 22:33:57 -04:00
Derrick Hammer 78d192f53f
refactor: move to new sdks 2023-07-05 22:33:44 -04:00
Derrick Hammer e62eadd436
feat: Update to capella fork 2023-06-18 03:49:49 -04:00
Derrick Hammer 5a03605ac8
*update deps 2023-04-16 23:28:27 -04:00
Derrick Hammer c98686d931
*Use @chainsafe/bls fork that handles top-level await issues 2023-04-08 22:38:59 -04:00
Derrick Hammer 3fa23a339b
*Update deps 2023-04-08 21:48:41 -04:00
Derrick Hammer c498d7ed03
*Update deps 2023-03-29 17:07:38 -04:00
Derrick Hammer a03ae542b1
*Add rpc ready call to consensusHandler and executionHandler to ensure we have a relay before continuing 2023-03-29 17:07:25 -04:00
Derrick Hammer 62c6950957
*If we have missing storageProofs, log and throw an error 2023-03-29 11:42:50 -04:00
Derrick Hammer 191ef04b46
*Add support to sync from the last committee 2023-03-29 05:32:39 -04:00
Derrick Hammer 4ac1621e3f
*Switch to optimistic approach that uses the relay as a light proxy 2023-03-29 00:03:48 -04:00
Derrick Hammer c7c44a0cd2
*Remove logging 2023-03-28 07:26:04 -04:00
Derrick Hammer d9a3d30b7d
*Create new version that uses kevlar/patronum light JS client 2023-03-28 07:03:15 -04:00
Derrick Hammer b6b419ec06
*Formatting 2023-03-27 15:23:23 -04:00
Derrick Hammer a9c8990baa
*Need to json parse params 2023-03-27 15:23:09 -04:00
Derrick Hammer 3873bd47a2
*Refactor addHandler 2023-03-27 15:22:51 -04:00
Derrick Hammer b39af76606
*Add dummy handlers for eth_accounts and eth_requestAccounts 2023-03-27 15:22:32 -04:00
Derrick Hammer a473dfdad5
*Update LICENSE 2023-03-27 09:02:26 -04:00
Derrick Hammer 4112f09826
*Add LICENSE 2023-03-27 09:00:55 -04:00
Derrick Hammer af1c52d5b4
*add ready api method 2023-03-27 08:17:36 -04:00
13 changed files with 24973 additions and 495 deletions

13
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,13 @@
name: Build/Publish
on:
push:
branches:
- master
- develop
- develop-*
jobs:
main:
uses: lumeweb/github-node-deploy-workflow/.github/workflows/main.yml@master
secrets: inherit

28
.presetterrc.json Normal file
View File

@ -0,0 +1,28 @@
{
"preset": [
"@lumeweb/presetter-kernel-module-preset"
],
"config": {
"tsconfig": {
"compilerOptions": {
"module": "nodenext"
}
},
"vite": {
"resolve": {
"alias": {
"ethereum-cryptography/secp256k1": "ethereum-cryptography/secp256k1.js",
"ethereum-cryptography/keccak": "ethereum-cryptography/keccak.js",
"ethereum-cryptography/utils": "ethereum-cryptography/utils.js",
"ethereum-cryptography/sha256": "ethereum-cryptography/sha256.js",
"ethereum-cryptography/ripemd160": "ethereum-cryptography/ripemd160.js",
"bigint-mod-arith": "@lumeweb/bigint-mod-arith",
"bigint-crypto-utils": "@lumeweb/bigint-crypto-utils"
}
},
"optimize": {
"./blst-native/index.js": "export default undefined;"
}
}
}
}

16
CHANGELOG.md Normal file
View File

@ -0,0 +1,16 @@
# [0.1.0-develop.1](https://git.lumeweb.com/LumeWeb/kernel-eth/compare/v0.0.1...v0.1.0-develop.1) (2023-10-12)
### Bug Fixes
* add synced listener on eth client to flag the synced status ([5f64664](https://git.lumeweb.com/LumeWeb/kernel-eth/commit/5f64664676babae7065dc7560a5db780799c2c9e))
* createEthClient needs logging functions and sync delay passed ([0324700](https://git.lumeweb.com/LumeWeb/kernel-eth/commit/032470029cec8266b925ddb689512e18a580183b))
### Features
* add caching of the last consensus update via leveldb ([9f35ea7](https://git.lumeweb.com/LumeWeb/kernel-eth/commit/9f35ea7f9b31430a5840dc431fadd4cfd560759a))
* add name api ([9370003](https://git.lumeweb.com/LumeWeb/kernel-eth/commit/9370003a59d1dd0c343f6817c2940f7309703854))
* add register method for network registry ([e7d85a2](https://git.lumeweb.com/LumeWeb/kernel-eth/commit/e7d85a2af4c2325aae8b0a0e4c327a8f121fae6a))
* add status api ([2a079f3](https://git.lumeweb.com/LumeWeb/kernel-eth/commit/2a079f340f5f1a61809aebf92ea038a97665684a))
* Update to capella fork ([e62eadd](https://git.lumeweb.com/LumeWeb/kernel-eth/commit/e62eadd43617d4da0903d2d60d7c057966c5c71e))

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Hammer Technologies LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,48 +0,0 @@
import esbuild from "esbuild";
import { readFile } from "fs/promises";
import path from "path";
await esbuild.build({
entryPoints: ["src/index.ts"],
outfile: "dist/index.js",
format: "esm",
bundle: true,
legalComments: "external",
define: {
global: "self",
"import.meta": "true",
},
plugins: [
{
name: "base64",
setup(build) {
build.onResolve({ filter: /\?base64$/ }, (args) => {
return {
path: args.path,
pluginData: {
isAbsolute: path.isAbsolute(args.path),
resolveDir: args.resolveDir,
},
namespace: "base64-loader",
};
});
build.onLoad(
{ filter: /\?base64$/, namespace: "base64-loader" },
async (args) => {
const fullPath = args.pluginData.isAbsolute
? args.path
: path.join(args.pluginData.resolveDir, args.path);
return {
contents: Buffer.from(
await readFile(fullPath.replace(/\?base64$/, ""))
).toString("base64"),
loader: "text",
};
}
);
},
},
],
});
export {};

24576
npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,32 @@
{
"name": "@lumeweb/kernel-eth",
"version": "0.1.0",
"version": "0.1.0-develop.1",
"license": "MIT",
"type": "module",
"scripts": {
"build-script": "tsc --project tsconfig.build.json && mv dist-build/build.js dist-build/build.mjs",
"compile": "npm run build-script && rimraf node_modules/@lumeweb/dht-rpc-client/node_modules node_modules/@lumeweb/kernel-dht-client/node_modules/libkmodule && node build.js",
"build": "npm run compile && node ./dist-build/build.mjs dev"
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "gitea@git.lumeweb.com:LumeWeb/kernel-eth.git"
},
"dependencies": {
"@lumeweb/kernel-rpc-client": "git+https://git.lumeweb.com/LumeWeb/kernel-rpc-client.git",
"libkmodule": "^0.2.53",
"yaml": "^2.2.1"
"scripts": {
"prepare": "presetter bootstrap",
"build": "run build",
"semantic-release": "semantic-release",
"postinstall": "patch-package"
},
"devDependencies": {
"@lumeweb/interface-relay": "git+https://git.lumeweb.com/LumeWeb/interface-relay.git",
"@scure/bip39": "^1.2.0",
"@skynetlabs/skynet-nodejs": "^2.9.0",
"@types/node": "^18.15.9",
"@types/read": "^0.0.29",
"buffer": "^6.0.3",
"cli-progress": "^3.12.0",
"esbuild": "^0.17.13",
"esbuild-plugin-wasm": "^1.0.0",
"prettier": "^2.8.7",
"read": "^2.0.0",
"typescript": "^5.0.2"
"@lumeweb/presetter-kernel-module-preset": "0.1.0-develop.42",
"@rollup/plugin-alias": "^5.0.0",
"patch-package": "^7.0.0",
"rollup-plugin-ignore-import": "^1.3.2"
},
"dependencies": {
"@lumeweb/bigint-crypto-utils": "1.0.0",
"@lumeweb/kernel-network-registry-client": "^0.1.0-develop.10",
"@lumeweb/kernel-rpc-client": "0.0.2-develop.17",
"@lumeweb/libethsync": "0.1.0-develop.61",
"@lumeweb/libkernel": "0.1.0-develop.66",
"level": "^8.0.0",
"p-defer": "^4.0.0"
}
}

View File

@ -0,0 +1,89 @@
diff --git a/node_modules/@chainsafe/bls/lib/getImplementation.js b/node_modules/@chainsafe/bls/lib/getImplementation.js
index 9137cd4..ba8de8a 100644
--- a/node_modules/@chainsafe/bls/lib/getImplementation.js
+++ b/node_modules/@chainsafe/bls/lib/getImplementation.js
@@ -3,7 +3,7 @@ const isNode = Object.prototype.toString.call(typeof process !== "undefined" ? p
export async function getImplementation(impl = "herumi") {
switch (impl) {
case "herumi": {
- return (await import("./herumi/index.js")).bls;
+ return (await import("./herumi/index.js")).bls();
}
case "blst-native":
// Lazy import native bindings to prevent automatically importing binding.node files
diff --git a/node_modules/@chainsafe/bls/lib/herumi/index.js b/node_modules/@chainsafe/bls/lib/herumi/index.js
index 4ed8fdd..f979984 100644
--- a/node_modules/@chainsafe/bls/lib/herumi/index.js
+++ b/node_modules/@chainsafe/bls/lib/herumi/index.js
@@ -1,16 +1,20 @@
-import { SecretKey } from "./secretKey.js";
-import { PublicKey } from "./publicKey.js";
-import { Signature } from "./signature.js";
-import { init, destroy } from "./context.js";
-import { functionalInterfaceFactory } from "../functional.js";
-await init();
-export * from "../constants.js";
-export { SecretKey, PublicKey, Signature, init, destroy };
-export const bls = {
- implementation: "herumi",
- SecretKey,
- PublicKey,
- Signature,
- ...functionalInterfaceFactory({ SecretKey, PublicKey, Signature }),
-};
-export default bls;
+import { SecretKey } from './secretKey.js'
+import { PublicKey } from './publicKey.js'
+import { Signature } from './signature.js'
+import { init, destroy } from './context.js'
+import { functionalInterfaceFactory } from '../functional.js'
+
+export * from '../constants.js'
+export { SecretKey, PublicKey, Signature, init, destroy }
+
+export const bls = async () => {
+ await init()
+ return {
+ implementation: 'herumi',
+ SecretKey,
+ PublicKey,
+ Signature,
+ ...functionalInterfaceFactory({ SecretKey, PublicKey, Signature }),
+ }
+}
+export default bls
diff --git a/node_modules/@chainsafe/bls/lib/index.d.ts b/node_modules/@chainsafe/bls/lib/index.d.ts
index 35a9432..097a938 100644
--- a/node_modules/@chainsafe/bls/lib/index.d.ts
+++ b/node_modules/@chainsafe/bls/lib/index.d.ts
@@ -1,3 +1,3 @@
import type { IBls } from "./types.js";
-declare let bls: IBls;
+export declare const bls: () => Promise<IBls>;
export default bls;
diff --git a/node_modules/@chainsafe/bls/lib/index.js b/node_modules/@chainsafe/bls/lib/index.js
index c2a5bdf..9572018 100644
--- a/node_modules/@chainsafe/bls/lib/index.js
+++ b/node_modules/@chainsafe/bls/lib/index.js
@@ -1,11 +1,14 @@
import { getImplementation } from "./getImplementation.js";
// Thanks https://github.com/iliakan/detect-node/blob/master/index.esm.js
const isNode = Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) === "[object process]";
-let bls;
-try {
- bls = await getImplementation(isNode ? "blst-native" : "herumi");
-}
-catch (e) {
- bls = await getImplementation("herumi");
-}
+export const bls = async () => {
+ let bls;
+ try {
+ bls = await getImplementation(isNode ? "blst-native" : "herumi");
+ }
+ catch (e) {
+ bls = await getImplementation("herumi");
+ }
+ return bls;
+};
export default bls;

View File

@ -1,218 +0,0 @@
// This is the standard build script for a kernel module.
import * as fs from "fs";
import read from "read";
import * as bip39 from "@scure/bip39";
import { wordlist } from "@scure/bip39/wordlists/english.js";
//@ts-ignore
import { SkynetClient } from "@skynetlabs/skynet-nodejs";
// Helper variables to make it easier to return empty values alongside errors.
const nu8 = new Uint8Array(0);
const nkp = {
publicKey: nu8,
secretKey: nu8,
};
// readFile is a wrapper for fs.readFileSync that handles the try-catch for the
// caller.
function readFile(fileName: string): [string, string | null] {
try {
let data = fs.readFileSync(fileName, "utf8");
return [data, null];
} catch (err) {
return ["", "unable to read file: " + JSON.stringify(err)];
}
}
// readFileBinary is a wrapper for fs.readFileSync that handles the try-catch
// for the caller.
function readFileBinary(fileName: string): [Uint8Array, string | null] {
try {
let data = fs.readFileSync(fileName, null);
return [data, null];
} catch (err) {
return [nu8, "unable to read file: " + JSON.stringify(err)];
}
}
// writeFile is a wrapper for fs.writeFileSync which handles the try-catch in a
// non-exception way.
function writeFile(fileName: string, fileData: string): string | null {
try {
fs.writeFileSync(fileName, fileData);
return null;
} catch (err) {
return "unable to write file: " + JSON.stringify(err);
}
}
// handlePass handles all portions of the script that occur after the password
// has been requested. If no password needs to be requested, handlePass will be
// called with a null input. We need to structure the code this way because the
// password reader is async and we can only access the password when using a
// callback.
function handlePass(password: string) {
try {
// If we are running prod and the seed file does not exist, we
// need to confirm the password and also warn the user to use a
// secure password.
if (!fs.existsSync(seedFile) && process.argv[2] === "prod") {
// The file does not exist, we need to confirm the
// password.
console.log();
console.log(
"No production entry found for module. Creating new production module..."
);
console.log(
"If someone can guess the password, they can push arbitrary changes to your module."
);
console.log("Please use a secure password.");
console.log();
read(
{ prompt: "Confirm Password: ", silent: true },
function (err: any, confirmPassword: string) {
if (err) {
console.error("unable to fetch password:", err);
process.exit(1);
}
if (password !== confirmPassword) {
console.error("passwords do not match");
process.exit(1);
}
handlePassConfirm(moduleSalt, password);
}
);
} else {
// If the seed file does exist, or if we are using dev,
// there's no need to confirm the password but we do
// need to pass the logic off to the handlePassConfirm
// callback.
handlePassConfirm(moduleSalt, password);
}
} catch (err) {
console.error("Unable to read seedFile:", err);
process.exit(1);
}
}
// handlePassConfirm handles the full script after the confirmation password
// has been provided. If not confirmation password is needed, this function
// will be called anyway using the unconfirmed password as input.
function handlePassConfirm(seed: string, password: string) {
// Create the seedFile if it does not exist. For dev we just save the
// seed to disk outright, because this is a dev build and therefore not
// security sensitive. Also the dev seed does not get pushed to the
// github repo.
//
// For prod, we use the seed to create a new seed (called the shield)
// which allows us to verify that the developer has provided the right
// password when deploying the module. The shield does get pushed to
// the github repo so that the production module is the same on all
// devices.
if (!fs.existsSync(seedFile) && process.argv[2] !== "prod") {
// Generate the seed phrase and write it to the file.
let seedPhrase = bip39.generateMnemonic(wordlist);
let errWF = writeFile(seedFile, seedPhrase);
if (errWF !== null) {
console.error("unable to write file:", errWF);
process.exit(1);
}
} else if (!fs.existsSync(seedFile) && process.argv[2] === "prod") {
// Generate the seed phrase.
let seedPhrase = bip39.generateMnemonic(wordlist);
// Write the registry link to the file.
}
// Load or verify the seed. If this is prod, the password is used to
// create and verify the seed. If this is dev, we just load the seed
// with no password.
let seedPhrase: string;
let registryLink: string;
if (process.argv[2] === "prod") {
// Generate the seed phrase from the password.
seedPhrase = bip39.generateMnemonic(wordlist);
} else {
let [sp, errRF] = readFile(seedFile);
if (errRF !== null) {
console.error("unable to read seed phrase for dev command from disk");
process.exit(1);
}
seedPhrase = sp;
}
let metadata = {
Filename: "index.js",
};
const client = new SkynetClient("https://web3portal.com");
client
.uploadFile("dist/index.js")
.then((result: any) => {
console.log("Immutable Link for module:", result);
})
.catch((err: any) => {
console.error("unable to upload file", err);
process.exit(1);
});
}
// Add a newline for readability.
console.log();
// Check for a 'dev' or 'prod' input to the script.
if (process.argv.length !== 3) {
console.error("need to provide either 'dev' or 'prod' as an input");
process.exit(1);
}
// Create the build folder if it does not exist.
if (!fs.existsSync("build")) {
fs.mkdirSync("build");
}
// Determine the seed file.
let seedFile: string;
if (process.argv[2] === "prod") {
seedFile = "module-skylink";
} else if (process.argv[2] === "dev") {
seedFile = "build/dev-seed";
} else {
console.error("need to provide either 'dev' or 'prod' as an input");
process.exit(1);
}
// If doing a prod deployment, check whether the salt file exists. If it does
// not, create it.
let moduleSalt: string;
if (!fs.existsSync(".module-salt")) {
moduleSalt = bip39.generateMnemonic(wordlist);
let errWF = writeFile(".module-salt", moduleSalt);
if (errWF !== null) {
console.error("unable to write module salt file:", errWF);
process.exit(1);
}
} else {
let [ms, errRF] = readFile(".module-salt");
if (errRF !== null) {
console.error("unable to read moduleSalt");
process.exit(1);
}
ms = ms.replace(/\n$/, "");
moduleSalt = ms;
}
// Need to get a password if this is a prod build.
if (process.argv[2] === "prod") {
read(
{ prompt: "Password: ", silent: true },
function (err: any, password: string) {
if (err) {
console.error("unable to fetch password:", err);
process.exit(1);
}
handlePass(password);
}
);
} else {
handlePass("");
}

View File

@ -1,28 +1,47 @@
import { ActiveQuery, addHandler, handleMessage } from "libkmodule";
import { createClient, RpcNetwork } from "@lumeweb/kernel-rpc-client";
import init, { Client } from "../wasm/helios_ts.js";
// @ts-ignore
import wasm from "../wasm/helios_ts_bg.wasm?base64";
import { Buffer } from "buffer";
import { RPCResponse } from "@lumeweb/interface-relay";
import { ConsensusRequest, ExecutionRequest } from "./types.js";
const CHECKPOINT =
"0x694433ba78dd08280df68d3713c0f79d668dbee9e0922ec2346fcceb1dc3daa9";
import {
ActiveQuery,
addHandler,
handleMessage,
log,
logErr,
} from "@lumeweb/libkernel/module";
import {
createClient as createRpcClient,
RpcNetwork,
} from "@lumeweb/kernel-rpc-client";
import { createClient as createNetworkRegistryClient } from "@lumeweb/kernel-network-registry-client";
import {
Client as EthClient,
ConsensusCommitteeUpdateRequest,
createDefaultClient as createEthClient,
} from "@lumeweb/libethsync/client";
import * as capella from "@lodestar/types/capella";
import defer from "p-defer";
import { Level } from "level";
onmessage = handleMessage;
let moduleReadyResolve: Function;
let moduleReady: Promise<void> = new Promise((resolve) => {
moduleReadyResolve = resolve;
const TYPES = ["blockchain"];
const networkRegistry = createNetworkRegistryClient();
const moduleReadyDefer = defer();
const clientInitDefer = defer();
let client: EthClient;
let rpc: RpcNetwork;
const db = new Level<number | string, Uint8Array>("consensus", {
valueEncoding: "buffer",
});
let client: Client;
let rpc: RpcNetwork;
addHandler("presentSeed", handlePresentSeed);
addHandler("presentKey", handlePresentKey);
addHandler("register", handleRegister);
addHandler("status", handleStatus, { receiveUpdates: true });
addHandler("name", handleName);
addHandler("ready", handleReady);
[
"eth_accounts",
"eth_requestAccounts",
"eth_getBalance",
"eth_chainId",
"eth_blockNumber",
@ -40,176 +59,191 @@ addHandler("presentSeed", handlePresentSeed);
"eth_getLogs",
"net_version",
].forEach((rpcMethod) => {
addHandler(rpcMethod, (aq: ActiveQuery) => {
aq.callerInput = aq.callerInput || {};
aq.callerInput.method = rpcMethod;
handleRpcMethod(aq);
addHandler(rpcMethod, async (aq: ActiveQuery) => {
aq.callerInput = {
params: aq.callerInput || {},
method: rpcMethod,
};
try {
const ret = await handleRpcMethod(aq);
aq.respond(ret);
} catch (e: any) {
aq.reject((e as Error).message);
}
});
});
async function handlePresentSeed() {
async function handlePresentKey() {
await setup();
moduleReadyResolve();
moduleReadyDefer.resolve();
}
async function handleRpcMethod(aq: ActiveQuery) {
await moduleReady;
switch (aq.callerInput?.method) {
case "eth_getBalance": {
return client.get_balance(
aq.callerInput?.params[0],
aq.callerInput?.params[1]
);
await moduleReadyDefer.promise;
if (!client.isSynced) {
await client.sync();
}
case "eth_chainId": {
return client.chain_id();
return client.rpcCall(aq.callerInput?.method, aq.callerInput?.params);
}
async function consensusHandler(method: string, data: any) {
await rpc.ready;
while (true) {
let query = await rpc.simpleQuery({
query: {
module: "eth",
method,
data,
},
options: {
relayTimeout: 30,
queryTimeout: 30,
},
});
const ret = await query.result;
if (ret.data) {
return ret.data;
}
case "eth_blockNumber": {
return client.get_block_number();
}
case "eth_getTransactionByHash": {
let tx = await client.get_transaction_by_hash(aq.callerInput?.params[0]);
return mapToObj(tx);
}
case "eth_getTransactionCount": {
return client.get_transaction_count(
aq.callerInput?.params[0],
aq.callerInput?.params[1]
);
}
case "eth_getBlockTransactionCountByHash": {
return client.get_block_transaction_count_by_hash(
aq.callerInput?.params[0]
);
}
case "eth_getBlockTransactionCountByNumber": {
return client.get_block_transaction_count_by_number(
aq.callerInput?.params[0]
);
}
case "eth_getCode": {
return client.get_code(
aq.callerInput?.params[0],
aq.callerInput?.params[1]
);
}
case "eth_call": {
return client.call(aq.callerInput?.params[0], aq.callerInput?.params[1]);
}
case "eth_estimateGas": {
return client.estimate_gas(aq.callerInput?.params[0]);
}
case "eth_gasPrice": {
return client.gas_price();
}
case "eth_maxPriorityFeePerGas": {
return client.max_priority_fee_per_gas();
}
case "eth_sendRawTransaction": {
return client.send_raw_transaction(aq.callerInput?.params[0]);
}
case "eth_getTransactionReceipt": {
return client.get_transaction_receipt(aq.callerInput?.params[0]);
}
case "eth_getLogs": {
return client.get_logs(aq.callerInput?.params[0]);
}
case "net_version": {
return client.chain_id();
}
async function executionHandler(data: Map<string, string | any>) {
await rpc.ready;
while (true) {
let query = await rpc.simpleQuery({
query: {
module: "eth",
method: "execution_request",
data,
},
options: {
relayTimeout: 30,
queryTimeout: 30,
},
});
let ret = await query.result;
if (ret.data) {
return ret.data;
}
}
}
async function setup() {
rpc = createClient();
rpc = createRpcClient();
await db.open();
await rpc.ready;
// @ts-ignore
await (
await rpc.ready
)();
client = createEthClient(
async (args: ConsensusCommitteeUpdateRequest) => {
const updates = await consensusHandler("consensus_updates", args);
await init(URL.createObjectURL(new Blob([Buffer.from(wasm, "base64")])));
(self as any).consensus_rpc_handler = async (
data: Map<string, string | any>
) => {
const method = data.get("method");
const path = data.get("path");
let query;
let ret: RPCResponse;
while (true) {
query = await rpc.simpleQuery({
query: {
module: "eth",
method: "consensus_request",
data: {
method,
path,
} as ConsensusRequest,
return updates
.map((u) => new Uint8Array(Object.values(u)))
.map((u) => capella.ssz.LightClientUpdate.deserialize(u))
.map((u) => capella.ssz.LightClientUpdate.toJson(u));
},
options: {
relayTimeout: 10,
queryTimeout: 10,
executionHandler,
async () => {
const update = await consensusHandler("consensus_optimistic_update", {});
return capella.ssz.LightClientOptimisticUpdate.deserialize(
new Uint8Array(Object.values(update)),
);
},
log,
logErr,
500,
);
let lastUpdate = 0;
client.store.on("set", async (period: number, update: Uint8Array) => {
if (period < lastUpdate) {
return;
}
await db.put("latest", update);
lastUpdate = period;
});
ret = await query.result;
if (ret?.data) {
break;
}
}
clientInitDefer.resolve();
if (path.startsWith("/eth/v1/beacon/light_client/updates")) {
return JSON.stringify(ret.data);
}
let synced = false;
return JSON.stringify({ data: ret.data });
};
(self as any).execution_rpc_handler = async (
data: Map<string, string | any>
) => {
const method = data.get("method");
let params = data.get("params");
while (!synced) {
try {
let consensus;
try {
consensus = await db.get("latest");
} catch {}
if (params === "null") {
params = null;
}
let query;
let ret: RPCResponse;
while (true) {
query = await rpc.simpleQuery({
query: {
module: "eth",
method: "execution_request",
data: {
method,
params,
} as ExecutionRequest,
},
});
ret = await query.result;
if (ret?.data) {
break;
}
}
return JSON.stringify(ret.data);
};
client = new Client(CHECKPOINT);
if (consensus) {
try {
await client.syncFromCheckpoint(
capella.ssz.LightClientUpdate.deserialize(consensus),
);
} catch {
await client.sync();
}
} else {
await client.sync();
}
synced = true;
} catch (e) {
logErr(e.message);
}
}
}
function mapToObj(map: Map<any, any> | undefined): Object | undefined {
if (!map) return undefined;
async function handleReady(aq: ActiveQuery) {
await moduleReadyDefer.promise;
return Array.from(map).reduce((obj: any, [key, value]) => {
obj[key] = value;
return obj;
}, {});
aq.respond();
}
async function handleRegister(aq: ActiveQuery) {
await networkRegistry.registerNetwork(TYPES);
aq.respond();
}
async function handleStatus(aq: ActiveQuery) {
await clientInitDefer.promise;
let chainProgress = 0;
const chainProgressListener = (currentUpdate, totalUpdates) => {
chainProgress = Math.round((currentUpdate / totalUpdates) * 100) / 100;
sendUpdate();
};
const chainSyncedListener = () => {
sendUpdate();
};
client.on("update", chainProgressListener);
client.on("synced", chainSyncedListener);
function sendUpdate() {
aq.sendUpdate({
sync: Math.floor(chainProgress * 100),
peers: 1,
ready: client.isSynced,
});
}
aq.setReceiveUpdate?.(() => {
client.off("update", chainProgressListener);
client.off("synced", chainSyncedListener);
aq.respond();
});
sendUpdate();
}
function handleName(aq: ActiveQuery) {
aq.respond("Ethereum");
}

View File

@ -1,8 +0,0 @@
export interface ConsensusRequest extends RequestInit {
path: string;
}
export interface ExecutionRequest {
method: string;
params: string;
}

View File

@ -1,14 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"declaration": true,
"outDir": "./dist-build",
"strict": true,
"esModuleInterop": true
},
"include": ["src-build"],
"exclude": ["node_modules", "**/__tests__/*"]
}

View File

@ -1,13 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"declaration": true,
"moduleResolution": "node",
"outDir": "./build",
"strict": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"]
}