*Update for new system design and use light client

This commit is contained in:
Derrick Hammer 2023-02-20 11:21:52 -05:00
parent 4a224bd8d6
commit 575b3198fb
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
6 changed files with 75 additions and 222 deletions

View File

@ -3,7 +3,7 @@ import esbuild from "esbuild"
esbuild.buildSync({ esbuild.buildSync({
entryPoints: ['src-module/index.ts'], entryPoints: ['src-module/index.ts'],
outfile: 'dist-module/index.js', outfile: 'dist-module/index.js',
format: 'esm', format: 'iife',
bundle: true, bundle: true,
legalComments: 'external', legalComments: 'external',
// minify: true // minify: true

View File

@ -10,16 +10,23 @@
"build-module": "npm run compile-module && node ./dist-build/build.mjs dev" "build-module": "npm run compile-module && node ./dist-build/build.mjs dev"
}, },
"dependencies": { "dependencies": {
"@lumeweb/kernel-libresolver": "https://github.com/LumeWeb/kernel-libresolver.git", "@lumeweb/kernel-handshake-client": "git+https://git.lumeweb.com/LumeWeb/kernel-handshake-client.git",
"@lumeweb/libresolver": "https://github.com/LumeWeb/libresolver.git", "@lumeweb/kernel-libresolver": "git+https://git.lumeweb.com/LumeWeb/kernel-libresolver.git",
"@lumeweb/tld-enum": "github:LumeWeb/list-of-top-level-domains" "@lumeweb/libresolver": "git+https://git.lumeweb.com/LumeWeb/libresolver.git",
"@lumeweb/tld-enum": "git+https://git.lumeweb.com/LumeWeb/list-of-top-level-domains.git"
}, },
"devDependencies": { "devDependencies": {
"@lumeweb/relay-types": "https://github.com/LumeWeb/relay-types.git", "@lumeweb/cfg": "git+https://git.lumeweb.com/LumeWeb/cfg.git",
"@types/node": "^18.7.8", "@lumeweb/relay-types": "git+https://git.lumeweb.com/LumeWeb/relay-types.git",
"@scure/bip39": "^1.1.1",
"@skynetlabs/skynet-nodejs": "^2.9.0",
"@types/node": "^18.14.0",
"@types/read": "^0.0.29", "@types/read": "^0.0.29",
"esbuild": "^0.15.5", "cli-progress": "^3.12.0",
"esbuild": "^0.15.18",
"libskynetnode": "^0.1.4", "libskynetnode": "^0.1.4",
"read": "^1.0.7" "node-cache": "^5.1.2",
"read": "^1.0.7",
"typescript": "^4.9.5"
} }
} }

View File

@ -1,23 +1,11 @@
// This is the standard build script for a kernel module. // This is the standard build script for a kernel module.
import * as fs from "fs"; import * as fs from "fs";
import {
addContextToErr,
b64ToBuf,
bufToHex,
deriveRegistryEntryID,
entryIDToSkylink,
generateSeedPhraseDeterministic,
seedPhraseToSeed,
sha512,
taggedRegistryEntryKeys,
} from "libskynet";
import {
generateSeedPhraseRandom,
overwriteRegistryEntry,
upload,
} from "libskynetnode";
import read from "read"; 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. // Helper variables to make it easier to return empty values alongside errors.
const nu8 = new Uint8Array(0); const nu8 = new Uint8Array(0);
@ -30,7 +18,7 @@ const nkp = {
// caller. // caller.
function readFile(fileName: string): [string, string | null] { function readFile(fileName: string): [string, string | null] {
try { try {
const data = fs.readFileSync(fileName, "utf8"); let data = fs.readFileSync(fileName, "utf8");
return [data, null]; return [data, null];
} catch (err) { } catch (err) {
return ["", "unable to read file: " + JSON.stringify(err)]; return ["", "unable to read file: " + JSON.stringify(err)];
@ -41,7 +29,7 @@ function readFile(fileName: string): [string, string | null] {
// for the caller. // for the caller.
function readFileBinary(fileName: string): [Uint8Array, string | null] { function readFileBinary(fileName: string): [Uint8Array, string | null] {
try { try {
const data = fs.readFileSync(fileName, null); let data = fs.readFileSync(fileName, null);
return [data, null]; return [data, null];
} catch (err) { } catch (err) {
return [nu8, "unable to read file: " + JSON.stringify(err)]; return [nu8, "unable to read file: " + JSON.stringify(err)];
@ -59,60 +47,6 @@ function writeFile(fileName: string, fileData: string): string | null {
} }
} }
// hardenedSeedPhrase will take a password, harden it with 100,000 iterations
// of hashing, and then turn it into a seed phrase.
function hardenedSeedPhrase(password: string): [string, string | null] {
// Add some hashing iterations to the password to make it stronger.
for (let i = 0; i < 1000000; i++) {
const passU8 = new TextEncoder().encode(password);
const hashIter = sha512(passU8);
password = bufToHex(hashIter);
}
return generateSeedPhraseDeterministic(password);
}
// seedPhraseToRegistryKeys will convert a seed phrase to the set of registry
// keys that govern the registry entry where the module is published.
function seedPhraseToRegistryKeys(
seedPhrase: string
): [any, Uint8Array, string | null] {
const [seed, errVSP] = seedPhraseToSeed(seedPhrase);
if (errVSP !== null) {
return [nkp, nu8, addContextToErr(errVSP, "unable to compute seed phrase")];
}
const [keypair, datakey, errTREK] = taggedRegistryEntryKeys(
seed,
"module-build",
"module-key"
);
if (errTREK !== null) {
return [
nkp,
nu8,
addContextToErr(errTREK, "unable to compute registry entry keys"),
];
}
return [keypair, datakey, null];
}
// seedPhraseToRegistryLink will take a seedPhrase as input and convert it to
// the registry link for the module.
function seedPhraseToRegistryLink(seedPhrase: string): [string, string | null] {
const [keypair, datakey, errSPTRK] = seedPhraseToRegistryKeys(seedPhrase);
if (errSPTRK !== null) {
return ["", addContextToErr(errSPTRK, "unable to compute registry keys")];
}
const [entryID, errDREID] = deriveRegistryEntryID(keypair.publicKey, datakey);
if (errDREID !== null) {
return [
"",
addContextToErr(errDREID, "unable to compute registry entry id"),
];
}
const registryLink = entryIDToSkylink(entryID);
return [registryLink, null];
}
// handlePass handles all portions of the script that occur after the password // 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 // 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 // called with a null input. We need to structure the code this way because the
@ -146,8 +80,7 @@ function handlePass(password: string) {
console.error("passwords do not match"); console.error("passwords do not match");
process.exit(1); process.exit(1);
} }
password = password + moduleSalt; handlePassConfirm(moduleSalt, password);
handlePassConfirm(password);
} }
); );
} else { } else {
@ -155,8 +88,7 @@ function handlePass(password: string) {
// there's no need to confirm the password but we do // there's no need to confirm the password but we do
// need to pass the logic off to the handlePassConfirm // need to pass the logic off to the handlePassConfirm
// callback. // callback.
password = password + moduleSalt; handlePassConfirm(moduleSalt, password);
handlePassConfirm(password);
} }
} catch (err) { } catch (err) {
console.error("Unable to read seedFile:", err); console.error("Unable to read seedFile:", err);
@ -167,7 +99,7 @@ function handlePass(password: string) {
// handlePassConfirm handles the full script after the confirmation password // handlePassConfirm handles the full script after the confirmation password
// has been provided. If not confirmation password is needed, this function // has been provided. If not confirmation password is needed, this function
// will be called anyway using the unconfirmed password as input. // will be called anyway using the unconfirmed password as input.
function handlePassConfirm(password: string) { function handlePassConfirm(seed: string, password: string) {
// Create the seedFile if it does not exist. For dev we just save the // 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 // 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 // security sensitive. Also the dev seed does not get pushed to the
@ -180,35 +112,16 @@ function handlePassConfirm(password: string) {
// devices. // devices.
if (!fs.existsSync(seedFile) && process.argv[2] !== "prod") { if (!fs.existsSync(seedFile) && process.argv[2] !== "prod") {
// Generate the seed phrase and write it to the file. // Generate the seed phrase and write it to the file.
const [seedPhrase, errGSP] = generateSeedPhraseRandom(); let seedPhrase = bip39.generateMnemonic(wordlist);
if (errGSP !== null) { let errWF = writeFile(seedFile, seedPhrase);
console.error("Unable to generate seed phrase:", errGSP);
process.exit(1);
}
const errWF = writeFile(seedFile, seedPhrase);
if (errWF !== null) { if (errWF !== null) {
console.error("unable to write file:", errWF); console.error("unable to write file:", errWF);
process.exit(1); process.exit(1);
} }
} else if (!fs.existsSync(seedFile) && process.argv[2] === "prod") { } else if (!fs.existsSync(seedFile) && process.argv[2] === "prod") {
// Generate the seed phrase. // Generate the seed phrase.
const [seedPhrase, errGSP] = hardenedSeedPhrase(password); let seedPhrase = bip39.generateMnemonic(wordlist);
if (errGSP !== null) {
console.error("Unable to generate seed phrase:", errGSP);
process.exit(1);
}
const [registryLink, errSPTRL] = seedPhraseToRegistryLink(seedPhrase);
if (errSPTRL !== null) {
console.error("Unable to generate registry link:", errSPTRL);
process.exit(1);
}
// Write the registry link to the file. // Write the registry link to the file.
const errWF = writeFile(seedFile, registryLink);
if (errWF !== null) {
console.error("unable to write registry link file:", errWF);
process.exit(1);
}
} }
// Load or verify the seed. If this is prod, the password is used to // Load or verify the seed. If this is prod, the password is used to
@ -218,85 +131,26 @@ function handlePassConfirm(password: string) {
let registryLink: string; let registryLink: string;
if (process.argv[2] === "prod") { if (process.argv[2] === "prod") {
// Generate the seed phrase from the password. // Generate the seed phrase from the password.
const [sp, errGSP] = hardenedSeedPhrase(password); seedPhrase = bip39.generateMnemonic(wordlist);
if (errGSP !== null) {
console.error("Unable to generate seed phrase: ", errGSP);
process.exit(1);
}
const [rl, errSPTRL] = seedPhraseToRegistryLink(sp);
registryLink = rl;
if (errSPTRL !== null) {
console.error("Unable to generate registry link:", errSPTRL);
process.exit(1);
}
const [registryLinkVerify, errRF] = readFile(seedFile);
if (errRF !== null) {
console.error("unable to read seedFile");
process.exit(1);
}
const replacedRegistryLinkVerify = registryLinkVerify.replace(/\n$/, "");
if (registryLink !== replacedRegistryLinkVerify) {
console.error("Incorrect password");
process.exit(1);
}
seedPhrase = sp;
} else { } else {
const [sp, errRF] = readFile(seedFile); let [sp, errRF] = readFile(seedFile);
if (errRF !== null) { if (errRF !== null) {
console.error("unable to read seed phrase for dev command from disk"); console.error("unable to read seed phrase for dev command from disk");
process.exit(1); process.exit(1);
} }
const [rl, errSPTRL] = seedPhraseToRegistryLink(sp);
registryLink = rl;
if (errSPTRL !== null) {
console.error("Unable to generate registry link:", errSPTRL);
process.exit(1);
}
// Write the registry link to the module skylink dev file.
const errWF = writeFile("build/module-skylink-dev", registryLink);
if (errWF !== null) {
console.error("unable to write registry link file:", errWF);
process.exit(1);
}
seedPhrase = sp; seedPhrase = sp;
} }
// Upload the module to Skynet. let metadata = {
const [distFile, errRF] = readFileBinary("dist-module/index.js");
if (errRF !== null) {
console.error("unable to read dist file for module");
process.exit(1);
}
const metadata = {
Filename: "index.js", Filename: "index.js",
}; };
console.log("Uploading module..."); const client = new SkynetClient("https://web3portal.com");
upload(distFile, metadata) client
.then((result) => { .uploadFile("dist-module/index.js")
console.log("Updating module's registry entry..."); .then((result: any) => {
// Update the v2 skylink. console.log("Immutable Link for kernel:", result);
const [keypair, datakey, errSPTRK] = seedPhraseToRegistryKeys(seedPhrase);
if (errSPTRK !== null) {
return [
"",
addContextToErr(errSPTRK, "unable to compute registry keys"),
];
}
const [bufLink, errBTB] = b64ToBuf(result);
if (errBTB !== null) {
return ["", addContextToErr(errBTB, "unable to decode skylink")];
}
overwriteRegistryEntry(keypair, datakey, bufLink)
.then(() => {
console.log("registry entry is updated");
console.log("Immutable Link for Module:", result);
console.log("Resolver Link for Module:", registryLink);
})
.catch((err: any) => {
console.log("unable to update registry entry:", err);
});
}) })
.catch((err) => { .catch((err: any) => {
console.error("unable to upload file", err); console.error("unable to upload file", err);
process.exit(1); process.exit(1);
}); });
@ -331,25 +185,20 @@ if (process.argv[2] === "prod") {
// not, create it. // not, create it.
let moduleSalt: string; let moduleSalt: string;
if (!fs.existsSync(".module-salt")) { if (!fs.existsSync(".module-salt")) {
const [ms, errGSPR] = generateSeedPhraseRandom(); moduleSalt = bip39.generateMnemonic(wordlist);
if (errGSPR !== null) { let errWF = writeFile(".module-salt", moduleSalt);
console.error("unable to generate module salt:", errGSPR);
process.exit(1);
}
moduleSalt = ms;
const errWF = writeFile(".module-salt", moduleSalt);
if (errWF !== null) { if (errWF !== null) {
console.error("unable to write module salt file:", errWF); console.error("unable to write module salt file:", errWF);
process.exit(1); process.exit(1);
} }
} else { } else {
const [ms, errRF] = readFile(".module-salt"); let [ms, errRF] = readFile(".module-salt");
if (errRF !== null) { if (errRF !== null) {
console.error("unable to read moduleSalt"); console.error("unable to read moduleSalt");
process.exit(1); process.exit(1);
} }
const replaceMS = ms.replace(/\n$/, ""); ms = ms.replace(/\n$/, "");
moduleSalt = replaceMS; moduleSalt = ms;
} }
// Need to get a password if this is a prod build. // Need to get a password if this is a prod build.

View File

@ -1,4 +1,4 @@
import { setup } from "@lumeweb/kernel-libresolver"; import { setup } from "@lumeweb/kernel-libresolver";
import Handshake from "../src/index.js"; import Handshake from "../src/index.js";
setup(Handshake); setup(new Handshake());

View File

@ -15,7 +15,13 @@ import {
getTld, getTld,
resolverError, resolverError,
} from "@lumeweb/libresolver"; } from "@lumeweb/libresolver";
import { RPCResponse } from "@lumeweb/relay-types"; import {
createClient,
Response as HandshakeResponse,
} from "@lumeweb/kernel-handshake-client";
import { ResolverModule } from "@lumeweb/kernel-libresolver";
const client = createClient();
const HIP5_EXTENSIONS = ["eth", "_eth"]; const HIP5_EXTENSIONS = ["eth", "_eth"];
@ -26,26 +32,20 @@ interface HnsRecord {
ns: string; ns: string;
} }
interface HandshakeRPCResponse extends RPCResponse {
data?: {
records: HnsRecord[];
};
}
export default class Handshake extends AbstractResolverModule { export default class Handshake extends AbstractResolverModule {
private async buildBlacklist(): Promise<Set<string>> { private async buildBlacklist(): Promise<Set<string>> {
const blacklist = new Set<string>(); const blacklist = new Set<string>();
let resolvers = this.resolver.resolvers; let resolvers = this.resolver.resolvers as unknown as Set<ResolverModule>;
if (isPromise(resolvers as any)) { if (isPromise(resolvers as any)) {
resolvers = await resolvers; resolvers = await resolvers;
} }
for (const resolver of resolvers) { for (const resolver of resolvers) {
let tlds = resolver.getSupportedTlds(); let tlds: string[] | Promise<string[]> = resolver.getSupportedTlds();
if (isPromise(tlds as any)) { if (isPromise(tlds as any)) {
tlds = await tlds; tlds = await tlds;
} }
tlds.map((item) => blacklist.add(item)); (tlds as string[]).map((item: string) => blacklist.add(item));
} }
return blacklist; return blacklist;
@ -73,25 +73,25 @@ export default class Handshake extends AbstractResolverModule {
return resolverEmptyResponse(); return resolverEmptyResponse();
} }
const chainRecords = await this.query(tld, bypassCache); const chainRecords = await this.query(tld);
if (chainRecords.error) { if (chainRecords.error) {
return resolverError(chainRecords.error); return resolverError(chainRecords.error);
} }
if (!chainRecords.data?.records.length) { if (!chainRecords.result?.records.length) {
return resolverEmptyResponse(); return resolverEmptyResponse();
} }
let records: DNSRecord[] = []; let records: DNSRecord[] = [];
for (const record of chainRecords.data?.records) { for (const record of chainRecords.result?.records) {
switch (record.type) { switch (record.type) {
case "NS": { case "NS": {
await this.processNs( await this.processNs(
domain, domain,
record, record,
records, records,
chainRecords.data?.records, chainRecords.result?.records,
options, options,
bypassCache bypassCache
); );
@ -269,17 +269,8 @@ export default class Handshake extends AbstractResolverModule {
} }
} }
private async query( private async query(tld: string): Promise<HandshakeResponse> {
tld: string, return client.query("getnameresource", [tld, true]);
bypassCache: boolean
): Promise<HandshakeRPCResponse> {
let query = this.resolver.rpcNetwork.wisdomQuery(
"getnameresource",
"handshake",
[tld],
bypassCache
);
return (await query.result) as HandshakeRPCResponse;
} }
private async processTxt( private async processTxt(

View File

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