mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Used npm package for client and contract lib examples. (#350)
This commit is contained in:
@@ -6,7 +6,6 @@ COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
COPY text-client.js ./
|
||||
COPY hp-client-lib.js ./
|
||||
|
||||
ENTRYPOINT ["node", "text-client.js"]
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# Hot Pocket javascript client library and examples
|
||||
|
||||
Single-file javascript library to support json and bson protocols in NodeJs and Browser environments.
|
||||
|
||||
## NodeJs
|
||||
1. Run `npm install` to install all the dependencies.
|
||||
1. `lib/hp-client-lib.js` is the Hot Pocket client library for NodeJs.
|
||||
1. `text-client.js` is the example for json mode.
|
||||
1. `file-client.js` is the example for bson mode.
|
||||
|
||||
## Browser
|
||||
1. Run `npm install` to install all the compilation dependencies.
|
||||
1. Run `npm run build-browser` to produced the minified library for the browser.
|
||||
1. `browser-example.html` is the simple html/javascript example for json mode.
|
||||
|
||||
(For BSON support in browser, a slightly modified version of https://www.npmjs.com/package/bson is used. The minified library includes this bson support library as well)
|
||||
@@ -3,8 +3,7 @@
|
||||
<head>
|
||||
<title>HotPocket browser example</title>
|
||||
|
||||
<!--'npm run build-browser' to generate the browser lib.-->
|
||||
<script src="dist/hp-client-lib.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/hotpocket-js-client/browser.min.js"></script>
|
||||
|
||||
<!--Hot Pocket client library requires libsodium js for cryptographic operations.-->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/libsodium-wrappers/0.5.4/sodium.min.js"
|
||||
|
||||
@@ -2,7 +2,7 @@ const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
const bson = require('bson');
|
||||
var path = require("path");
|
||||
const HotPocket = require('./lib/hp-client-lib');
|
||||
const HotPocket = require('hotpocket-js-client');
|
||||
|
||||
async function main() {
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
114
examples/js_client/package-lock.json
generated
114
examples/js_client/package-lock.json
generated
@@ -2,11 +2,6 @@
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@@ -18,12 +13,11 @@
|
||||
"integrity": "sha512-70hmx0lPd6zmtNwxPT4/1P0pqaEUlTJ0noUBvCXPLfMpN0o8PPaK3q7ZlpRIyhrqcXxeMAJSowNm/L9oi/x1XA=="
|
||||
},
|
||||
"bson": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-4.0.4.tgz",
|
||||
"integrity": "sha512-Ioi3TD0/1V3aI8+hPfC56TetYmzfq2H07jJa9A1lKTxWsFtHtYdLMGMXjtGEg9v0f72NSM07diRQEUNYhLupIA==",
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-4.5.3.tgz",
|
||||
"integrity": "sha512-qVX7LX79Mtj7B3NPLzCfBiCP6RAsjiV8N63DjlaVVpZW+PFoDTxQ4SeDbSpcqgE6mXksM5CAwZnXxxxn/XwC0g==",
|
||||
"requires": {
|
||||
"buffer": "^5.1.0",
|
||||
"long": "^4.0.0"
|
||||
"buffer": "^5.6.0"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
@@ -35,17 +29,39 @@
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
"hotpocket-js-client": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/hotpocket-js-client/-/hotpocket-js-client-0.5.1.tgz",
|
||||
"integrity": "sha512-l11Fh6+Qia3gzk/latGf1ePoemIp/XO6L73mvo3b3XEyEF93Ew6+DxQzCsCYmXWEsSp5WgYsma5NOPvVMypDEw==",
|
||||
"requires": {
|
||||
"blake3": "2.1.4",
|
||||
"bson": "4.5.3",
|
||||
"libsodium-wrappers": "0.7.9",
|
||||
"ws": "8.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"bson": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-4.5.3.tgz",
|
||||
"integrity": "sha512-qVX7LX79Mtj7B3NPLzCfBiCP6RAsjiV8N63DjlaVVpZW+PFoDTxQ4SeDbSpcqgE6mXksM5CAwZnXxxxn/XwC0g==",
|
||||
"requires": {
|
||||
"buffer": "^5.6.0"
|
||||
}
|
||||
},
|
||||
"libsodium-wrappers": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz",
|
||||
"integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==",
|
||||
"requires": {
|
||||
"libsodium": "^0.7.0"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
|
||||
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
@@ -56,62 +72,6 @@
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.6.tgz",
|
||||
"integrity": "sha512-hPb/04sEuLcTRdWDtd+xH3RXBihpmbPCsKW/Jtf4PsvdyKh+D6z2D2gvp/5BfoxseP+0FCOg66kE+0oGUE/loQ=="
|
||||
},
|
||||
"libsodium-wrappers": {
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.6.tgz",
|
||||
"integrity": "sha512-OUO2CWW5bHdLr6hkKLHIKI4raEkZrf3QHkhXsJ1yCh6MZ3JDA7jFD3kCATNquuGSG6MjjPHQIQms0y0gBDzjQg==",
|
||||
"requires": {
|
||||
"libsodium": "0.7.6"
|
||||
}
|
||||
},
|
||||
"long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz",
|
||||
"integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.19"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz",
|
||||
"integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==",
|
||||
"requires": {
|
||||
"async-limiter": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
{
|
||||
"scripts": {
|
||||
"merge-browser": "rm -f -r dist ; mkdir -p dist && cp lib/bson-browser.js dist/hp-client-lib.js && cat lib/hp-client-lib.js >> dist/hp-client-lib.js",
|
||||
"minify-browser": "terser --compress --mangle reserved=['Binary','Buffer'] -- dist/hp-client-lib.js > dist/hp-client-lib.min.js && rm dist/hp-client-lib.js",
|
||||
"build-browser": "npm run merge-browser && npm run minify-browser",
|
||||
"build-nodejs": "ncc build lib/hp-client-lib.js -o dist/hp-client"
|
||||
},
|
||||
"dependencies": {
|
||||
"libsodium-wrappers": "0.7.6",
|
||||
"blake3": "2.1.4",
|
||||
"ws": "7.1.2",
|
||||
"bson": "4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"terser": "5.7.0"
|
||||
"hotpocket-js-client": "0.5.1",
|
||||
"bson": "4.5.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const readline = require('readline');
|
||||
const HotPocket = require('./lib/hp-client-lib');
|
||||
const HotPocket = require('hotpocket-js-client');
|
||||
|
||||
async function main() {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const HotPocket = require("./hp-contract-lib");
|
||||
const HotPocket = require("hotpocket-nodejs-contract");
|
||||
const fs = require('fs').promises;
|
||||
var seedrandom = require('seedrandom');
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const HotPocket = require("./hp-contract-lib");
|
||||
const HotPocket = require("hotpocket-nodejs-contract");
|
||||
const fs = require('fs');
|
||||
|
||||
const exectsFile = "exects.txt";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const HotPocket = require("./hp-contract-lib");
|
||||
const HotPocket = require("hotpocket-nodejs-contract");
|
||||
const fs = require('fs');
|
||||
const bson = require('bson');
|
||||
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const tty = require('tty');
|
||||
|
||||
// Hot Pocket contract library version 0.5.0
|
||||
|
||||
const MAX_SEQ_PACKET_SIZE = 128 * 1024;
|
||||
const controlMessages = {
|
||||
contractEnd: "contract_end",
|
||||
unlChangeset: "unl_changeset"
|
||||
}
|
||||
Object.freeze(controlMessages);
|
||||
|
||||
const clientProtocols = {
|
||||
json: "json",
|
||||
bson: "bson"
|
||||
}
|
||||
Object.freeze(clientProtocols);
|
||||
|
||||
const PATCH_CONFIG_PATH = "../patch.cfg";
|
||||
const POST_EXEC_SCRIPT_NAME = "post_exec.sh";
|
||||
|
||||
/*
|
||||
* Class members prefixed with '__' represent private members until ES spec fully supports '#' notation.
|
||||
*/
|
||||
|
||||
class HotPocketContract {
|
||||
|
||||
constructor() {
|
||||
this.__controlChannel = null;
|
||||
this.__clientProtocol = null;
|
||||
|
||||
this.__executeContract = (hpargs, contractFunc) => {
|
||||
// Keeps track of all the tasks (promises) that must be awaited before the termination.
|
||||
const pendingTasks = [];
|
||||
const nplChannel = new NplChannel(hpargs.npl_fd);
|
||||
|
||||
const users = new UsersCollection(hpargs.user_in_fd, hpargs.users, this.__clientProtocol);
|
||||
const unl = new UnlCollection(hpargs.readonly, hpargs.unl, nplChannel, pendingTasks);
|
||||
const executionContext = new ContractContext(hpargs, users, unl, this.__controlChannel);
|
||||
|
||||
invokeCallback(contractFunc, executionContext).catch(errHandler).finally(() => {
|
||||
// Wait for any pending tasks added during execution.
|
||||
Promise.all(pendingTasks).catch(errHandler).finally(() => {
|
||||
nplChannel.close();
|
||||
this.__terminate();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.__terminate = () => {
|
||||
this.__controlChannel.send({ type: controlMessages.contractEnd });
|
||||
this.__controlChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
init(contractFunc, clientProtocol = clientProtocols.json) {
|
||||
|
||||
return new Promise(resolve => {
|
||||
if (this.__controlChannel) { // Already initialized.
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.__clientProtocol = clientProtocol;
|
||||
|
||||
// Check whether we are running on a console and provide error.
|
||||
if (tty.isatty(process.stdin.fd)) {
|
||||
console.error("Error: Hot Pocket smart contracts must be executed via Hot Pocket.");
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse HotPocket args.
|
||||
fs.readFile(process.stdin.fd, 'utf8', (err, argsJson) => {
|
||||
const hpargs = JSON.parse(argsJson);
|
||||
this.__controlChannel = new ControlChannel(hpargs.control_fd);
|
||||
this.__executeContract(hpargs, contractFunc);
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ContractContext {
|
||||
|
||||
constructor(hpargs, users, unl, controlChannel) {
|
||||
this.__controlChannel = controlChannel;
|
||||
this.readonly = hpargs.readonly;
|
||||
this.timestamp = hpargs.timestamp;
|
||||
this.users = users;
|
||||
this.unl = unl; // Not available in readonly mode.
|
||||
this.lcl_seq_no = hpargs.lcl_seq_no; // Not available in readonly mode.
|
||||
this.lcl_hash = hpargs.lcl_hash; // Not available in readonly mode.
|
||||
this.__patchConfig = new PatchConfig();
|
||||
}
|
||||
|
||||
// Returns the config values in patch config.
|
||||
getConfig() {
|
||||
return this.__patchConfig.getConfig();
|
||||
}
|
||||
|
||||
// Updates the config with given config object and save the patch config.
|
||||
updateConfig(config) {
|
||||
return this.__patchConfig.updateConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles patch config manipulation.
|
||||
class PatchConfig {
|
||||
|
||||
// Loads the config value if there's a patch config file. Otherwise throw error.
|
||||
getConfig() {
|
||||
if (!fs.existsSync(PATCH_CONFIG_PATH))
|
||||
throw "Patch config file does not exist.";
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readFile(PATCH_CONFIG_PATH, 'utf8', function (err, data) {
|
||||
if (err) reject(err);
|
||||
else resolve(JSON.parse(data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateConfig(config) {
|
||||
|
||||
this.validateConfig(config);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Format json to match with the patch.cfg json format created by HP at the startup.
|
||||
fs.writeFile(PATCH_CONFIG_PATH, JSON.stringify(config, null, 4), (err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
validateConfig(config) {
|
||||
// Validate all config fields.
|
||||
if (!config.version)
|
||||
throw "Contract version is not specified.";
|
||||
if (!config.unl || !config.unl.length)
|
||||
throw "UNL list cannot be empty.";
|
||||
for (let pubKey of config.unl) {
|
||||
// Pubkeys are validated against length, ed prefix and hex characters.
|
||||
if (!pubKey.length)
|
||||
throw "UNL pubKey not specified.";
|
||||
else if (!(/^(e|E)(d|D)[0-9a-fA-F]{64}$/g.test(pubKey)))
|
||||
throw "Invalid UNL pubKey specified.";
|
||||
}
|
||||
if (!config.bin_path || !config.bin_path.length)
|
||||
throw "Binary path cannot be empty.";
|
||||
if (config.roundtime < 1 && config.roundtime > 3600000)
|
||||
throw "Round time must be between 1 and 3600000ms inclusive.";
|
||||
if (config.stage_slice < 1 || config.stage_slice > 33)
|
||||
throw "Stage slice must be between 1 and 33 percent inclusive.";
|
||||
if (config.consensus != "public" && config.consensus != "private")
|
||||
throw "Invalid consensus flag configured in patch file. Valid values: public|private";
|
||||
if (config.npl != "public" && config.npl != "private")
|
||||
throw "Invalid npl flag configured in patch file. Valid values: public|private";
|
||||
if (config.round_limits.user_input_bytes < 0 || config.round_limits.user_output_bytes < 0 || config.round_limits.npl_output_bytes < 0 ||
|
||||
config.round_limits.proc_cpu_seconds < 0 || config.round_limits.proc_mem_bytes < 0 || config.round_limits.proc_ofd_count < 0)
|
||||
throw "Invalid round limits.";
|
||||
if (config.max_input_ledger_offset < 0)
|
||||
throw "Invalid max input ledger offset";
|
||||
}
|
||||
}
|
||||
|
||||
class UsersCollection {
|
||||
|
||||
constructor(userInputsFd, usersObj, clientProtocol) {
|
||||
this.__users = {};
|
||||
this.__infd = userInputsFd;
|
||||
|
||||
Object.entries(usersObj).forEach(([pubKey, arr]) => {
|
||||
|
||||
const outfd = arr[0]; // First array element is the output fd.
|
||||
arr.splice(0, 1); // Remove first element (output fd). The rest are pairs of msg offset/length tuples.
|
||||
|
||||
const channel = new UserChannel(outfd, clientProtocol);
|
||||
this.__users[pubKey] = new User(pubKey, channel, arr);
|
||||
});
|
||||
}
|
||||
|
||||
// Returns the User for the specified pubkey. Returns null if not found.
|
||||
find(pubKey) {
|
||||
return this.__users[pubKey]
|
||||
}
|
||||
|
||||
// Returns all the currently connected users.
|
||||
list() {
|
||||
return Object.values(this.__users);
|
||||
}
|
||||
|
||||
count() {
|
||||
return Object.keys(this.__users).length;
|
||||
}
|
||||
|
||||
async read(input) {
|
||||
const [offset, size] = input;
|
||||
const buf = Buffer.alloc(size);
|
||||
await readAsync(this.__infd, buf, offset, size);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
class User {
|
||||
|
||||
constructor(pubKey, channel, inputs) {
|
||||
this.pubKey = pubKey;
|
||||
this.inputs = inputs;
|
||||
this.__channel = channel;
|
||||
}
|
||||
|
||||
async send(msg) {
|
||||
await this.__channel.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
class UserChannel {
|
||||
|
||||
constructor(outfd, clientProtocol) {
|
||||
this.__outfd = outfd;
|
||||
this.__clientProtocol = clientProtocol;
|
||||
}
|
||||
|
||||
send(msg) {
|
||||
const messageBuf = this.serialize(msg);
|
||||
let headerBuf = Buffer.alloc(4);
|
||||
// Writing message length in big endian format.
|
||||
headerBuf.writeUInt32BE(messageBuf.byteLength)
|
||||
return writevAsync(this.__outfd, [headerBuf, messageBuf]);
|
||||
}
|
||||
|
||||
serialize(msg) {
|
||||
|
||||
if (!msg)
|
||||
throw "Cannot serialize null content.";
|
||||
|
||||
if (Buffer.isBuffer(msg))
|
||||
return msg;
|
||||
|
||||
if (this.__clientProtocol == clientProtocols.bson) {
|
||||
return Buffer.from(msg);
|
||||
}
|
||||
else { // json
|
||||
|
||||
// In JSON, we need to ensure that the final buffer contains a string.
|
||||
if (typeof msg === "string" || msg instanceof String)
|
||||
return Buffer.from(msg);
|
||||
else
|
||||
return Buffer.from(JSON.stringify(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UnlCollection {
|
||||
|
||||
constructor(readonly, unl, channel, pendingTasks) {
|
||||
this.nodes = {};
|
||||
this.__readonly = readonly;
|
||||
this.__pendingTasks = pendingTasks;
|
||||
|
||||
if (!readonly) {
|
||||
unl.forEach(pubKey => {
|
||||
this.nodes[pubKey] = new UnlNode(pubKey);
|
||||
});
|
||||
|
||||
this.__channel = channel;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the unl node for the specified pubkey. Returns null if not found.
|
||||
find(pubKey) {
|
||||
return this.nodes[pubKey];
|
||||
}
|
||||
|
||||
// Returns all the unl nodes.
|
||||
list() {
|
||||
return Object.values(this.nodes);
|
||||
}
|
||||
|
||||
count() {
|
||||
return Object.keys(this.nodes).length;
|
||||
}
|
||||
|
||||
// Registers for NPL messages.
|
||||
onMessage(callback) {
|
||||
|
||||
if (this.__readonly)
|
||||
throw "NPL messages not available in readonly mode.";
|
||||
|
||||
this.__channel.consume((pubKey, msg) => {
|
||||
this.__pendingTasks.push(invokeCallback(callback, this.nodes[pubKey], msg));
|
||||
});
|
||||
}
|
||||
|
||||
// Broadcasts a message to all unl nodes (including self if self is part of unl).
|
||||
async send(msg) {
|
||||
if (this.__readonly)
|
||||
throw "NPL messages not available in readonly mode.";
|
||||
|
||||
await this.__channel.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Represents a node that's part of unl.
|
||||
class UnlNode {
|
||||
|
||||
constructor(pubKey) {
|
||||
this.pubKey = pubKey;
|
||||
}
|
||||
}
|
||||
|
||||
// Represents the node-party-line that can be used to communicate with unl nodes.
|
||||
class NplChannel {
|
||||
|
||||
constructor(fd) {
|
||||
this.__fd = fd;
|
||||
this.__readStream = null;
|
||||
}
|
||||
|
||||
consume(onMessage) {
|
||||
|
||||
this.__readStream = fs.createReadStream(null, { fd: this.__fd, highWaterMark: MAX_SEQ_PACKET_SIZE });
|
||||
|
||||
// From the hotpocket when sending the npl messages first it sends the pubkey of the particular node
|
||||
// and then the message, First data buffer is taken as pubkey and the second one as message,
|
||||
// then npl message object is constructed and the event is emmited.
|
||||
let pubKey = null;
|
||||
|
||||
this.__readStream.on("data", (data) => {
|
||||
if (!pubKey) {
|
||||
pubKey = data.toString();
|
||||
}
|
||||
else {
|
||||
onMessage(pubKey, data);
|
||||
pubKey = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.__readStream.on("error", (err) => { });
|
||||
}
|
||||
|
||||
send(msg) {
|
||||
const buf = Buffer.from(msg);
|
||||
if (buf.length > MAX_SEQ_PACKET_SIZE)
|
||||
throw ("NPL message exceeds max size " + MAX_SEQ_PACKET_SIZE);
|
||||
return writeAsync(this.__fd, buf);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.__readStream && this.__readStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ControlChannel {
|
||||
|
||||
constructor(fd) {
|
||||
this.__fd = fd;
|
||||
this.__readStream = null;
|
||||
}
|
||||
|
||||
consume(onMessage) {
|
||||
this.__readStream = fs.createReadStream(null, { fd: this.__fd, highWaterMark: MAX_SEQ_PACKET_SIZE });
|
||||
this.__readStream.on("data", onMessage);
|
||||
this.__readStream.on("error", (err) => { });
|
||||
}
|
||||
|
||||
send(obj) {
|
||||
const buf = Buffer.from(JSON.stringify(obj));
|
||||
if (buf.length > MAX_SEQ_PACKET_SIZE)
|
||||
throw ("Control message exceeds max size " + MAX_SEQ_PACKET_SIZE);
|
||||
return writeAsync(this.__fd, buf);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.__readStream && this.__readStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
const writeAsync = (fd, buf) => new Promise(resolve => fs.write(fd, buf, resolve));
|
||||
const writevAsync = (fd, bufList) => new Promise(resolve => fs.writev(fd, bufList, resolve));
|
||||
const readAsync = (fd, buf, offset, size) => new Promise(resolve => fs.read(fd, buf, 0, size, offset, resolve));
|
||||
|
||||
const invokeCallback = async (callback, ...args) => {
|
||||
if (!callback)
|
||||
return;
|
||||
|
||||
if (callback.constructor.name === 'AsyncFunction') {
|
||||
await callback(...args).catch(errHandler);
|
||||
}
|
||||
else {
|
||||
callback(...args);
|
||||
}
|
||||
}
|
||||
|
||||
const errHandler = (err) => console.log(err);
|
||||
|
||||
module.exports = {
|
||||
Contract: HotPocketContract,
|
||||
clientProtocols,
|
||||
POST_EXEC_SCRIPT_NAME,
|
||||
}
|
||||
5
examples/nodejs_contract/package-lock.json
generated
5
examples/nodejs_contract/package-lock.json
generated
@@ -25,6 +25,11 @@
|
||||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"hotpocket-nodejs-contract": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/hotpocket-nodejs-contract/-/hotpocket-nodejs-contract-0.5.0.tgz",
|
||||
"integrity": "sha512-KSxlnmgu9AAZEChom/Q8NxAiTUE47xDGgt5eHMEWf0U+95ezr8yxLjZEyoIWrhFZGNMvjQ+7ITjMmskSF3onQA=="
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"build-diag": "ncc build diagnostic_contract.js -o dist/diagnostic-contract"
|
||||
},
|
||||
"dependencies": {
|
||||
"hotpocket-nodejs-contract": "0.5.0",
|
||||
"bson": "4.0.4",
|
||||
"seedrandom": "3.0.5"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// HotPocket test client to collect metrics.
|
||||
// This assumes the HotPocket server we are connecting to is hosting the echo contract.
|
||||
|
||||
const HotPocket = require('../../examples/js_client/lib/hp-client-lib');
|
||||
const HotPocket = require('hotpocket-js-client');
|
||||
|
||||
let server = 'wss://localhost:8080';
|
||||
if (process.argv.length == 3) server = 'wss://localhost:' + process.argv[2];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"hotpocket-js-client": "0.5.1",
|
||||
"libsodium-wrappers": "0.7.6",
|
||||
"blake3": "2.1.4",
|
||||
"ws": "7.1.2"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"hotpocket-js-client": "0.5.1",
|
||||
"azure-storage": "2.10.4",
|
||||
"blake3": "2.1.4",
|
||||
"libsodium-wrappers": "0.7.6",
|
||||
"ws": "7.1.2",
|
||||
"node-fetch": "2.6.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
const HotPocket = require('../../examples/js_client/lib/hp-client-lib');
|
||||
const HotPocket = require('hotpocket-js-client');
|
||||
const azure = require('azure-storage');
|
||||
const fs = require('fs').promises;
|
||||
const https = require('https');
|
||||
|
||||
Reference in New Issue
Block a user