parent
ff7ebb0984
commit
070b7825e2
|
@ -12,20 +12,21 @@
|
||||||
"@hyperswarm/dht": "^6.0.1",
|
"@hyperswarm/dht": "^6.0.1",
|
||||||
"@hyperswarm/dht-relay": "^0.3.0",
|
"@hyperswarm/dht-relay": "^0.3.0",
|
||||||
"@pokt-network/pocket-js": "^0.8.0-rc",
|
"@pokt-network/pocket-js": "^0.8.0-rc",
|
||||||
"@root/greenlock": "^4.0.5",
|
|
||||||
"@solana/web3.js": "^1.47.3",
|
"@solana/web3.js": "^1.47.3",
|
||||||
|
"@types/acme-client": "^3.3.0",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"@types/node-cron": "^3.0.2",
|
"@types/node-cron": "^3.0.2",
|
||||||
"@types/ws": "^8.5.3",
|
"@types/ws": "^8.5.3",
|
||||||
"@types/xml2js": "^0.4.11",
|
"@types/xml2js": "^0.4.11",
|
||||||
|
"acme-client": "^4.2.5",
|
||||||
"algosdk": "^1.18.1",
|
"algosdk": "^1.18.1",
|
||||||
"async-mutex": "^0.3.2",
|
"async-mutex": "^0.3.2",
|
||||||
"bcfg": "^0.1.7",
|
"bcfg": "^0.1.7",
|
||||||
|
"date-fns": "^2.28.0",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"ethers": "^5.6.9",
|
"ethers": "^5.6.9",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"greenlock-express": "^4.0.3",
|
"hsd": "https://github.com/LumeWeb/hsd.git#spv-namestate",
|
||||||
"hsd": "^4.0.1",
|
|
||||||
"jayson": "^3.6.6",
|
"jayson": "^3.6.6",
|
||||||
"json-stable-stringify": "^1.0.1",
|
"json-stable-stringify": "^1.0.1",
|
||||||
"libskynet": "^0.0.48",
|
"libskynet": "^0.0.48",
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/minimatch": "^3.0.5",
|
"@types/minimatch": "^3.0.5",
|
||||||
"@types/sprintf-js": "^1.1.2",
|
"@types/sprintf-js": "^1.1.2",
|
||||||
|
"esbuild": "^0.14.49",
|
||||||
"hyper-typings": "^1.0.0",
|
"hyper-typings": "^1.0.0",
|
||||||
"prettier": "^2.7.1"
|
"prettier": "^2.7.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ config.load({
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
config.open("config.conf");
|
config.open("config.conf");
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
}
|
||||||
|
|
||||||
for (const setting of ["relay-domain", "afraid-username", "relay-seed"]) {
|
for (const setting of ["relay-domain", "afraid-username", "relay-seed"]) {
|
||||||
if (!config.get(setting)) {
|
if (!config.get(setting)) {
|
||||||
|
|
30
src/dht.ts
30
src/dht.ts
|
@ -1,4 +1,5 @@
|
||||||
import {createRequire} from "module";
|
import {createRequire} from "module";
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
const DHT = require("@hyperswarm/dht");
|
const DHT = require("@hyperswarm/dht");
|
||||||
import {errorExit} from "./util.js";
|
import {errorExit} from "./util.js";
|
||||||
|
@ -10,9 +11,15 @@ import {
|
||||||
} from "libskynet";
|
} from "libskynet";
|
||||||
import config from "./config.js";
|
import config from "./config.js";
|
||||||
|
|
||||||
let server: {
|
let node: {
|
||||||
listen: (arg0: ed25519Keypair) => void;
|
|
||||||
ready: () => any;
|
ready: () => any;
|
||||||
|
createServer: () => any;
|
||||||
|
defaultKeyPair: any;
|
||||||
|
on: any;
|
||||||
|
};
|
||||||
|
let server: {
|
||||||
|
listen: (arg0: ed25519Keypair) => any;
|
||||||
|
on: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
|
@ -25,17 +32,26 @@ async function start() {
|
||||||
|
|
||||||
const keyPair = deriveMyskyRootKeypair(seedPhraseToSeed(seed)[0]);
|
const keyPair = deriveMyskyRootKeypair(seedPhraseToSeed(seed)[0]);
|
||||||
|
|
||||||
const node = new DHT({ keyPair });
|
node = new DHT({keyPair});
|
||||||
|
|
||||||
await node.ready();
|
await node.ready();
|
||||||
|
|
||||||
return (server = node);
|
server = node.createServer();
|
||||||
|
await server.listen(keyPair);
|
||||||
|
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get() {
|
export async function get(
|
||||||
if (!server) {
|
ret: "server" | "dht" = "dht"
|
||||||
return start();
|
): Promise<typeof server | typeof node> {
|
||||||
|
if (!node) {
|
||||||
|
await start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret == "server") {
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
46
src/dns.ts
46
src/dns.ts
|
@ -3,10 +3,9 @@ import fetch from "node-fetch";
|
||||||
import {get as getDHT} from "./dht.js";
|
import {get as getDHT} from "./dht.js";
|
||||||
import {overwriteRegistryEntry} from "libskynetnode";
|
import {overwriteRegistryEntry} from "libskynetnode";
|
||||||
import {Buffer} from "buffer";
|
import {Buffer} from "buffer";
|
||||||
import { blake2b } from "libskynet";
|
|
||||||
import {Parser} from "xml2js";
|
import {Parser} from "xml2js";
|
||||||
import {URL} from "url";
|
import {URL} from "url";
|
||||||
import { errorExit } from "./util.js";
|
import {errorExit, hashDataKey} from "./util.js";
|
||||||
import {pack} from "msgpackr";
|
import {pack} from "msgpackr";
|
||||||
import config from "./config.js";
|
import config from "./config.js";
|
||||||
|
|
||||||
|
@ -14,7 +13,7 @@ const { createHash } = await import("crypto");
|
||||||
|
|
||||||
let activeIp: string;
|
let activeIp: string;
|
||||||
|
|
||||||
const REGISTRY_DHT_KEY = "lumeweb-dht-relay";
|
const REGISTRY_NODE_KEY = "lumeweb-dht-node";
|
||||||
|
|
||||||
async function ipUpdate() {
|
async function ipUpdate() {
|
||||||
let currentIp = await getCurrentIp();
|
let currentIp = await getCurrentIp();
|
||||||
|
@ -31,44 +30,24 @@ async function ipUpdate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function start() {
|
export async function start() {
|
||||||
const dht = await getDHT();
|
const dht = (await getDHT()) as any;
|
||||||
|
|
||||||
await ipUpdate();
|
await ipUpdate();
|
||||||
|
|
||||||
await overwriteRegistryEntry(
|
await overwriteRegistryEntry(
|
||||||
dht.defaultKeyPair,
|
dht.defaultKeyPair,
|
||||||
hashDataKey(REGISTRY_DHT_KEY),
|
hashDataKey(REGISTRY_NODE_KEY),
|
||||||
pack(`${config.str("relay-domain")}:${config.uint("relay-port")}`)
|
pack(`${config.str("relay-domain")}:${config.uint("relay-port")}`)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"node pubkey:",
|
||||||
|
Buffer.from(dht.defaultKeyPair.publicKey).toString("hex")
|
||||||
|
);
|
||||||
|
|
||||||
cron.schedule("0 * * * *", ipUpdate);
|
cron.schedule("0 * * * *", ipUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashDataKey(dataKey: string): Uint8Array {
|
|
||||||
return blake2b(encodeUtf8String(dataKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeUtf8String(str: string): Uint8Array {
|
|
||||||
const byteArray = stringToUint8ArrayUtf8(str);
|
|
||||||
const encoded = new Uint8Array(8 + byteArray.length);
|
|
||||||
encoded.set(encodeNumber(byteArray.length));
|
|
||||||
encoded.set(byteArray, 8);
|
|
||||||
return encoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringToUint8ArrayUtf8(str: string): Uint8Array {
|
|
||||||
return Uint8Array.from(Buffer.from(str, "utf-8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeNumber(num: number): Uint8Array {
|
|
||||||
const encoded = new Uint8Array(8);
|
|
||||||
for (let index = 0; index < encoded.length; index++) {
|
|
||||||
encoded[index] = num & 0xff;
|
|
||||||
num = num >> 8;
|
|
||||||
}
|
|
||||||
return encoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getDomainInfo() {
|
async function getDomainInfo() {
|
||||||
const relayDomain = config.str("relay-domain");
|
const relayDomain = config.str("relay-domain");
|
||||||
const parser = new Parser();
|
const parser = new Parser();
|
||||||
|
@ -113,10 +92,5 @@ async function getDomainInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCurrentIp(): Promise<string> {
|
async function getCurrentIp(): Promise<string> {
|
||||||
const response = await (await fetch("http://checkip.dyndns.org")).text();
|
return await (await fetch("http://ip1.dynupdate.no-ip.com/")).text();
|
||||||
const parser = new Parser();
|
|
||||||
|
|
||||||
const html = await parser.parseStringPromise(response.trim());
|
|
||||||
|
|
||||||
return html.html.body[0].split(":").pop();
|
|
||||||
}
|
}
|
||||||
|
|
115
src/relay.ts
115
src/relay.ts
|
@ -1,45 +1,56 @@
|
||||||
import WS from "ws";
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import DHT from "@hyperswarm/dht";
|
import DHT from "@hyperswarm/dht";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import {relay} from "@hyperswarm/dht-relay";
|
import {relay} from "@hyperswarm/dht-relay";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Stream from "@hyperswarm/dht-relay/ws";
|
import Stream from "@hyperswarm/dht-relay/ws";
|
||||||
import { get as getDHT } from "./dht.js";
|
import express, {Express} from "express";
|
||||||
// @ts-ignore
|
|
||||||
import GLE from "greenlock-express";
|
|
||||||
// @ts-ignore
|
|
||||||
import Greenlock from "@root/greenlock";
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {fileURLToPath} from "url";
|
import {fileURLToPath} from "url";
|
||||||
import config from "./config.js";
|
import config from "./config.js";
|
||||||
|
import * as http from "http";
|
||||||
|
import * as https from "https";
|
||||||
|
import * as tls from "tls";
|
||||||
|
import * as acme from "acme-client";
|
||||||
|
import {Buffer} from "buffer";
|
||||||
|
import {intervalToDuration} from "date-fns";
|
||||||
|
import cron from "node-cron";
|
||||||
|
import {get as getDHT} from "./dht.js";
|
||||||
|
import WS from "ws";
|
||||||
|
// @ts-ignore
|
||||||
|
import DHT from "@hyperswarm/dht";
|
||||||
|
import {pack} from "msgpackr";
|
||||||
|
import {overwriteRegistryEntry} from "libskynetnode";
|
||||||
|
import {hashDataKey} from "./util.js";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
let sslCtx: tls.SecureContext = tls.createSecureContext();
|
||||||
const __dirname = path.dirname(__filename);
|
const sslParams: tls.SecureContextOptions = {};
|
||||||
|
|
||||||
const sslConfig = {
|
const sslPrivateKey = await acme.forge.createPrivateKey();
|
||||||
packageRoot: path.dirname(__dirname),
|
const acmeClient = new acme.Client({
|
||||||
maintainerEmail: "contact@lumeweb.com",
|
accountKey: sslPrivateKey,
|
||||||
configDir: path.resolve(config.prefix, "ssl"),
|
directoryUrl: acme.directory.letsencrypt.production,
|
||||||
cluster: false,
|
});
|
||||||
agreeToTerms: true,
|
|
||||||
staging: true,
|
let app: Express;
|
||||||
};
|
let router = express.Router();
|
||||||
|
|
||||||
export async function start() {
|
export async function start() {
|
||||||
const relayDomain = config.str("relay-domain");
|
|
||||||
const relayPort = config.str("relay-port");
|
const relayPort = config.str("relay-port");
|
||||||
const greenlock = Greenlock.create(sslConfig);
|
app = express();
|
||||||
await greenlock.add({
|
app.use(function (req, res, next) {
|
||||||
subject: relayDomain,
|
router(req, res, next);
|
||||||
altnames: [relayDomain],
|
|
||||||
});
|
});
|
||||||
// @ts-ignore
|
|
||||||
config.greenlock = greenlock;
|
let httpsServer = https.createServer({
|
||||||
GLE.init(sslConfig).ready(async (GLEServer: any) => {
|
SNICallback(servername, cb) {
|
||||||
let httpsServer = GLEServer.httpsServer();
|
cb(null, sslCtx);
|
||||||
var httpServer = GLEServer.httpServer();
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let httpServer = http.createServer(app);
|
||||||
|
|
||||||
|
cron.schedule("0 * * * *", createOrRenewSSl);
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
httpServer.listen(80, "0.0.0.0", function () {
|
httpServer.listen(80, "0.0.0.0", function () {
|
||||||
|
@ -47,14 +58,14 @@ export async function start() {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const dht = await getDHT();
|
const dht = await getDHT();
|
||||||
|
|
||||||
let wsServer = new WS.Server({ server: httpServer });
|
let wsServer = new WS.Server({server: httpsServer});
|
||||||
|
|
||||||
wsServer.on("connection", (socket: any) => {
|
wsServer.on("connection", (socket: any) => {
|
||||||
relay(dht, new Stream(false, socket));
|
relay(dht, new Stream(false, socket));
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
httpsServer.listen(relayPort, "0.0.0.0", function () {
|
httpsServer.listen(relayPort, "0.0.0.0", function () {
|
||||||
console.info("Relay started on ", httpsServer.address());
|
console.info("Relay started on ", httpsServer.address());
|
||||||
|
@ -62,8 +73,46 @@ export async function start() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await greenlock.get({
|
await createOrRenewSSl();
|
||||||
servername: relayDomain,
|
}
|
||||||
});
|
|
||||||
});
|
async function createOrRenewSSl() {
|
||||||
|
if (sslParams.cert) {
|
||||||
|
const expires = (
|
||||||
|
await acme.forge.readCertificateInfo(sslParams.cert as Buffer)
|
||||||
|
).notAfter;
|
||||||
|
|
||||||
|
let duration = intervalToDuration({start: new Date(), end: expires});
|
||||||
|
|
||||||
|
let daysLeft = (duration.months as number) * 30 + (duration.days as number);
|
||||||
|
|
||||||
|
if (daysLeft > 30) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [certificateKey, certificateRequest] = await acme.forge.createCsr({
|
||||||
|
commonName: config.str("relay-domain"),
|
||||||
|
});
|
||||||
|
|
||||||
|
sslParams.cert = await acmeClient.auto({
|
||||||
|
csr: certificateRequest,
|
||||||
|
termsOfServiceAgreed: true,
|
||||||
|
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
||||||
|
router.get(
|
||||||
|
`/.well-known/acme-challenge/${challenge.token}`,
|
||||||
|
(req, res) => {
|
||||||
|
res.send(keyAuthorization);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
challengeRemoveFn: async () => {
|
||||||
|
router = express.Router();
|
||||||
|
},
|
||||||
|
challengePriority: ["http-01"],
|
||||||
|
});
|
||||||
|
sslParams.key = certificateKey;
|
||||||
|
sslCtx = tls.createSecureContext(sslParams);
|
||||||
|
|
||||||
|
console.log("SSL Certificate Updated");
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import NodeCache from "node-cache";
|
||||||
import {get as getDHT} from "./dht.js";
|
import {get as getDHT} from "./dht.js";
|
||||||
import {rpcMethods} from "./rpc/index.js";
|
import {rpcMethods} from "./rpc/index.js";
|
||||||
import PocketPKG from "@pokt-network/pocket-js";
|
import PocketPKG from "@pokt-network/pocket-js";
|
||||||
|
|
||||||
const {Configuration, HttpRpcProvider, PocketAAT, Pocket} = PocketPKG;
|
const {Configuration, HttpRpcProvider, PocketAAT, Pocket} = PocketPKG;
|
||||||
import {
|
import {
|
||||||
JSONRPCRequest,
|
JSONRPCRequest,
|
||||||
|
@ -101,6 +102,7 @@ async function processRequest(request: RPCRequest): Promise<RPCResponse> {
|
||||||
method: request.query,
|
method: request.query,
|
||||||
jsonrpc: "2.0",
|
jsonrpc: "2.0",
|
||||||
params: request.data,
|
params: request.data,
|
||||||
|
id: 1
|
||||||
} as unknown as JSONRPCRequest,
|
} as unknown as JSONRPCRequest,
|
||||||
request.chain
|
request.chain
|
||||||
);
|
);
|
||||||
|
@ -147,6 +149,7 @@ export function updateAat(aat: PocketAATObject): void {
|
||||||
export function getAat(): PocketAATObject {
|
export function getAat(): PocketAATObject {
|
||||||
return _aat;
|
return _aat;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPocketServer(): typeof Pocket {
|
export function getPocketServer(): typeof Pocket {
|
||||||
return pocketServer;
|
return pocketServer;
|
||||||
}
|
}
|
||||||
|
@ -241,7 +244,8 @@ export async function start() {
|
||||||
|
|
||||||
jsonServer = new jayson.Server(rpcMethods, {useContext: true});
|
jsonServer = new jayson.Server(rpcMethods, {useContext: true});
|
||||||
|
|
||||||
(await getDHT()).on("connection", (socket: any) => {
|
(await getDHT("server")).on("connection", (socket: any) => {
|
||||||
|
socket.rawStream._ondestroy = () => false;
|
||||||
socket.on("data", async (data: any) => {
|
socket.on("data", async (data: any) => {
|
||||||
let request: RPCRequest;
|
let request: RPCRequest;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -18,20 +18,53 @@ let clientArgs = {
|
||||||
apiKey: rand.generate(),
|
apiKey: rand.generate(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!config.bool("hsd-use-extenal-node")) {
|
if (!config.bool("hsd-use-external-node")) {
|
||||||
hsdServer = new SPVNode({
|
hsdServer = new SPVNode({
|
||||||
config: false,
|
config: false,
|
||||||
argv: false,
|
argv: false,
|
||||||
env: true,
|
env: true,
|
||||||
noDns: true,
|
noDns: true,
|
||||||
|
memory: false,
|
||||||
httpHost: "127.0.0.1",
|
httpHost: "127.0.0.1",
|
||||||
apiKey: clientArgs.apiKey,
|
apiKey: clientArgs.apiKey,
|
||||||
logFile: false,
|
logFile: false,
|
||||||
logConsole: false,
|
logConsole: true,
|
||||||
logLevel: "info",
|
logLevel: "info",
|
||||||
workers: true,
|
workers: true,
|
||||||
network: "main",
|
network: "main",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(`HSD API KEY: ${clientArgs.apiKey}`);
|
||||||
|
|
||||||
|
hsdServer.on("abort", async (err: any) => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
console.error("Shutdown is taking a long time. Exiting.");
|
||||||
|
process.exit(3);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
timeout.unref();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.error("Shutting down...");
|
||||||
|
await hsdServer.close();
|
||||||
|
clearTimeout(timeout);
|
||||||
|
console.error((err as Error).stack);
|
||||||
|
process.exit(2);
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(`Error occurred during shutdown: ${(e as Error).message}`);
|
||||||
|
process.exit(3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await hsdServer.ensure();
|
||||||
|
await hsdServer.open();
|
||||||
|
await hsdServer.connect();
|
||||||
|
|
||||||
|
hsdServer.startSync();
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error((e as Error).stack);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
clientArgs = {
|
clientArgs = {
|
||||||
network: config.str("hsd-network-type"),
|
network: config.str("hsd-network-type"),
|
||||||
|
|
27
src/util.ts
27
src/util.ts
|
@ -1,4 +1,6 @@
|
||||||
import * as chainNetworks from "./networks.json" assert {type: "json"};
|
import * as chainNetworks from "./networks.json" assert {type: "json"};
|
||||||
|
import {Buffer} from "buffer";
|
||||||
|
import {blake2b} from "libskynet";
|
||||||
|
|
||||||
type networks = { [net: string]: string };
|
type networks = { [net: string]: string };
|
||||||
|
|
||||||
|
@ -37,3 +39,28 @@ export function isIp(ip: string) {
|
||||||
ip
|
ip
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hashDataKey(dataKey: string): Uint8Array {
|
||||||
|
return blake2b(encodeUtf8String(dataKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeUtf8String(str: string): Uint8Array {
|
||||||
|
const byteArray = stringToUint8ArrayUtf8(str);
|
||||||
|
const encoded = new Uint8Array(8 + byteArray.length);
|
||||||
|
encoded.set(encodeNumber(byteArray.length));
|
||||||
|
encoded.set(byteArray, 8);
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToUint8ArrayUtf8(str: string): Uint8Array {
|
||||||
|
return Uint8Array.from(Buffer.from(str, "utf-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeNumber(num: number): Uint8Array {
|
||||||
|
const encoded = new Uint8Array(8);
|
||||||
|
for (let index = 0; index < encoded.length; index++) {
|
||||||
|
encoded[index] = num & 0xff;
|
||||||
|
num = num >> 8;
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue