diff --git a/build.js b/build.js index df74223..d8947a2 100644 --- a/build.js +++ b/build.js @@ -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 diff --git a/package.json b/package.json index 06e364c..10c03a1 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/src-build/build.ts b/src-build/build.ts index e66a9b8..b8b4d6f 100644 --- a/src-build/build.ts +++ b/src-build/build.ts @@ -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. diff --git a/src-module/index.ts b/src-module/index.ts index a2f6c31..4c73906 100644 --- a/src-module/index.ts +++ b/src-module/index.ts @@ -1,4 +1,4 @@ import { setup } from "@lumeweb/kernel-libresolver"; import Handshake from "../src/index.js"; -setup(Handshake); +setup(new Handshake()); diff --git a/src/index.ts b/src/index.ts index 4646759..15431ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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> { const blacklist = new Set(); - let resolvers = this.resolver.resolvers; + let resolvers = this.resolver.resolvers as unknown as Set; if (isPromise(resolvers as any)) { resolvers = await resolvers; } for (const resolver of resolvers) { - let tlds = resolver.getSupportedTlds(); + let tlds: string[] | Promise = 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 { - let query = this.resolver.rpcNetwork.wisdomQuery( - "getnameresource", - "handshake", - [tld], - bypassCache - ); - return (await query.result) as HandshakeRPCResponse; + private async query(tld: string): Promise { + return client.query("getnameresource", [tld, true]); } private async processTxt( diff --git a/tsconfig.build.json b/tsconfig.build.json index 56508e4..f519274 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -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__/*" + ] }