*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({
entryPoints: ['src-module/index.ts'],
outfile: 'dist-module/index.js',
format: 'esm',
format: 'iife',
bundle: true,
legalComments: 'external',
// minify: true

View File

@ -10,16 +10,23 @@
"build-module": "npm run compile-module && node ./dist-build/build.mjs dev"
},
"dependencies": {
"@lumeweb/kernel-libresolver": "https://github.com/LumeWeb/kernel-libresolver.git",
"@lumeweb/libresolver": "https://github.com/LumeWeb/libresolver.git",
"@lumeweb/tld-enum": "github:LumeWeb/list-of-top-level-domains"
"@lumeweb/kernel-handshake-client": "git+https://git.lumeweb.com/LumeWeb/kernel-handshake-client.git",
"@lumeweb/kernel-libresolver": "git+https://git.lumeweb.com/LumeWeb/kernel-libresolver.git",
"@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": {
"@lumeweb/relay-types": "https://github.com/LumeWeb/relay-types.git",
"@types/node": "^18.7.8",
"@lumeweb/cfg": "git+https://git.lumeweb.com/LumeWeb/cfg.git",
"@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",
"esbuild": "^0.15.5",
"cli-progress": "^3.12.0",
"esbuild": "^0.15.18",
"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.
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 * 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);
@ -30,7 +18,7 @@ const nkp = {
// caller.
function readFile(fileName: string): [string, string | null] {
try {
const data = fs.readFileSync(fileName, "utf8");
let data = fs.readFileSync(fileName, "utf8");
return [data, null];
} catch (err) {
return ["", "unable to read file: " + JSON.stringify(err)];
@ -41,7 +29,7 @@ function readFile(fileName: string): [string, string | null] {
// for the caller.
function readFileBinary(fileName: string): [Uint8Array, string | null] {
try {
const data = fs.readFileSync(fileName, null);
let data = fs.readFileSync(fileName, null);
return [data, null];
} catch (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
// 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
@ -146,8 +80,7 @@ function handlePass(password: string) {
console.error("passwords do not match");
process.exit(1);
}
password = password + moduleSalt;
handlePassConfirm(password);
handlePassConfirm(moduleSalt, password);
}
);
} else {
@ -155,8 +88,7 @@ function handlePass(password: string) {
// there's no need to confirm the password but we do
// need to pass the logic off to the handlePassConfirm
// callback.
password = password + moduleSalt;
handlePassConfirm(password);
handlePassConfirm(moduleSalt, password);
}
} catch (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
// has been provided. If not confirmation password is needed, this function
// 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
// 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
@ -180,35 +112,16 @@ function handlePassConfirm(password: string) {
// devices.
if (!fs.existsSync(seedFile) && process.argv[2] !== "prod") {
// Generate the seed phrase and write it to the file.
const [seedPhrase, errGSP] = generateSeedPhraseRandom();
if (errGSP !== null) {
console.error("Unable to generate seed phrase:", errGSP);
process.exit(1);
}
const errWF = writeFile(seedFile, seedPhrase);
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.
const [seedPhrase, errGSP] = hardenedSeedPhrase(password);
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);
}
let seedPhrase = bip39.generateMnemonic(wordlist);
// 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
@ -218,85 +131,26 @@ function handlePassConfirm(password: string) {
let registryLink: string;
if (process.argv[2] === "prod") {
// Generate the seed phrase from the password.
const [sp, errGSP] = hardenedSeedPhrase(password);
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;
seedPhrase = bip39.generateMnemonic(wordlist);
} else {
const [sp, errRF] = readFile(seedFile);
let [sp, errRF] = readFile(seedFile);
if (errRF !== null) {
console.error("unable to read seed phrase for dev command from disk");
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;
}
// Upload the module to Skynet.
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 = {
let metadata = {
Filename: "index.js",
};
console.log("Uploading module...");
upload(distFile, metadata)
.then((result) => {
console.log("Updating module's registry entry...");
// Update the v2 skylink.
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);
});
const client = new SkynetClient("https://web3portal.com");
client
.uploadFile("dist-module/index.js")
.then((result: any) => {
console.log("Immutable Link for kernel:", result);
})
.catch((err) => {
.catch((err: any) => {
console.error("unable to upload file", err);
process.exit(1);
});
@ -331,25 +185,20 @@ if (process.argv[2] === "prod") {
// not, create it.
let moduleSalt: string;
if (!fs.existsSync(".module-salt")) {
const [ms, errGSPR] = generateSeedPhraseRandom();
if (errGSPR !== null) {
console.error("unable to generate module salt:", errGSPR);
process.exit(1);
}
moduleSalt = ms;
const errWF = writeFile(".module-salt", moduleSalt);
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 {
const [ms, errRF] = readFile(".module-salt");
let [ms, errRF] = readFile(".module-salt");
if (errRF !== null) {
console.error("unable to read moduleSalt");
process.exit(1);
}
const replaceMS = ms.replace(/\n$/, "");
moduleSalt = replaceMS;
ms = ms.replace(/\n$/, "");
moduleSalt = ms;
}
// Need to get a password if this is a prod build.

View File

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

View File

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

View File

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