mirror of
https://github.com/EvernodeXRPL/sashimono.git
synced 2026-04-29 15:38:00 +00:00
Setup related implementation
This commit is contained in:
@@ -37,87 +37,64 @@ const funcs = {
|
||||
},
|
||||
|
||||
'validate-account': async (args) => {
|
||||
checkParams(args, 3);
|
||||
const rippledUrl = args[0];
|
||||
const governorAddress = args[1];
|
||||
const accountAddress = args[2];
|
||||
const validateFor = args[3] || "register";
|
||||
let result = { success: false, result: 'Unknown error.' };
|
||||
let hostClient, xrplApi;
|
||||
try {
|
||||
checkParams(args, 3);
|
||||
const rippledUrl = args[0];
|
||||
const governorAddress = args[1];
|
||||
const accountAddress = args[2];
|
||||
|
||||
await evernode.Defaults.useNetwork(NETWORK);
|
||||
await evernode.Defaults.useNetwork(NETWORK);
|
||||
|
||||
evernode.Defaults.set({
|
||||
rippledServer: rippledUrl,
|
||||
governorAddress: governorAddress
|
||||
});
|
||||
evernode.Defaults.set({
|
||||
rippledServer: rippledUrl,
|
||||
governorAddress: governorAddress
|
||||
});
|
||||
|
||||
const xrplApi = new evernode.XrplApi(null, { autoReconnect: false });
|
||||
await xrplApi.connect();
|
||||
xrplApi = new evernode.XrplApi(null, { autoReconnect: false });
|
||||
await xrplApi.connect();
|
||||
|
||||
evernode.Defaults.set({
|
||||
xrplApi: xrplApi
|
||||
});
|
||||
evernode.Defaults.set({
|
||||
xrplApi: xrplApi
|
||||
});
|
||||
|
||||
const hostClient = new evernode.HostClient(accountAddress, null);
|
||||
hostClient = new evernode.HostClient(accountAddress, null);
|
||||
|
||||
if (!await hostClient.xrplAcc.exists())
|
||||
return { success: false, result: "Account not found." };
|
||||
if (!await hostClient.xrplAcc.exists())
|
||||
result = { success: false, result: "Account not found." };
|
||||
|
||||
await hostClient.connect();
|
||||
await hostClient.connect();
|
||||
|
||||
if (validateFor === "register" || validateFor === "re-register") {
|
||||
// Check whether is there any missed NFT sell offers
|
||||
try {
|
||||
const registryAcc = new evernode.XrplAccount(hostClient.config.registryAddress, null);
|
||||
const regUriToken = await hostClient.getRegistrationUriToken();
|
||||
// Check whether host has a registration token.
|
||||
const regUriToken = await hostClient.getRegistrationUriToken();
|
||||
if (regUriToken)
|
||||
result = { success: true, result: "HAS_REG_TOKEN" };
|
||||
|
||||
if (!regUriToken) {
|
||||
const regInfo = await hostClient.getHostInfo(accountAddress);
|
||||
const sellOffer = (await registryAcc.getURITokens()).find(o => o.Issuer == registryAcc.address && o.index == regInfo.uriTokenId && o.Amount);
|
||||
if (sellOffer)
|
||||
result = { success: true, result: "HAS_SELL_OFFER" };
|
||||
|
||||
if (regInfo) {
|
||||
const sellOffer = (await registryAcc.getURITokens()).find(o => o.index == regInfo.uriTokenId && o.Amount);
|
||||
|
||||
if (sellOffer) {
|
||||
await hostClient.disconnect();
|
||||
await xrplApi.disconnect();
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
await hostClient.disconnect();
|
||||
await xrplApi.disconnect();
|
||||
return { success: false, result: 'Error occurred in missed sell offers check.' };
|
||||
}
|
||||
}
|
||||
|
||||
const registered = await hostClient.isRegistered();
|
||||
// For register validation the host should not be registered in evernode.
|
||||
// For other validations host should be registered in evernode.
|
||||
if (validateFor === "register") {
|
||||
const registered = await hostClient.isRegistered();
|
||||
if (registered)
|
||||
return { success: false, result: "Host is already registered." };
|
||||
}
|
||||
else if (!registered)
|
||||
return { success: false, result: "Host is not registered." };
|
||||
result = { success: true, result: "REGISTERED" };
|
||||
|
||||
// Check whether pending transfer exists.
|
||||
const isTransferPending = await hostClient.isTransferee();
|
||||
|
||||
// For register validation check the available balance enough for transfer and non transfer registrations.
|
||||
// For other validations there should not be a pending transfer for the host.
|
||||
if (validateFor === "register") {
|
||||
const minEverBalance = isTransferPending ? 1 : hostClient.config.hostRegFee;
|
||||
// Check whether pending transfer exists.
|
||||
const transferPending = await hostClient.isTransferee();
|
||||
const minEverBalance = transferPending ? 1 : hostClient.config.hostRegFee;
|
||||
const currentBalance = await hostClient.getEVRBalance();
|
||||
if (currentBalance < minEverBalance)
|
||||
return { success: false, result: `The account needs minimum balance of ${minEverBalance} EVR. Current balance is ${currentBalance} EVR.` }
|
||||
result = { success: false, result: `The account needs minimum balance of ${minEverBalance} EVR. Current balance is ${currentBalance} EVR.` }
|
||||
} catch (e) {
|
||||
result = { success: false, result: e };
|
||||
} finally {
|
||||
if (hostClient)
|
||||
await hostClient.disconnect();
|
||||
if (xrplApi)
|
||||
await xrplApi.disconnect();
|
||||
}
|
||||
else if (isTransferPending)
|
||||
return { success: false, result: "There's a pending transfer for this host." };
|
||||
|
||||
await hostClient.disconnect();
|
||||
await xrplApi.disconnect();
|
||||
return { success: true };
|
||||
return result;
|
||||
},
|
||||
|
||||
'validate-keys': async (args) => {
|
||||
|
||||
112
installer/setup-test.sh
Executable file
112
installer/setup-test.sh
Executable file
@@ -0,0 +1,112 @@
|
||||
#!/bin/bash
|
||||
# Evernode host setup tool to manage Sashimono installation and host registration.
|
||||
# This script is also used as the 'evernode' cli alias after the installation.
|
||||
# usage: ./setup.sh install
|
||||
|
||||
# surrounding braces are needed make the whole script to be buffered on client before execution.
|
||||
{
|
||||
instance_count=10
|
||||
mb_error="Evernode Xahau message board exiting with error."
|
||||
choice_result=""
|
||||
|
||||
function confirm() {
|
||||
local prompt=$1
|
||||
local defaultChoice=${2:-y} #Default choice is set to 'y' if $2 parameter is not provided.
|
||||
|
||||
local choiceDisplay="[Y/n]"
|
||||
if [ "$defaultChoice" == "n" ]; then
|
||||
choiceDisplay="[y/N]"
|
||||
fi
|
||||
|
||||
echo -en "$prompt $choiceDisplay "
|
||||
local yn=""
|
||||
read yn </dev/tty
|
||||
|
||||
# Default choice is 'y'
|
||||
[ -z $yn ] && yn="$defaultChoice"
|
||||
while ! [[ $yn =~ ^[Yy|Nn]$ ]]; do
|
||||
read -ep "'y' or 'n' expected: " yn </dev/tty
|
||||
done
|
||||
|
||||
echo "" # Insert new line after answering.
|
||||
[[ $yn =~ ^[Yy]$ ]] && return 0 || return 1 # 0 means success.
|
||||
}
|
||||
|
||||
function choice() {
|
||||
local prompt=$1
|
||||
|
||||
local choiceDisplay=${2:-y/n}
|
||||
|
||||
echo -en "$prompt [$choiceDisplay]? "
|
||||
read choice_result </dev/tty
|
||||
|
||||
IFS='/'
|
||||
read -ra ADDR <<<"$choiceDisplay"
|
||||
|
||||
while ! [[ "${ADDR[@]}" =~ $choice_result ]]; do
|
||||
read -ep "[$choiceDisplay] expected: " choice_result </dev/tty
|
||||
done
|
||||
}
|
||||
|
||||
function choice_output() {
|
||||
echo $choice_result
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
echo "Rollbacking the instalation.."
|
||||
exit 0
|
||||
}
|
||||
|
||||
function abort() {
|
||||
echo "Aborting the instalation.."
|
||||
exit 0
|
||||
}
|
||||
|
||||
function exec_mb() {
|
||||
local res=$(MB_DATA_DIR="/home/chalith/Workspace/HotpocketDev/sashimono/mb-xrpl" node "/home/chalith/Workspace/HotpocketDev/sashimono/mb-xrpl/app.js" "$@" | tee /dev/fd/2)
|
||||
echo $res
|
||||
}
|
||||
|
||||
function burn_leases() {
|
||||
local res=$(exec_mb burn-leases)
|
||||
if [[ "$res" == *"$mb_error"* ]]; then
|
||||
choice "An error occured while burning! What do you want to do" "retry/abort/rollback" && local input=$(choice_output)
|
||||
if [ "$input" == "retry" ]; then
|
||||
burn_leases
|
||||
elif [ "$input" == "rollback" ]; then
|
||||
rollback
|
||||
else
|
||||
abort
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function mint_leases() {
|
||||
local res=$(exec_mb mint-leases $instance_count)
|
||||
if [[ "$res" == *"$mb_error"* ]]; then
|
||||
res=$(echo "$res" | tail -n 2 | head -n 1)
|
||||
if [[ "$res" == "LEASE_ERR"* ]]; then
|
||||
if confirm "Do you want to burn minted tokens. (N will abort the installation)" "n"; then
|
||||
burn_leases && mint_leases
|
||||
else
|
||||
abort
|
||||
fi
|
||||
else
|
||||
choice "An error occured while minting! What do you want to do" "retry/abort/rollback" && local input=$(choice_output)
|
||||
if [ "$input" == "retry" ]; then
|
||||
mint_leases
|
||||
elif [ "$input" == "rollback" ]; then
|
||||
rollback
|
||||
else
|
||||
abort
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
mint_leases
|
||||
|
||||
exit 0
|
||||
|
||||
# surrounding braces are needed make the whole script to be buffered on client before execution.
|
||||
}
|
||||
3045
installer/setup.sh
3045
installer/setup.sh
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,15 @@ async function main() {
|
||||
await new Setup().register(process.argv[3], parseInt(process.argv[4]), parseInt(process.argv[5]),
|
||||
parseInt(process.argv[6]), parseInt(process.argv[7]), parseInt(process.argv[8]), process.argv[9], parseInt(process.argv[10]), parseInt(process.argv[11]), process.argv[12], process.argv[13]);
|
||||
}
|
||||
else if (process.argv.length >= 3 && process.argv[2] === 'mint-leases') {
|
||||
await new Setup().mintLeases(process.argv[3]);
|
||||
}
|
||||
else if (process.argv.length >= 2 && process.argv[2] === 'offer-leases') {
|
||||
await new Setup().offerLeases();
|
||||
}
|
||||
else if (process.argv.length >= 2 && process.argv[2] === 'burn-leases') {
|
||||
await new Setup().burnLeases();
|
||||
}
|
||||
else if (process.argv.length >= 3 && process.argv[2] === 'transfer') {
|
||||
(process.argv[3]) ? await new Setup().transfer(process.argv[3]) : await new Setup().transfer();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
const https = require('https');
|
||||
const { appenv } = require('./appenv');
|
||||
const evernode = require('evernode-js-client');
|
||||
const fs = require('fs');
|
||||
@@ -125,13 +124,6 @@ class Setup {
|
||||
await hostClient.register(countryCode, cpuMicroSec,
|
||||
Math.floor((ramKb + swapKb) / 1000), Math.floor(diskKb / 1000), totalInstanceCount, cpuModelFormatted.substring(0, 40), cpuCount, cpuSpeed, description.replaceAll('_', ' '), emailAddress, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } });
|
||||
|
||||
// Create lease offers.
|
||||
console.log("Creating lease offers for instance slots...");
|
||||
for (let i = 0; i < totalInstanceCount; i++) {
|
||||
await hostClient.offerLease(i, acc.leaseAmount, appenv.TOS_HASH, config?.networking?.ipv6?.subnet ? UtilHelper.generateIPV6Address(config.networking.ipv6.subnet, i) : null, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } });
|
||||
console.log(`Created lease offer ${i + 1} of ${totalInstanceCount}.`);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
catch (err) {
|
||||
@@ -148,6 +140,144 @@ class Setup {
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
|
||||
async mintLeases(totalInstanceCount) {
|
||||
const config = this.#getConfig();
|
||||
const acc = config.xrpl;
|
||||
await setEvernodeDefaults(acc.network, acc.governorAddress, acc.rippledServer);
|
||||
|
||||
const hostClient = new evernode.HostClient(acc.address, acc.secret);
|
||||
await hostClient.connect();
|
||||
|
||||
// Update the Defaults with "xrplApi" of the client.
|
||||
evernode.Defaults.set({
|
||||
xrplApi: hostClient.xrplApi
|
||||
});
|
||||
|
||||
try {
|
||||
const leases = await hostClient.getLeases();
|
||||
|
||||
// Terminate if existing leases are inconsistent with current.
|
||||
let lastIndex = 0;
|
||||
if (leases.length) {
|
||||
for (const l of leases) {
|
||||
if (l.Amount && l.Amount.value !== acc.leaseAmount) {
|
||||
throw 'LEASE_ERR: Lease amount is inconsistent with existing.';
|
||||
}
|
||||
const tokenInfo = evernode.UtilHelpers.decodeLeaseTokenUri(l.URI);
|
||||
if (tokenInfo.leaseAmount !== acc.leaseAmount) {
|
||||
throw 'LEASE_ERR: Lease amount is inconsistent with existing.';
|
||||
}
|
||||
const leaseIndex = tokenInfo.leaseIndex;
|
||||
const outboundIP = tokenInfo.outboundIP;
|
||||
|
||||
if ((outboundIP && !config?.networking?.ipv6?.subnet) || (!outboundIP && config?.networking?.ipv6?.subnet)) {
|
||||
throw 'LEASE_ERR: Outbound IP is inconsistent with existing.';
|
||||
}
|
||||
else if (outboundIP && config?.networking?.ipv6?.subnet) {
|
||||
if (!UtilHelper.isSameIPV6Subnet(outboundIP, config?.networking?.ipv6?.subnet)) {
|
||||
throw 'LEASE_ERR: Outbound IP is inconsistent with existing.';
|
||||
}
|
||||
}
|
||||
|
||||
if (leaseIndex > lastIndex) {
|
||||
lastIndex = leaseIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalInstanceCount >= leases.length) {
|
||||
// Create leases.
|
||||
console.log("Minting leases for instance slots...");
|
||||
for (let i = (leases.length > 0 ? (lastIndex + 1) : 0); i < totalInstanceCount; i++) {
|
||||
await hostClient.mintLease(i, acc.leaseAmount, appenv.TOS_HASH, config?.networking?.ipv6?.subnet ? UtilHelper.generateIPV6Address(config.networking.ipv6.subnet, i) : null, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } });
|
||||
console.log(`Minted lease ${i + 1} of ${totalInstanceCount}.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Burn leases.
|
||||
console.log("Burning previous leases...");
|
||||
for (let i = totalInstanceCount; i < leases.length; i++) {
|
||||
await hostClient.expireLease(leases[i].index, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } });
|
||||
console.log(`Burned lease ${i + 1} of ${leases.length}.`);
|
||||
}
|
||||
}
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
catch (e) {
|
||||
await hostClient.disconnect();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async offerLeases() {
|
||||
const config = this.#getConfig();
|
||||
const acc = config.xrpl;
|
||||
await setEvernodeDefaults(acc.network, acc.governorAddress, acc.rippledServer);
|
||||
|
||||
const hostClient = new evernode.HostClient(acc.address, acc.secret);
|
||||
await hostClient.connect();
|
||||
|
||||
// Update the Defaults with "xrplApi" of the client.
|
||||
evernode.Defaults.set({
|
||||
xrplApi: hostClient.xrplApi
|
||||
});
|
||||
|
||||
try {
|
||||
const unoffered = await hostClient.getUnofferedLeases();
|
||||
|
||||
if (unoffered.length > 0) {
|
||||
// Create lease offers.
|
||||
console.log("Creating lease offers for instance slots...");
|
||||
let i = 0;
|
||||
for (let t of unoffered) {
|
||||
const uriInfo = evernode.UtilHelpers.decodeLeaseTokenUri(uriToken.URI);
|
||||
if (uriInfo.leaseAmount == acc.leaseAmount) {
|
||||
await hostClient.offerMintedLease(t.index, acc.leaseAmount, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } });
|
||||
console.log(`Created lease offer ${i + 1} of ${unoffered.length}.`);
|
||||
}
|
||||
else {
|
||||
throw 'LEASE_ERR: Lease amounts are inconsistent.';
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw 'LEASE_ERR: No unoffered leases.';
|
||||
}
|
||||
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
catch (e) {
|
||||
await hostClient.disconnect();
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async burnLeases() {
|
||||
const config = this.#getConfig();
|
||||
const acc = config.xrpl;
|
||||
await setEvernodeDefaults(acc.network, acc.governorAddress, acc.rippledServer);
|
||||
|
||||
const hostClient = new evernode.HostClient(acc.address, acc.secret);
|
||||
await hostClient.connect();
|
||||
|
||||
// Update the Defaults with "xrplApi" of the client.
|
||||
evernode.Defaults.set({
|
||||
xrplApi: hostClient.xrplApi
|
||||
});
|
||||
|
||||
try {
|
||||
await this.burnMintedURITokens(hostClient, { retryOptions: { maxRetryAttempts: MAX_TX_RETRY_ATTEMPTS, feeUplift: Math.floor(acc.affordableExtraFee / MAX_TX_RETRY_ATTEMPTS) } });
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
catch (e) {
|
||||
await hostClient.disconnect();
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async deregister(error = null) {
|
||||
console.log("Deregistering host...");
|
||||
const acc = this.#getConfig().xrpl;
|
||||
|
||||
@@ -18,6 +18,27 @@ class UtilHelper {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static isSameIPV6Subnet(address1, address2) {
|
||||
const [ip1, ip1PrefixLen] = address1.split('/');
|
||||
const [ip2, ip2PrefixLen] = address2.split('/');
|
||||
|
||||
if (!(ip1 && ip2 && ip1PrefixLen && ip2PrefixLen && !isNaN(ip1PrefixLen) && !isNaN(ip2PrefixLen)))
|
||||
return false;
|
||||
|
||||
try {
|
||||
// This will return the normalized abbreviated subnet CIDR notation.
|
||||
const ip1Cidr = ip6addr.createCIDR(ip1, parseInt(ip1PrefixLen));
|
||||
const ip2Cidr = ip6addr.createCIDR(ip2, parseInt(ip2PrefixLen));
|
||||
|
||||
return (ip1Cidr === ip2Cidr)
|
||||
}
|
||||
catch {
|
||||
// Silent catch so that we don't log exceptions to console.
|
||||
// This will be treated as ip validation failure.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user