Message board instance life time record keeping (#57)

This commit is contained in:
Chalith Desaman
2021-09-03 18:07:02 +05:30
committed by GitHub
parent 25897047a4
commit 76c6781e2e
11 changed files with 1343 additions and 73 deletions

View File

@@ -77,9 +77,10 @@ target_precompile_headers(sagent PUBLIC src/pchheader.hpp)
# Add target to generate the installer setup.
add_custom_target(installer
COMMAND mkdir -p ./build/sashimono-installer
COMMAND bash -c "cp -r ./build/{sagent,sashi,hpfs,user-install.sh,user-uninstall.sh,contract_template,mb-xrpl} ./build/sashimono-installer/"
COMMAND bash -c "cp -r ./build/{sagent,sashi,hpfs,user-install.sh,user-uninstall.sh,contract_template} ./build/sashimono-installer/"
COMMAND bash -c "cp -r ./installer/{docker-install.sh,registry-install.sh,registry-uninstall.sh,sashimono-install.sh,sashimono-uninstall.sh} ./build/sashimono-installer/"
COMMAND bash -c "cp -r ./dependencies/{user-cgcreate.sh,libblake3.so} ./build/sashimono-installer/"
COMMAND bash -c "cp -r ./mb-xrpl/dist/mb-xrpl ./build/sashimono-installer/"
COMMAND tar cfz ./build/sashimono-installer.tar.gz --directory=./build/ sashimono-installer
COMMAND rm -r ./build/sashimono-installer
)

View File

@@ -2,16 +2,13 @@ const fs = require('fs');
const readLine = require('readline');
const { v4: uuidv4 } = require('uuid');
const fetch = require('node-fetch');
const xrpl = require('../../mb-xrpl/ripple-handler');
const XrplAccount = xrpl.XrplAccount;
const RippleAPIWarpper = xrpl.RippleAPIWarpper;
const { XrplAccount, RippleAPIWarpper, Events, MemoFormats, MemoTypes } = require('../../mb-xrpl/lib/ripple-handler');
const RIPPLE_SERVER = 'wss://hooks-testnet.xrpl-labs.com';
const FAUSET_URL = 'https://hooks-testnet.xrpl-labs.com/newcreds';
const OWNER_PUBKEY = 'ed5cb83404120ac759609819591ef839b7d222c84f1f08b3012f490586159d2b50'
const CONFIG_PATH = 'user.cfg';
const REDEEM_FEE = 1;
// Test Hook
// rwQ7ECXhkF1ZF6qFHH4y7sc1y3ZnXgf6Rh
@@ -64,7 +61,7 @@ class TestUser {
this.xrplAcc = new XrplAccount(this.ripplAPI.api, this.cfg.xrpl.address, this.cfg.xrpl.secret);
this.evernodeXrplAcc = new XrplAccount(this.ripplAPI.api, this.cfg.xrpl.hookAddress);
this.evernodeXrplAcc.events.on(xrpl.Events.PAYMENT, (data, error) => {
this.evernodeXrplAcc.events.on(Events.PAYMENT, (data, error) => {
if (data) {
// Check whether issued currency
const isXrp = (typeof data.Amount !== "object");
@@ -79,8 +76,8 @@ class TestUser {
data: m.Memo.MemoData ? hexToASCII(m.Memo.MemoData) : null
};
});
const instanceRef = deserialized.filter(m => m.data && m.type === xrpl.MemoTypes.INST_CRET_REF && m.format === xrpl.MemoFormats.BINARY);
const instanceInfo = deserialized.filter(m => m.data && m.type === xrpl.MemoTypes.INST_CRET_RESP && m.format === xrpl.MemoFormats.BINARY);
const instanceRef = deserialized.filter(m => m.data && m.type === MemoTypes.REDEEM_REF && m.format === MemoFormats.BINARY);
const instanceInfo = deserialized.filter(m => m.data && m.type === MemoTypes.REDEEM_RESP && m.format === MemoFormats.BINARY);
if (instanceRef && instanceRef.length && instanceInfo && instanceInfo.length) {
const ref = instanceRef[0].data;
@@ -109,7 +106,7 @@ class TestUser {
// Subscribe to transactions when api is reconnected.
// Because API will be automatically reconnected if it's disconnected.
this.ripplAPI.events.on(xrpl.Events.RECONNECTED, (e) => {
this.ripplAPI.events.on(Events.RECONNECTED, (e) => {
this.evernodeXrplAcc.subscribe();
});
@@ -122,7 +119,7 @@ class TestUser {
}
async inputPump() {
const inp = await this.askForInput('');
const inp = await this.askForInput('Enter command');
if (inp && inp.length > 0) {
switch (inp) {
case 'create':
@@ -139,7 +136,7 @@ class TestUser {
askForInput(label, defaultValue) {
return new Promise(resolve => {
this.rl.question(label ? `${label}? ` : '', (input) => {
this.rl.question(label ? `${label} : ` : '', (input) => {
resolve(input && input.length > 0 ? input : defaultValue);
})
})
@@ -333,6 +330,7 @@ class TestUser {
}
async createInstance() {
const tokenCount = await this.askForInput(`${this.cfg.xrpl.hostToken} amount (default:1)`, 1);
const contractId = await this.askForInput('Contract ID (default:uuidv4)', uuidv4());
const image = await this.askForInput('Image: 1=ubuntu(default) | 2=nodejs', "1");
if (image != "1" && image != "2") {
@@ -355,14 +353,15 @@ class TestUser {
const memoData = JSON.stringify(data);
const res = await this.xrplAcc.makePayment(this.cfg.xrpl.hookAddress,
REDEEM_FEE,
+tokenCount,
this.cfg.xrpl.hostToken,
this.cfg.xrpl.hostAddress,
[{ type: xrpl.MemoTypes.INST_CRET, format: xrpl.MemoFormats.BINARY, data: memoData }]);
[{ type: MemoTypes.REDEEM, format: MemoFormats.BINARY, data: memoData }]);
if (res) {
console.log("Transaction succeed, wait for the instance creation...");
return new Promise(resolve => {
this.promises[res] = resolve
this.promises[res.txHash] = resolve;
});
}
else {

View File

@@ -183,7 +183,7 @@ if [ "$quiet" != "-q" ]; then
Group=root
Type=simple
WorkingDirectory=$mb_xrpl_dir
ExecStart=node $mb_xrpl_dir $xrpl_server_url
ExecStart=node $mb_xrpl_dir $xrpl_server_url --enable-logging
Restart=on-failure
RestartSec=5
[Install]

5
mb-xrpl/.gitignore vendored
View File

@@ -1,2 +1,5 @@
node_modules
mb-xrpl.cfg
dist
log
mb-xrpl.cfg
mb-xrpl.sqlite

31
mb-xrpl/lib/logger.js Normal file
View File

@@ -0,0 +1,31 @@
const fs = require('fs');
const path = require('path');
const util = require('util');
const formatText = (text, logType = 'dbg') => {
const date = new Date().toISOString().
replace(/T/, ' '). // Replace T with a space.
replace(/\..+/, ''). // Delete the dot and everything after.
replace(/-/, ''); // Delete the dashes.
return `${date} [${logType}] ${text}\n`;
}
exports.init = (logPath) => {
const dirname = path.dirname(logPath);
if (!fs.existsSync(dirname))
fs.mkdirSync(dirname, { recursive: true });
// Formating logs and printing.
const ws = fs.createWriteStream(logPath, { flags: 'a' });
console.log = function () {
const text = formatText(util.format.apply(this, arguments));
ws.write(text);
process.stdout.write(text);
};
console.error = function () {
const text = formatText(util.format.apply(this, arguments), 'err');
ws.write(text);
process.stderr.write(text);
};
}

View File

@@ -6,10 +6,10 @@ const CONNECTION_RETRY_INTERVAL = 1000;
const maxLedgerOffset = 10;
const MemoTypes = {
INST_CRET: 'evndInstCreate',
INST_CRET_REF: 'evndInstCreateRef',
INST_CRET_RESP: 'evndInstCreateResp',
HOST_REG: 'evndHostReg'
REDEEM: 'evnRedeem',
REDEEM_REF: 'evnRedeemRef',
REDEEM_RESP: 'evnRedeemResp',
HOST_REG: 'evnHostReg'
}
const MemoFormats = {
@@ -20,6 +20,7 @@ const MemoFormats = {
const Events = {
RECONNECTED: 'reconnected',
LEDGER: 'ledger',
PAYMENT: 'payment'
}
@@ -68,6 +69,9 @@ class RippleAPIWarpper {
}
catch (e) { console.error(e); };
});
this.api.on('ledger', (ledger) => {
this.events.emit(Events.LEDGER, ledger);
});
}
async connect() {
@@ -92,6 +96,10 @@ class RippleAPIWarpper {
this.connectionRetryCount = 0;
throw `Max connection retry count reached for ${this.rippleServer}. Try again later.`;
}
async getLedgerVersion() {
return (await this.api.getLedgerVersion());
}
}
class XrplAccount {
@@ -143,7 +151,7 @@ class XrplAccount {
await this.api.submit(signed.signedTransaction);
const verified = await this.verifyTransaction(signed.id, ledger, maxLedger);
return verified ? signed.id : false;
return verified ? verified : false;
}
async createTrustline(currency, issuer, limit, memos = null) {
@@ -189,7 +197,7 @@ class XrplAccount {
await this.api.submit(signed.signedTransaction);
console.log("Submitted trust line.");
const verified = await this.verifyTransaction(signed.id, ledger, maxLedger);
verified ? resolve(signed.id) : resolve(false);
verified ? resolve(verified) : resolve(false);
}));
}
@@ -206,8 +214,7 @@ class XrplAccount {
console.log(data.outcome.result);
if (data.outcome.result !== 'tesSUCCESS')
console.log("Transaction verification failed. Result: " + data.outcome.result);
resolve(data.outcome.result === 'tesSUCCESS');
resolve(data.outcome.result === 'tesSUCCESS' ? { txHash: data.id, ledgerVersion: data.outcome.ledgerVersion } : false);
}).catch(error => {
// If transaction not in latest validated ledger, try again until max ledger is hit.
if (error instanceof this.api.errors.PendingLedgerVersionError || error instanceof this.api.errors.NotFoundError) {

View File

@@ -0,0 +1,153 @@
const sqlite3 = require('sqlite3').verbose();
const DataTypes = {
TEXT: 'TEXT',
INTEGER: 'INTEGER',
NULL: 'NULL'
}
class SqliteDatabase {
constructor(dbFile) {
this.dbFile = dbFile;
}
open() {
this.db = new sqlite3.Database(this.dbFile);
}
close() {
this.db.close();
this.db = null;
}
async createTableIfNotExists(tableName, columnInfo) {
if (!this.db)
throw 'Database connection is not open.';
const columns = columnInfo.map(c => {
let info = `${c.name} ${c.type}`;
if (c.default)
info += ` DEFAULT ${c.default}`;
if (c.unique)
info += ' UNIQUE';
if (c.primary)
info += ' PRIMARY KEY';
if (c.notNull)
info += ' NOT NULL';
return info;
}).join(', ');
const query = `CREATE TABLE IF NOT EXISTS ${tableName}(${columns})`;
await this.runQuery(query);
}
getValues(tableName, filter = null) {
if (!this.db)
throw 'Database connection is not open.';
let values = [];
let filterStr = '1 AND '
if (filter) {
const columnNames = Object.keys(filter);
for (const columnName of columnNames) {
filterStr += `${columnName} = ? AND `;
values.push(filter[columnName] ? filter[columnName] : 'NULL');
}
}
filterStr = filterStr.slice(0, -5);
const query = `SELECT * FROM ${tableName}` + (filterStr ? ` WHERE ${filterStr};` : ';');
return new Promise((resolve, reject) => {
let rows = [];
this.db.each(query, values, function (err, row) {
if (err) {
reject(err);
return;
}
rows.push(row);
}, function (err, count) {
if (err) {
reject(err);
return;
}
resolve(rows);
});
});
}
async insertValue(tableName, value) {
return (await this.insertValues(tableName, [value]));
}
async updateValue(tableName, value, filter = null) {
if (!this.db)
throw 'Database connection is not open.';
let columnNames = Object.keys(value);
let valueStr = '';
let values = [];
for (const columnName of columnNames) {
valueStr += `${columnName} = ?,`;
values.push(value[columnName] ? value[columnName] : 'NULL');
}
valueStr = valueStr.slice(0, -1);
let filterStr = '1 AND '
if (filter) {
columnNames = Object.keys(filter);
for (const columnName of columnNames) {
filterStr += `${columnName} = ? AND `;
values.push(filter[columnName] ? filter[columnName] : 'NULL');
}
}
filterStr = filterStr.slice(0, -5);
const query = `UPDATE ${tableName} SET ${valueStr} WHERE ${filterStr};`;
return (await this.runQuery(query, values));
}
async insertValues(tableName, values) {
if (!this.db)
throw 'Database connection is not open.';
if (values.length) {
const columnNames = Object.keys(values[0]);
let rowValueStr = '';
let rowValues = [];
for (const val of values) {
rowValueStr += '(';
for (const columnName of columnNames) {
rowValueStr += ('?,');
rowValues.push(val[columnName] ? val[columnName] : 'NULL');
}
rowValueStr = rowValueStr.slice(0, -1) + '),';
}
rowValueStr = rowValueStr.slice(0, -1);
const query = `INSERT INTO ${tableName}(${columnNames.join(', ')}) VALUES ${rowValueStr}`;
return (await this.runQuery(query, rowValues));
}
}
runQuery(query, params = null) {
return new Promise((resolve, reject) => {
this.db.run(query, params ? params : [], function (err) {
if (err) {
reject(err);
return;
}
resolve({ lastId: this.lastID, changes: this.changes });
});
});
}
}
module.exports = {
SqliteDatabase,
DataTypes
}

View File

@@ -1,14 +1,24 @@
const fs = require('fs');
const { execSync } = require("child_process");
const xrpl = require('./ripple-handler');
const XrplAccount = xrpl.XrplAccount;
const RippleAPIWarpper = xrpl.RippleAPIWarpper;
const { exec } = require("child_process");
const logger = require('./lib/logger');
const { XrplAccount, RippleAPIWarpper, Events, MemoFormats, MemoTypes } = require('./lib/ripple-handler');
const { SqliteDatabase, DataTypes } = require('./lib/sqlite-handler');
const CONFIG_PATH = 'mb-xrpl.cfg';
const DB_PATH = 'mb-xrpl.sqlite';
const DB_TABLE_NAME = 'redeem_ops';
const EVR_CUR_CODE = 'EVR';
const EVR_LIMIT = 99999999;
const REG_FEE = 5;
const RES_FEE = 0.000001;
const LEDGERS_PER_MOMENT = 72;
const RedeemStatus = {
REDEEMING: 'Redeeming',
REDEEMED: 'Redeemed',
FAILED: 'Failed',
EXPIRED: 'Expired'
}
const SASHI_CLI_PATH_DEV = "../build/sashi";
const SASHI_CLI_PATH_PROD = "/usr/bin/sashi";
@@ -22,33 +32,63 @@ const hexToASCII = (hex) => {
}
class MessageBoard {
constructor(configPath, sashiCliPath, rippleServer) {
constructor(configPath, dbPath, sashiCliPath, rippleServer) {
this.configPath = configPath;
this.redeemTable = DB_TABLE_NAME;
this.expiryList = [];
if (!fs.existsSync(this.configPath))
throw `${this.configPath} does not exist.`;
else if (!fs.existsSync(sashiCliPath))
throw `Sashi CLI does not exist in ${sashiCliPath}.`;
this.readConfig();
this.sashiCli = new SashiCLI(sashiCliPath);
this.ripplAPI = new RippleAPIWarpper(rippleServer);
this.db = new SqliteDatabase(dbPath);
}
async init() {
this.readConfig();
if (!this.cfg.xrpl.address || !this.cfg.xrpl.secret || !this.cfg.xrpl.token || !this.cfg.xrpl.hookAddress)
throw "Required cfg fields cannot be empty.";
try { await this.ripplAPI.connect(); }
catch (e) { throw e; }
this.db.open();
await this.createRedeemTable();
const redeems = await this.getRedeemedRecords();
for (const redeem of redeems)
this.addToExpiryList(redeem.tx_hash, redeem.container_name, this.getExpiryLedger(redeem.created_on_ledger, redeem.h_token_amount));
this.db.close();
// Check for instance expiry.
this.ripplAPI.events.on(Events.LEDGER, async (e) => {
const expired = this.expiryList.filter(x => x.expiryLedger <= e.ledgerVersion);
if (expired && expired.length) {
this.expiryList = this.expiryList.filter(x => x.expiryLedger > e.ledgerVersion);
this.db.open();
for (const x of expired) {
console.log(`Moments exceeded. Destroying ${x.containerName}`);
await this.sashiCli.destroyInstance(x.containerName);
await this.updateRedeemStatus(x.txHash, RedeemStatus.EXPIRED);
console.log(`Destroyed ${x.containerName}`);
}
this.db.close();
}
});
this.xrplAcc = new XrplAccount(this.ripplAPI.api, this.cfg.xrpl.address, this.cfg.xrpl.secret);
// Create trustline with evernode account.
if (!this.cfg.xrpl.regTrustHash) {
const res = await this.xrplAcc.createTrustline(EVR_CUR_CODE, this.cfg.xrpl.hookAddress, EVR_LIMIT);
if (res) {
this.cfg.xrpl.regTrustHash = res;
this.cfg.xrpl.regTrustHash = res.txHash;
this.persistConfig();
console.log(`Created ${EVR_CUR_CODE} trustline with evernode account.`)
}
@@ -62,14 +102,14 @@ class MessageBoard {
// REG_FEE,
// EVR_CUR_CODE,
// this.cfg.xrpl.address,
// [{ type: xrpl.MemoTypes.HOST_REG, format: xrpl.MemoFormats.TEXT, data: memoData }]);
// [{ type: MemoTypes.HOST_REG, format: MemoFormats.TEXT, data: memoData }]);
const res = await this.xrplAcc.makePayment(this.cfg.xrpl.hookAddress,
REG_FEE,
"XRP",
null,
[{ type: xrpl.MemoTypes.HOST_REG, format: xrpl.MemoFormats.TEXT, data: memoData }]);
[{ type: MemoTypes.HOST_REG, format: MemoFormats.TEXT, data: memoData }]);
if (res) {
this.cfg.xrpl.regFeeHash = res;
this.cfg.xrpl.regFeeHash = res.txHash;
this.persistConfig();
console.log('Registration payment made for evernode account.')
}
@@ -77,7 +117,7 @@ class MessageBoard {
this.evernodeXrplAcc = new XrplAccount(this.ripplAPI.api, this.cfg.xrpl.hookAddress);
this.evernodeXrplAcc.events.on(xrpl.Events.PAYMENT, (data, error) => {
this.evernodeXrplAcc.events.on(Events.PAYMENT, async (data, error) => {
if (data) {
// Check whether issued currency
const isIssuedCurrency = (typeof data.Amount === "object");
@@ -87,36 +127,61 @@ class MessageBoard {
const token = data.Amount.currency;
const issuer = data.Amount.issuer;
const amount = parseInt(data.Amount.value);
const isInstruction = (token === this.cfg.xrpl.token && issuer === this.cfg.xrpl.address);
if (isInstruction) {
const isRedeem = (token === this.cfg.xrpl.token && issuer === this.cfg.xrpl.address);
if (isRedeem) {
const memos = data.Memos;
const txHash = data.hash;
const txAccount = data.Account;
const deserialized = memos.map(m => {
return {
type: m.Memo.MemoType ? hexToASCII(m.Memo.MemoType) : null,
format: m.Memo.MemoFormat ? hexToASCII(m.Memo.MemoFormat) : null,
data: m.Memo.MemoData ? hexToASCII(m.Memo.MemoData) : null
};
}).filter(m => m.data && m.type === xrpl.MemoTypes.INST_CRET && m.format === xrpl.MemoFormats.BINARY);
const txHash = data.hash;
const txAccount = data.Account;
}).filter(m => m.data && m.type === MemoTypes.REDEEM && m.format === MemoFormats.BINARY);
this.db.open();
for (let instance of deserialized) {
let res;
let createRes;
let hasError = false;
try {
res = this.sashiCli.createInstance(JSON.parse(instance.data));
console.log(`Received redeem from ${txAccount}`)
await this.createRedeemRecord(txHash, txAccount, amount);
createRes = await this.sashiCli.createInstance(JSON.parse(instance.data));
console.log(`Instance created for ${txAccount}`)
}
catch (e) {
res = e;
console.error(e)
hasError = true;
console.error(e);
createRes = {
code: 'REDEEM_ERR',
message: 'Error occured while redeeming.'
}
}
this.xrplAcc.makePayment(this.cfg.xrpl.hookAddress,
RES_FEE,
"XRP",
null,
[{ type: xrpl.MemoTypes.INST_CRET_REF, format: xrpl.MemoFormats.BINARY, data: txHash },
{ type: xrpl.MemoTypes.INST_CRET_RESP, format: xrpl.MemoFormats.BINARY, data: res }])
.then(res => console.log(res)).catch(console.error);
try {
const data = await this.xrplAcc.makePayment(this.cfg.xrpl.hookAddress,
RES_FEE,
"XRP",
null,
[{ type: MemoTypes.REDEEM_REF, format: MemoFormats.BINARY, data: txHash },
{ type: MemoTypes.REDEEM_RESP, format: MemoFormats.BINARY, data: createRes }]);
if (!hasError) {
this.addToExpiryList(txHash, createRes.content.name, this.getExpiryLedger(data.ledgerVersion, amount));
await this.updateRedeemedRecord(txHash, createRes.content.name, data.ledgerVersion);
}
}
catch (e) {
hasError = true;
console.error(e);
}
if (hasError)
await this.updateRedeemStatus(txHash, RedeemStatus.FAILED);
}
this.db.close();
}
}
}
@@ -128,11 +193,62 @@ class MessageBoard {
// Subscribe to transactions when api is reconnected.
// Because API will be automatically reconnected if it's disconnected.
this.ripplAPI.events.on(xrpl.Events.RECONNECTED, (e) => {
this.ripplAPI.events.on(Events.RECONNECTED, (e) => {
this.evernodeXrplAcc.subscribe();
});
}
addToExpiryList(txHash, containerName, expiryLedger) {
this.expiryList.push({
txHash: txHash,
containerName: containerName,
expiryLedger: expiryLedger,
});
}
async createRedeemTable() {
// Create table if not exists.
await this.db.createTableIfNotExists(this.redeemTable, [
{ name: 'timestamp', type: DataTypes.INTEGER, notNull: true },
{ name: 'tx_hash', type: DataTypes.TEXT, primary: true, notNull: true },
{ name: 'user_xrp_address', type: DataTypes.TEXT, notNull: true },
{ name: 'h_token_amount', type: DataTypes.INTEGER, notNull: true },
{ name: 'container_name', type: DataTypes.TEXT },
{ name: 'created_on_ledger', type: DataTypes.INTEGER },
{ name: 'status', type: DataTypes.TEXT, notNull: true }
]);
}
async getRedeemedRecords() {
return (await this.db.getValues(this.redeemTable, { status: RedeemStatus.REDEEMED }));
}
async createRedeemRecord(txHash, txUserAddress, txAmount) {
await this.db.insertValue(this.redeemTable, {
timestamp: Date.now(),
tx_hash: txHash,
user_xrp_address: txUserAddress,
h_token_amount: txAmount,
status: RedeemStatus.REDEEMING
});
}
async updateRedeemedRecord(txHash, containerName, ledgerVersion) {
await this.db.updateValue(this.redeemTable, {
container_name: containerName,
created_on_ledger: ledgerVersion,
status: RedeemStatus.REDEEMED
}, { tx_hash: txHash });
}
async updateRedeemStatus(txHash, status) {
await this.db.updateValue(this.redeemTable, { status: status }, { tx_hash: txHash });
}
getExpiryLedger(createdOnLedger, moments) {
return createdOnLedger + (moments * LEDGERS_PER_MOMENT);
}
readConfig() {
this.cfg = JSON.parse(fs.readFileSync(this.configPath).toString());
}
@@ -147,41 +263,81 @@ class SashiCLI {
this.cliPath = cliPath;
}
createInstance(msg) {
if (!msg.type)
msg.type = 'create';
async createInstance(requirements) {
if (!requirements.type)
requirements.type = 'create';
let output = execSync(`${this.cliPath} json -m '${JSON.stringify(msg)}'`, { stdio: 'pipe' });
let message = Buffer.from(output).toString();
message = JSON.parse(message.substring(0, message.length - 1)); // Skipping the \n from the result.
if (message.content && typeof message.content == 'string' && message.content.endsWith("error"))
throw message;
const res = await this.execSashiCli(requirements);
if (res.content && typeof res.content == 'string' && res.content.endsWith("error"))
throw res;
return message;
return res;
}
async destroyInstance(containerName) {
const msg = {
type: 'destroy',
container_name: containerName
};
const res = await this.execSashiCli(msg);
if (res.content && typeof res.content == 'string' && res.content.endsWith("error"))
throw res;
return res;
}
execSashiCli(msg) {
return new Promise((resolve, reject) => {
exec(`${this.cliPath} json -m '${JSON.stringify(msg)}'`, { stdio: 'pipe' }, (err, stdout, stderr) => {
if (err || stderr) {
reject(err || stderr);
return;
}
let message = Buffer.from(stdout).toString();
resolve(JSON.parse(message.substring(0, message.length - 1))); // Skipping the \n from the result.
});
})
}
checkStatus() {
const output = execSync(`${this.cliPath} status`, { stdio: 'pipe' });
let message = Buffer.from(output).toString();
message = message.substring(0, message.length - 1); // Skipping the \n from the result.
console.log(`Sashi CLI : ${message}`);
return message;
return new Promise((resolve, reject) => {
exec(`${this.cliPath} status`, { stdio: 'pipe' }, (err, stdout, stderr) => {
if (err || stderr) {
reject(err || stderr);
return;
}
let message = Buffer.from(stdout).toString();
message = message.substring(0, message.length - 1); // Skipping the \n from the result.
console.log(`Sashi CLI : ${message}`);
resolve(message);
});
});
}
}
async function main() {
// Read Ripple Server Url.
const args = process.argv;
// This is used for logging purposes.
// Logs are formatted with the timestamp and a log file will be created inside log directory.
if (args.includes('--enable-logging'))
logger.init('log/mb-xrpl.log');
if (args.length < 3)
throw "Arguments mismatch.\n Usage: node mb-xrpl rippleServer";
throw "Arguments mismatch.\n Usage: node mb-xrpl <ripple server url>";
let sashiCliPath = SASHI_CLI_PATH_PROD;
// Use sashi CLI in the build folder for dev environment.
if (args.length == 4 && args[3] == 'dev')
if (args.includes('--dev'))
sashiCliPath = SASHI_CLI_PATH_DEV;
console.log('Starting the xrpl message board' + (args[3] == '--dev' ? ' (in dev mode)' : ''));
// Read Ripple Server Url.
const rippleServer = args[2];
const mb = new MessageBoard(CONFIG_PATH, sashiCliPath, rippleServer);
const mb = new MessageBoard(CONFIG_PATH, DB_PATH, sashiCliPath, rippleServer);
await mb.init();
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,10 @@
{
"name": "mb-xrpl",
"scripts": {
"build": "ncc build mb-xrpl.js -o ../build/mb-xrpl"
"build": "ncc build mb-xrpl.js -o dist/mb-xrpl"
},
"dependencies": {
"ripple-lib": "^1.9.8"
"ripple-lib": "^1.9.8",
"sqlite3": "^5.0.2"
}
}

View File

@@ -90,7 +90,7 @@ fi
shopt -s expand_aliases
alias sshskp='ssh -o StrictHostKeychecking=no'
if [ "$sshpass" != "" ] && [ "$sshpass" != "null" ]; then
alias sshskp='sshpass -p $sshpass ssh -o StrictHostKeychecking=no'
alias sshskp="sshpass -p $sshpass ssh -o StrictHostKeychecking=no"
fi
function updateconfig() {