mirror of
https://github.com/EvernodeXRPL/sashimono.git
synced 2026-07-30 18:40:29 +00:00
Imported bootstrap contract as git submodule.
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "evernode-bootstrap-contract"]
|
||||
path = evernode-bootstrap-contract
|
||||
url = https://github.com/HotPocketDev/evernode-bootstrap-contract.git
|
||||
@@ -15,7 +15,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
|
||||
#-------Bootstrap contract-------
|
||||
|
||||
add_executable(bootstrap_contract
|
||||
bootstrap-contract/bootstrap_contract.cpp
|
||||
evernode-bootstrap-contract/src/bootstrap_contract.cpp
|
||||
)
|
||||
|
||||
#-------Sashi CLI-------
|
||||
@@ -66,7 +66,7 @@ add_custom_command(TARGET sagent POST_BUILD
|
||||
COMMAND bash -c "cp -r ./dependencies/{hpfs,user-install.sh,user-uninstall.sh} ./build/"
|
||||
COMMAND tar xf ./dependencies/contract_template.tar -C ./build/ --no-same-owner
|
||||
COMMAND cp ./dependencies/hp.cfg ./build/contract_template/cfg/
|
||||
COMMAND cp ./bootstrap-contract/bootstrap_upgrade.sh ./build/contract_template/contract_fs/seed/state/
|
||||
COMMAND cp ./evernode-bootstrap-contract/src/bootstrap_upgrade.sh ./build/contract_template/contract_fs/seed/state/
|
||||
COMMAND mv ./build/bootstrap_contract ./build/contract_template/contract_fs/seed/state/
|
||||
COMMAND ./installer/docker-install.sh ./build/dockerbin
|
||||
COMMAND npm --prefix ./mb-xrpl install && npm run --prefix ./mb-xrpl build
|
||||
|
||||
@@ -19,6 +19,7 @@ Tested on Ubuntu 20.04
|
||||
1. Run `./dev-setup.sh`
|
||||
|
||||
## Build Sashimono Agent
|
||||
1. Run `git submodule update --init --recursive` to clone the bootstrap contract for first time.
|
||||
1. Run `cmake .` (You only have to do this once)
|
||||
1. Run `make` (Sashimono agent binary 'sagent' and dependencies will be placed in build directory)
|
||||
|
||||
|
||||
1
bootstrap-contract/.gitignore
vendored
1
bootstrap-contract/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
bootstrap_contract
|
||||
@@ -1,117 +0,0 @@
|
||||
#include "bootstrap_contract.hpp"
|
||||
|
||||
// This script will be renamed by this contract as post_exec.sh
|
||||
constexpr const char *SCRIPT_NAME = "bootstrap_upgrade.sh";
|
||||
constexpr const char *BUNDLE_NAME = "bundle.zip";
|
||||
#define HP_DEINIT \
|
||||
{ \
|
||||
hp_deinit_user_input_mmap(); \
|
||||
hp_deinit_contract(); \
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Owner pubkey not given.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hp_init_contract() == -1)
|
||||
return 1;
|
||||
|
||||
const struct hp_contract_context *ctx = hp_get_context();
|
||||
|
||||
// Read and process all user inputs from the mmap.
|
||||
const void *input_mmap = hp_init_user_input_mmap();
|
||||
|
||||
// Iterate through all users.
|
||||
for (size_t u = 0; u < ctx->users.count; u++)
|
||||
{
|
||||
const struct hp_user *user = &ctx->users.list[u];
|
||||
|
||||
// We allow only the owner of the instance to upload the bundle.zip
|
||||
if (strcmp(user->public_key.data, argv[1]) != 0)
|
||||
continue;
|
||||
|
||||
// Iterate through all inputs from this user.
|
||||
for (size_t i = 0; i < user->inputs.count; i++)
|
||||
{
|
||||
const struct hp_user_input input = user->inputs.list[i];
|
||||
|
||||
// Instead of mmap, we can also read the inputs from 'ctx->users.in_fd' using file I/O.
|
||||
// However, using mmap is recommended because user inputs already reside in memory.
|
||||
const void *buf = (uint8_t *)input_mmap + input.offset;
|
||||
std::string_view buffer((char *)buf, input.size);
|
||||
try
|
||||
{
|
||||
const jsoncons::ojson d = jsoncons::bson::decode_bson<jsoncons::ojson>(buffer);
|
||||
const std::string type = d["type"].as_string();
|
||||
if (type == "upload")
|
||||
{
|
||||
const jsoncons::byte_string_view data = d["content"].as_byte_string_view();
|
||||
const int archive_fd = open(BUNDLE_NAME, O_CREAT | O_TRUNC | O_RDWR, 0644);
|
||||
|
||||
if (archive_fd == -1 || write(archive_fd, data.begin(), data.size()) == -1)
|
||||
{
|
||||
std::cerr << errno << ": Error saving given file.\n";
|
||||
close(archive_fd);
|
||||
HP_DEINIT;
|
||||
return -1;
|
||||
}
|
||||
close(archive_fd);
|
||||
std::vector<uint8_t> msg;
|
||||
create_response_message(msg, "uploadResult", "uploadSuccess");
|
||||
hp_write_user_msg(user, msg.data(), msg.size());
|
||||
// Rename bootstrap_upgrade.sh to post_exec.sh and grant executing permissions.
|
||||
rename(SCRIPT_NAME, HP_POST_EXEC_SCRIPT_NAME);
|
||||
const mode_t permission_mode = 0777;
|
||||
if (chmod(HP_POST_EXEC_SCRIPT_NAME, permission_mode) < 0)
|
||||
{
|
||||
std::cerr << errno << ": Chmod failed for " << HP_POST_EXEC_SCRIPT_NAME << std::endl;
|
||||
HP_DEINIT;
|
||||
return -1;
|
||||
}
|
||||
// We have found our contract package input. No need to iterate furthur.
|
||||
break;
|
||||
}
|
||||
else if (type == "status")
|
||||
{
|
||||
std::vector<uint8_t> msg;
|
||||
create_response_message(msg, "statusResult", "Bootstrap contract is online");
|
||||
hp_write_user_msg(user, msg.data(), msg.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Invalid message type" << std::endl;
|
||||
HP_DEINIT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
HP_DEINIT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// We don't need to further itereate through user list. We have found our authenticated user to reach this place.
|
||||
break;
|
||||
}
|
||||
HP_DEINIT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void create_response_message(std::vector<uint8_t> &msg, std::string_view type, std::string_view message)
|
||||
{
|
||||
jsoncons::bson::bson_bytes_encoder encoder(msg);
|
||||
encoder.begin_object();
|
||||
encoder.key("type");
|
||||
encoder.string_value(type);
|
||||
encoder.key("status");
|
||||
encoder.string_value("ok");
|
||||
encoder.key("message");
|
||||
encoder.string_value(message);
|
||||
encoder.end_object();
|
||||
encoder.flush();
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef _SA_BOOTSTRAP_CONTRACT_
|
||||
#define _SA_BOOTSTRAP_CONTRACT_
|
||||
|
||||
#include "hotpocket_contract.h"
|
||||
#include <jsoncons/json.hpp>
|
||||
#include <jsoncons_ext/bson/bson.hpp>
|
||||
|
||||
void create_response_message(std::vector<uint8_t> &msg, std::string_view type, std::string_view message);
|
||||
|
||||
#endif
|
||||
@@ -1,264 +0,0 @@
|
||||
#!/bin/bash
|
||||
echo "Sashimono bootstrap contract upgrader."
|
||||
echo "Execution lcl $1-$2"
|
||||
|
||||
archive_name="bundle.zip"
|
||||
bootstrap_bin="bootstrap_contract"
|
||||
install_script="install.sh"
|
||||
patch_cfg="../patch.cfg"
|
||||
patch_cfg_bk="../patch.cfg.bk"
|
||||
contract_config="contract.config"
|
||||
self_original_name="bootstrap_upgrade.sh" # Original name of this script before it was renamed to post_exec.sh
|
||||
self_path=$(realpath $0) # Full path of this script.
|
||||
self_name=$(basename $self_path) # File name of this script.
|
||||
self_dir=$(dirname $self_path) # Parent path of this script.
|
||||
|
||||
# If field exists, append to patch json.
|
||||
function append_string_field() {
|
||||
local field=$1
|
||||
local req=$2 # '1' if empty value not allowed.
|
||||
val=$(jq ".$field" $contract_config)
|
||||
if [ "$val" != "null" ]; then
|
||||
if [ "$req" == "1" ] && [ ${#val} -eq 2 ]; then # Empty means "" or ''
|
||||
echo "$field cannot be empty."
|
||||
return 1
|
||||
fi
|
||||
[ "${patch_json: -1}" != "{" ] && patch_json="$patch_json,"
|
||||
patch_json="$patch_json${field##*.}:$val" # Append last . component field name with value.
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# If field exists, append to patch json.
|
||||
function append_mode_field() {
|
||||
local field=$1
|
||||
val=$(jq ".$field" $contract_config)
|
||||
if [ "$val" != "null" ]; then
|
||||
if [ ${#val} -eq 2 ] || { [ "$val" != "\"public\"" ] && [ "$val" != "\"private\"" ]; }; then
|
||||
echo "Invalid $field mode. Valid values: public|private."
|
||||
return 1
|
||||
fi
|
||||
[ "${patch_json: -1}" != "{" ] && patch_json="$patch_json,"
|
||||
patch_json="$patch_json${field##*.}:$val" # Append last . component field name with value.
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# If field exists, append to patch json.
|
||||
function append_gt0_field() {
|
||||
local field=$1
|
||||
local val=$(jq ".$field" $contract_config)
|
||||
if [ "$val" != "null" ]; then
|
||||
if [ "$val" -lt 0 ]; then
|
||||
echo "Invalid $field. Should be greater than zero."
|
||||
return 1
|
||||
fi
|
||||
[ "${patch_json: -1}" != "{" ] && patch_json="$patch_json,"
|
||||
patch_json="$patch_json${field##*.}:$val" # Append last . component field name with value.
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# If field exists, append to patch json.
|
||||
function append_range_field() {
|
||||
local field=$1
|
||||
local min=$2
|
||||
local max=$3
|
||||
|
||||
local val=$(jq ".$field" $contract_config)
|
||||
if [ "$val" != "null" ]; then
|
||||
if [ "$val" -le $min ] || [ "$val" -gt $max ]; then
|
||||
echo "$field must be between $min and $max inclusive."
|
||||
return 1
|
||||
fi
|
||||
[ "${patch_json: -1}" != "{" ] && patch_json="$patch_json,"
|
||||
patch_json="$patch_json${field##*.}:$val" # Append last . component field name with value.
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function upgrade() {
|
||||
|
||||
# Check for binary archive availability.
|
||||
if [ ! -f "$archive_name" ]; then
|
||||
echo "Required $archive_name not found. Exiting.."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Unzipping the archive.
|
||||
|
||||
# unzip command is used for zip extraction.
|
||||
if ! command -v unzip &>/dev/null; then
|
||||
echo "unzip utility not found. Exiting.."
|
||||
return 1
|
||||
fi
|
||||
|
||||
unzip -o $archive_name >>/dev/null
|
||||
|
||||
if [ -f "$contract_config" ]; then
|
||||
|
||||
# jq command is used for json manipulation.
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "jq utility not found. Exiting.."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ********Config check********
|
||||
|
||||
# bin_path is the only field that we require. Everything else can be ommitted.
|
||||
local bin_path=$(jq '.bin_path' $contract_config)
|
||||
if [ "$bin_path" == "null" ] || [ ${#bin_path} -eq 2 ]; then # Empty means "" or ''
|
||||
echo "bin_path cannot be empty"
|
||||
return 1
|
||||
elif [ ! -f "${bin_path:1:-1}" ]; then
|
||||
echo "Given binary file: $bin_path not found"
|
||||
return 1
|
||||
else
|
||||
patch_json="{bin_path:$bin_path"
|
||||
fi
|
||||
|
||||
if ! append_string_field "bin_args"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! append_string_field "environment"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! append_string_field "version" 1; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! append_gt0_field "max_input_ledger_offset"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local unl=$(jq '.unl' $contract_config)
|
||||
if [ "$unl" != "null" ]; then
|
||||
unl_res=$(jq '.unl? | map(length == 66 and startswith("ed")) | index(false)' $contract_config)
|
||||
if [ "$unl_res" != "null" ]; then
|
||||
echo "Unl pubkey invalid. Invalid format. Key should be 66 in length with ed prefix"
|
||||
return 1
|
||||
fi
|
||||
patch_json="$patch_json,unl:$unl"
|
||||
fi
|
||||
|
||||
local consensus_config=$(jq '.consensus' $contract_config)
|
||||
if [ "$consensus_config" != "null" ]; then
|
||||
patch_json="$patch_json,consensus:{"
|
||||
|
||||
if ! append_mode_field "consensus.mode"; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_range_field "consensus.roundtime" 0 3600000; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_range_field "consensus.stage_slice" 0 33; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_range_field "consensus.threshold" 1 100; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
patch_json="$patch_json}"
|
||||
fi
|
||||
|
||||
local npl_config=$(jq '.npl' $contract_config)
|
||||
if [ "$npl_config" != "null" ]; then
|
||||
patch_json="$patch_json,npl:{"
|
||||
|
||||
if ! append_mode_field "npl.mode"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
patch_json="$patch_json}"
|
||||
fi
|
||||
|
||||
local round_limits=$(jq '.round_limits' $contract_config)
|
||||
if [ "$round_limits" != "null" ]; then
|
||||
patch_json="$patch_json,round_limits:{"
|
||||
|
||||
if ! append_gt0_field "round_limits.user_input_bytes"; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_gt0_field "round_limits.user_output_bytes"; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_gt0_field "round_limits.npl_output_bytes"; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_gt0_field "round_limits.proc_cpu_seconds"; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_gt0_field "round_limits.proc_mem_bytes"; then
|
||||
return 1
|
||||
fi
|
||||
if ! append_gt0_field "round_limits.proc_ofd_count"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
patch_json="$patch_json}"
|
||||
fi
|
||||
|
||||
patch_json="$patch_json}"
|
||||
|
||||
echo "All $contract_config checks passed."
|
||||
|
||||
echo "Updating $patch_cfg file."
|
||||
local new_patch=$(jq -M ". + $patch_json" $patch_cfg) # Merge jsons
|
||||
cp $patch_cfg $patch_cfg_bk # Make a backup.
|
||||
echo "$new_patch" >$patch_cfg
|
||||
|
||||
# Remove contract.config after patch file update.
|
||||
rm $contract_config
|
||||
fi
|
||||
|
||||
# *****Install Script*****.
|
||||
if [ -f "$install_script" ]; then
|
||||
echo "$install_script found. Executing..."
|
||||
|
||||
chmod +x $install_script
|
||||
./$install_script
|
||||
installcode=$?
|
||||
|
||||
rm $install_script
|
||||
|
||||
if [ "$installcode" -eq "0" ]; then
|
||||
echo "$install_script executed successfully."
|
||||
return 0
|
||||
else
|
||||
echo "$install_script ended with exit code:$installcode"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
# Restore self-script original name (Because hp requires it to be named post_exec.sh before execution)
|
||||
cp $self_name $self_original_name
|
||||
# Restore patch.cfg if backup exists
|
||||
[ -f $patch_cfg_bk ] && mv $patch_cfg_bk $patch_cfg
|
||||
# Remove all files except the ones we need.
|
||||
find . -not \( -name $bootstrap_bin -or -name $self_original_name \) -delete
|
||||
return 0
|
||||
}
|
||||
|
||||
# Perform upgrade and rollback if failed.
|
||||
upgrade
|
||||
upgradecode=$?
|
||||
|
||||
pushd $self_dir >/dev/null 2>&1
|
||||
if [ "$upgradecode" -eq "0" ]; then
|
||||
# We have upgraded the contract successfully. Cleanup bootstrap contract resources.
|
||||
echo "Upgrade successful. Cleaning up."
|
||||
rm $archive_name $bootstrap_bin $patch_cfg_bk
|
||||
else
|
||||
echo "Upgrade failed. Rolling back."
|
||||
rollback
|
||||
fi
|
||||
finalcode=$?
|
||||
popd >/dev/null 2>&1
|
||||
|
||||
exit $finalcode
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1
evernode-bootstrap-contract
Submodule
1
evernode-bootstrap-contract
Submodule
Submodule evernode-bootstrap-contract added at cabc4cfdb6
Reference in New Issue
Block a user