mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
State synchronization logic (#67)
* Added flat buffer state message request * Added state vote * Added state to ledger history and did necessary changes * Completed receiveing state request * State read/write helpers. * Added new fbs schema * Added more state_store helper methods. * Started processing response * Fixed compile errors * Added get file length. * Handled state content response * Statefs code cleanup and fixes. * Completed response handling * Completed changes in handling state response * State sync integration fixes. * Fuse mount waiting logic. * Fixed state syncing issues * state sync fixes * fixes * State sync fixes. * Fixed fs entries retrieval issues. * changed desync logic * Added directory helper functions. * Handled return statemetns from statefs * Fixed state folder deletion. * handled errors from statefs * Working for small files * Got state sync working. * Removed cout. * Fixed catering for stae issue * Fixed block hash map flatbuf issue. * Added expected hash * Added helpers for expected hash comparison. * Improved state req/resp awaiting logic. * Fixes. * Fixes. * Block request ordering fix. * Removed couts * Closed non-closed file descriptors * Minor fixes. * Cluster create script changes. * Fixed reset time off issue.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
**/**
|
||||
!build/hpcore
|
||||
!build/hpstatemon
|
||||
!libfuse3.so.3
|
||||
!libfuse3.so.3
|
||||
!fusermount3
|
||||
|
||||
@@ -46,6 +46,7 @@ add_library(hpstatefs
|
||||
src/statefs/hashmap_builder.cpp
|
||||
src/statefs/hashtree_builder.cpp
|
||||
src/statefs/state_restore.cpp
|
||||
src/statefs/state_store.cpp
|
||||
)
|
||||
target_link_libraries(hpstatefs hpsupport)
|
||||
|
||||
@@ -91,6 +92,7 @@ target_link_libraries(hpusr hpsupport hpsock hpschema)
|
||||
add_library(hpcons
|
||||
src/cons/cons.cpp
|
||||
src/cons/ledger_handler.cpp
|
||||
src/cons/state_handler.cpp
|
||||
)
|
||||
target_link_libraries(hpcons hpsupport hpproc hpp2p hpusr)
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
# Otherwise, hpcore itself can run on any docker image like ubuntu or debian without NodeJs.
|
||||
FROM node:10.17.0-buster-slim
|
||||
|
||||
# Install fuse.
|
||||
# Copy fuse shared library and register it.
|
||||
COPY ./libfuse3.so.3 /usr/local/lib/
|
||||
RUN ldconfig
|
||||
# Install fuse.
|
||||
RUN apt-get update && apt-get install -y fuse && rm -rf /var/lib/apt/lists/*
|
||||
COPY ./fusermount3 /usr/local/bin/
|
||||
|
||||
# hpcore binary is copied to /hp directory withtin the docker image.
|
||||
WORKDIR /hp
|
||||
|
||||
@@ -52,7 +52,7 @@ do
|
||||
pubport: ${pubport}, \
|
||||
roundtime: 1000, \
|
||||
loglevel: 'debug', \
|
||||
loggers:['console'] \
|
||||
loggers:['console', 'file'] \
|
||||
}, null, 2)" > hp.cfg
|
||||
rm tmp.json
|
||||
|
||||
|
||||
BIN
fusermount3
Executable file
BIN
fusermount3
Executable file
Binary file not shown.
12
reset.sh
Executable file
12
reset.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
sudo ./cluster-create.sh 5
|
||||
sudo mkdir -p /home/geveodev/hpcore/hpcluster/node1/statehist/0/data
|
||||
sudo mkdir -p /home/geveodev/hpcore/hpcluster/node2/statehist/0/data/
|
||||
sudo mkdir -p /home/geveodev/hpcore/hpcluster/node3/statehist/0/data
|
||||
sudo mkdir -p /home/geveodev/hpcore/hpcluster/node4/statehist/0/data/
|
||||
|
||||
sudo cp -r /home/geveodev/Desktop/Share/* /home/geveodev/hpcore/hpcluster/node1/statehist/0/data
|
||||
sudo cp -r /home/geveodev/Desktop/Share/* /home/geveodev/hpcore/hpcluster/node2/statehist/0/data
|
||||
sudo cp -r /home/geveodev/Desktop/Share/* /home/geveodev/hpcore/hpcluster/node3/statehist/0/data
|
||||
sudo cp -r /home/geveodev/Desktop/Share/* /home/geveodev/hpcore/hpcluster/node4/statehist/0/data
|
||||
@@ -12,7 +12,9 @@
|
||||
#include "../crypto.hpp"
|
||||
#include "../proc/proc.hpp"
|
||||
#include "ledger_handler.hpp"
|
||||
#include "state_handler.hpp"
|
||||
#include "cons.hpp"
|
||||
#include "../statefs/state_store.hpp"
|
||||
|
||||
namespace p2pmsg = fbschema::p2pmsg;
|
||||
namespace jusrmsg = jsonschema::usrmsg;
|
||||
@@ -26,6 +28,7 @@ namespace cons
|
||||
constexpr float STAGE1_THRESHOLD = 0.5;
|
||||
constexpr float STAGE2_THRESHOLD = 0.65;
|
||||
constexpr float STAGE3_THRESHOLD = 0.8;
|
||||
constexpr float MAJORITY_THRESHOLD = 0.8;
|
||||
|
||||
consensus_context ctx;
|
||||
|
||||
@@ -38,7 +41,32 @@ int init()
|
||||
ledger_history ldr_hist = load_ledger();
|
||||
ctx.led_seq_no = ldr_hist.led_seq_no;
|
||||
ctx.lcl = ldr_hist.lcl;
|
||||
ctx.lcl_list.swap(ldr_hist.lcl_list);
|
||||
ctx.cache.swap(ldr_hist.cache);
|
||||
|
||||
hasher::B2H root_hash = hasher::B2H_empty;
|
||||
if (statefs::compute_hash_tree(root_hash, true) == -1)
|
||||
return -1;
|
||||
|
||||
LOG_INFO << "Initial state: " << root_hash;
|
||||
|
||||
std::string str_root_hash(reinterpret_cast<const char *>(&root_hash), hasher::HASH_SIZE);
|
||||
str_root_hash.swap(ctx.curr_hash_state);
|
||||
|
||||
if (!ctx.cache.empty())
|
||||
{
|
||||
ctx.prev_hash_state = ctx.cache.rbegin()->second.state;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.prev_hash_state = ctx.curr_hash_state;
|
||||
}
|
||||
|
||||
ctx.state_syncing_thread = std::thread([&] {
|
||||
run_state_sync_iterator();
|
||||
LOG_ERR << "Exit state sync thread\n";
|
||||
exit(1);
|
||||
});
|
||||
|
||||
ctx.prev_close_time = util::get_epoch_milliseconds();
|
||||
return 0;
|
||||
}
|
||||
@@ -46,7 +74,6 @@ int init()
|
||||
void consensus()
|
||||
{
|
||||
// A consensus round consists of 4 stages (0,1,2,3).
|
||||
|
||||
// For a given stage, this function may get visited multiple times due to time-wait conditions.
|
||||
|
||||
// Get the latest current time.
|
||||
@@ -145,9 +172,17 @@ void consensus()
|
||||
}
|
||||
if (is_lcl_desync)
|
||||
{
|
||||
int64_t diff = 0;
|
||||
if (time_off > ctx.time_now)
|
||||
diff = time_off - ctx.time_now;
|
||||
|
||||
else
|
||||
diff = ctx.time_now - time_off;
|
||||
//We are resetting to stage 0 to avoid possible deadlock situations by resetting every node in random time using max time.
|
||||
//this might not make sense now after stage 1 now since we are applying a stage time resolution?.
|
||||
timewait_stage(true, time_off - ctx.time_now);
|
||||
|
||||
LOG_DBG << "time off: " << std::to_string(diff);
|
||||
timewait_stage(true, diff);
|
||||
//LOG_DBG << "time off: " << std::to_string(time_off);
|
||||
return;
|
||||
}
|
||||
@@ -157,17 +192,23 @@ void consensus()
|
||||
conf::change_operating_mode(conf::OPERATING_MODE::PROPOSING);
|
||||
}
|
||||
|
||||
// In stage 1, 2, 3 we vote for incoming proposals and promote winning votes based on thresholds.
|
||||
const p2p::proposal stg_prop = create_stage123_proposal(votes);
|
||||
broadcast_proposal(stg_prop);
|
||||
if (ctx.stage == 1 || (ctx.stage == 3 && ctx.is_state_syncing))
|
||||
check_state(votes);
|
||||
|
||||
if (ctx.stage == 3)
|
||||
if (!ctx.is_state_syncing)
|
||||
{
|
||||
ctx.prev_close_time = stg_prop.time;
|
||||
apply_ledger(stg_prop);
|
||||
// In stage 1, 2, 3 we vote for incoming proposals and promote winning votes based on thresholds.
|
||||
const p2p::proposal stg_prop = create_stage123_proposal(votes);
|
||||
broadcast_proposal(stg_prop);
|
||||
|
||||
// We have finished a consensus round (all 4 stages).
|
||||
LOG_INFO << "****Stage 3 consensus reached****";
|
||||
if (ctx.stage == 3)
|
||||
{
|
||||
ctx.prev_close_time = stg_prop.time;
|
||||
apply_ledger(stg_prop);
|
||||
|
||||
// We have finished a consensus round (all 4 stages).
|
||||
LOG_INFO << "****Stage 3 consensus reached****";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,12 +317,13 @@ p2p::proposal create_stage0_proposal()
|
||||
ctx.novel_proposal_time = ctx.time_now;
|
||||
stg_prop.stage = 0;
|
||||
stg_prop.lcl = ctx.lcl;
|
||||
stg_prop.curr_hash_state = ctx.curr_hash_state;
|
||||
|
||||
// Populate the proposal with set of candidate user pubkeys.
|
||||
for (const std::string &pubkey : ctx.candidate_users)
|
||||
stg_prop.users.emplace(pubkey);
|
||||
|
||||
// We don't need candidate_users anymore, so clear it. It will be repopulated during next censensus round.
|
||||
// We don't need candidate_users anymore, so clear it. It will be repopulated during next consensus round.
|
||||
ctx.candidate_users.clear();
|
||||
|
||||
// Populate the proposal with hashes of user inputs.
|
||||
@@ -292,7 +334,6 @@ p2p::proposal create_stage0_proposal()
|
||||
for (const auto &[hash, cand_output] : ctx.candidate_user_outputs)
|
||||
stg_prop.hash_outputs.emplace(hash);
|
||||
|
||||
// todo: set propsal states
|
||||
// todo: generate stg_prop hash and check with ctx.novel_proposal, we are sending same proposal again.
|
||||
|
||||
return stg_prop;
|
||||
@@ -304,10 +345,11 @@ p2p::proposal create_stage123_proposal(vote_counter &votes)
|
||||
p2p::proposal stg_prop;
|
||||
stg_prop.stage = ctx.stage;
|
||||
|
||||
// we always vote for our current lcl regardless of what other peers are saying
|
||||
// we always vote for our current lcl and state regardless of what other peers are saying
|
||||
// if there's a fork condition we will either request history and state from
|
||||
// our peers or we will halt depending on level of consensus on the sides of the fork
|
||||
stg_prop.lcl = ctx.lcl;
|
||||
stg_prop.curr_hash_state = ctx.curr_hash_state;
|
||||
|
||||
// Vote for rest of the proposal fields by looking at candidate proposals.
|
||||
for (const auto &[pubkey, cp] : ctx.candidate_proposals)
|
||||
@@ -330,8 +372,6 @@ p2p::proposal create_stage123_proposal(vote_counter &votes)
|
||||
for (const std::string &hash : cp.hash_outputs)
|
||||
if (ctx.candidate_user_outputs.count(hash) > 0)
|
||||
increment(votes.outputs, hash);
|
||||
|
||||
// todo: repeat above for state
|
||||
}
|
||||
|
||||
const float_t vote_threshold = get_stage_threshold(ctx.stage);
|
||||
@@ -356,15 +396,13 @@ p2p::proposal create_stage123_proposal(vote_counter &votes)
|
||||
if (numvotes >= vote_threshold)
|
||||
stg_prop.hash_outputs.emplace(hash);
|
||||
|
||||
// todo:add states which have votes over stage threshold to proposal.
|
||||
|
||||
// time is voted on a simple sorted and majority basis, since there will always be disagreement.
|
||||
int32_t highest_votes = 0;
|
||||
int32_t highest_time_vote = 0;
|
||||
for (const auto [time, numvotes] : votes.time)
|
||||
{
|
||||
if (numvotes > highest_votes)
|
||||
if (numvotes > highest_time_vote)
|
||||
{
|
||||
highest_votes = numvotes;
|
||||
highest_time_vote = numvotes;
|
||||
stg_prop.time = time;
|
||||
}
|
||||
}
|
||||
@@ -393,10 +431,10 @@ void broadcast_proposal(const p2p::proposal &p)
|
||||
p2pmsg::create_msg_from_proposal(msg.builder(), p);
|
||||
p2p::broadcast_message(msg, true);
|
||||
|
||||
LOG_DBG << "Proposed [stage" << std::to_string(p.stage)
|
||||
<< "] users:" << p.users.size()
|
||||
<< " hinp:" << p.hash_inputs.size()
|
||||
<< " hout:" << p.hash_outputs.size();
|
||||
// LOG_DBG << "Proposed [stage" << std::to_string(p.stage)
|
||||
// << "] users:" << p.users.size()
|
||||
// << " hinp:" << p.hash_inputs.size()
|
||||
// << " hout:" << p.hash_outputs.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,7 +490,7 @@ void check_lcl_votes(bool &is_desync, bool &should_request_history, uint64_t &ti
|
||||
}
|
||||
|
||||
//keep track of max time of peers, so we can reset nodes in a random time range to increase reliability.
|
||||
//This is very usefull especially boostrapping a node cluster.
|
||||
//This is very useful especially boostrapping a node cluster.
|
||||
if (cp.time > time_off)
|
||||
time_off = cp.time;
|
||||
}
|
||||
@@ -460,9 +498,9 @@ void check_lcl_votes(bool &is_desync, bool &should_request_history, uint64_t &ti
|
||||
is_desync = false;
|
||||
should_request_history = false;
|
||||
|
||||
if (total_lcl_votes < (0.8 * conf::cfg.unl.size()))
|
||||
if (total_lcl_votes < (MAJORITY_THRESHOLD * conf::cfg.unl.size()))
|
||||
{
|
||||
LOG_DBG << "Not enough peers proposing to perform consensus" << std::to_string(total_lcl_votes) << " needed " << std::to_string(0.8 * conf::cfg.unl.size());
|
||||
LOG_DBG << "Not enough peers proposing to perform consensus" << std::to_string(total_lcl_votes) << " needed " << std::to_string(MAJORITY_THRESHOLD * conf::cfg.unl.size());
|
||||
is_desync = true;
|
||||
|
||||
//Not enough nodes are propsing. So Node is switching to Proposing if it's in observing mode.
|
||||
@@ -496,7 +534,7 @@ void check_lcl_votes(bool &is_desync, bool &should_request_history, uint64_t &ti
|
||||
return;
|
||||
}
|
||||
|
||||
if (winning_votes < 0.8 * ctx.candidate_proposals.size())
|
||||
if (winning_votes < MAJORITY_THRESHOLD * ctx.candidate_proposals.size())
|
||||
{
|
||||
// potential fork condition.
|
||||
LOG_WARN << "No consensus on lcl. Possible fork condition. " << std::to_string(winning_votes) << std::to_string(ctx.candidate_proposals.size());
|
||||
@@ -580,6 +618,7 @@ void apply_ledger(const p2p::proposal &cons_prop)
|
||||
const std::tuple<const uint64_t, std::string> new_lcl = save_ledger(cons_prop);
|
||||
ctx.led_seq_no = std::get<0>(new_lcl);
|
||||
ctx.lcl = std::get<1>(new_lcl);
|
||||
ctx.prev_hash_state = ctx.curr_hash_state;
|
||||
|
||||
// After the current ledger seq no is updated, we remove any newly expired inputs from candidate set.
|
||||
{
|
||||
@@ -596,9 +635,6 @@ void apply_ledger(const p2p::proposal &cons_prop)
|
||||
// Send any output from the previous consensus round to locally connected users.
|
||||
dispatch_user_outputs(cons_prop);
|
||||
|
||||
// todo:check state against the winning / canonical state
|
||||
// and act accordingly (rollback, ask state from peer, etc.)
|
||||
|
||||
// This will hold a list of file blocks that was updated by the contract process.
|
||||
// We then feed this information to state tracking logic.
|
||||
proc::contract_fblockmap_t updated_blocks;
|
||||
@@ -663,6 +699,65 @@ void dispatch_user_outputs(const p2p::proposal &cons_prop)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check state against the winning and canonical state
|
||||
* @param cons_prop The proposal that achieved consensus.
|
||||
*/
|
||||
void check_state(vote_counter &votes)
|
||||
{
|
||||
std::string majority_state;
|
||||
|
||||
for (const auto &[pubkey, cp] : ctx.candidate_proposals)
|
||||
{
|
||||
increment(votes.state, cp.curr_hash_state);
|
||||
}
|
||||
|
||||
int32_t winning_votes = 0;
|
||||
for (const auto [state, votes] : votes.state)
|
||||
{
|
||||
if (votes > winning_votes)
|
||||
{
|
||||
winning_votes = votes;
|
||||
majority_state = state;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.stage == 1 || ctx.stage == 3)
|
||||
{
|
||||
if (ctx.is_state_syncing)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cons::ctx.state_syncing_mutex);
|
||||
hasher::B2H root_hash = hasher::B2H_empty;
|
||||
int ret = statefs::compute_hash_tree(root_hash);
|
||||
std::string str_root_hash(reinterpret_cast<const char *>(&root_hash), hasher::HASH_SIZE);
|
||||
str_root_hash.swap(ctx.curr_hash_state);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.stage == 1 && majority_state != ctx.curr_hash_state)
|
||||
{
|
||||
if (ctx.state_sync_lcl != ctx.lcl)
|
||||
{
|
||||
LOG_DBG << "State mismatch. Starting state sync...";
|
||||
|
||||
// Change the mode to passive and not sending out proposals till the state is synced
|
||||
conf::change_operating_mode(conf::OPERATING_MODE::OBSERVING);
|
||||
|
||||
const hasher::B2H majority_state_hash = *reinterpret_cast<const hasher::B2H *>(majority_state.c_str());
|
||||
start_state_sync(majority_state_hash);
|
||||
|
||||
ctx.is_state_syncing = true;
|
||||
ctx.state_sync_lcl = ctx.lcl;
|
||||
}
|
||||
}
|
||||
else if (majority_state == ctx.curr_hash_state)
|
||||
{
|
||||
ctx.is_state_syncing = false;
|
||||
ctx.state_sync_lcl.clear();
|
||||
conf::change_operating_mode(conf::OPERATING_MODE::PROPOSING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers consensus-reached inputs into the provided contract buf map so it can be fed into the contract process.
|
||||
* @param bufmap The contract bufmap which needs to be populated with inputs.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "../proc/proc.hpp"
|
||||
#include "../p2p/p2p.hpp"
|
||||
#include "../usr/user_input.hpp"
|
||||
#include "ledger_handler.hpp"
|
||||
#include "state_handler.hpp"
|
||||
|
||||
namespace cons
|
||||
{
|
||||
@@ -72,14 +74,22 @@ struct consensus_context
|
||||
uint64_t time_now;
|
||||
std::string lcl;
|
||||
uint64_t led_seq_no;
|
||||
std::string curr_hash_state;
|
||||
std::string prev_hash_state;
|
||||
|
||||
//Map of closed ledgers(only lrdgername[sequnece_number-hash], state hash) with sequence number as map key.
|
||||
//contains closed ledgers from latest to latest - MAX_LEDGER_SEQUENCE.
|
||||
//this is loaded when node started and updated throughout consensus - delete ledgers that falls behind MAX_LEDGER_SEQUENCE range.
|
||||
//We will use this to track lcls related logic.- track state, lcl request, response.
|
||||
std::map<uint64_t, std::string> lcl_list;
|
||||
std::map<uint64_t, ledger_cache> cache;
|
||||
//ledger close time of previous hash
|
||||
uint64_t prev_close_time;
|
||||
|
||||
bool is_state_syncing;
|
||||
std::string state_sync_lcl;
|
||||
std::thread state_syncing_thread;
|
||||
std::mutex state_syncing_mutex;
|
||||
|
||||
consensus_context() : recent_userinput_hashes(200)
|
||||
{
|
||||
}
|
||||
@@ -93,6 +103,7 @@ struct vote_counter
|
||||
std::map<std::string, int32_t> users;
|
||||
std::map<std::string, int32_t> inputs;
|
||||
std::map<std::string, int32_t> outputs;
|
||||
std::map<std::string, int32_t> state;
|
||||
};
|
||||
|
||||
extern consensus_context ctx;
|
||||
@@ -127,6 +138,8 @@ void apply_ledger(const p2p::proposal &proposal);
|
||||
|
||||
void dispatch_user_outputs(const p2p::proposal &cons_prop);
|
||||
|
||||
void check_state(vote_counter &votes);
|
||||
|
||||
void feed_user_inputs_to_contract_bufmap(proc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop);
|
||||
|
||||
void extract_user_outputs_from_contract_bufmap(proc::contract_bufmap_t &bufmap);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../conf.hpp"
|
||||
#include "../crypto.hpp"
|
||||
#include "../p2p/p2p.hpp"
|
||||
#include "../fbschema/common_helpers.hpp"
|
||||
#include "../fbschema/ledger_helpers.hpp"
|
||||
#include "../fbschema/p2pmsg_helpers.hpp"
|
||||
#include "ledger_handler.hpp"
|
||||
@@ -59,7 +60,10 @@ const std::tuple<const uint64_t, std::string> save_ledger(const p2p::proposal &p
|
||||
|
||||
write_ledger(file_name, ledger_str.data(), ledger_str.size());
|
||||
|
||||
cons::ctx.lcl_list.emplace(led_seq_no, file_name);
|
||||
ledger_cache c;
|
||||
c.lcl = file_name;
|
||||
c.state = proposal.curr_hash_state;
|
||||
cons::ctx.cache.emplace(led_seq_no, std::move(c));
|
||||
|
||||
//Remove old ledgers that exceeds max sequence range.
|
||||
if (led_seq_no > MAX_LEDGER_SEQUENCE)
|
||||
@@ -76,7 +80,7 @@ const std::tuple<const uint64_t, std::string> save_ledger(const p2p::proposal &p
|
||||
*/
|
||||
void remove_old_ledgers(const uint64_t led_seq_no)
|
||||
{
|
||||
std::map<uint64_t, std::string>::iterator itr;
|
||||
std::map<uint64_t, ledger_cache>::iterator itr;
|
||||
|
||||
std::string dir_path;
|
||||
|
||||
@@ -84,13 +88,13 @@ void remove_old_ledgers(const uint64_t led_seq_no)
|
||||
dir_path.append(conf::ctx.histdir)
|
||||
.append("/");
|
||||
|
||||
for (itr = cons::ctx.lcl_list.begin();
|
||||
itr != cons::ctx.lcl_list.lower_bound(led_seq_no + 1);
|
||||
for (itr = cons::ctx.cache.begin();
|
||||
itr != cons::ctx.cache.lower_bound(led_seq_no + 1);
|
||||
itr++)
|
||||
{
|
||||
const std::string file_name = itr->second;
|
||||
const std::string file_name = itr->second.lcl;
|
||||
std::string file_path;
|
||||
file_path.reserve(dir_path.size() + itr->second.size() + 4);
|
||||
file_path.reserve(dir_path.size() + itr->second.lcl.size() + 4);
|
||||
file_path.append(dir_path)
|
||||
.append(file_name)
|
||||
.append(".lcl");
|
||||
@@ -98,8 +102,9 @@ void remove_old_ledgers(const uint64_t led_seq_no)
|
||||
if (boost::filesystem::exists(file_path))
|
||||
boost::filesystem::remove(file_path);
|
||||
}
|
||||
if (!cons::ctx.lcl_list.empty())
|
||||
cons::ctx.lcl_list.erase(cons::ctx.lcl_list.begin(), cons::ctx.lcl_list.lower_bound(led_seq_no + 1));
|
||||
|
||||
if (!cons::ctx.cache.empty())
|
||||
cons::ctx.cache.erase(cons::ctx.cache.begin(), cons::ctx.cache.lower_bound(led_seq_no + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +159,7 @@ const ledger_history load_ledger()
|
||||
for (const auto &entry : boost::filesystem::directory_iterator(conf::ctx.histdir))
|
||||
{
|
||||
const boost::filesystem::path file_path = entry.path();
|
||||
const std::string file_name = entry.path().stem().string();
|
||||
const std::string file_name = file_path.stem().string();
|
||||
|
||||
if (boost::filesystem::is_directory(file_path))
|
||||
{
|
||||
@@ -172,7 +177,22 @@ const ledger_history load_ledger()
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
seq_no = std::stoull(file_name.substr(0, pos));
|
||||
ldg_hist.lcl_list.emplace(seq_no, file_name); //lcl -> [seq_no-hash]
|
||||
|
||||
std::ifstream file(file_path.string(), std::ios::binary | std::ios::ate);
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> buffer(size);
|
||||
if (file.read(buffer.data(), size))
|
||||
{
|
||||
const uint8_t *ledger_buf_ptr = reinterpret_cast<const uint8_t *>(buffer.data());
|
||||
const fbschema::ledger::Ledger *ledger = fbschema::ledger::GetLedger(ledger_buf_ptr);
|
||||
ledger_cache c;
|
||||
c.lcl = file_name;
|
||||
c.state = fbschema::flatbuff_bytes_to_sv(ledger->state());
|
||||
|
||||
ldg_hist.cache.emplace(seq_no, std::move(c)); //lcl_cache -> [seq_no-hash]
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -183,15 +203,15 @@ const ledger_history load_ledger()
|
||||
}
|
||||
|
||||
//check if there is a saved lcl file -> if no send genesis lcl.
|
||||
if (ldg_hist.lcl_list.empty())
|
||||
if (ldg_hist.cache.empty())
|
||||
{
|
||||
ldg_hist.led_seq_no = 0;
|
||||
ldg_hist.lcl = "0-genesis";
|
||||
}
|
||||
else
|
||||
{
|
||||
ldg_hist.led_seq_no = ldg_hist.lcl_list.rbegin()->first;
|
||||
ldg_hist.lcl = ldg_hist.lcl_list.rbegin()->second;
|
||||
ldg_hist.led_seq_no = ldg_hist.cache.rbegin()->first;
|
||||
ldg_hist.lcl = ldg_hist.cache.rbegin()->second.lcl;
|
||||
|
||||
//Remove old ledgers that exceeds max sequence range.
|
||||
if (ldg_hist.led_seq_no > MAX_LEDGER_SEQUENCE)
|
||||
@@ -241,15 +261,15 @@ bool check_required_lcl_availability(const p2p::history_request &hr)
|
||||
|
||||
if (req_seq_no > 0)
|
||||
{
|
||||
const auto itr = cons::ctx.lcl_list.find(req_seq_no);
|
||||
if (itr == cons::ctx.lcl_list.end())
|
||||
const auto itr = cons::ctx.cache.find(req_seq_no);
|
||||
if (itr == cons::ctx.cache.end())
|
||||
{
|
||||
LOG_DBG << "Required lcl peer asked for is not in our lcl cache.";
|
||||
//either this node is also not in consesnsus ledger or other node requesting a lcl that is older than node's current
|
||||
// minimum lcl sequence becuase of maximum ledger history range.
|
||||
return false;
|
||||
}
|
||||
else if (itr->second != hr.required_lcl)
|
||||
else if (itr->second.lcl != hr.required_lcl)
|
||||
{
|
||||
LOG_DBG << "Required lcl peer asked for is not in our lcl cache.";
|
||||
//either this node or requesting node is in a fork condition.
|
||||
@@ -281,20 +301,20 @@ const p2p::history_response retrieve_ledger_history(const p2p::history_request &
|
||||
min_seq_no = std::stoull(hr.minimum_lcl.substr(0, pos)); //get required lcl sequence number
|
||||
}
|
||||
|
||||
const auto itr = cons::ctx.lcl_list.find(min_seq_no);
|
||||
if (itr != cons::ctx.lcl_list.end()) //requested minimum lcl is not in our lcl history cache
|
||||
const auto itr = cons::ctx.cache.find(min_seq_no);
|
||||
if (itr != cons::ctx.cache.end()) //requested minimum lcl is not in our lcl history cache
|
||||
{
|
||||
min_seq_no = itr->first;
|
||||
//check whether minimum lcl node ask for is same as this node's.
|
||||
//eventhough sequence number are same, lcl hash can be changed if one of node is in a fork condition.
|
||||
if (hr.minimum_lcl != itr->second)
|
||||
if (hr.minimum_lcl != itr->second.lcl)
|
||||
{
|
||||
LOG_DBG << "Invalid minimum ledger. Recieved min hash: " << min_lcl_hash << " Node hash: " << itr->second;
|
||||
LOG_DBG << "Invalid minimum ledger. Recieved min hash: "<< min_lcl_hash << " Node hash: " << itr->second.lcl;
|
||||
history_response.error = p2p::LEDGER_RESPONSE_ERROR::INVALID_MIN_LEDGER;
|
||||
return history_response;
|
||||
}
|
||||
}
|
||||
else if (min_seq_no > cons::ctx.lcl_list.rbegin()->first) //Recieved minimum lcl sequence is ahead of node's lcl sequence.
|
||||
else if (min_seq_no > cons::ctx.cache.rbegin()->first) //Recieved minimum lcl sequence is ahead of node's lcl sequence.
|
||||
{
|
||||
LOG_DBG << "Invalid minimum ledger. Recieved minimum sequence number is ahead of node current lcl sequence. hash: " << min_lcl_hash;
|
||||
history_response.error = p2p::LEDGER_RESPONSE_ERROR::INVALID_MIN_LEDGER;
|
||||
@@ -303,32 +323,32 @@ const p2p::history_response retrieve_ledger_history(const p2p::history_request &
|
||||
else
|
||||
{
|
||||
LOG_DBG << "Minimum lcl peer asked for is not in our lcl cache. Therefore sending from node minimum lcl";
|
||||
min_seq_no = cons::ctx.lcl_list.begin()->first;
|
||||
min_seq_no = cons::ctx.cache.begin()->first;
|
||||
}
|
||||
|
||||
//LOG_DBG << "history request min seq: " << std::to_string(min_seq_no);
|
||||
|
||||
//copy current history cache.
|
||||
std::map<uint64_t, std::string>
|
||||
lcl_list = cons::ctx.lcl_list;
|
||||
std::map<uint64_t, ledger_cache> led_cache = cons::ctx.cache;
|
||||
|
||||
//filter out cache from finalized minimum sequence.
|
||||
lcl_list.erase(
|
||||
lcl_list.begin(),
|
||||
lcl_list.lower_bound(min_seq_no));
|
||||
//filter out cache and get raw files here.
|
||||
led_cache.erase(
|
||||
led_cache.begin(),
|
||||
led_cache.lower_bound(min_seq_no));
|
||||
|
||||
//Get raw content of lcls that going to be send.
|
||||
for (auto &[seq_no, lcl_hash] : lcl_list)
|
||||
//Get raw content of lcls that going to be send.
|
||||
for (auto &[seq_no, cache] : led_cache)
|
||||
{
|
||||
p2p::history_ledger ledger;
|
||||
ledger.lcl = lcl_hash;
|
||||
ledger.lcl = cache.lcl;
|
||||
ledger.state = cache.state;
|
||||
|
||||
std::string path;
|
||||
|
||||
path.reserve(conf::ctx.histdir.size() + lcl_hash.size() + 5);
|
||||
path.reserve(conf::ctx.histdir.size() + cache.lcl.size() + 5);
|
||||
path.append(conf::ctx.histdir)
|
||||
.append("/")
|
||||
.append(lcl_hash)
|
||||
.append(cache.lcl)
|
||||
.append(".lcl");
|
||||
|
||||
//read lcl file
|
||||
@@ -379,7 +399,7 @@ void handle_ledger_history_response(const p2p::history_response &hr)
|
||||
// This means we are in a fork ledger.Remove/rollback current ledger.
|
||||
// Basically in the long run we'll rolback one by one untill we catch up to valid minimum ledger .
|
||||
remove_ledger(ctx.lcl);
|
||||
cons::ctx.lcl_list.erase(ctx.lcl_list.rbegin()->first);
|
||||
cons::ctx.cache.erase(ctx.cache.rbegin()->first);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -433,27 +453,34 @@ void handle_ledger_history_response(const p2p::history_response &hr)
|
||||
//Save recieved lcl in file system and update lcl history cache
|
||||
for (auto &[seq_no, ledger] : hr.hist_ledgers)
|
||||
{
|
||||
auto prev_dup_itr = cons::ctx.lcl_list.find(seq_no);
|
||||
if (prev_dup_itr != cons::ctx.lcl_list.end())
|
||||
auto prev_dup_itr = cons::ctx.cache.find(seq_no);
|
||||
if (prev_dup_itr != cons::ctx.cache.end())
|
||||
{
|
||||
remove_ledger(prev_dup_itr->second);
|
||||
cons::ctx.lcl_list.erase(prev_dup_itr);
|
||||
remove_ledger(prev_dup_itr->second.lcl);
|
||||
cons::ctx.cache.erase(prev_dup_itr);
|
||||
}
|
||||
write_ledger(ledger.lcl, reinterpret_cast<const char *>(&ledger.raw_ledger[0]), ledger.raw_ledger.size());
|
||||
cons::ctx.lcl_list.emplace(seq_no, ledger.lcl);
|
||||
ledger_cache l;
|
||||
l.lcl = ledger.lcl;
|
||||
l.state = ledger.state;
|
||||
cons::ctx.cache.emplace(seq_no, std::move(l));
|
||||
}
|
||||
|
||||
last_requested_lcl = "";
|
||||
|
||||
const auto latest_lcl_itr = cons::ctx.cache.rbegin();
|
||||
cons::ctx.lcl = latest_lcl_itr->second.lcl;
|
||||
cons::ctx.led_seq_no = latest_lcl_itr->first;
|
||||
|
||||
if (cons::ctx.lcl_list.empty())
|
||||
if (cons::ctx.cache.empty())
|
||||
{
|
||||
cons::ctx.led_seq_no = 0;
|
||||
cons::ctx.lcl = "0-genesis";
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto latest_lcl_itr = cons::ctx.lcl_list.rbegin();
|
||||
cons::ctx.lcl = latest_lcl_itr->second;
|
||||
const auto latest_lcl_itr = cons::ctx.cache.rbegin();
|
||||
cons::ctx.lcl = latest_lcl_itr->second.lcl;
|
||||
cons::ctx.led_seq_no = latest_lcl_itr->first;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,22 @@ namespace cons
|
||||
//max ledger count
|
||||
constexpr uint64_t MAX_LEDGER_SEQUENCE = 200;
|
||||
|
||||
struct ledger_cache
|
||||
{
|
||||
std::string lcl;
|
||||
std::string state;
|
||||
};
|
||||
|
||||
extern ledger_cache cache;
|
||||
|
||||
struct ledger_history
|
||||
{
|
||||
std::string lcl;
|
||||
uint64_t led_seq_no;
|
||||
std::map<uint64_t, std::string> lcl_list;
|
||||
std::map<uint64_t, ledger_cache> cache;
|
||||
};
|
||||
|
||||
|
||||
extern std::string last_requested_lcl;
|
||||
|
||||
const std::tuple<const uint64_t, std::string> save_ledger(const p2p::proposal &proposal);
|
||||
@@ -39,6 +48,6 @@ p2p::peer_outbound_message send_ledger_history(const p2p::history_request &hr);
|
||||
|
||||
void handle_ledger_history_response(const p2p::history_response &hr);
|
||||
|
||||
}
|
||||
} // namespace cons
|
||||
|
||||
#endif
|
||||
339
src/cons/state_handler.cpp
Normal file
339
src/cons/state_handler.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
#include "state_handler.hpp"
|
||||
#include "../fbschema/p2pmsg_helpers.hpp"
|
||||
#include "../fbschema/p2pmsg_content_generated.h"
|
||||
#include "../fbschema/common_helpers.hpp"
|
||||
#include "../p2p/p2p.hpp"
|
||||
#include "../pchheader.hpp"
|
||||
#include "../cons/cons.hpp"
|
||||
#include "../statefs/state_store.hpp"
|
||||
|
||||
namespace cons
|
||||
{
|
||||
|
||||
constexpr uint16_t MAX_AWAITING_REQUESTS = 1;
|
||||
constexpr uint16_t MAX_RESPONSE_WAIT_CYCLES = 100;
|
||||
|
||||
// List of state responses flatbuffer messages to be processed.
|
||||
std::list<std::string> candidate_state_responses;
|
||||
|
||||
// List of pending sync requests to be sent out.
|
||||
std::list<backlog_item> pending_requests;
|
||||
|
||||
// List of submitted requests we are awaiting responses for, keyed by expected response hash.
|
||||
std::unordered_map<hasher::B2H, backlog_item, hasher::B2H_std_key_hasher> submitted_requests;
|
||||
|
||||
void request_state_from_peer(const std::string &path, const bool is_file, const std::string &lcl, const int32_t block_id, const hasher::B2H expected_hash)
|
||||
{
|
||||
p2p::state_request sr;
|
||||
sr.parent_path = path;
|
||||
sr.is_file = is_file;
|
||||
sr.block_id = block_id;
|
||||
sr.expected_hash = expected_hash;
|
||||
|
||||
p2p::peer_outbound_message msg(std::make_unique<flatbuffers::FlatBufferBuilder>(1024));
|
||||
fbschema::p2pmsg::create_msg_from_state_request(msg.builder(), sr, lcl);
|
||||
p2p::send_message_to_random_peer(msg);
|
||||
}
|
||||
|
||||
int create_state_response(p2p::peer_outbound_message &msg, const p2p::state_request &sr)
|
||||
{
|
||||
if (sr.block_id > -1)
|
||||
{
|
||||
std::vector<uint8_t> blocks;
|
||||
|
||||
if (statefs::get_block(blocks, sr.parent_path, sr.block_id, sr.expected_hash) == -1)
|
||||
return -1;
|
||||
|
||||
p2p::block_response resp;
|
||||
resp.path = sr.parent_path;
|
||||
resp.block_id = sr.block_id;
|
||||
resp.hash = sr.expected_hash;
|
||||
resp.data = std::string_view(reinterpret_cast<const char *>(blocks.data()), blocks.size());
|
||||
|
||||
fbschema::p2pmsg::create_msg_from_block_response(msg.builder(), resp, ctx.lcl);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sr.is_file)
|
||||
{
|
||||
std::vector<uint8_t> existing_block_hashmap;
|
||||
|
||||
if (statefs::get_block_hash_map(existing_block_hashmap, sr.parent_path, sr.expected_hash) == -1)
|
||||
return -1;
|
||||
|
||||
fbschema::p2pmsg::create_msg_from_filehashmap_response(msg.builder(), sr.parent_path, existing_block_hashmap, statefs::get_file_length(sr.parent_path), sr.expected_hash, ctx.lcl);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unordered_map<std::string, p2p::state_fs_hash_entry> existing_fs_entries;
|
||||
|
||||
if (statefs::get_fs_entry_hashes(existing_fs_entries, sr.parent_path, sr.expected_hash) == -1)
|
||||
return -1;
|
||||
|
||||
fbschema::p2pmsg::create_msg_from_fsentry_response(msg.builder(), sr.parent_path, existing_fs_entries, sr.expected_hash, ctx.lcl);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void start_state_sync(const hasher::B2H state_hash_to_request)
|
||||
{
|
||||
std::cout << "start_state_sync() " << state_hash_to_request << "\n";
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p2p::ctx.collected_msgs.state_response_mutex);
|
||||
p2p::ctx.collected_msgs.state_response.clear();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cons::ctx.state_syncing_mutex);
|
||||
candidate_state_responses.clear();
|
||||
pending_requests.clear();
|
||||
submitted_requests.clear();
|
||||
}
|
||||
|
||||
// Send the root state request.
|
||||
submit_request(backlog_item{BACKLOG_ITEM_TYPE::DIR, "/", -1, state_hash_to_request});
|
||||
}
|
||||
|
||||
int run_state_sync_iterator()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
util::sleep(120);
|
||||
|
||||
// TODO: Also bypass peer session handler responses if not syncing.
|
||||
if (!ctx.is_state_syncing)
|
||||
continue;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p2p::ctx.collected_msgs.state_response_mutex);
|
||||
|
||||
// Move collected state responses over to local candidate responses list.
|
||||
if (!p2p::ctx.collected_msgs.state_response.empty())
|
||||
candidate_state_responses.splice(candidate_state_responses.end(), p2p::ctx.collected_msgs.state_response);
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(cons::ctx.state_syncing_mutex);
|
||||
|
||||
for (auto &response : candidate_state_responses)
|
||||
{
|
||||
const fbschema::p2pmsg::Content *content = fbschema::p2pmsg::GetContent(response.data());
|
||||
const fbschema::p2pmsg::State_Response_Message *resp_msg = content->message_as_State_Response_Message();
|
||||
|
||||
// Check whether we are actually waiting for this response's hash. If not, ignore it.
|
||||
hasher::B2H response_hash = fbschema::flatbuff_bytes_to_hash(resp_msg->hash());
|
||||
const auto pending_resp_itr = submitted_requests.find(response_hash);
|
||||
if (pending_resp_itr == submitted_requests.end())
|
||||
{
|
||||
std::cout << "Ignoring state response.\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now that we have received matching hash, remove it from the waiting list.
|
||||
submitted_requests.erase(pending_resp_itr);
|
||||
|
||||
// Process the message based on response type.
|
||||
const fbschema::p2pmsg::State_Response msg_type = resp_msg->state_response_type();
|
||||
|
||||
if (msg_type == fbschema::p2pmsg::State_Response_Fs_Entry_Response)
|
||||
{
|
||||
if (handle_fs_entry_response(resp_msg->state_response_as_Fs_Entry_Response()) == -1)
|
||||
return -1;
|
||||
}
|
||||
else if (msg_type == fbschema::p2pmsg::State_Response_File_HashMap_Response)
|
||||
{
|
||||
if (handle_file_hashmap_response(resp_msg->state_response_as_File_HashMap_Response()) == -1)
|
||||
return -1;
|
||||
}
|
||||
else if (msg_type == fbschema::p2pmsg::State_Response_Block_Response)
|
||||
{
|
||||
if (handle_file_block_response(resp_msg->state_response_as_Block_Response()) == -1)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
candidate_state_responses.clear();
|
||||
|
||||
// Check for long-awaited responses and re-request them.
|
||||
for (auto &[hash, request] : submitted_requests)
|
||||
{
|
||||
if (request.waiting_cycles < MAX_RESPONSE_WAIT_CYCLES)
|
||||
{
|
||||
// Increment counter.
|
||||
request.waiting_cycles++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset the counter and re-submit request.
|
||||
request.waiting_cycles = 0;
|
||||
std::cout << "Resubmit state request\n";
|
||||
submit_request(request);
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether we can submit any more requests.
|
||||
if (!pending_requests.empty() && submitted_requests.size() < MAX_AWAITING_REQUESTS)
|
||||
{
|
||||
const uint16_t available_slots = MAX_AWAITING_REQUESTS - submitted_requests.size();
|
||||
for (int i = 0; i < available_slots && !pending_requests.empty(); i++)
|
||||
{
|
||||
const backlog_item &request = pending_requests.front();
|
||||
submit_request(request);
|
||||
pending_requests.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void submit_request(const backlog_item &request)
|
||||
{
|
||||
std::cout << "Submitting state request. type: " << request.type << " path:" << request.path << " blockid: " << request.block_id << "\n";
|
||||
|
||||
submitted_requests.try_emplace(request.expected_hash, request);
|
||||
|
||||
const bool is_file = request.type != BACKLOG_ITEM_TYPE::DIR;
|
||||
request_state_from_peer(request.path, is_file, ctx.lcl, request.block_id, request.expected_hash);
|
||||
}
|
||||
|
||||
int handle_fs_entry_response(const fbschema::p2pmsg::Fs_Entry_Response *fs_entry_resp)
|
||||
{
|
||||
std::cout << "Recieved state fs entry response\n";
|
||||
|
||||
std::unordered_map<std::string, p2p::state_fs_hash_entry> state_fs_entry_list;
|
||||
fbschema::p2pmsg::flatbuf_statefshashentry_to_statefshashentry(state_fs_entry_list, fs_entry_resp->entries());
|
||||
|
||||
for (const auto [a, b] : state_fs_entry_list)
|
||||
std::cout << "Recieved fsentry: " << a << "\n";
|
||||
|
||||
std::unordered_map<std::string, p2p::state_fs_hash_entry> existing_fs_entries;
|
||||
std::string_view root_path_sv = fbschema::flatbuff_str_to_sv(fs_entry_resp->path());
|
||||
std::string root_path_str(root_path_sv.data(), root_path_sv.size());
|
||||
|
||||
if (!statefs::is_dir_exists(root_path_str))
|
||||
{
|
||||
statefs::create_dir(root_path_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (statefs::get_fs_entry_hashes(existing_fs_entries, std::move(root_path_str), hasher::B2H_empty) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Request more info on fs entries that exist on both sides but are different.
|
||||
for (const auto &[path, fs_entry] : existing_fs_entries)
|
||||
{
|
||||
std::cout << "Existing path :" << path << std::endl;
|
||||
const auto fs_itr = state_fs_entry_list.find(path);
|
||||
if (fs_itr != state_fs_entry_list.end())
|
||||
{
|
||||
std::cout << "Existing fs_entry_hash :" << fs_entry.hash << std::endl;
|
||||
std::cout << "Recieved fs_entry_hash :" << fs_itr->second.hash << std::endl;
|
||||
if (fs_itr->second.hash != fs_entry.hash)
|
||||
{
|
||||
if (fs_entry.is_file)
|
||||
pending_requests.push_front(backlog_item{BACKLOG_ITEM_TYPE::FILE, path, -1, fs_itr->second.hash});
|
||||
else
|
||||
pending_requests.push_back(backlog_item{BACKLOG_ITEM_TYPE::DIR, path, -1, fs_itr->second.hash});
|
||||
}
|
||||
|
||||
state_fs_entry_list.erase(fs_itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there was an entry that does not exist on other side, delete it from this node.
|
||||
if (fs_entry.is_file)
|
||||
{
|
||||
if (statefs::delete_file(path) == -1)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (statefs::delete_dir(path) == -1)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Queue the remaining fs entries (that this node does not have at all) to request.
|
||||
for (const auto &[path, fs_entry] : state_fs_entry_list)
|
||||
{
|
||||
if (fs_entry.is_file)
|
||||
pending_requests.push_front(backlog_item{BACKLOG_ITEM_TYPE::FILE, path, -1, fs_entry.hash});
|
||||
else
|
||||
pending_requests.push_back(backlog_item{BACKLOG_ITEM_TYPE::DIR, path, -1, fs_entry.hash});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_file_hashmap_response(const fbschema::p2pmsg::File_HashMap_Response *file_resp)
|
||||
{
|
||||
std::string_view path_sv = fbschema::flatbuff_str_to_sv(file_resp->path());
|
||||
const std::string path_str(path_sv.data(), path_sv.size());
|
||||
|
||||
std::cout << "Recieved file hash map of " << path_str << std::endl;
|
||||
|
||||
std::vector<uint8_t> existing_block_hashmap;
|
||||
if (statefs::get_block_hash_map(existing_block_hashmap, path_str, hasher::B2H_empty) == -1)
|
||||
return -1;
|
||||
|
||||
const hasher::B2H *existing_hashes = reinterpret_cast<const hasher::B2H *>(existing_block_hashmap.data());
|
||||
auto existing_hash_count = existing_block_hashmap.size() / hasher::HASH_SIZE;
|
||||
|
||||
const hasher::B2H *resp_hashes = reinterpret_cast<const hasher::B2H *>(file_resp->hash_map()->data());
|
||||
auto resp_hash_count = file_resp->hash_map()->size() / hasher::HASH_SIZE;
|
||||
|
||||
std::cout << "Reieved file hashmap size :" << file_resp->hash_map()->size() << std::endl;
|
||||
std::cout << "Existing file hashmap size :" << existing_block_hashmap.size() << std::endl;
|
||||
|
||||
auto insert_itr = pending_requests.begin();
|
||||
|
||||
for (int block_id = 0; block_id < existing_hash_count; ++block_id)
|
||||
{
|
||||
if (block_id >= resp_hash_count)
|
||||
break;
|
||||
|
||||
if (existing_hashes[block_id] != resp_hashes[block_id])
|
||||
{
|
||||
std::cout << "Mismatch in file block :" << block_id << std::endl;
|
||||
// Insert at front to give priority to block requests while preserving block order.
|
||||
pending_requests.insert(insert_itr, backlog_item{BACKLOG_ITEM_TYPE::BLOCK, path_str, block_id, resp_hashes[block_id]});
|
||||
}
|
||||
}
|
||||
|
||||
if (existing_hash_count > resp_hash_count)
|
||||
{
|
||||
if (statefs::truncate_file(path_str, file_resp->file_length()) == -1)
|
||||
return -1;
|
||||
}
|
||||
else if (existing_hash_count < resp_hash_count)
|
||||
{
|
||||
for (int block_id = existing_hash_count; block_id < resp_hash_count; ++block_id)
|
||||
{
|
||||
std::cout << "Missing block: " << block_id << "\n";
|
||||
// Insert at front to give priority to block requests while preserving block order.
|
||||
pending_requests.insert(insert_itr, backlog_item{BACKLOG_ITEM_TYPE::BLOCK, path_str, block_id, resp_hashes[block_id]});
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_file_block_response(const fbschema::p2pmsg::Block_Response *block_msg)
|
||||
{
|
||||
p2p::block_response block_resp = fbschema::p2pmsg::create_block_response_from_msg(*block_msg);
|
||||
|
||||
std::cout << "Recieved block " << block_resp.block_id << " of " << block_resp.path << "\n";
|
||||
|
||||
if (statefs::write_block(block_resp.path, block_resp.block_id, block_resp.data.data(), block_resp.data.size()) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace cons
|
||||
52
src/cons/state_handler.hpp
Normal file
52
src/cons/state_handler.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef _HP_CONS_STATE_HANDLER_
|
||||
#define _HP_CONS_STATE_HANDLER_
|
||||
|
||||
#include "../pchheader.hpp"
|
||||
#include "../p2p/p2p.hpp"
|
||||
#include "../fbschema/p2pmsg_content_generated.h"
|
||||
#include "../statefs/hasher.hpp"
|
||||
|
||||
namespace cons
|
||||
{
|
||||
|
||||
enum BACKLOG_ITEM_TYPE
|
||||
{
|
||||
DIR = 0,
|
||||
FILE = 1,
|
||||
BLOCK = 2
|
||||
};
|
||||
|
||||
// Represents a queued up state sync operation which needs to be performed.
|
||||
struct backlog_item
|
||||
{
|
||||
BACKLOG_ITEM_TYPE type;
|
||||
std::string path;
|
||||
int32_t block_id = -1; // Only relevant if type=BLOCK
|
||||
hasher::B2H expected_hash;
|
||||
|
||||
// No. of cycles that this item has been waiting in pending state.
|
||||
// Used by pending_responses list to increase wait count.
|
||||
int16_t waiting_cycles = 0;
|
||||
};
|
||||
|
||||
extern std::list<std::string> candidate_state_responses;
|
||||
|
||||
int create_state_response(p2p::peer_outbound_message &msg, const p2p::state_request &sr);
|
||||
|
||||
void request_state_from_peer(const std::string &path, const bool is_file, const std::string &lcl, const int32_t block_id, const hasher::B2H expected_hash);
|
||||
|
||||
void start_state_sync(const hasher::B2H state_hash_to_request);
|
||||
|
||||
int run_state_sync_iterator();
|
||||
|
||||
void submit_request(const backlog_item &request);
|
||||
|
||||
int handle_fs_entry_response(const fbschema::p2pmsg::Fs_Entry_Response *fs_entry_resp);
|
||||
|
||||
int handle_file_hashmap_response(const fbschema::p2pmsg::File_HashMap_Response *file_resp);
|
||||
|
||||
int handle_file_block_response(const fbschema::p2pmsg::Block_Response *block_msg);
|
||||
|
||||
} // namespace cons
|
||||
|
||||
#endif
|
||||
@@ -15,13 +15,29 @@ std::string_view flatbuff_bytes_to_sv(const uint8_t *data, const flatbuffers::uo
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns return string_view from Flat Buffer vector of bytes.
|
||||
* Returns string_view from Flat Buffer vector of bytes.
|
||||
*/
|
||||
std::string_view flatbuff_bytes_to_sv(const flatbuffers::Vector<uint8_t> *buffer)
|
||||
{
|
||||
return flatbuff_bytes_to_sv(buffer->Data(), buffer->size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns return string_view from Flat Buffer string.
|
||||
*/
|
||||
std::string_view flatbuff_str_to_sv(const flatbuffers::String *buffer)
|
||||
{
|
||||
return flatbuff_bytes_to_sv(buffer->Data(), buffer->size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hash from Flat Buffer vector of bytes.
|
||||
*/
|
||||
hasher::B2H flatbuff_bytes_to_hash(const flatbuffers::Vector<uint8_t> *buffer)
|
||||
{
|
||||
return *reinterpret_cast<const hasher::B2H *>(buffer->data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set from Flatbuffer vector of ByteArrays.
|
||||
*/
|
||||
@@ -58,6 +74,24 @@ sv_to_flatbuff_bytes(flatbuffers::FlatBufferBuilder &builder, std::string_view s
|
||||
return builder.CreateVector(reinterpret_cast<const uint8_t *>(sv.data()), sv.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Flatbuffer string from string_view.
|
||||
*/
|
||||
const flatbuffers::Offset<flatbuffers::String>
|
||||
sv_to_flatbuff_str(flatbuffers::FlatBufferBuilder &builder, std::string_view sv)
|
||||
{
|
||||
return builder.CreateString(sv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Flatbuffer bytes vector from hash.
|
||||
*/
|
||||
const flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
|
||||
hash_to_flatbuff_bytes(flatbuffers::FlatBufferBuilder &builder, const hasher::B2H hash)
|
||||
{
|
||||
return builder.CreateVector(reinterpret_cast<const uint8_t *>(&hash), hasher::HASH_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Flatbuffer vector of ByteArrays from given set of strings.
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../pchheader.hpp"
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
#include "common_schema_generated.h"
|
||||
#include "../statefs/hasher.hpp"
|
||||
|
||||
namespace fbschema
|
||||
{
|
||||
@@ -17,6 +18,10 @@ std::string_view flatbuff_bytes_to_sv(const uint8_t *data, const flatbuffers::uo
|
||||
|
||||
std::string_view flatbuff_bytes_to_sv(const flatbuffers::Vector<uint8_t> *buffer);
|
||||
|
||||
std::string_view flatbuff_str_to_sv(const flatbuffers::String *buffer);
|
||||
|
||||
hasher::B2H flatbuff_bytes_to_hash(const flatbuffers::Vector<uint8_t> *buffer);
|
||||
|
||||
const std::set<std::string>
|
||||
flatbuf_bytearrayvector_to_stringlist(const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *fbvec);
|
||||
|
||||
@@ -28,6 +33,12 @@ flatbuf_pairvector_to_stringmap(const flatbuffers::Vector<flatbuffers::Offset<By
|
||||
const flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
|
||||
sv_to_flatbuff_bytes(flatbuffers::FlatBufferBuilder &builder, std::string_view sv);
|
||||
|
||||
const flatbuffers::Offset<flatbuffers::String>
|
||||
sv_to_flatbuff_str(flatbuffers::FlatBufferBuilder &builder, std::string_view sv);
|
||||
|
||||
const flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
|
||||
hash_to_flatbuff_bytes(flatbuffers::FlatBufferBuilder &builder, hasher::B2H hash);
|
||||
|
||||
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>>
|
||||
stringlist_to_flatbuf_bytearrayvector(flatbuffers::FlatBufferBuilder &builder, const std::set<std::string> &set);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace fbschema::ledger
|
||||
* Create ledger from the given proposal struct.
|
||||
* @param p The proposal struct to be placed in ledger.
|
||||
*/
|
||||
std::string_view create_ledger_from_proposal(flatbuffers::FlatBufferBuilder &builder, const p2p::proposal &p, const uint64_t seq_no)
|
||||
const std::string_view create_ledger_from_proposal(flatbuffers::FlatBufferBuilder &builder, const p2p::proposal &p, const uint64_t seq_no)
|
||||
{
|
||||
flatbuffers::Offset<ledger::Ledger> ledger =
|
||||
ledger::CreateLedger(
|
||||
@@ -20,13 +20,13 @@ std::string_view create_ledger_from_proposal(flatbuffers::FlatBufferBuilder &bui
|
||||
seq_no,
|
||||
p.time,
|
||||
sv_to_flatbuff_bytes(builder, p.lcl),
|
||||
sv_to_flatbuff_bytes(builder, p.curr_hash_state),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.users),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_inputs),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_outputs)
|
||||
);
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_outputs));
|
||||
|
||||
builder.Finish(ledger); // Finished building message content to get serialised content.
|
||||
|
||||
return flatbuff_bytes_to_sv(builder.GetBufferPointer(), builder.GetSize());
|
||||
}
|
||||
} // namespace fbschema
|
||||
} // namespace fbschema::ledger
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace fbschema::ledger
|
||||
{
|
||||
|
||||
std::string_view create_ledger_from_proposal(flatbuffers::FlatBufferBuilder &builder, const p2p::proposal &p, const uint64_t seq_no);
|
||||
const std::string_view create_ledger_from_proposal(flatbuffers::FlatBufferBuilder &builder, const p2p::proposal &p, const uint64_t seq_no);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@ table Ledger {
|
||||
seq_no:uint64;
|
||||
time:uint64;
|
||||
lcl:[ubyte];
|
||||
state:[ubyte];
|
||||
users: [ByteArray];
|
||||
inputs: [ByteArray];
|
||||
outputs: [ByteArray];
|
||||
|
||||
@@ -20,9 +20,10 @@ struct Ledger FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
VT_SEQ_NO = 4,
|
||||
VT_TIME = 6,
|
||||
VT_LCL = 8,
|
||||
VT_USERS = 10,
|
||||
VT_INPUTS = 12,
|
||||
VT_OUTPUTS = 14
|
||||
VT_STATE = 10,
|
||||
VT_USERS = 12,
|
||||
VT_INPUTS = 14,
|
||||
VT_OUTPUTS = 16
|
||||
};
|
||||
uint64_t seq_no() const {
|
||||
return GetField<uint64_t>(VT_SEQ_NO, 0);
|
||||
@@ -42,6 +43,12 @@ struct Ledger FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
flatbuffers::Vector<uint8_t> *mutable_lcl() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_LCL);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *state() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_STATE);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_state() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_STATE);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *users() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *>(VT_USERS);
|
||||
}
|
||||
@@ -66,6 +73,8 @@ struct Ledger FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
VerifyField<uint64_t>(verifier, VT_TIME) &&
|
||||
VerifyOffset(verifier, VT_LCL) &&
|
||||
verifier.VerifyVector(lcl()) &&
|
||||
VerifyOffset(verifier, VT_STATE) &&
|
||||
verifier.VerifyVector(state()) &&
|
||||
VerifyOffset(verifier, VT_USERS) &&
|
||||
verifier.VerifyVector(users()) &&
|
||||
verifier.VerifyVectorOfTables(users()) &&
|
||||
@@ -91,6 +100,9 @@ struct LedgerBuilder {
|
||||
void add_lcl(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl) {
|
||||
fbb_.AddOffset(Ledger::VT_LCL, lcl);
|
||||
}
|
||||
void add_state(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> state) {
|
||||
fbb_.AddOffset(Ledger::VT_STATE, state);
|
||||
}
|
||||
void add_users(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> users) {
|
||||
fbb_.AddOffset(Ledger::VT_USERS, users);
|
||||
}
|
||||
@@ -117,6 +129,7 @@ inline flatbuffers::Offset<Ledger> CreateLedger(
|
||||
uint64_t seq_no = 0,
|
||||
uint64_t time = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> state = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> users = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> inputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> outputs = 0) {
|
||||
@@ -126,6 +139,7 @@ inline flatbuffers::Offset<Ledger> CreateLedger(
|
||||
builder_.add_outputs(outputs);
|
||||
builder_.add_inputs(inputs);
|
||||
builder_.add_users(users);
|
||||
builder_.add_state(state);
|
||||
builder_.add_lcl(lcl);
|
||||
return builder_.Finish();
|
||||
}
|
||||
@@ -135,10 +149,12 @@ inline flatbuffers::Offset<Ledger> CreateLedgerDirect(
|
||||
uint64_t seq_no = 0,
|
||||
uint64_t time = 0,
|
||||
const std::vector<uint8_t> *lcl = nullptr,
|
||||
const std::vector<uint8_t> *state = nullptr,
|
||||
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *users = nullptr,
|
||||
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *inputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *outputs = nullptr) {
|
||||
auto lcl__ = lcl ? _fbb.CreateVector<uint8_t>(*lcl) : 0;
|
||||
auto state__ = state ? _fbb.CreateVector<uint8_t>(*state) : 0;
|
||||
auto users__ = users ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*users) : 0;
|
||||
auto inputs__ = inputs ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*inputs) : 0;
|
||||
auto outputs__ = outputs ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*outputs) : 0;
|
||||
@@ -147,6 +163,7 @@ inline flatbuffers::Offset<Ledger> CreateLedgerDirect(
|
||||
seq_no,
|
||||
time,
|
||||
lcl__,
|
||||
state__,
|
||||
users__,
|
||||
inputs__,
|
||||
outputs__);
|
||||
|
||||
@@ -12,7 +12,7 @@ table UserSubmittedMessageGroup {
|
||||
messages:[UserSubmittedMessage];
|
||||
}
|
||||
|
||||
union Message { NonUnl_Proposal_Message, Proposal_Message, Npl_Message, History_Request_Message, History_Response_Message } //message content type
|
||||
union Message { NonUnl_Proposal_Message, Proposal_Message, Npl_Message, State_Request_Message, State_Response_Message, History_Request_Message, History_Response_Message } //message content type
|
||||
|
||||
table Content {
|
||||
message:Message;
|
||||
@@ -28,7 +28,7 @@ table Proposal_Message { //Proposal type message schema
|
||||
users:[ByteArray];
|
||||
hash_inputs:[ByteArray]; //stage > 0 inputs (hash of stage 0 inputs)
|
||||
hash_outputs:[ByteArray]; //stage > 0 outputs (hash of stage 0 outputs)
|
||||
state: State;
|
||||
curr_state_hash: [ubyte];
|
||||
}
|
||||
|
||||
table Npl_Message { //NPL type message schema
|
||||
@@ -58,21 +58,46 @@ table HistoryLedgerPair { //A key, value pair of byte[].
|
||||
}
|
||||
|
||||
table HistoryLedger {
|
||||
state:[ubyte];
|
||||
lcl:[ubyte];
|
||||
raw_ledger:[ubyte];
|
||||
}
|
||||
|
||||
table StateDifference { //Represent state difference by tracking created,updated and deleted state files.
|
||||
created: [BytesKeyValuePair]; //list of { fn => hash }
|
||||
updated: [BytesKeyValuePair];
|
||||
deleted: [BytesKeyValuePair];
|
||||
table State_Request_Message { //State request message schema
|
||||
parent_path:string;
|
||||
is_file:bool;
|
||||
block_id:int32;
|
||||
expected_hash:[ubyte];
|
||||
}
|
||||
|
||||
table State {
|
||||
previous: [ubyte]; // hash of the previous state
|
||||
current: [ubyte]; // hash of the current state
|
||||
difference: StateDifference;
|
||||
patch: [BytesKeyValuePair]; // fn -> bsdiff patch going from previous state to new state
|
||||
union State_Response{ File_HashMap_Response, Block_Response, Fs_Entry_Response }
|
||||
|
||||
table State_Response_Message{
|
||||
state_response:State_Response;
|
||||
hash:[ubyte];
|
||||
}
|
||||
|
||||
table Fs_Entry_Response{
|
||||
path: string;
|
||||
entries: [State_FS_Hash_Entry];
|
||||
}
|
||||
|
||||
table File_HashMap_Response{
|
||||
path: string;
|
||||
file_length:uint64;
|
||||
hash_map:[ubyte];
|
||||
}
|
||||
|
||||
table Block_Response{
|
||||
path: string;
|
||||
block_id:uint32;
|
||||
data: [ubyte];
|
||||
}
|
||||
|
||||
table State_FS_Hash_Entry{
|
||||
path: string;
|
||||
is_file: bool;
|
||||
hash: [ubyte];
|
||||
}
|
||||
|
||||
root_type Content; //root type for message content
|
||||
@@ -31,27 +31,39 @@ struct HistoryLedgerPair;
|
||||
|
||||
struct HistoryLedger;
|
||||
|
||||
struct StateDifference;
|
||||
struct State_Request_Message;
|
||||
|
||||
struct State;
|
||||
struct State_Response_Message;
|
||||
|
||||
struct Fs_Entry_Response;
|
||||
|
||||
struct File_HashMap_Response;
|
||||
|
||||
struct Block_Response;
|
||||
|
||||
struct State_FS_Hash_Entry;
|
||||
|
||||
enum Message {
|
||||
Message_NONE = 0,
|
||||
Message_NonUnl_Proposal_Message = 1,
|
||||
Message_Proposal_Message = 2,
|
||||
Message_Npl_Message = 3,
|
||||
Message_History_Request_Message = 4,
|
||||
Message_History_Response_Message = 5,
|
||||
Message_State_Request_Message = 4,
|
||||
Message_State_Response_Message = 5,
|
||||
Message_History_Request_Message = 6,
|
||||
Message_History_Response_Message = 7,
|
||||
Message_MIN = Message_NONE,
|
||||
Message_MAX = Message_History_Response_Message
|
||||
};
|
||||
|
||||
inline const Message (&EnumValuesMessage())[6] {
|
||||
inline const Message (&EnumValuesMessage())[8] {
|
||||
static const Message values[] = {
|
||||
Message_NONE,
|
||||
Message_NonUnl_Proposal_Message,
|
||||
Message_Proposal_Message,
|
||||
Message_Npl_Message,
|
||||
Message_State_Request_Message,
|
||||
Message_State_Response_Message,
|
||||
Message_History_Request_Message,
|
||||
Message_History_Response_Message
|
||||
};
|
||||
@@ -64,6 +76,8 @@ inline const char * const *EnumNamesMessage() {
|
||||
"NonUnl_Proposal_Message",
|
||||
"Proposal_Message",
|
||||
"Npl_Message",
|
||||
"State_Request_Message",
|
||||
"State_Response_Message",
|
||||
"History_Request_Message",
|
||||
"History_Response_Message",
|
||||
nullptr
|
||||
@@ -93,6 +107,14 @@ template<> struct MessageTraits<Npl_Message> {
|
||||
static const Message enum_value = Message_Npl_Message;
|
||||
};
|
||||
|
||||
template<> struct MessageTraits<State_Request_Message> {
|
||||
static const Message enum_value = Message_State_Request_Message;
|
||||
};
|
||||
|
||||
template<> struct MessageTraits<State_Response_Message> {
|
||||
static const Message enum_value = Message_State_Response_Message;
|
||||
};
|
||||
|
||||
template<> struct MessageTraits<History_Request_Message> {
|
||||
static const Message enum_value = Message_History_Request_Message;
|
||||
};
|
||||
@@ -137,6 +159,61 @@ inline const char *EnumNameLedger_Response_Error(Ledger_Response_Error e) {
|
||||
return EnumNamesLedger_Response_Error()[index];
|
||||
}
|
||||
|
||||
enum State_Response {
|
||||
State_Response_NONE = 0,
|
||||
State_Response_File_HashMap_Response = 1,
|
||||
State_Response_Block_Response = 2,
|
||||
State_Response_Fs_Entry_Response = 3,
|
||||
State_Response_MIN = State_Response_NONE,
|
||||
State_Response_MAX = State_Response_Fs_Entry_Response
|
||||
};
|
||||
|
||||
inline const State_Response (&EnumValuesState_Response())[4] {
|
||||
static const State_Response values[] = {
|
||||
State_Response_NONE,
|
||||
State_Response_File_HashMap_Response,
|
||||
State_Response_Block_Response,
|
||||
State_Response_Fs_Entry_Response
|
||||
};
|
||||
return values;
|
||||
}
|
||||
|
||||
inline const char * const *EnumNamesState_Response() {
|
||||
static const char * const names[] = {
|
||||
"NONE",
|
||||
"File_HashMap_Response",
|
||||
"Block_Response",
|
||||
"Fs_Entry_Response",
|
||||
nullptr
|
||||
};
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameState_Response(State_Response e) {
|
||||
if (e < State_Response_NONE || e > State_Response_Fs_Entry_Response) return "";
|
||||
const size_t index = static_cast<size_t>(e);
|
||||
return EnumNamesState_Response()[index];
|
||||
}
|
||||
|
||||
template<typename T> struct State_ResponseTraits {
|
||||
static const State_Response enum_value = State_Response_NONE;
|
||||
};
|
||||
|
||||
template<> struct State_ResponseTraits<File_HashMap_Response> {
|
||||
static const State_Response enum_value = State_Response_File_HashMap_Response;
|
||||
};
|
||||
|
||||
template<> struct State_ResponseTraits<Block_Response> {
|
||||
static const State_Response enum_value = State_Response_Block_Response;
|
||||
};
|
||||
|
||||
template<> struct State_ResponseTraits<Fs_Entry_Response> {
|
||||
static const State_Response enum_value = State_Response_Fs_Entry_Response;
|
||||
};
|
||||
|
||||
bool VerifyState_Response(flatbuffers::Verifier &verifier, const void *obj, State_Response type);
|
||||
bool VerifyState_ResponseVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
|
||||
|
||||
struct UserSubmittedMessage FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_CONTENT = 4,
|
||||
@@ -302,6 +379,12 @@ struct Content FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
const Npl_Message *message_as_Npl_Message() const {
|
||||
return message_type() == Message_Npl_Message ? static_cast<const Npl_Message *>(message()) : nullptr;
|
||||
}
|
||||
const State_Request_Message *message_as_State_Request_Message() const {
|
||||
return message_type() == Message_State_Request_Message ? static_cast<const State_Request_Message *>(message()) : nullptr;
|
||||
}
|
||||
const State_Response_Message *message_as_State_Response_Message() const {
|
||||
return message_type() == Message_State_Response_Message ? static_cast<const State_Response_Message *>(message()) : nullptr;
|
||||
}
|
||||
const History_Request_Message *message_as_History_Request_Message() const {
|
||||
return message_type() == Message_History_Request_Message ? static_cast<const History_Request_Message *>(message()) : nullptr;
|
||||
}
|
||||
@@ -332,6 +415,14 @@ template<> inline const Npl_Message *Content::message_as<Npl_Message>() const {
|
||||
return message_as_Npl_Message();
|
||||
}
|
||||
|
||||
template<> inline const State_Request_Message *Content::message_as<State_Request_Message>() const {
|
||||
return message_as_State_Request_Message();
|
||||
}
|
||||
|
||||
template<> inline const State_Response_Message *Content::message_as<State_Response_Message>() const {
|
||||
return message_as_State_Response_Message();
|
||||
}
|
||||
|
||||
template<> inline const History_Request_Message *Content::message_as<History_Request_Message>() const {
|
||||
return message_as_History_Request_Message();
|
||||
}
|
||||
@@ -432,7 +523,7 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
VT_USERS = 8,
|
||||
VT_HASH_INPUTS = 10,
|
||||
VT_HASH_OUTPUTS = 12,
|
||||
VT_STATE = 14
|
||||
VT_CURR_STATE_HASH = 14
|
||||
};
|
||||
uint8_t stage() const {
|
||||
return GetField<uint8_t>(VT_STAGE, 0);
|
||||
@@ -464,11 +555,11 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *mutable_hash_outputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *>(VT_HASH_OUTPUTS);
|
||||
}
|
||||
const State *state() const {
|
||||
return GetPointer<const State *>(VT_STATE);
|
||||
const flatbuffers::Vector<uint8_t> *curr_state_hash() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_CURR_STATE_HASH);
|
||||
}
|
||||
State *mutable_state() {
|
||||
return GetPointer<State *>(VT_STATE);
|
||||
flatbuffers::Vector<uint8_t> *mutable_curr_state_hash() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_CURR_STATE_HASH);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
@@ -483,8 +574,8 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
VerifyOffset(verifier, VT_HASH_OUTPUTS) &&
|
||||
verifier.VerifyVector(hash_outputs()) &&
|
||||
verifier.VerifyVectorOfTables(hash_outputs()) &&
|
||||
VerifyOffset(verifier, VT_STATE) &&
|
||||
verifier.VerifyTable(state()) &&
|
||||
VerifyOffset(verifier, VT_CURR_STATE_HASH) &&
|
||||
verifier.VerifyVector(curr_state_hash()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
@@ -507,8 +598,8 @@ struct Proposal_MessageBuilder {
|
||||
void add_hash_outputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> hash_outputs) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_HASH_OUTPUTS, hash_outputs);
|
||||
}
|
||||
void add_state(flatbuffers::Offset<State> state) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_STATE, state);
|
||||
void add_curr_state_hash(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> curr_state_hash) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_CURR_STATE_HASH, curr_state_hash);
|
||||
}
|
||||
explicit Proposal_MessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
@@ -529,10 +620,10 @@ inline flatbuffers::Offset<Proposal_Message> CreateProposal_Message(
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> users = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> hash_inputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> hash_outputs = 0,
|
||||
flatbuffers::Offset<State> state = 0) {
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> curr_state_hash = 0) {
|
||||
Proposal_MessageBuilder builder_(_fbb);
|
||||
builder_.add_time(time);
|
||||
builder_.add_state(state);
|
||||
builder_.add_curr_state_hash(curr_state_hash);
|
||||
builder_.add_hash_outputs(hash_outputs);
|
||||
builder_.add_hash_inputs(hash_inputs);
|
||||
builder_.add_users(users);
|
||||
@@ -547,10 +638,11 @@ inline flatbuffers::Offset<Proposal_Message> CreateProposal_MessageDirect(
|
||||
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *users = nullptr,
|
||||
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *hash_inputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *hash_outputs = nullptr,
|
||||
flatbuffers::Offset<State> state = 0) {
|
||||
const std::vector<uint8_t> *curr_state_hash = nullptr) {
|
||||
auto users__ = users ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*users) : 0;
|
||||
auto hash_inputs__ = hash_inputs ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*hash_inputs) : 0;
|
||||
auto hash_outputs__ = hash_outputs ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*hash_outputs) : 0;
|
||||
auto curr_state_hash__ = curr_state_hash ? _fbb.CreateVector<uint8_t>(*curr_state_hash) : 0;
|
||||
return fbschema::p2pmsg::CreateProposal_Message(
|
||||
_fbb,
|
||||
stage,
|
||||
@@ -558,7 +650,7 @@ inline flatbuffers::Offset<Proposal_Message> CreateProposal_MessageDirect(
|
||||
users__,
|
||||
hash_inputs__,
|
||||
hash_outputs__,
|
||||
state);
|
||||
curr_state_hash__);
|
||||
}
|
||||
|
||||
struct Npl_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
@@ -812,9 +904,16 @@ inline flatbuffers::Offset<HistoryLedgerPair> CreateHistoryLedgerPair(
|
||||
|
||||
struct HistoryLedger FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_LCL = 4,
|
||||
VT_RAW_LEDGER = 6
|
||||
VT_STATE = 4,
|
||||
VT_LCL = 6,
|
||||
VT_RAW_LEDGER = 8
|
||||
};
|
||||
const flatbuffers::Vector<uint8_t> *state() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_STATE);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_state() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_STATE);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *lcl() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_LCL);
|
||||
}
|
||||
@@ -829,6 +928,8 @@ struct HistoryLedger FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_STATE) &&
|
||||
verifier.VerifyVector(state()) &&
|
||||
VerifyOffset(verifier, VT_LCL) &&
|
||||
verifier.VerifyVector(lcl()) &&
|
||||
VerifyOffset(verifier, VT_RAW_LEDGER) &&
|
||||
@@ -840,6 +941,9 @@ struct HistoryLedger FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
struct HistoryLedgerBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_state(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> state) {
|
||||
fbb_.AddOffset(HistoryLedger::VT_STATE, state);
|
||||
}
|
||||
void add_lcl(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl) {
|
||||
fbb_.AddOffset(HistoryLedger::VT_LCL, lcl);
|
||||
}
|
||||
@@ -860,218 +964,561 @@ struct HistoryLedgerBuilder {
|
||||
|
||||
inline flatbuffers::Offset<HistoryLedger> CreateHistoryLedger(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> state = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> raw_ledger = 0) {
|
||||
HistoryLedgerBuilder builder_(_fbb);
|
||||
builder_.add_raw_ledger(raw_ledger);
|
||||
builder_.add_lcl(lcl);
|
||||
builder_.add_state(state);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<HistoryLedger> CreateHistoryLedgerDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<uint8_t> *state = nullptr,
|
||||
const std::vector<uint8_t> *lcl = nullptr,
|
||||
const std::vector<uint8_t> *raw_ledger = nullptr) {
|
||||
auto state__ = state ? _fbb.CreateVector<uint8_t>(*state) : 0;
|
||||
auto lcl__ = lcl ? _fbb.CreateVector<uint8_t>(*lcl) : 0;
|
||||
auto raw_ledger__ = raw_ledger ? _fbb.CreateVector<uint8_t>(*raw_ledger) : 0;
|
||||
return fbschema::p2pmsg::CreateHistoryLedger(
|
||||
_fbb,
|
||||
state__,
|
||||
lcl__,
|
||||
raw_ledger__);
|
||||
}
|
||||
|
||||
struct StateDifference FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
struct State_Request_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_CREATED = 4,
|
||||
VT_UPDATED = 6,
|
||||
VT_DELETED = 8
|
||||
VT_PARENT_PATH = 4,
|
||||
VT_IS_FILE = 6,
|
||||
VT_BLOCK_ID = 8,
|
||||
VT_EXPECTED_HASH = 10
|
||||
};
|
||||
const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *created() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_CREATED);
|
||||
const flatbuffers::String *parent_path() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_PARENT_PATH);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *mutable_created() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_CREATED);
|
||||
flatbuffers::String *mutable_parent_path() {
|
||||
return GetPointer<flatbuffers::String *>(VT_PARENT_PATH);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *updated() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_UPDATED);
|
||||
bool is_file() const {
|
||||
return GetField<uint8_t>(VT_IS_FILE, 0) != 0;
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *mutable_updated() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_UPDATED);
|
||||
bool mutate_is_file(bool _is_file) {
|
||||
return SetField<uint8_t>(VT_IS_FILE, static_cast<uint8_t>(_is_file), 0);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *deleted() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_DELETED);
|
||||
int32_t block_id() const {
|
||||
return GetField<int32_t>(VT_BLOCK_ID, 0);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *mutable_deleted() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_DELETED);
|
||||
bool mutate_block_id(int32_t _block_id) {
|
||||
return SetField<int32_t>(VT_BLOCK_ID, _block_id, 0);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *expected_hash() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_EXPECTED_HASH);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_expected_hash() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_EXPECTED_HASH);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_CREATED) &&
|
||||
verifier.VerifyVector(created()) &&
|
||||
verifier.VerifyVectorOfTables(created()) &&
|
||||
VerifyOffset(verifier, VT_UPDATED) &&
|
||||
verifier.VerifyVector(updated()) &&
|
||||
verifier.VerifyVectorOfTables(updated()) &&
|
||||
VerifyOffset(verifier, VT_DELETED) &&
|
||||
verifier.VerifyVector(deleted()) &&
|
||||
verifier.VerifyVectorOfTables(deleted()) &&
|
||||
VerifyOffset(verifier, VT_PARENT_PATH) &&
|
||||
verifier.VerifyString(parent_path()) &&
|
||||
VerifyField<uint8_t>(verifier, VT_IS_FILE) &&
|
||||
VerifyField<int32_t>(verifier, VT_BLOCK_ID) &&
|
||||
VerifyOffset(verifier, VT_EXPECTED_HASH) &&
|
||||
verifier.VerifyVector(expected_hash()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct StateDifferenceBuilder {
|
||||
struct State_Request_MessageBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_created(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> created) {
|
||||
fbb_.AddOffset(StateDifference::VT_CREATED, created);
|
||||
void add_parent_path(flatbuffers::Offset<flatbuffers::String> parent_path) {
|
||||
fbb_.AddOffset(State_Request_Message::VT_PARENT_PATH, parent_path);
|
||||
}
|
||||
void add_updated(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> updated) {
|
||||
fbb_.AddOffset(StateDifference::VT_UPDATED, updated);
|
||||
void add_is_file(bool is_file) {
|
||||
fbb_.AddElement<uint8_t>(State_Request_Message::VT_IS_FILE, static_cast<uint8_t>(is_file), 0);
|
||||
}
|
||||
void add_deleted(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> deleted) {
|
||||
fbb_.AddOffset(StateDifference::VT_DELETED, deleted);
|
||||
void add_block_id(int32_t block_id) {
|
||||
fbb_.AddElement<int32_t>(State_Request_Message::VT_BLOCK_ID, block_id, 0);
|
||||
}
|
||||
explicit StateDifferenceBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
void add_expected_hash(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> expected_hash) {
|
||||
fbb_.AddOffset(State_Request_Message::VT_EXPECTED_HASH, expected_hash);
|
||||
}
|
||||
explicit State_Request_MessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
StateDifferenceBuilder &operator=(const StateDifferenceBuilder &);
|
||||
flatbuffers::Offset<StateDifference> Finish() {
|
||||
State_Request_MessageBuilder &operator=(const State_Request_MessageBuilder &);
|
||||
flatbuffers::Offset<State_Request_Message> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<StateDifference>(end);
|
||||
auto o = flatbuffers::Offset<State_Request_Message>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<StateDifference> CreateStateDifference(
|
||||
inline flatbuffers::Offset<State_Request_Message> CreateState_Request_Message(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> created = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> updated = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> deleted = 0) {
|
||||
StateDifferenceBuilder builder_(_fbb);
|
||||
builder_.add_deleted(deleted);
|
||||
builder_.add_updated(updated);
|
||||
builder_.add_created(created);
|
||||
flatbuffers::Offset<flatbuffers::String> parent_path = 0,
|
||||
bool is_file = false,
|
||||
int32_t block_id = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> expected_hash = 0) {
|
||||
State_Request_MessageBuilder builder_(_fbb);
|
||||
builder_.add_expected_hash(expected_hash);
|
||||
builder_.add_block_id(block_id);
|
||||
builder_.add_parent_path(parent_path);
|
||||
builder_.add_is_file(is_file);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<StateDifference> CreateStateDifferenceDirect(
|
||||
inline flatbuffers::Offset<State_Request_Message> CreateState_Request_MessageDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *created = nullptr,
|
||||
const std::vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *updated = nullptr,
|
||||
const std::vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *deleted = nullptr) {
|
||||
auto created__ = created ? _fbb.CreateVector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>(*created) : 0;
|
||||
auto updated__ = updated ? _fbb.CreateVector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>(*updated) : 0;
|
||||
auto deleted__ = deleted ? _fbb.CreateVector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>(*deleted) : 0;
|
||||
return fbschema::p2pmsg::CreateStateDifference(
|
||||
const char *parent_path = nullptr,
|
||||
bool is_file = false,
|
||||
int32_t block_id = 0,
|
||||
const std::vector<uint8_t> *expected_hash = nullptr) {
|
||||
auto parent_path__ = parent_path ? _fbb.CreateString(parent_path) : 0;
|
||||
auto expected_hash__ = expected_hash ? _fbb.CreateVector<uint8_t>(*expected_hash) : 0;
|
||||
return fbschema::p2pmsg::CreateState_Request_Message(
|
||||
_fbb,
|
||||
created__,
|
||||
updated__,
|
||||
deleted__);
|
||||
parent_path__,
|
||||
is_file,
|
||||
block_id,
|
||||
expected_hash__);
|
||||
}
|
||||
|
||||
struct State FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
struct State_Response_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PREVIOUS = 4,
|
||||
VT_CURRENT = 6,
|
||||
VT_DIFFERENCE = 8,
|
||||
VT_PATCH = 10
|
||||
VT_STATE_RESPONSE_TYPE = 4,
|
||||
VT_STATE_RESPONSE = 6,
|
||||
VT_HASH = 8
|
||||
};
|
||||
const flatbuffers::Vector<uint8_t> *previous() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_PREVIOUS);
|
||||
State_Response state_response_type() const {
|
||||
return static_cast<State_Response>(GetField<uint8_t>(VT_STATE_RESPONSE_TYPE, 0));
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_previous() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_PREVIOUS);
|
||||
bool mutate_state_response_type(State_Response _state_response_type) {
|
||||
return SetField<uint8_t>(VT_STATE_RESPONSE_TYPE, static_cast<uint8_t>(_state_response_type), 0);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *current() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_CURRENT);
|
||||
const void *state_response() const {
|
||||
return GetPointer<const void *>(VT_STATE_RESPONSE);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_current() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_CURRENT);
|
||||
template<typename T> const T *state_response_as() const;
|
||||
const File_HashMap_Response *state_response_as_File_HashMap_Response() const {
|
||||
return state_response_type() == State_Response_File_HashMap_Response ? static_cast<const File_HashMap_Response *>(state_response()) : nullptr;
|
||||
}
|
||||
const StateDifference *difference() const {
|
||||
return GetPointer<const StateDifference *>(VT_DIFFERENCE);
|
||||
const Block_Response *state_response_as_Block_Response() const {
|
||||
return state_response_type() == State_Response_Block_Response ? static_cast<const Block_Response *>(state_response()) : nullptr;
|
||||
}
|
||||
StateDifference *mutable_difference() {
|
||||
return GetPointer<StateDifference *>(VT_DIFFERENCE);
|
||||
const Fs_Entry_Response *state_response_as_Fs_Entry_Response() const {
|
||||
return state_response_type() == State_Response_Fs_Entry_Response ? static_cast<const Fs_Entry_Response *>(state_response()) : nullptr;
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *patch() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_PATCH);
|
||||
void *mutable_state_response() {
|
||||
return GetPointer<void *>(VT_STATE_RESPONSE);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *mutable_patch() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_PATCH);
|
||||
const flatbuffers::Vector<uint8_t> *hash() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_HASH);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_hash() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_HASH);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_PREVIOUS) &&
|
||||
verifier.VerifyVector(previous()) &&
|
||||
VerifyOffset(verifier, VT_CURRENT) &&
|
||||
verifier.VerifyVector(current()) &&
|
||||
VerifyOffset(verifier, VT_DIFFERENCE) &&
|
||||
verifier.VerifyTable(difference()) &&
|
||||
VerifyOffset(verifier, VT_PATCH) &&
|
||||
verifier.VerifyVector(patch()) &&
|
||||
verifier.VerifyVectorOfTables(patch()) &&
|
||||
VerifyField<uint8_t>(verifier, VT_STATE_RESPONSE_TYPE) &&
|
||||
VerifyOffset(verifier, VT_STATE_RESPONSE) &&
|
||||
VerifyState_Response(verifier, state_response(), state_response_type()) &&
|
||||
VerifyOffset(verifier, VT_HASH) &&
|
||||
verifier.VerifyVector(hash()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct StateBuilder {
|
||||
template<> inline const File_HashMap_Response *State_Response_Message::state_response_as<File_HashMap_Response>() const {
|
||||
return state_response_as_File_HashMap_Response();
|
||||
}
|
||||
|
||||
template<> inline const Block_Response *State_Response_Message::state_response_as<Block_Response>() const {
|
||||
return state_response_as_Block_Response();
|
||||
}
|
||||
|
||||
template<> inline const Fs_Entry_Response *State_Response_Message::state_response_as<Fs_Entry_Response>() const {
|
||||
return state_response_as_Fs_Entry_Response();
|
||||
}
|
||||
|
||||
struct State_Response_MessageBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_previous(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> previous) {
|
||||
fbb_.AddOffset(State::VT_PREVIOUS, previous);
|
||||
void add_state_response_type(State_Response state_response_type) {
|
||||
fbb_.AddElement<uint8_t>(State_Response_Message::VT_STATE_RESPONSE_TYPE, static_cast<uint8_t>(state_response_type), 0);
|
||||
}
|
||||
void add_current(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> current) {
|
||||
fbb_.AddOffset(State::VT_CURRENT, current);
|
||||
void add_state_response(flatbuffers::Offset<void> state_response) {
|
||||
fbb_.AddOffset(State_Response_Message::VT_STATE_RESPONSE, state_response);
|
||||
}
|
||||
void add_difference(flatbuffers::Offset<StateDifference> difference) {
|
||||
fbb_.AddOffset(State::VT_DIFFERENCE, difference);
|
||||
void add_hash(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> hash) {
|
||||
fbb_.AddOffset(State_Response_Message::VT_HASH, hash);
|
||||
}
|
||||
void add_patch(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> patch) {
|
||||
fbb_.AddOffset(State::VT_PATCH, patch);
|
||||
}
|
||||
explicit StateBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
explicit State_Response_MessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
StateBuilder &operator=(const StateBuilder &);
|
||||
flatbuffers::Offset<State> Finish() {
|
||||
State_Response_MessageBuilder &operator=(const State_Response_MessageBuilder &);
|
||||
flatbuffers::Offset<State_Response_Message> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<State>(end);
|
||||
auto o = flatbuffers::Offset<State_Response_Message>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<State> CreateState(
|
||||
inline flatbuffers::Offset<State_Response_Message> CreateState_Response_Message(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> previous = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> current = 0,
|
||||
flatbuffers::Offset<StateDifference> difference = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> patch = 0) {
|
||||
StateBuilder builder_(_fbb);
|
||||
builder_.add_patch(patch);
|
||||
builder_.add_difference(difference);
|
||||
builder_.add_current(current);
|
||||
builder_.add_previous(previous);
|
||||
State_Response state_response_type = State_Response_NONE,
|
||||
flatbuffers::Offset<void> state_response = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> hash = 0) {
|
||||
State_Response_MessageBuilder builder_(_fbb);
|
||||
builder_.add_hash(hash);
|
||||
builder_.add_state_response(state_response);
|
||||
builder_.add_state_response_type(state_response_type);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<State> CreateStateDirect(
|
||||
inline flatbuffers::Offset<State_Response_Message> CreateState_Response_MessageDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<uint8_t> *previous = nullptr,
|
||||
const std::vector<uint8_t> *current = nullptr,
|
||||
flatbuffers::Offset<StateDifference> difference = 0,
|
||||
const std::vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *patch = nullptr) {
|
||||
auto previous__ = previous ? _fbb.CreateVector<uint8_t>(*previous) : 0;
|
||||
auto current__ = current ? _fbb.CreateVector<uint8_t>(*current) : 0;
|
||||
auto patch__ = patch ? _fbb.CreateVector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>(*patch) : 0;
|
||||
return fbschema::p2pmsg::CreateState(
|
||||
State_Response state_response_type = State_Response_NONE,
|
||||
flatbuffers::Offset<void> state_response = 0,
|
||||
const std::vector<uint8_t> *hash = nullptr) {
|
||||
auto hash__ = hash ? _fbb.CreateVector<uint8_t>(*hash) : 0;
|
||||
return fbschema::p2pmsg::CreateState_Response_Message(
|
||||
_fbb,
|
||||
previous__,
|
||||
current__,
|
||||
difference,
|
||||
patch__);
|
||||
state_response_type,
|
||||
state_response,
|
||||
hash__);
|
||||
}
|
||||
|
||||
struct Fs_Entry_Response FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PATH = 4,
|
||||
VT_ENTRIES = 6
|
||||
};
|
||||
const flatbuffers::String *path() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
flatbuffers::String *mutable_path() {
|
||||
return GetPointer<flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>> *entries() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>> *>(VT_ENTRIES);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>> *mutable_entries() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>> *>(VT_ENTRIES);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_PATH) &&
|
||||
verifier.VerifyString(path()) &&
|
||||
VerifyOffset(verifier, VT_ENTRIES) &&
|
||||
verifier.VerifyVector(entries()) &&
|
||||
verifier.VerifyVectorOfTables(entries()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct Fs_Entry_ResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_path(flatbuffers::Offset<flatbuffers::String> path) {
|
||||
fbb_.AddOffset(Fs_Entry_Response::VT_PATH, path);
|
||||
}
|
||||
void add_entries(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>>> entries) {
|
||||
fbb_.AddOffset(Fs_Entry_Response::VT_ENTRIES, entries);
|
||||
}
|
||||
explicit Fs_Entry_ResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
Fs_Entry_ResponseBuilder &operator=(const Fs_Entry_ResponseBuilder &);
|
||||
flatbuffers::Offset<Fs_Entry_Response> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<Fs_Entry_Response>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Fs_Entry_Response> CreateFs_Entry_Response(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::String> path = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>>> entries = 0) {
|
||||
Fs_Entry_ResponseBuilder builder_(_fbb);
|
||||
builder_.add_entries(entries);
|
||||
builder_.add_path(path);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<Fs_Entry_Response> CreateFs_Entry_ResponseDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const char *path = nullptr,
|
||||
const std::vector<flatbuffers::Offset<State_FS_Hash_Entry>> *entries = nullptr) {
|
||||
auto path__ = path ? _fbb.CreateString(path) : 0;
|
||||
auto entries__ = entries ? _fbb.CreateVector<flatbuffers::Offset<State_FS_Hash_Entry>>(*entries) : 0;
|
||||
return fbschema::p2pmsg::CreateFs_Entry_Response(
|
||||
_fbb,
|
||||
path__,
|
||||
entries__);
|
||||
}
|
||||
|
||||
struct File_HashMap_Response FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PATH = 4,
|
||||
VT_FILE_LENGTH = 6,
|
||||
VT_HASH_MAP = 8
|
||||
};
|
||||
const flatbuffers::String *path() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
flatbuffers::String *mutable_path() {
|
||||
return GetPointer<flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
uint64_t file_length() const {
|
||||
return GetField<uint64_t>(VT_FILE_LENGTH, 0);
|
||||
}
|
||||
bool mutate_file_length(uint64_t _file_length) {
|
||||
return SetField<uint64_t>(VT_FILE_LENGTH, _file_length, 0);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *hash_map() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_HASH_MAP);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_hash_map() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_HASH_MAP);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_PATH) &&
|
||||
verifier.VerifyString(path()) &&
|
||||
VerifyField<uint64_t>(verifier, VT_FILE_LENGTH) &&
|
||||
VerifyOffset(verifier, VT_HASH_MAP) &&
|
||||
verifier.VerifyVector(hash_map()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct File_HashMap_ResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_path(flatbuffers::Offset<flatbuffers::String> path) {
|
||||
fbb_.AddOffset(File_HashMap_Response::VT_PATH, path);
|
||||
}
|
||||
void add_file_length(uint64_t file_length) {
|
||||
fbb_.AddElement<uint64_t>(File_HashMap_Response::VT_FILE_LENGTH, file_length, 0);
|
||||
}
|
||||
void add_hash_map(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> hash_map) {
|
||||
fbb_.AddOffset(File_HashMap_Response::VT_HASH_MAP, hash_map);
|
||||
}
|
||||
explicit File_HashMap_ResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
File_HashMap_ResponseBuilder &operator=(const File_HashMap_ResponseBuilder &);
|
||||
flatbuffers::Offset<File_HashMap_Response> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<File_HashMap_Response>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<File_HashMap_Response> CreateFile_HashMap_Response(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::String> path = 0,
|
||||
uint64_t file_length = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> hash_map = 0) {
|
||||
File_HashMap_ResponseBuilder builder_(_fbb);
|
||||
builder_.add_file_length(file_length);
|
||||
builder_.add_hash_map(hash_map);
|
||||
builder_.add_path(path);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<File_HashMap_Response> CreateFile_HashMap_ResponseDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const char *path = nullptr,
|
||||
uint64_t file_length = 0,
|
||||
const std::vector<uint8_t> *hash_map = nullptr) {
|
||||
auto path__ = path ? _fbb.CreateString(path) : 0;
|
||||
auto hash_map__ = hash_map ? _fbb.CreateVector<uint8_t>(*hash_map) : 0;
|
||||
return fbschema::p2pmsg::CreateFile_HashMap_Response(
|
||||
_fbb,
|
||||
path__,
|
||||
file_length,
|
||||
hash_map__);
|
||||
}
|
||||
|
||||
struct Block_Response FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PATH = 4,
|
||||
VT_BLOCK_ID = 6,
|
||||
VT_DATA = 8
|
||||
};
|
||||
const flatbuffers::String *path() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
flatbuffers::String *mutable_path() {
|
||||
return GetPointer<flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
uint32_t block_id() const {
|
||||
return GetField<uint32_t>(VT_BLOCK_ID, 0);
|
||||
}
|
||||
bool mutate_block_id(uint32_t _block_id) {
|
||||
return SetField<uint32_t>(VT_BLOCK_ID, _block_id, 0);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *data() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_DATA);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_data() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_DATA);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_PATH) &&
|
||||
verifier.VerifyString(path()) &&
|
||||
VerifyField<uint32_t>(verifier, VT_BLOCK_ID) &&
|
||||
VerifyOffset(verifier, VT_DATA) &&
|
||||
verifier.VerifyVector(data()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct Block_ResponseBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_path(flatbuffers::Offset<flatbuffers::String> path) {
|
||||
fbb_.AddOffset(Block_Response::VT_PATH, path);
|
||||
}
|
||||
void add_block_id(uint32_t block_id) {
|
||||
fbb_.AddElement<uint32_t>(Block_Response::VT_BLOCK_ID, block_id, 0);
|
||||
}
|
||||
void add_data(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data) {
|
||||
fbb_.AddOffset(Block_Response::VT_DATA, data);
|
||||
}
|
||||
explicit Block_ResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
Block_ResponseBuilder &operator=(const Block_ResponseBuilder &);
|
||||
flatbuffers::Offset<Block_Response> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<Block_Response>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Block_Response> CreateBlock_Response(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::String> path = 0,
|
||||
uint32_t block_id = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data = 0) {
|
||||
Block_ResponseBuilder builder_(_fbb);
|
||||
builder_.add_data(data);
|
||||
builder_.add_block_id(block_id);
|
||||
builder_.add_path(path);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<Block_Response> CreateBlock_ResponseDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const char *path = nullptr,
|
||||
uint32_t block_id = 0,
|
||||
const std::vector<uint8_t> *data = nullptr) {
|
||||
auto path__ = path ? _fbb.CreateString(path) : 0;
|
||||
auto data__ = data ? _fbb.CreateVector<uint8_t>(*data) : 0;
|
||||
return fbschema::p2pmsg::CreateBlock_Response(
|
||||
_fbb,
|
||||
path__,
|
||||
block_id,
|
||||
data__);
|
||||
}
|
||||
|
||||
struct State_FS_Hash_Entry FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PATH = 4,
|
||||
VT_IS_FILE = 6,
|
||||
VT_HASH = 8
|
||||
};
|
||||
const flatbuffers::String *path() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
flatbuffers::String *mutable_path() {
|
||||
return GetPointer<flatbuffers::String *>(VT_PATH);
|
||||
}
|
||||
bool is_file() const {
|
||||
return GetField<uint8_t>(VT_IS_FILE, 0) != 0;
|
||||
}
|
||||
bool mutate_is_file(bool _is_file) {
|
||||
return SetField<uint8_t>(VT_IS_FILE, static_cast<uint8_t>(_is_file), 0);
|
||||
}
|
||||
const flatbuffers::Vector<uint8_t> *hash() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_HASH);
|
||||
}
|
||||
flatbuffers::Vector<uint8_t> *mutable_hash() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_HASH);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_PATH) &&
|
||||
verifier.VerifyString(path()) &&
|
||||
VerifyField<uint8_t>(verifier, VT_IS_FILE) &&
|
||||
VerifyOffset(verifier, VT_HASH) &&
|
||||
verifier.VerifyVector(hash()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct State_FS_Hash_EntryBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_path(flatbuffers::Offset<flatbuffers::String> path) {
|
||||
fbb_.AddOffset(State_FS_Hash_Entry::VT_PATH, path);
|
||||
}
|
||||
void add_is_file(bool is_file) {
|
||||
fbb_.AddElement<uint8_t>(State_FS_Hash_Entry::VT_IS_FILE, static_cast<uint8_t>(is_file), 0);
|
||||
}
|
||||
void add_hash(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> hash) {
|
||||
fbb_.AddOffset(State_FS_Hash_Entry::VT_HASH, hash);
|
||||
}
|
||||
explicit State_FS_Hash_EntryBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
State_FS_Hash_EntryBuilder &operator=(const State_FS_Hash_EntryBuilder &);
|
||||
flatbuffers::Offset<State_FS_Hash_Entry> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<State_FS_Hash_Entry>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<State_FS_Hash_Entry> CreateState_FS_Hash_Entry(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::String> path = 0,
|
||||
bool is_file = false,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> hash = 0) {
|
||||
State_FS_Hash_EntryBuilder builder_(_fbb);
|
||||
builder_.add_hash(hash);
|
||||
builder_.add_path(path);
|
||||
builder_.add_is_file(is_file);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<State_FS_Hash_Entry> CreateState_FS_Hash_EntryDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const char *path = nullptr,
|
||||
bool is_file = false,
|
||||
const std::vector<uint8_t> *hash = nullptr) {
|
||||
auto path__ = path ? _fbb.CreateString(path) : 0;
|
||||
auto hash__ = hash ? _fbb.CreateVector<uint8_t>(*hash) : 0;
|
||||
return fbschema::p2pmsg::CreateState_FS_Hash_Entry(
|
||||
_fbb,
|
||||
path__,
|
||||
is_file,
|
||||
hash__);
|
||||
}
|
||||
|
||||
inline bool VerifyMessage(flatbuffers::Verifier &verifier, const void *obj, Message type) {
|
||||
@@ -1091,6 +1538,14 @@ inline bool VerifyMessage(flatbuffers::Verifier &verifier, const void *obj, Mess
|
||||
auto ptr = reinterpret_cast<const Npl_Message *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
case Message_State_Request_Message: {
|
||||
auto ptr = reinterpret_cast<const State_Request_Message *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
case Message_State_Response_Message: {
|
||||
auto ptr = reinterpret_cast<const State_Response_Message *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
case Message_History_Request_Message: {
|
||||
auto ptr = reinterpret_cast<const History_Request_Message *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
@@ -1115,6 +1570,39 @@ inline bool VerifyMessageVector(flatbuffers::Verifier &verifier, const flatbuffe
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool VerifyState_Response(flatbuffers::Verifier &verifier, const void *obj, State_Response type) {
|
||||
switch (type) {
|
||||
case State_Response_NONE: {
|
||||
return true;
|
||||
}
|
||||
case State_Response_File_HashMap_Response: {
|
||||
auto ptr = reinterpret_cast<const File_HashMap_Response *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
case State_Response_Block_Response: {
|
||||
auto ptr = reinterpret_cast<const Block_Response *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
case State_Response_Fs_Entry_Response: {
|
||||
auto ptr = reinterpret_cast<const Fs_Entry_Response *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool VerifyState_ResponseVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types) {
|
||||
if (!values || !types) return !values && !types;
|
||||
if (values->size() != types->size()) return false;
|
||||
for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
|
||||
if (!VerifyState_Response(
|
||||
verifier, values->Get(i), types->GetEnum<State_Response>(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const fbschema::p2pmsg::Content *GetContent(const void *buf) {
|
||||
return flatbuffers::GetRoot<fbschema::p2pmsg::Content>(buf);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ const p2p::history_response create_history_response_from_msg(const History_Respo
|
||||
if (msg.hist_ledgers())
|
||||
hr.hist_ledgers = flatbuf_historyledgermap_to_historyledgermap(msg.hist_ledgers());
|
||||
|
||||
if (msg.error())
|
||||
if (msg.error())
|
||||
hr.error = (p2p::LEDGER_RESPONSE_ERROR)msg.error();
|
||||
|
||||
return hr;
|
||||
@@ -174,7 +174,7 @@ const p2p::history_response create_history_response_from_msg(const History_Respo
|
||||
|
||||
/**
|
||||
* Creates a proposal stuct from the given proposal message.
|
||||
* @param The Flatbuffer poporal received from the peer.
|
||||
* @param The Flatbuffer poposal received from the peer.
|
||||
* @return A proposal struct representing the message.
|
||||
*/
|
||||
const p2p::proposal create_proposal_from_msg(const Proposal_Message &msg, const flatbuffers::Vector<uint8_t> *pubkey, const uint64_t timestamp, const flatbuffers::Vector<uint8_t> *lcl)
|
||||
@@ -186,6 +186,7 @@ const p2p::proposal create_proposal_from_msg(const Proposal_Message &msg, const
|
||||
p.time = msg.time();
|
||||
p.stage = msg.stage();
|
||||
p.lcl = flatbuff_bytes_to_sv(lcl);
|
||||
p.curr_hash_state = flatbuff_bytes_to_sv(msg.curr_state_hash());
|
||||
|
||||
if (msg.users())
|
||||
p.users = flatbuf_bytearrayvector_to_stringlist(msg.users());
|
||||
@@ -217,6 +218,38 @@ const p2p::history_request create_history_request_from_msg(const History_Request
|
||||
return hr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a state request struct from the given state request message.
|
||||
* @param msg Flatbuffer State request message received from the peer.
|
||||
* @return A State request struct representing the message.
|
||||
*/
|
||||
const p2p::state_request create_state_request_from_msg(const State_Request_Message &msg)
|
||||
{
|
||||
p2p::state_request sr;
|
||||
|
||||
sr.block_id = msg.block_id();
|
||||
sr.is_file = msg.is_file();
|
||||
sr.parent_path = flatbuff_str_to_sv(msg.parent_path());
|
||||
sr.expected_hash = flatbuff_bytes_to_hash(msg.expected_hash());
|
||||
|
||||
return sr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a block response struct from the given block response message.
|
||||
* @param msg Flatbuffer block response message received from the peer.
|
||||
* @return A Block response struct representing the message.
|
||||
*/
|
||||
const p2p::block_response create_block_response_from_msg(const Block_Response &msg)
|
||||
{
|
||||
p2p::block_response br;
|
||||
|
||||
br.path = flatbuff_str_to_sv(msg.path());
|
||||
br.block_id = msg.block_id();
|
||||
br.data = flatbuff_bytes_to_sv(msg.data());
|
||||
return br;
|
||||
}
|
||||
|
||||
//---Message creation helpers---//
|
||||
|
||||
void create_msg_from_nonunl_proposal(flatbuffers::FlatBufferBuilder &container_builder, const p2p::nonunl_proposal &nup)
|
||||
@@ -253,7 +286,8 @@ void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder,
|
||||
p.time,
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.users),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_inputs),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_outputs));
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_outputs),
|
||||
sv_to_flatbuff_bytes(builder, p.curr_hash_state));
|
||||
|
||||
const flatbuffers::Offset<Content> message = CreateContent(builder, Message_Proposal_Message, proposal.Union());
|
||||
builder.Finish(message); // Finished building message content to get serialised content.
|
||||
@@ -332,6 +366,144 @@ void create_msg_from_history_response(flatbuffers::FlatBufferBuilder &container_
|
||||
create_containermsg_from_content(container_builder, builder, nullptr, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create state request message from the given state request struct.
|
||||
* @param container_builder Flatbuffer builder for the container message.
|
||||
* @param sr The state request struct to be placed in the container message.
|
||||
*/
|
||||
void create_msg_from_state_request(flatbuffers::FlatBufferBuilder &container_builder, const p2p::state_request &hr, std::string_view lcl)
|
||||
{
|
||||
flatbuffers::FlatBufferBuilder builder(1024);
|
||||
|
||||
flatbuffers::Offset<State_Request_Message> srmsg =
|
||||
CreateState_Request_Message(
|
||||
builder,
|
||||
sv_to_flatbuff_str(builder, hr.parent_path),
|
||||
hr.is_file,
|
||||
hr.block_id,
|
||||
hash_to_flatbuff_bytes(builder, hr.expected_hash));
|
||||
|
||||
flatbuffers::Offset<Content> message = CreateContent(builder, Message_State_Request_Message, srmsg.Union());
|
||||
builder.Finish(message); // Finished building message content to get serialised content.
|
||||
|
||||
// Now that we have built the content message,
|
||||
// we need to sign it and place it inside a container message.
|
||||
create_containermsg_from_content(container_builder, builder, lcl, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content response message from the given content response.
|
||||
* @param container_builder Flatbuffer builder for the container message.
|
||||
* @param path The path of the directory.
|
||||
* @param fs_entries File or directory entries in the given parent path.
|
||||
* @param expected_hash The exptected hash of the requested path.
|
||||
* @param lcl Lcl to be include in the container msg.
|
||||
*/
|
||||
void create_msg_from_fsentry_response(flatbuffers::FlatBufferBuilder &container_builder, const std::string_view path, std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries, hasher::B2H expected_hash, std::string_view lcl)
|
||||
{
|
||||
flatbuffers::FlatBufferBuilder builder(1024);
|
||||
|
||||
const flatbuffers::Offset<Fs_Entry_Response> resp =
|
||||
CreateFs_Entry_Response(
|
||||
builder,
|
||||
sv_to_flatbuff_str(builder, path),
|
||||
statefshashentry_to_flatbuff_statefshashentry(builder, fs_entries));
|
||||
|
||||
const flatbuffers::Offset<State_Response_Message> st_resp = CreateState_Response_Message(
|
||||
builder, State_Response_Fs_Entry_Response,
|
||||
resp.Union(),
|
||||
hash_to_flatbuff_bytes(builder, expected_hash));
|
||||
|
||||
flatbuffers::Offset<Content> message = CreateContent(builder, Message_State_Response_Message, st_resp.Union());
|
||||
builder.Finish(message); // Finished building message content to get serialised content.
|
||||
|
||||
// Now that we have built the content message,
|
||||
// we need to sign it and place it inside a container message.
|
||||
create_containermsg_from_content(container_builder, builder, lcl, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content response message from the given content response.
|
||||
* @param container_builder Flatbuffer builder for the container message.
|
||||
* @param path The path of the directory.
|
||||
* @param hashmap Hashmap of the file
|
||||
* @param lcl Lcl to be include in the container msg.
|
||||
*/
|
||||
void create_msg_from_filehashmap_response(flatbuffers::FlatBufferBuilder &container_builder, std::string_view path, std::vector<uint8_t> &hashmap, std::size_t file_length, hasher::B2H expected_hash, std::string_view lcl)
|
||||
{
|
||||
// todo:get a average propsal message size and allocate content builder based on that.
|
||||
flatbuffers::FlatBufferBuilder builder(1024);
|
||||
|
||||
std::string_view hashmap_sv(reinterpret_cast<const char *>(hashmap.data()), hashmap.size());
|
||||
|
||||
const flatbuffers::Offset<File_HashMap_Response> resp =
|
||||
CreateFile_HashMap_Response(
|
||||
builder,
|
||||
sv_to_flatbuff_str(builder, path),
|
||||
file_length,
|
||||
sv_to_flatbuff_bytes(builder, hashmap_sv));
|
||||
|
||||
const flatbuffers::Offset<State_Response_Message> st_resp = CreateState_Response_Message(
|
||||
builder,
|
||||
State_Response_File_HashMap_Response,
|
||||
resp.Union(),
|
||||
hash_to_flatbuff_bytes(builder, expected_hash));
|
||||
|
||||
flatbuffers::Offset<Content> message = CreateContent(builder, Message_State_Response_Message, st_resp.Union());
|
||||
builder.Finish(message); // Finished building message content to get serialised content.
|
||||
|
||||
// Now that we have built the content message,
|
||||
// we need to sign it and place it inside a container message.
|
||||
create_containermsg_from_content(container_builder, builder, lcl, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content response message from the given content response.
|
||||
* @param container_builder Flatbuffer builder for the container message.
|
||||
* @param block_resp Block response struct to place in the message
|
||||
* @param lcl Lcl to be include in the container message.
|
||||
*/
|
||||
void create_msg_from_block_response(flatbuffers::FlatBufferBuilder &container_builder, p2p::block_response &block_resp, std::string_view lcl)
|
||||
{
|
||||
// todo:get a average propsal message size and allocate content builder based on that.
|
||||
flatbuffers::FlatBufferBuilder builder(1024);
|
||||
|
||||
const flatbuffers::Offset<Block_Response> resp =
|
||||
CreateBlock_Response(
|
||||
builder,
|
||||
sv_to_flatbuff_str(builder, block_resp.path),
|
||||
block_resp.block_id,
|
||||
sv_to_flatbuff_bytes(builder, block_resp.data));
|
||||
|
||||
const flatbuffers::Offset<State_Response_Message> st_resp = CreateState_Response_Message(
|
||||
builder,
|
||||
State_Response_Block_Response,
|
||||
resp.Union(),
|
||||
hash_to_flatbuff_bytes(builder, block_resp.hash));
|
||||
|
||||
flatbuffers::Offset<Content> message = CreateContent(builder, Message_State_Response_Message, st_resp.Union());
|
||||
builder.Finish(message); // Finished building message content to get serialised content.
|
||||
|
||||
// Now that we have built the content message,
|
||||
// we need to sign it and place it inside a container message.
|
||||
create_containermsg_from_content(container_builder, builder, lcl, true);
|
||||
}
|
||||
|
||||
void create_msg_from_state_error_response(flatbuffers::FlatBufferBuilder &container_builder, std::string_view lcl)
|
||||
{
|
||||
// todo:get a average propsal message size and allocate content builder based on that.
|
||||
flatbuffers::FlatBufferBuilder builder(1024);
|
||||
|
||||
const flatbuffers::Offset<State_Response_Message> st_resp = CreateState_Response_Message(builder, State_Response_NONE, 0, true);
|
||||
|
||||
flatbuffers::Offset<Content> message = CreateContent(builder, Message_State_Response_Message, st_resp.Union());
|
||||
builder.Finish(message); // Finished building message content to get serialised content.
|
||||
|
||||
// Now that we have built the content message,
|
||||
// we need to sign it and place it inside a container message.
|
||||
create_containermsg_from_content(container_builder, builder, lcl, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Flatbuffer container message from the given Content message.
|
||||
* @param container_builder The Flatbuffer builder to which the final container message should be written to.
|
||||
@@ -457,6 +629,7 @@ historyledgermap_to_flatbuf_historyledgermap(flatbuffers::FlatBufferBuilder &bui
|
||||
{
|
||||
flatbuffers::Offset<HistoryLedger> history_ledger = CreateHistoryLedger(
|
||||
builder,
|
||||
sv_to_flatbuff_bytes(builder, ledger.state),
|
||||
sv_to_flatbuff_bytes(builder, ledger.lcl),
|
||||
builder.CreateVector(ledger.raw_ledger));
|
||||
|
||||
@@ -468,4 +641,35 @@ historyledgermap_to_flatbuf_historyledgermap(flatbuffers::FlatBufferBuilder &bui
|
||||
return builder.CreateVector(fbvec);
|
||||
}
|
||||
|
||||
void flatbuf_statefshashentry_to_statefshashentry(std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries, const flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>> *fhashes)
|
||||
{
|
||||
|
||||
for (const State_FS_Hash_Entry *f_hash : *fhashes)
|
||||
{
|
||||
p2p::state_fs_hash_entry h;
|
||||
|
||||
h.is_file = f_hash->is_file();
|
||||
h.hash = flatbuff_bytes_to_hash(f_hash->hash());
|
||||
fs_entries.emplace(flatbuff_str_to_sv(f_hash->path()), std::move(h));
|
||||
}
|
||||
}
|
||||
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>>>
|
||||
statefshashentry_to_flatbuff_statefshashentry(flatbuffers::FlatBufferBuilder &builder, std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries)
|
||||
{
|
||||
std::vector<flatbuffers::Offset<State_FS_Hash_Entry>> fbvec;
|
||||
fbvec.reserve(fs_entries.size());
|
||||
for (auto const &[path, fs_entry] : fs_entries)
|
||||
{
|
||||
flatbuffers::Offset<State_FS_Hash_Entry> state_fs_entry = CreateState_FS_Hash_Entry(
|
||||
builder,
|
||||
sv_to_flatbuff_str(builder, path),
|
||||
fs_entry.is_file,
|
||||
hash_to_flatbuff_bytes(builder, fs_entry.hash));
|
||||
|
||||
fbvec.push_back(state_fs_entry);
|
||||
}
|
||||
return builder.CreateVector(fbvec);
|
||||
}
|
||||
|
||||
} // namespace fbschema::p2pmsg
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "p2pmsg_container_generated.h"
|
||||
#include "p2pmsg_content_generated.h"
|
||||
#include "../p2p/p2p.hpp"
|
||||
#include "../statefs/hasher.hpp"
|
||||
|
||||
namespace fbschema::p2pmsg
|
||||
{
|
||||
@@ -29,6 +30,10 @@ const p2p::history_request create_history_request_from_msg(const History_Request
|
||||
|
||||
const p2p::history_response create_history_response_from_msg(const History_Response_Message &msg);
|
||||
|
||||
const p2p::state_request create_state_request_from_msg(const State_Request_Message &msg);
|
||||
|
||||
const p2p::block_response create_block_response_from_msg(const Block_Response &msg);
|
||||
|
||||
//---Message creation helpers---//
|
||||
|
||||
void create_msg_from_nonunl_proposal(flatbuffers::FlatBufferBuilder &container_builder, const p2p::nonunl_proposal &nup);
|
||||
@@ -41,6 +46,15 @@ void create_msg_from_history_response(flatbuffers::FlatBufferBuilder &container_
|
||||
|
||||
void create_msg_from_npl_output(flatbuffers::FlatBufferBuilder &container_builder, const p2p::npl_message &npl, std::string_view lcl);
|
||||
|
||||
void create_msg_from_state_request(flatbuffers::FlatBufferBuilder &container_builder, const p2p::state_request &hr, std::string_view lcl);
|
||||
|
||||
void create_msg_from_fsentry_response(flatbuffers::FlatBufferBuilder &container_builder, const std::string_view path,
|
||||
std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries, hasher::B2H expected_hash, std::string_view lcl);
|
||||
|
||||
void create_msg_from_filehashmap_response(flatbuffers::FlatBufferBuilder &container_builder, std::string_view path, std::vector<uint8_t> &hashmap, std::size_t file_length, hasher::B2H expected_hash, std::string_view lcl);
|
||||
|
||||
void create_msg_from_block_response(flatbuffers::FlatBufferBuilder &container_builder, p2p::block_response &block_resp, std::string_view lcl);
|
||||
|
||||
void create_containermsg_from_content(
|
||||
flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder, std::string_view lcl, const bool sign);
|
||||
|
||||
@@ -60,6 +74,17 @@ flatbuf_historyledgermap_to_historyledgermap(const flatbuffers::Vector<flatbuffe
|
||||
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<HistoryLedgerPair>>>
|
||||
historyledgermap_to_flatbuf_historyledgermap(flatbuffers::FlatBufferBuilder &builder, const std::map<uint64_t, const p2p::history_ledger> &map);
|
||||
|
||||
void
|
||||
flatbuf_statefshashentry_to_statefshashentry(std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries,
|
||||
const flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>> *fhashes);
|
||||
|
||||
void statefilehash_to_flatbuf_statefilehash(flatbuffers::FlatBufferBuilder &builder, std::vector<flatbuffers::Offset<State_FS_Hash_Entry>> &list,
|
||||
std::string_view full_path, bool is_file, std::string_view hash);
|
||||
|
||||
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<State_FS_Hash_Entry>>>
|
||||
statefshashentry_to_flatbuff_statefshashentry(flatbuffers::FlatBufferBuilder &builder, std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries);
|
||||
|
||||
} // namespace fbschema::p2pmsg
|
||||
|
||||
#endif
|
||||
12
src/main.cpp
12
src/main.cpp
@@ -79,7 +79,7 @@ void signal_handler(int signum)
|
||||
*/
|
||||
void boost::throw_exception(std::exception const &e)
|
||||
{
|
||||
LOG_ERR << "Boost error:" << e.what();
|
||||
std::cerr << "Boost error:" << e.what() << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -97,16 +97,16 @@ void std_terminate() noexcept
|
||||
}
|
||||
catch (std::exception &ex)
|
||||
{
|
||||
LOG_ERR << "std error: " << ex.what();
|
||||
std::cerr << "std error: " << ex.what() << "\n";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERR << "std error: Terminated due to unknown exception";
|
||||
std::cerr << "std error: Terminated due to unknown exception" << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERR << "std error: Terminated due to unknown reason";
|
||||
std::cerr << "std error: Terminated due to unknown reason" << "\n";
|
||||
}
|
||||
|
||||
exit(1);
|
||||
@@ -170,11 +170,11 @@ int main(int argc, char **argv)
|
||||
LOG_INFO << "Operating mode: "
|
||||
<< (conf::cfg.mode == conf::OPERATING_MODE::OBSERVING ? "Observing" : "Proposing");
|
||||
|
||||
statefs::init(conf::ctx.statehistdir);
|
||||
|
||||
if (p2p::init() != 0 || usr::init() != 0 || cons::init() != 0)
|
||||
return -1;
|
||||
|
||||
statefs::init(conf::ctx.statehistdir);
|
||||
|
||||
// After initializing primary subsystems, register the SIGINT handler.
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
|
||||
@@ -103,6 +103,9 @@ void broadcast_message(const peer_outbound_message msg, bool send_to_self)
|
||||
*/
|
||||
void send_message_to_random_peer(peer_outbound_message msg)
|
||||
{
|
||||
//Send while locking the peer_connections.
|
||||
std::lock_guard<std::mutex> lock(p2p::ctx.peer_connections_mutex);
|
||||
|
||||
size_t connected_peers = ctx.peer_connections.size();
|
||||
if (connected_peers == 0)
|
||||
{
|
||||
@@ -115,19 +118,20 @@ void send_message_to_random_peer(peer_outbound_message msg)
|
||||
return;
|
||||
}
|
||||
|
||||
//Send while locking the peer_connections.
|
||||
std::lock_guard<std::mutex> lock(p2p::ctx.peer_connections_mutex);
|
||||
|
||||
// Initialize random number generator with current timestamp.
|
||||
int random_peer_index = (rand() % connected_peers); // select a random peer index.
|
||||
auto it = ctx.peer_connections.begin();
|
||||
std::advance(it, random_peer_index); //move iterator to point to random selected peer.
|
||||
|
||||
//send message to selecte peer.
|
||||
auto session = it->second;
|
||||
if (!session->is_self)
|
||||
while (true)
|
||||
{
|
||||
session->send(msg);
|
||||
// Initialize random number generator with current timestamp.
|
||||
int random_peer_index = (rand() % connected_peers); // select a random peer index.
|
||||
auto it = ctx.peer_connections.begin();
|
||||
std::advance(it, random_peer_index); //move iterator to point to random selected peer.
|
||||
|
||||
//send message to selecte peer.
|
||||
auto session = it->second;
|
||||
if (!session->is_self)
|
||||
{
|
||||
session->send(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../sock/socket_session.hpp"
|
||||
#include "../usr/user_input.hpp"
|
||||
#include "peer_session_handler.hpp"
|
||||
#include "../statefs/hasher.hpp"
|
||||
|
||||
namespace p2p
|
||||
{
|
||||
@@ -16,6 +17,7 @@ struct proposal
|
||||
uint64_t time;
|
||||
uint8_t stage;
|
||||
std::string lcl;
|
||||
std::string curr_hash_state;
|
||||
std::set<std::string> users;
|
||||
std::set<std::string> hash_inputs;
|
||||
std::set<std::string> hash_outputs;
|
||||
@@ -34,6 +36,7 @@ struct history_request
|
||||
|
||||
struct history_ledger
|
||||
{
|
||||
std::string state;
|
||||
std::string lcl;
|
||||
std::vector<uint8_t> raw_ledger;
|
||||
};
|
||||
@@ -45,30 +48,53 @@ enum LEDGER_RESPONSE_ERROR
|
||||
REQ_LEDGER_NOT_FOUND = 2
|
||||
};
|
||||
|
||||
|
||||
struct history_response
|
||||
{
|
||||
std::map<uint64_t,const history_ledger> hist_ledgers;
|
||||
std::map<uint64_t, const history_ledger> hist_ledgers;
|
||||
LEDGER_RESPONSE_ERROR error;
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct npl_message
|
||||
{
|
||||
std::string data;
|
||||
};
|
||||
|
||||
struct state_request
|
||||
{
|
||||
std::string parent_path;
|
||||
bool is_file;
|
||||
int32_t block_id;
|
||||
hasher::B2H expected_hash;
|
||||
};
|
||||
|
||||
struct state_fs_hash_entry
|
||||
{
|
||||
bool is_file;
|
||||
hasher::B2H hash;
|
||||
};
|
||||
|
||||
struct block_response
|
||||
{
|
||||
std::string path;
|
||||
uint32_t block_id;
|
||||
std::string_view data;
|
||||
hasher::B2H hash;
|
||||
};
|
||||
|
||||
struct message_collection
|
||||
{
|
||||
std::list<proposal> proposals;
|
||||
std::mutex proposals_mutex; // Mutex for proposals access race conditions.
|
||||
|
||||
std::list<proposal> proposals;
|
||||
std::mutex proposals_mutex; // Mutex for proposals access race conditions.
|
||||
|
||||
std::list<nonunl_proposal> nonunl_proposals;
|
||||
std::mutex nonunl_proposals_mutex; // Mutex for non-unl proposals access race conditions.
|
||||
std::mutex nonunl_proposals_mutex; // Mutex for non-unl proposals access race conditions.
|
||||
|
||||
// NPL messages are stored as string list because we are feeding the npl messages as it is (byte array) to the contract.
|
||||
std::list<std::string> npl_messages;
|
||||
std::mutex npl_messages_mutex; // Mutex for npl_messages access race conditions.
|
||||
std::list<std::string> npl_messages;
|
||||
std::mutex npl_messages_mutex; // Mutex for npl_messages access race conditions.
|
||||
|
||||
std::list<std::string> state_response;
|
||||
std::mutex state_response_mutex; // Mutex for state response access race conditions.
|
||||
};
|
||||
|
||||
struct connected_context
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "p2p.hpp"
|
||||
#include "peer_session_handler.hpp"
|
||||
#include "../cons/ledger_handler.hpp"
|
||||
#include "../cons/state_handler.hpp"
|
||||
#include "../cons/cons.hpp"
|
||||
|
||||
namespace p2pmsg = fbschema::p2pmsg;
|
||||
|
||||
@@ -110,10 +112,29 @@ void peer_session_handler::on_message(sock::socket_session<peer_outbound_message
|
||||
const std::string npl_message(reinterpret_cast<const char *>(container_buf_ptr), container_buf_size);
|
||||
ctx.collected_msgs.npl_messages.push_back(std::move(npl_message));
|
||||
}
|
||||
else if (content_message_type == p2pmsg::Message_State_Request_Message)
|
||||
{
|
||||
if (p2pmsg::validate_container_trust(container) != 0)
|
||||
{
|
||||
LOG_DBG << "State request message rejected due to trust failure.";
|
||||
return;
|
||||
}
|
||||
|
||||
const p2p::state_request sr = p2pmsg::create_state_request_from_msg(*content->message_as_State_Request_Message());
|
||||
p2p::peer_outbound_message msg(std::make_unique<flatbuffers::FlatBufferBuilder>(1024));
|
||||
|
||||
if (cons::create_state_response(msg, sr) == 0)
|
||||
session->send(std::move(msg));
|
||||
}
|
||||
else if (content_message_type == p2pmsg::Message_State_Response_Message)
|
||||
{
|
||||
LOG_INFO << "Received State Response Message\n";
|
||||
std::lock_guard<std::mutex> lock(ctx.collected_msgs.state_response_mutex); // Insert state_response with lock.
|
||||
std::string response(reinterpret_cast<const char *>(content_ptr), content_size);
|
||||
ctx.collected_msgs.state_response.push_back(std::move(response));
|
||||
}
|
||||
else if (content_message_type == p2pmsg::Message_History_Request_Message) //message is a lcl history request message
|
||||
{
|
||||
LOG_DBG << "Received history request message type from peer.";
|
||||
|
||||
const p2p::history_request hr = p2pmsg::create_history_request_from_msg(*content->message_as_History_Request_Message());
|
||||
//first check node has the required lcl available. -> if so send lcl history accordingly.
|
||||
bool req_lcl_avail = cons::check_required_lcl_availability(hr);
|
||||
@@ -125,8 +146,6 @@ void peer_session_handler::on_message(sock::socket_session<peer_outbound_message
|
||||
}
|
||||
else if (content_message_type == p2pmsg::Message_History_Response_Message) //message is a lcl history response message
|
||||
{
|
||||
LOG_DBG << "Received history response message type from peer.";
|
||||
|
||||
cons::handle_ledger_history_response(
|
||||
p2pmsg::create_history_response_from_msg(*content->message_as_History_Response_Message()));
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <math.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/istreamwrapper.h>
|
||||
#include <rapidjson/ostreamwrapper.h>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "../statefs/state_common.hpp"
|
||||
#include "../statefs/hashtree_builder.hpp"
|
||||
#include "proc.hpp"
|
||||
#include "../cons/cons.hpp"
|
||||
|
||||
namespace proc
|
||||
{
|
||||
@@ -40,6 +41,8 @@ pid_t contract_pid;
|
||||
// Holds the state monitor process id (if currently executing).
|
||||
pid_t statemon_pid;
|
||||
|
||||
const char *FINDMNT_COMMAND = "findmnt --noheadings ";
|
||||
|
||||
/**
|
||||
* Executes the contract process and passes the specified arguments.
|
||||
* @return 0 on successful process creation. -1 on failure or contract process is already running.
|
||||
@@ -144,7 +147,22 @@ int start_state_monitor()
|
||||
{
|
||||
// HP process.
|
||||
statemon_pid = pid;
|
||||
return 0;
|
||||
|
||||
// Give enough time for the state monitor to start.
|
||||
// We wait until Fuse filesystem is mounted for max number of retries.
|
||||
uint16_t retry_count = 0;
|
||||
std::string findmnt_command = FINDMNT_COMMAND + conf::ctx.statedir;
|
||||
while (retry_count < 50)
|
||||
{
|
||||
util::sleep(10);
|
||||
int ret = system(findmnt_command.c_str());
|
||||
if (WEXITSTATUS(ret) == 0) // Success. Fuse fs has been mounted.
|
||||
return 0;
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
// We waited enough time for Fuse fs to be mounted but no luck.
|
||||
return -1;
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
@@ -186,11 +204,14 @@ int stop_state_monitor()
|
||||
LOG_ERR << "State monitor process exited with non-normal status code: " << presult;
|
||||
|
||||
// Update the hash tree.
|
||||
hasher::B2H statehash = {0, 0, 0, 0};
|
||||
hasher::B2H statehash = hasher::B2H_empty;
|
||||
statefs::hashtree_builder htreebuilder(statefs::get_statedir_context());
|
||||
if (htreebuilder.generate(statehash) != 0)
|
||||
return -1;
|
||||
|
||||
std::string root_hash(reinterpret_cast<const char*>(&statehash), hasher::HASH_SIZE);
|
||||
root_hash.swap(cons::ctx.curr_hash_state);
|
||||
|
||||
LOG_DBG << "State hash: " << std::hex << statehash << std::dec;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
namespace usr
|
||||
{
|
||||
|
||||
user_outbound_message::user_outbound_message(std::string &&_msg)
|
||||
user_outbound_message::user_outbound_message(std::string &&msg)
|
||||
{
|
||||
msg = std::move(_msg);
|
||||
this->msg = std::move(msg);
|
||||
}
|
||||
|
||||
// Returns the buffer that should be written to the socket.
|
||||
@@ -22,9 +22,9 @@ namespace p2p
|
||||
{
|
||||
|
||||
peer_outbound_message::peer_outbound_message(
|
||||
std::shared_ptr<flatbuffers::FlatBufferBuilder> _fbbuilder_ptr)
|
||||
std::shared_ptr<flatbuffers::FlatBufferBuilder> fbbuilder_ptr)
|
||||
{
|
||||
fbbuilder_ptr = _fbbuilder_ptr;
|
||||
this->fbbuilder_ptr = fbbuilder_ptr;
|
||||
}
|
||||
|
||||
// Returns a reference to the flatbuffer builder object.
|
||||
@@ -37,8 +37,8 @@ flatbuffers::FlatBufferBuilder &peer_outbound_message::builder()
|
||||
std::string_view peer_outbound_message::buffer()
|
||||
{
|
||||
return std::string_view(
|
||||
reinterpret_cast<const char *>((*fbbuilder_ptr).GetBufferPointer()),
|
||||
(*fbbuilder_ptr).GetSize());
|
||||
reinterpret_cast<const char *>(fbbuilder_ptr->GetBufferPointer()),
|
||||
fbbuilder_ptr->GetSize());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -72,7 +72,6 @@ void socket_session<T>::increment_metric(const SESSION_THRESHOLDS threshold_type
|
||||
|
||||
LOG_INFO << "Session " << this->uniqueid << " threshold exceeded. (type:" << threshold_type << " limit:" << t.threshold_limit << ")";
|
||||
corebill::report_violation(this->address);
|
||||
|
||||
}
|
||||
else if (elapsed_time > t.intervalms)
|
||||
{
|
||||
@@ -119,7 +118,7 @@ void socket_session<T>::run(const std::string &&address, const std::string &&por
|
||||
this->uniqueid.append(address).append(":").append(port);
|
||||
|
||||
// This indicates the connection is a self connection (node connects to the same node through server port)
|
||||
if(address == "0.0.0.0")
|
||||
if (address == "0.0.0.0")
|
||||
this->is_self = true;
|
||||
|
||||
// Set the timeout.
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
#include "hasher.hpp"
|
||||
|
||||
/**
|
||||
* Contains hashing functions and helpers used to manipulate block hashes used in state management.
|
||||
* This could also be used throughout rest of the application as well. However for now we are only
|
||||
* using this for state management code base only.
|
||||
*
|
||||
* Based on https://github.com/codetsunami/file-ptracer/blob/master/merkle.cpp
|
||||
*/
|
||||
namespace hasher
|
||||
{
|
||||
|
||||
// provide some helper functions for working with 32 byte hash type
|
||||
bool operator==(const B2H &lhs, const B2H &rhs)
|
||||
// Represents empty/default B2H hash value.
|
||||
B2H B2H_empty = hasher::B2H_empty;
|
||||
|
||||
/**
|
||||
* Helper functions for working with 32 byte hash type B2H.
|
||||
*/
|
||||
|
||||
bool B2H::operator==(const B2H rhs) const
|
||||
{
|
||||
return lhs.data[0] == rhs.data[0] && lhs.data[1] == rhs.data[1] && lhs.data[2] == rhs.data[2] && lhs.data[3] == rhs.data[3];
|
||||
return this->data[0] == rhs.data[0] && this->data[1] == rhs.data[1] && this->data[2] == rhs.data[2] && this->data[3] == rhs.data[3];
|
||||
}
|
||||
|
||||
bool operator!=(const B2H &lhs, const B2H &rhs)
|
||||
bool B2H::operator!=(const B2H rhs) const
|
||||
{
|
||||
return lhs.data[0] != rhs.data[0] || lhs.data[1] != rhs.data[1] || lhs.data[2] != rhs.data[2] || lhs.data[3] != rhs.data[3];
|
||||
return this->data[0] != rhs.data[0] || this->data[1] != rhs.data[1] || this->data[2] != rhs.data[2] || this->data[3] != rhs.data[3];
|
||||
}
|
||||
|
||||
void operator^=(B2H &lhs, const B2H &rhs)
|
||||
void B2H::operator^=(const B2H rhs)
|
||||
{
|
||||
lhs.data[0] ^= rhs.data[0];
|
||||
lhs.data[1] ^= rhs.data[1];
|
||||
lhs.data[2] ^= rhs.data[2];
|
||||
lhs.data[3] ^= rhs.data[3];
|
||||
this->data[0] ^= rhs.data[0];
|
||||
this->data[1] ^= rhs.data[1];
|
||||
this->data[2] ^= rhs.data[2];
|
||||
this->data[3] ^= rhs.data[3];
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &output, const B2H &h)
|
||||
@@ -34,8 +47,9 @@ std::stringstream &operator<<(std::stringstream &output, const B2H &h)
|
||||
return output;
|
||||
}
|
||||
|
||||
// the actual hash function, note that the B2H datatype is always passed by value being only 4 quadwords
|
||||
B2H hash(const void *buf1, size_t buf1len, const void *buf2, size_t buf2len)
|
||||
// The actual hash function, note that the B2H datatype is always passed by value being only 4 quadwords.
|
||||
// This function accepts two buffers to hash together in order to support common use case in state handling.
|
||||
B2H hash(const void *buf1, const size_t buf1len, const void *buf2, const size_t buf2len)
|
||||
{
|
||||
crypto_generichash_blake2b_state state;
|
||||
crypto_generichash_blake2b_init(&state, NULL, 0, HASH_SIZE);
|
||||
@@ -52,4 +66,17 @@ B2H hash(const void *buf1, size_t buf1len, const void *buf2, size_t buf2len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Helper class to support std::map/std::unordered_map custom hashing function.
|
||||
// This is needed to use B2H as the std map container key.
|
||||
size_t B2H_std_key_hasher::operator()(const hasher::B2H h) const
|
||||
{
|
||||
// Compute individual hash values. http://stackoverflow.com/a/1646913/126995
|
||||
size_t res = 17;
|
||||
res = res * 31 + std::hash<uint64_t>()(h.data[0]);
|
||||
res = res * 31 + std::hash<uint64_t>()(h.data[1]);
|
||||
res = res * 31 + std::hash<uint64_t>()(h.data[2]);
|
||||
res = res * 31 + std::hash<uint64_t>()(h.data[3]);
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace hasher
|
||||
@@ -6,21 +6,34 @@
|
||||
namespace hasher
|
||||
{
|
||||
|
||||
// Hash length (32 bytes)
|
||||
constexpr size_t HASH_SIZE = crypto_generichash_blake2b_BYTES;
|
||||
|
||||
struct B2H // blake2b hash is 32 bytes which we store as 4 quad words
|
||||
// blake2b hash is 32 bytes which we store as 4 quad words
|
||||
// Originally from https://github.com/codetsunami/file-ptracer/blob/master/merkle.cpp
|
||||
struct B2H
|
||||
{
|
||||
uint64_t data[4];
|
||||
|
||||
bool operator==(const B2H rhs) const;
|
||||
bool operator!=(const B2H rhs) const;
|
||||
void operator^=(const B2H rhs);
|
||||
};
|
||||
|
||||
// provide some helper functions for working with 32 byte hash type
|
||||
bool operator==(const B2H &lhs, const B2H &rhs);
|
||||
bool operator!=(const B2H &lhs, const B2H &rhs);
|
||||
void operator^=(B2H &lhs, const B2H &rhs);
|
||||
extern B2H B2H_empty;
|
||||
|
||||
std::ostream &operator<<(std::ostream &output, const B2H &h);
|
||||
std::stringstream &operator<<(std::stringstream &output, const B2H &h);
|
||||
|
||||
B2H hash(const void *buf1, size_t buf1len, const void *buf2, size_t buf2len);
|
||||
B2H hash(const void *buf1, const size_t buf1len, const void *buf2, const size_t buf2len);
|
||||
|
||||
// Helper class to support std::map/std::unordered_map custom hashing function.
|
||||
// This is needed to use B2H as the std map container key.
|
||||
class B2H_std_key_hasher
|
||||
{
|
||||
public:
|
||||
size_t operator()(const hasher::B2H h) const;
|
||||
};
|
||||
|
||||
} // namespace hasher
|
||||
|
||||
|
||||
@@ -11,17 +11,18 @@ hashmap_builder::hashmap_builder(const statedir_context &ctx) : ctx(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
int hashmap_builder::generate_hashmap_forfile(hasher::B2H &parentdirhash, const std::string &filepath)
|
||||
int hashmap_builder::generate_hashmap_forfile(hasher::B2H &parentdirhash, const std::string &filepath, const std::string filerelpath, const std::map<uint32_t, hasher::B2H> &changedblocks)
|
||||
{
|
||||
// We attempt to avoid a full rebuild of the block hash map file when possible.
|
||||
// For this optimisation, both the block hash map (.bhmap) file and the
|
||||
// delta block index (.bindex) file must exist.
|
||||
// delta block index must exist.
|
||||
|
||||
// Block index may be provided as an argument. If it is empty we attempt to read from the
|
||||
// .bindex file from the state checkpoint delta.
|
||||
|
||||
// If the block index exists, we generate/update the hashmap file with the aid of that.
|
||||
// Block index file contains the updated blockids. If not, we simply rehash all the blocks.
|
||||
|
||||
std::string relpath = get_relpath(filepath, ctx.datadir);
|
||||
|
||||
// Open the actual data file and calculate the block count.
|
||||
int orifd = open(filepath.data(), O_RDONLY);
|
||||
if (orifd == -1)
|
||||
@@ -35,31 +36,41 @@ int hashmap_builder::generate_hashmap_forfile(hasher::B2H &parentdirhash, const
|
||||
// Attempt to read the existing block hash map file.
|
||||
std::string bhmapfile;
|
||||
std::vector<char> bhmapdata;
|
||||
if (read_blockhashmap(bhmapdata, bhmapfile, relpath) == -1)
|
||||
if (read_blockhashmap(bhmapdata, bhmapfile, filerelpath) == -1)
|
||||
return -1;
|
||||
|
||||
hasher::B2H oldfilehash = {0, 0, 0, 0};
|
||||
hasher::B2H oldfilehash = hasher::B2H_empty;
|
||||
if (!bhmapdata.empty())
|
||||
memcpy(&oldfilehash, bhmapdata.data(), hasher::HASH_SIZE);
|
||||
|
||||
// Attempt to read the delta block index file.
|
||||
std::map<uint32_t, hasher::B2H> bindex;
|
||||
uint32_t original_blockcount;
|
||||
if (get_blockindex(bindex, original_blockcount, relpath) == -1)
|
||||
return -1;
|
||||
|
||||
// Array to contain the updated block hashes. Slot 0 is for the root hash.
|
||||
// Allocating hash array on the heap to avoid filling limited stack space.
|
||||
std::unique_ptr<hasher::B2H[]> hashes = std::make_unique<hasher::B2H[]>(1 + blockcount);
|
||||
const size_t hashes_size = (1 + blockcount) * hasher::HASH_SIZE;
|
||||
|
||||
if (update_hashes(hashes.get(), hashes_size, relpath, orifd, blockcount, original_blockcount, bindex, bhmapdata) == -1)
|
||||
return -1;
|
||||
if (changedblocks.empty())
|
||||
{
|
||||
// Attempt to read the delta block index file.
|
||||
std::map<uint32_t, hasher::B2H> bindex;
|
||||
uint32_t original_blockcount;
|
||||
if (get_blockindex(bindex, original_blockcount, filerelpath) == -1)
|
||||
return -1;
|
||||
|
||||
if (update_hashes_with_backup_blockhints(hashes.get(), hashes_size, filerelpath, orifd, blockcount, original_blockcount, bindex, bhmapdata) == -1)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (update_hashes_with_changed_blockhints(hashes.get(), hashes_size, filerelpath, orifd, blockcount, changedblocks, bhmapdata) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(orifd);
|
||||
|
||||
if (write_blockhashmap(bhmapfile, hashes.get(), hashes_size) == -1)
|
||||
return -1;
|
||||
|
||||
if (update_hashtree_entry(parentdirhash, !bhmapdata.empty(), oldfilehash, hashes[0], bhmapfile, relpath) == -1)
|
||||
if (update_hashtree_entry(parentdirhash, !bhmapdata.empty(), oldfilehash, hashes[0], bhmapfile, filerelpath) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
@@ -151,7 +162,7 @@ int hashmap_builder::get_blockindex(std::map<uint32_t, hasher::B2H> &idxmap, uin
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_builder::update_hashes(
|
||||
int hashmap_builder::update_hashes_with_backup_blockhints(
|
||||
hasher::B2H *hashes, const off_t hashes_size, const std::string &relpath, const int orifd,
|
||||
const uint32_t blockcount, const uint32_t original_blockcount, const std::map<uint32_t, hasher::B2H> &bindex, const std::vector<char> &bhmapdata)
|
||||
{
|
||||
@@ -193,7 +204,58 @@ int hashmap_builder::update_hashes(
|
||||
}
|
||||
|
||||
// Calculate the new file hash: filehash = HASH(filename + XOR(block hashes))
|
||||
hasher::B2H filehash{0, 0, 0, 0};
|
||||
hasher::B2H filehash = hasher::B2H_empty;
|
||||
for (int i = 1; i <= blockcount; i++)
|
||||
filehash ^= hashes[i];
|
||||
|
||||
// Rehash the file hash with filename included.
|
||||
const std::string filename = boost::filesystem::path(relpath.data()).filename().string();
|
||||
filehash = hasher::hash(filename.c_str(), filename.length(), &filehash, hasher::HASH_SIZE);
|
||||
|
||||
hashes[0] = filehash;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_builder::update_hashes_with_changed_blockhints(
|
||||
hasher::B2H *hashes, const off_t hashes_size, const std::string &relpath, const int orifd,
|
||||
const uint32_t blockcount, const std::map<uint32_t, hasher::B2H> &bindex, const std::vector<char> &bhmapdata)
|
||||
{
|
||||
// If both existing delta block index and block hash map is available, we can just overlay the
|
||||
// changed block hashes (mentioned in the delta block index) on top of the old block hashes.
|
||||
// This would prevent unncessarily hashing lot of blocks.
|
||||
|
||||
if (!bindex.empty())
|
||||
{
|
||||
// Load old hashes if exists.
|
||||
if (!bhmapdata.empty())
|
||||
memcpy(hashes, bhmapdata.data(), hashes_size < bhmapdata.size() ? hashes_size : bhmapdata.size());
|
||||
|
||||
// Refer to the block index and overlay the new hash into the hashes array.
|
||||
for (const auto [blockid, newhash] : bindex)
|
||||
hashes[blockid + 1] = newhash;
|
||||
|
||||
// If the block hash map didn't existed, we need to calculate and fill the unchanged block hashes from the actual file.
|
||||
if (bhmapdata.empty())
|
||||
{
|
||||
for (uint32_t blockid = 0; blockid < blockcount; blockid++)
|
||||
{
|
||||
if (bindex.count(blockid) == 0 && compute_blockhash(hashes[blockid + 1], blockid, orifd, relpath) == -1)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we don't have the changed block index, we have to hash the entire file blocks again.
|
||||
for (uint32_t blockid = 0; blockid < blockcount; blockid++)
|
||||
{
|
||||
if (compute_blockhash(hashes[blockid + 1], blockid, orifd, relpath) == -1)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the new file hash: filehash = HASH(filename + XOR(block hashes))
|
||||
hasher::B2H filehash = hasher::B2H_empty;
|
||||
for (int i = 1; i <= blockcount; i++)
|
||||
filehash ^= hashes[i];
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _STATEFS_HASHMAP_BUILDER_
|
||||
#define _STATEFS_HASHMAP_BUILDER_
|
||||
#ifndef _HP_STATEFS_HASHMAP_BUILDER_
|
||||
#define _HP_STATEFS_HASHMAP_BUILDER_
|
||||
|
||||
#include "../pchheader.hpp"
|
||||
#include "hasher.hpp"
|
||||
@@ -17,16 +17,19 @@ private:
|
||||
|
||||
int read_blockhashmap(std::vector<char> &bhmapdata, std::string &hmapfile, const std::string &relpath);
|
||||
int get_blockindex(std::map<uint32_t, hasher::B2H> &idxmap, uint32_t &totalblockcount, const std::string &filerelpath);
|
||||
int update_hashes(
|
||||
int update_hashes_with_backup_blockhints(
|
||||
hasher::B2H *hashes, const off_t hashes_size, const std::string &relpath, const int orifd,
|
||||
const uint32_t blockcount, const uint32_t original_blockcount, const std::map<uint32_t, hasher::B2H> &bindex, const std::vector<char> &bhmapdata);
|
||||
int update_hashes_with_changed_blockhints(
|
||||
hasher::B2H *hashes, const off_t hashes_size, const std::string &relpath, const int orifd,
|
||||
const uint32_t blockcount, const std::map<uint32_t, hasher::B2H> &bindex, const std::vector<char> &bhmapdata);
|
||||
int compute_blockhash(hasher::B2H &hash, uint32_t blockid, int filefd, const std::string &relpath);
|
||||
int write_blockhashmap(const std::string &bhmapfile, const hasher::B2H *hashes, const off_t hashes_size);
|
||||
int update_hashtree_entry(hasher::B2H &parentdirhash, const bool oldbhmap_exists, const hasher::B2H oldfilehash, const hasher::B2H newfilehash, const std::string &bhmapfile, const std::string &relpath);
|
||||
|
||||
public:
|
||||
hashmap_builder(const statedir_context &ctx);
|
||||
int generate_hashmap_forfile(hasher::B2H &parentdirhash, const std::string &filepath);
|
||||
int generate_hashmap_forfile(hasher::B2H &parentdirhash, const std::string &filepath, const std::string filerelpath, const std::map<uint32_t, hasher::B2H> &changedblocks);
|
||||
int remove_hashmapfile(hasher::B2H &parentdirhash, const std::string &filepath);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,15 +8,51 @@ namespace statefs
|
||||
|
||||
hashtree_builder::hashtree_builder(const statedir_context &ctx) : ctx(ctx), hmapbuilder(ctx)
|
||||
{
|
||||
force_rebuild_all = false;
|
||||
hintmode = false;
|
||||
}
|
||||
|
||||
int hashtree_builder::generate(hasher::B2H &roothash)
|
||||
{
|
||||
// Load modified file path hints if available.
|
||||
populate_hintpaths(IDX_TOUCHEDFILES);
|
||||
populate_hintpaths(IDX_NEWFILES);
|
||||
populate_hintpaths_from_idxfile(IDX_TOUCHEDFILES);
|
||||
populate_hintpaths_from_idxfile(IDX_NEWFILES);
|
||||
hintmode = !hintpaths.empty();
|
||||
|
||||
return traverse_and_generate(roothash);
|
||||
}
|
||||
|
||||
int hashtree_builder::generate(hasher::B2H &roothash, const bool force_all)
|
||||
{
|
||||
force_rebuild_all = force_all;
|
||||
if (force_rebuild_all)
|
||||
{
|
||||
boost::filesystem::remove_all(ctx.blockhashmapdir);
|
||||
boost::filesystem::remove_all(ctx.hashtreedir);
|
||||
|
||||
boost::filesystem::create_directories(ctx.blockhashmapdir);
|
||||
boost::filesystem::create_directories(ctx.hashtreedir);
|
||||
}
|
||||
|
||||
return traverse_and_generate(roothash);
|
||||
}
|
||||
|
||||
int hashtree_builder::generate(hasher::B2H &roothash, const std::unordered_map<std::string, std::map<uint32_t, hasher::B2H>> &touchedfiles)
|
||||
{
|
||||
hintmode = true;
|
||||
fileblockindex = touchedfiles;
|
||||
for (const auto &[relpath, bindex] : touchedfiles)
|
||||
insert_hintpath(relpath);
|
||||
|
||||
return traverse_and_generate(roothash);
|
||||
}
|
||||
|
||||
int hashtree_builder::traverse_and_generate(hasher::B2H &roothash)
|
||||
{
|
||||
// Load current root hash if exist.
|
||||
const std::string dirhashfile = ctx.hashtreedir + "/" + DIRHASH_FNAME;
|
||||
roothash = get_existingdirhash(dirhashfile);
|
||||
|
||||
traversel_rootdir = ctx.datadir;
|
||||
removal_mode = false;
|
||||
if (update_hashtree(roothash) != 0)
|
||||
@@ -117,6 +153,10 @@ int hashtree_builder::update_hashtree_fordir(hasher::B2H &parentdirhash, const s
|
||||
parentdirhash ^= original_dirhash;
|
||||
parentdirhash ^= dirhash;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentdirhash = dirhash;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -124,7 +164,7 @@ int hashtree_builder::update_hashtree_fordir(hasher::B2H &parentdirhash, const s
|
||||
hasher::B2H hashtree_builder::get_existingdirhash(const std::string &dirhashfile)
|
||||
{
|
||||
// Load current dir hash if exist.
|
||||
hasher::B2H dirhash{0, 0, 0, 0};
|
||||
hasher::B2H dirhash = hasher::B2H_empty;
|
||||
int dirhashfd = open(dirhashfile.c_str(), O_RDONLY);
|
||||
if (dirhashfd > 0)
|
||||
{
|
||||
@@ -152,11 +192,17 @@ int hashtree_builder::save_dirhash(const std::string &dirhashfile, hasher::B2H d
|
||||
|
||||
inline bool hashtree_builder::should_process_dir(hintpath_map::iterator &dir_itr, const std::string &dirpath)
|
||||
{
|
||||
if (force_rebuild_all)
|
||||
return true;
|
||||
|
||||
return (hintmode ? get_hinteddir_match(dir_itr, dirpath) : true);
|
||||
}
|
||||
|
||||
bool hashtree_builder::should_process_file(const hintpath_map::iterator hintdir_itr, const std::string filepath)
|
||||
{
|
||||
if (force_rebuild_all)
|
||||
return true;
|
||||
|
||||
if (hintmode)
|
||||
{
|
||||
if (hintdir_itr == hintpaths.end())
|
||||
@@ -191,7 +237,10 @@ int hashtree_builder::process_file(hasher::B2H &parentdirhash, const std::string
|
||||
created_htreesubdirs.emplace(htreedirpath);
|
||||
}
|
||||
|
||||
if (hmapbuilder.generate_hashmap_forfile(parentdirhash, filepath) == -1)
|
||||
std::string relpath = get_relpath(filepath, ctx.datadir);
|
||||
std::map<uint32_t, hasher::B2H> changedblocks = fileblockindex[relpath];
|
||||
|
||||
if (hmapbuilder.generate_hashmap_forfile(parentdirhash, filepath, relpath, changedblocks) == -1)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
@@ -203,20 +252,24 @@ int hashtree_builder::process_file(hasher::B2H &parentdirhash, const std::string
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hashtree_builder::populate_hintpaths(const char *const idxfile)
|
||||
void hashtree_builder::populate_hintpaths_from_idxfile(const char *const idxfile)
|
||||
{
|
||||
std::ifstream infile(std::string(ctx.deltadir).append(idxfile));
|
||||
if (!infile.fail())
|
||||
{
|
||||
for (std::string relpath; std::getline(infile, relpath);)
|
||||
{
|
||||
std::string parentdir = boost::filesystem::path(relpath).parent_path().string();
|
||||
hintpaths[parentdir].emplace(relpath);
|
||||
}
|
||||
insert_hintpath(relpath);
|
||||
infile.close();
|
||||
}
|
||||
}
|
||||
|
||||
void hashtree_builder::insert_hintpath(const std::string &relpath)
|
||||
{
|
||||
boost::filesystem::path p_relpath(relpath);
|
||||
std::string parentdir = p_relpath.parent_path().string();
|
||||
hintpaths[parentdir].emplace(relpath);
|
||||
}
|
||||
|
||||
bool hashtree_builder::get_hinteddir_match(hintpath_map::iterator &matchitr, const std::string &dirpath)
|
||||
{
|
||||
// First check whether there's an exact match. If not check for a partial match.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _STATEFS_HASHTREE_BUILDER_
|
||||
#define _STATEFS_HASHTREE_BUILDER_
|
||||
#ifndef _HP_STATEFS_HASHTREE_BUILDER_
|
||||
#define _HP_STATEFS_HASHTREE_BUILDER_
|
||||
|
||||
#include "../pchheader.hpp"
|
||||
#include "hasher.hpp"
|
||||
@@ -19,13 +19,16 @@ private:
|
||||
|
||||
// Hint path map with parent dir as key and list of file paths under each parent dir.
|
||||
hintpath_map hintpaths;
|
||||
bool force_rebuild_all;
|
||||
bool hintmode;
|
||||
bool removal_mode;
|
||||
std::string traversel_rootdir;
|
||||
std::unordered_map<std::string, std::map<uint32_t, hasher::B2H>> fileblockindex;
|
||||
|
||||
// List of new root hash map sub directories created during the session.
|
||||
std::unordered_set<std::string> created_htreesubdirs;
|
||||
|
||||
int traverse_and_generate(hasher::B2H &roothash);
|
||||
int update_hashtree(hasher::B2H &roothash);
|
||||
int update_hashtree_fordir(hasher::B2H &parentdirhash, const std::string &relpath, const hintpath_map::iterator hintdir_itr, const bool isrootlevel);
|
||||
|
||||
@@ -35,12 +38,15 @@ private:
|
||||
bool should_process_file(const hintpath_map::iterator hintdir_itr, const std::string filepath);
|
||||
int process_file(hasher::B2H &parentdirhash, const std::string &filepath, const std::string &htreedirpath);
|
||||
int update_hashtree_entry(hasher::B2H &parentdirhash, const bool oldbhmap_exists, const hasher::B2H oldfilehash, const hasher::B2H newfilehash, const std::string &bhmapfile, const std::string &relpath);
|
||||
void populate_hintpaths(const char *const idxfile);
|
||||
void populate_hintpaths_from_idxfile(const char *const idxfile);
|
||||
void insert_hintpath(const std::string &relpath);
|
||||
bool get_hinteddir_match(hintpath_map::iterator &matchitr, const std::string &dirpath);
|
||||
|
||||
public:
|
||||
hashtree_builder(const statedir_context &ctx);
|
||||
int generate(hasher::B2H &roothash);
|
||||
int generate(hasher::B2H &roothash, const bool force_all);
|
||||
int generate(hasher::B2H &roothash, const std::unordered_map<std::string, std::map<uint32_t, hasher::B2H>> &touchedfiles);
|
||||
};
|
||||
|
||||
} // namespace statefs
|
||||
|
||||
@@ -6,13 +6,14 @@ namespace statefs
|
||||
{
|
||||
|
||||
std::string statehistdir;
|
||||
statedir_context current_ctx;
|
||||
|
||||
void init(const std::string &statehist_dir_root)
|
||||
{
|
||||
statehistdir = realpath(statehist_dir_root.c_str(), NULL);
|
||||
|
||||
// Initialize 0 state (current state) directory.
|
||||
get_statedir_context(0, true);
|
||||
current_ctx = get_statedir_context(0, true);
|
||||
}
|
||||
|
||||
std::string get_statedir_root(const int16_t checkpointid)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _STATEFS_STATE_COMMON_
|
||||
#define _STATEFS_STATE_COMMON_
|
||||
#ifndef _HP_STATEFS_STATE_COMMON_
|
||||
#define _HP_STATEFS_STATE_COMMON_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace statefs
|
||||
{
|
||||
|
||||
// Max number of state history checkpoints to keep.
|
||||
constexpr int16_t MAX_CHECKPOINTS = 5;
|
||||
|
||||
// Cache block size.
|
||||
@@ -15,7 +16,6 @@ constexpr size_t BLOCK_SIZE = 4 * 1024 * 1024; // 4MB
|
||||
|
||||
// Cache block index entry bytes length.
|
||||
constexpr size_t BLOCKINDEX_ENTRY_SIZE = 44;
|
||||
constexpr size_t MAX_HASHES = BLOCK_SIZE / hasher::HASH_SIZE;
|
||||
|
||||
// Permissions used when creating block cache and index files.
|
||||
constexpr int FILE_PERMS = 0644;
|
||||
@@ -38,17 +38,24 @@ const char *const BHMAP_DIR = "/bhmap";
|
||||
const char *const HTREE_DIR = "/htree";
|
||||
const char *const DELTA_DIR = "/delta";
|
||||
|
||||
extern std::string statehistdir;
|
||||
|
||||
/**
|
||||
* Context struct to hold all state-related directory paths.
|
||||
*/
|
||||
struct statedir_context
|
||||
{
|
||||
std::string rootdir;
|
||||
std::string datadir;
|
||||
std::string blockhashmapdir;
|
||||
std::string hashtreedir;
|
||||
std::string deltadir;
|
||||
std::string rootdir; // Directory holding state sub dirs.
|
||||
std::string datadir; // Directory containing smart contract data.
|
||||
std::string blockhashmapdir; // Directory containing block hash map files.
|
||||
std::string hashtreedir; // Directory containing hash tree files (dir.hash and hard links).
|
||||
std::string deltadir; // Directory containing original smart contract data.
|
||||
};
|
||||
|
||||
// Container directory to contain all checkpoints.
|
||||
extern std::string statehistdir;
|
||||
|
||||
// Currently loaded state checkpoint directory context (usually checkpoint 0)
|
||||
extern statedir_context current_ctx;
|
||||
|
||||
void init(const std::string &statehist_dir_root);
|
||||
std::string get_statedir_root(const int16_t checkpointid);
|
||||
statedir_context get_statedir_context(int16_t checkpointid = 0, bool createdirs = false);
|
||||
|
||||
@@ -553,6 +553,7 @@ static void sfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
// state monitor hook.
|
||||
std::string oldfilepath, newfilepath;
|
||||
if (helpers::getfilepath(oldfilepath, inode_p.fd, name) == 0 &&
|
||||
helpers::getfilepath(newfilepath, inode_np.fd, newname) == 0)
|
||||
@@ -568,6 +569,7 @@ static void sfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
|
||||
{
|
||||
Inode &inode_p = get_inode(parent);
|
||||
|
||||
// state monitor hook.
|
||||
std::string filepath;
|
||||
if (helpers::getfilepath(filepath, inode_p.fd, name) == 0)
|
||||
statemonitor.ondelete(filepath);
|
||||
@@ -867,6 +869,7 @@ static void sfs_create(fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
}
|
||||
else
|
||||
{
|
||||
// state monitor hook.
|
||||
statemonitor.oncreate(fd);
|
||||
fuse_reply_create(req, &e, fi);
|
||||
}
|
||||
@@ -911,6 +914,7 @@ static void sfs_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
|
||||
char buf[64];
|
||||
sprintf(buf, "/proc/self/fd/%i", inode.fd);
|
||||
|
||||
// state monitor hook.
|
||||
statemonitor.onopen(inode.fd, fi->flags);
|
||||
|
||||
auto fd = open(buf, fi->flags & ~O_NOFOLLOW);
|
||||
@@ -933,7 +937,10 @@ static void sfs_release(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
|
||||
{
|
||||
(void)ino;
|
||||
close(fi->fh);
|
||||
|
||||
// state monitor hook.
|
||||
statemonitor.onclose(fi->fh);
|
||||
|
||||
fuse_reply_err(req, 0);
|
||||
}
|
||||
|
||||
@@ -997,6 +1004,7 @@ static void sfs_write_buf(fuse_req_t req, fuse_ino_t ino, fuse_bufvec *in_buf,
|
||||
(void)ino;
|
||||
auto size{fuse_buf_size(in_buf)};
|
||||
|
||||
// state monitor hook.
|
||||
statemonitor.onwrite(fi->fh, off, size);
|
||||
|
||||
do_write_buf(req, size, off, in_buf, fi);
|
||||
@@ -1251,7 +1259,14 @@ void maximize_fd_limit()
|
||||
warn("WARNING: setrlimit() failed with");
|
||||
}
|
||||
|
||||
int start(const char *arg0, const char *statehistdir, const char *fusemntdir)
|
||||
/**
|
||||
* Starts hosting the fuse file system along with the state monitor.
|
||||
* @param arg0 First CLI argument to be passed into fuse main.
|
||||
* @param state_hist_dir Hot pocket state history directory.
|
||||
* @param fuse_mnt_dir Directory to mound the fuse filesystem.
|
||||
* @return 0 on success. 1 on failure.
|
||||
*/
|
||||
int start(const char *arg0, const char *state_hist_dir, const char *fuse_mnt_dir)
|
||||
{
|
||||
// We need an fd for every entry in our the filesystem that the
|
||||
// kernel knows about. This is way more than most processes need,
|
||||
@@ -1259,14 +1274,14 @@ int start(const char *arg0, const char *statehistdir, const char *fusemntdir)
|
||||
maximize_fd_limit();
|
||||
|
||||
// We consider this as the first run of the history dir is empty.
|
||||
const bool firstrun = boost::filesystem::is_empty(statehistdir);
|
||||
const bool is_first_run = boost::filesystem::is_empty(state_hist_dir);
|
||||
|
||||
statefs::init(statehistdir);
|
||||
statefs::init(state_hist_dir);
|
||||
statemonitor.ctx = statefs::get_statedir_context();
|
||||
fs.source = statemonitor.ctx.datadir;
|
||||
|
||||
// Create a checkpoint from the second run onwards.
|
||||
if (!firstrun)
|
||||
if (!is_first_run)
|
||||
statemonitor.create_checkpoint();
|
||||
|
||||
// Initialize filesystem root
|
||||
@@ -1311,7 +1326,7 @@ int start(const char *arg0, const char *statehistdir, const char *fusemntdir)
|
||||
struct fuse_loop_config loop_config;
|
||||
loop_config.clone_fd = 0;
|
||||
loop_config.max_idle_threads = 10;
|
||||
if (fuse_session_mount(se, fusemntdir) != 0)
|
||||
if (fuse_session_mount(se, fuse_mnt_dir) != 0)
|
||||
goto err_out3;
|
||||
|
||||
ret = fuse_session_loop_mt(se, &loop_config);
|
||||
@@ -1338,5 +1353,5 @@ int main(int argc, char *argv[])
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fusefs::start(argv[0], argv[1], argv[2]);
|
||||
return fusefs::start(argv[0], argv[1], argv[2]);
|
||||
}
|
||||
@@ -19,11 +19,22 @@
|
||||
namespace statefs
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates a new checkpoint directory. This will remove the oldest checkpoint if we have
|
||||
* reached MAX_CHECKPOINTS. This is called whenever fuse filesystem is run so the contract
|
||||
* always runs on a new checkpoint.
|
||||
*/
|
||||
void state_monitor::create_checkpoint()
|
||||
{
|
||||
// Shift -1 and below checkpoints by 1 more.
|
||||
/**
|
||||
* Checkpoints are numbered 0, -1, -2, ...
|
||||
* Checkpoint 0 is the latest state containing "state", "data", "delta", "bhmap", "htree" directories.
|
||||
* Checkpoints -1 and lower cotnains only the "delta" dirs containing older state changesets.
|
||||
*/
|
||||
|
||||
// Shift "-1" and older checkpoints by 1 more. And then copy checkpoint 0 delta dir to "-1"
|
||||
// If MAX oldest checkpoint is there, remove it and work our way upwards.
|
||||
int16_t oldest_chkpnt = (MAX_CHECKPOINTS + 1) * -1; // +1 because we maintain one extra checkpoint in case of rollbacks.
|
||||
int16_t oldest_chkpnt = MAX_CHECKPOINTS * -1;
|
||||
for (int16_t chkpnt = oldest_chkpnt; chkpnt <= -1; chkpnt++)
|
||||
{
|
||||
std::string dir = get_statedir_root(chkpnt);
|
||||
@@ -36,8 +47,8 @@ void state_monitor::create_checkpoint()
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string dirshift = get_statedir_root(chkpnt - 1);
|
||||
boost::filesystem::rename(dir, dirshift);
|
||||
std::string dir_shift = get_statedir_root(chkpnt - 1);
|
||||
boost::filesystem::rename(dir, dir_shift);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +68,10 @@ void state_monitor::create_checkpoint()
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a new file is created in the fuse fs.
|
||||
* @param fd The fd of the created file.
|
||||
*/
|
||||
void state_monitor::oncreate(const int fd)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(monitor_mutex);
|
||||
@@ -66,27 +81,46 @@ void state_monitor::oncreate(const int fd)
|
||||
oncreate_filepath(filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a file is going to be opened.
|
||||
* @param inodefd inode fd given by fuse fs. This is used to find the physical path of the file.
|
||||
* @param flags Open flags.
|
||||
*/
|
||||
void state_monitor::onopen(const int inodefd, const int flags)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(monitor_mutex);
|
||||
|
||||
// Find the actual file path which is going to be opened and add that path to tracked file info list.
|
||||
std::string filepath;
|
||||
if (extract_filepath(filepath, inodefd) == 0)
|
||||
{
|
||||
state_file_info *fi;
|
||||
if (get_tracked_fileinfo(&fi, filepath) == 0)
|
||||
{
|
||||
// Check whether fd is open in truncate mode. If so cache the entire file immediately.
|
||||
// Check whether the file is going to be opened in truncate mode.
|
||||
// If so cache the entire file immediately because this is the last chance we get to backup the data.
|
||||
if (flags & O_TRUNC)
|
||||
cache_blocks(*fi, 0, fi->original_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever a file is being written to.
|
||||
* @param fd fd of the file being written to.
|
||||
* @param offset Byte offset of the write.
|
||||
* @param length Number of bytes being overwritten.
|
||||
*/
|
||||
void state_monitor::onwrite(const int fd, const off_t offset, const size_t length)
|
||||
{
|
||||
// TODO: Known issue: Onwrite can get called if the client program deletes a file before
|
||||
// closing the currently open file. If there were some bytes on the write buffer, the flush happens
|
||||
// when the client closes the fd. By that time the fd is invalid since the file is deleted.
|
||||
// However nothing happens to us as our code simply returns on invalild fd error.
|
||||
|
||||
std::lock_guard<std::mutex> lock(monitor_mutex);
|
||||
|
||||
// Find the actual filepath being written to and cache the blocks to server as backup.
|
||||
std::string filepath;
|
||||
if (get_fd_filepath(filepath, fd) == 0)
|
||||
{
|
||||
@@ -96,20 +130,30 @@ void state_monitor::onwrite(const int fd, const off_t offset, const size_t lengt
|
||||
}
|
||||
}
|
||||
|
||||
void state_monitor::onrename(const std::string &oldfilepath, const std::string &newfilepath)
|
||||
/**
|
||||
* Called when a file is being renamed.
|
||||
* We simply treat this as delete-and-create operation.
|
||||
*/
|
||||
void state_monitor::onrename(const std::string &old_filepath, const std::string &new_filepath)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(monitor_mutex);
|
||||
|
||||
ondelete_filepath(oldfilepath);
|
||||
oncreate_filepath(newfilepath);
|
||||
ondelete_filepath(old_filepath);
|
||||
oncreate_filepath(new_filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file is being deleted.
|
||||
*/
|
||||
void state_monitor::ondelete(const std::string &filepath)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(monitor_mutex);
|
||||
ondelete_filepath(filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file is being truncated.
|
||||
*/
|
||||
void state_monitor::ontruncate(const int fd, const off_t newsize)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(monitor_mutex);
|
||||
@@ -124,19 +168,25 @@ void state_monitor::ontruncate(const int fd, const off_t newsize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an open file is being closed. Here, we clear any tracking information we kept for this file
|
||||
* and close off any related fds associated with any backup operations for this file.
|
||||
*/
|
||||
void state_monitor::onclose(const int fd)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(monitor_mutex);
|
||||
|
||||
auto pitr = fdpathmap.find(fd);
|
||||
if (pitr != fdpathmap.end())
|
||||
// fd_path_map should contain this fd already if we were tracking it.
|
||||
|
||||
auto pitr = fd_path_map.find(fd);
|
||||
if (pitr != fd_path_map.end())
|
||||
{
|
||||
// Close any block cache/index fds we have opened for this file.
|
||||
auto fitr = fileinfomap.find(pitr->second); // pitr->second is the filepath string.
|
||||
if (fitr != fileinfomap.end())
|
||||
close_cachingfds(fitr->second); // fitr->second is the fileinfo struct.
|
||||
auto fitr = file_info_map.find(pitr->second); // pitr->second is the filepath string.
|
||||
if (fitr != file_info_map.end())
|
||||
close_caching_fds(fitr->second); // fitr->second is the fileinfo struct.
|
||||
|
||||
fdpathmap.erase(pitr);
|
||||
fd_path_map.erase(pitr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +220,8 @@ int state_monitor::extract_filepath(std::string &filepath, const int fd)
|
||||
int state_monitor::get_fd_filepath(std::string &filepath, const int fd)
|
||||
{
|
||||
// Return path from the map if found.
|
||||
const auto itr = fdpathmap.find(fd);
|
||||
if (itr != fdpathmap.end())
|
||||
const auto itr = fd_path_map.find(fd);
|
||||
if (itr != fd_path_map.end())
|
||||
{
|
||||
filepath = itr->second;
|
||||
return 0;
|
||||
@@ -180,42 +230,51 @@ int state_monitor::get_fd_filepath(std::string &filepath, const int fd)
|
||||
// Extract the file path and populate the fd-->filepath map.
|
||||
if (extract_filepath(filepath, fd) == 0)
|
||||
{
|
||||
fdpathmap[fd] = filepath;
|
||||
fd_path_map[fd] = filepath;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new file is going to be created. fd is not yet open at this point.
|
||||
* We need to catch this and start tracking this filepath.
|
||||
*/
|
||||
void state_monitor::oncreate_filepath(const std::string &filepath)
|
||||
{
|
||||
// Check whether we are already tracking this file path.
|
||||
// Only way this could happen is deleting an existing file and creating a new file with same name.
|
||||
if (fileinfomap.count(filepath) == 0)
|
||||
// Only way we could be tracking this patth already is deleting an existing file and creating
|
||||
// a new file with same name.
|
||||
if (file_info_map.count(filepath) == 0)
|
||||
{
|
||||
// Add an entry for the new file in the file info map. This information will be used to ignore
|
||||
// future operations (eg. write/delete) done to this file.
|
||||
state_file_info fi;
|
||||
fi.isnew = true;
|
||||
fi.is_new = true;
|
||||
fi.filepath = filepath;
|
||||
fileinfomap[filepath] = std::move(fi);
|
||||
file_info_map[filepath] = std::move(fi);
|
||||
|
||||
// Add to the list of new files added during this session.
|
||||
write_newfileentry(filepath);
|
||||
write_new_file_entry(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file is going to be deleted. We use this to remove any tracking information
|
||||
* regarding this file and to backup the file before deletion.
|
||||
*/
|
||||
void state_monitor::ondelete_filepath(const std::string &filepath)
|
||||
{
|
||||
state_file_info *fi;
|
||||
if (get_tracked_fileinfo(&fi, filepath) == 0)
|
||||
{
|
||||
if (fi->isnew)
|
||||
if (fi->is_new)
|
||||
{
|
||||
// If this is a new file, just remove from existing index entries.
|
||||
// No need to cache the file blocks.
|
||||
remove_newfileentry(fi->filepath);
|
||||
fileinfomap.erase(filepath);
|
||||
remove_new_file_entry(fi->filepath);
|
||||
file_info_map.erase(filepath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -234,15 +293,15 @@ void state_monitor::ondelete_filepath(const std::string &filepath)
|
||||
int state_monitor::get_tracked_fileinfo(state_file_info **fi, const std::string &filepath)
|
||||
{
|
||||
// Return from filepath-->fileinfo map if found.
|
||||
const auto itr = fileinfomap.find(filepath);
|
||||
if (itr != fileinfomap.end())
|
||||
const auto itr = file_info_map.find(filepath);
|
||||
if (itr != file_info_map.end())
|
||||
{
|
||||
*fi = &itr->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize a new state file info struct for the given filepath.
|
||||
state_file_info &fileinfo = fileinfomap[filepath];
|
||||
state_file_info &fileinfo = file_info_map[filepath];
|
||||
|
||||
// We use stat() to find out the length of the file.
|
||||
struct stat stat_buf;
|
||||
@@ -259,7 +318,8 @@ int state_monitor::get_tracked_fileinfo(state_file_info **fi, const std::string
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the specified bytes range of the given file.
|
||||
* Backs up the specified bytes range of the given file. This is called whenever a file is being
|
||||
* overwritten/deleted.
|
||||
* @param fi The file info struct pointing to the file to be cached.
|
||||
* @param offset The start byte position for caching.
|
||||
* @param length How many bytes to cache.
|
||||
@@ -268,7 +328,7 @@ int state_monitor::get_tracked_fileinfo(state_file_info **fi, const std::string
|
||||
int state_monitor::cache_blocks(state_file_info &fi, const off_t offset, const size_t length)
|
||||
{
|
||||
// No caching required if this is a new file created during this session.
|
||||
if (fi.isnew)
|
||||
if (fi.is_new)
|
||||
return 0;
|
||||
|
||||
uint32_t original_blockcount = ceil((double)fi.original_length / (double)BLOCK_SIZE);
|
||||
@@ -277,7 +337,7 @@ int state_monitor::cache_blocks(state_file_info &fi, const off_t offset, const s
|
||||
if (original_blockcount == fi.cached_blockids.size())
|
||||
return 0;
|
||||
|
||||
// Initialize fds and indexes required for caching.
|
||||
// Initialize fds and indexes required for caching the file.
|
||||
if (prepare_caching(fi) != 0)
|
||||
return -1;
|
||||
|
||||
@@ -291,7 +351,8 @@ int state_monitor::cache_blocks(state_file_info &fi, const off_t offset, const s
|
||||
// std::cout << "Cache blocks: '" << fi.filepath << "' [" << offset << "," << length << "] " << startblock << "," << endblock << "\n";
|
||||
|
||||
// If this is the first time we are caching this file, write an entry to the touched file index.
|
||||
if (fi.cached_blockids.empty() && write_touchedfileentry(fi.filepath) != 0)
|
||||
// Touched file index is used by rollback to server as a guide.
|
||||
if (fi.cached_blockids.empty() && write_touched_file_entry(fi.filepath) != 0)
|
||||
return -1;
|
||||
|
||||
for (uint32_t i = startblock; i <= endblock; i++)
|
||||
@@ -326,6 +387,7 @@ int state_monitor::cache_blocks(state_file_info &fi, const off_t offset, const s
|
||||
// Whoever is using the index must sort it if required.
|
||||
// Entry format: [blocknum(4 bytes) | cacheoffset(8 bytes) | blockhash(32 bytes)]
|
||||
|
||||
// Calculate the block hash by combining block offset with block data.
|
||||
char entrybuf[BLOCKINDEX_ENTRY_SIZE];
|
||||
hasher::B2H hash = hasher::hash(&blockoffset, 8, blockbuf.get(), bytesread);
|
||||
|
||||
@@ -350,13 +412,13 @@ int state_monitor::cache_blocks(state_file_info &fi, const off_t offset, const s
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes fds and indexes required for caching.
|
||||
* Initializes fds and indexes required for caching a particular file.
|
||||
* @param fi The state file info struct pointing to the file being cached.
|
||||
* @return 0 on succesful initialization. -1 on failure.
|
||||
*/
|
||||
int state_monitor::prepare_caching(state_file_info &fi)
|
||||
{
|
||||
// If readfd is greater than 0 then we take it as caching being already initialized.
|
||||
// If readfd is greater than 0 then we take it as caching being already initialized for this file.
|
||||
if (fi.readfd > 0)
|
||||
return 0;
|
||||
|
||||
@@ -378,10 +440,10 @@ int state_monitor::prepare_caching(state_file_info &fi)
|
||||
|
||||
// Create directory tree if not exist so we are able to create the cache and index files.
|
||||
boost::filesystem::path cachesubdir = boost::filesystem::path(tmppath).parent_path();
|
||||
if (created_cachesubdirs.count(cachesubdir.string()) == 0)
|
||||
if (created_cache_subdirs.count(cachesubdir.string()) == 0)
|
||||
{
|
||||
boost::filesystem::create_directories(cachesubdir);
|
||||
created_cachesubdirs.emplace(cachesubdir.string());
|
||||
created_cache_subdirs.emplace(cachesubdir.string());
|
||||
}
|
||||
|
||||
// Create and open the block cache file.
|
||||
@@ -415,7 +477,7 @@ int state_monitor::prepare_caching(state_file_info &fi)
|
||||
/**
|
||||
* Closes any open caching fds for a given file.
|
||||
*/
|
||||
void state_monitor::close_cachingfds(state_file_info &fi)
|
||||
void state_monitor::close_caching_fds(state_file_info &fi)
|
||||
{
|
||||
if (fi.readfd > 0)
|
||||
close(fi.readfd);
|
||||
@@ -433,25 +495,25 @@ void state_monitor::close_cachingfds(state_file_info &fi)
|
||||
|
||||
/**
|
||||
* Inserts a file into the modified files list of this session.
|
||||
* This index is used to restore modified files during restore.
|
||||
* This index is used to restore modified files during rollback.
|
||||
*/
|
||||
int state_monitor::write_touchedfileentry(std::string_view filepath)
|
||||
int state_monitor::write_touched_file_entry(std::string_view filepath)
|
||||
{
|
||||
if (touchedfileindexfd <= 0)
|
||||
if (touched_fileindex_fd <= 0)
|
||||
{
|
||||
std::string indexfile = ctx.deltadir + "/idxtouched.idx";
|
||||
touchedfileindexfd = open(indexfile.c_str(), O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS);
|
||||
if (touchedfileindexfd <= 0)
|
||||
std::string index_file = ctx.deltadir + IDX_TOUCHEDFILES;
|
||||
touched_fileindex_fd = open(index_file.c_str(), O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS);
|
||||
if (touched_fileindex_fd <= 0)
|
||||
{
|
||||
std::cerr << errno << ": Open failed " << indexfile << "\n";
|
||||
std::cerr << errno << ": Open failed " << index_file << "\n";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the relative file path line to the index.
|
||||
filepath = filepath.substr(ctx.datadir.length(), filepath.length() - ctx.datadir.length());
|
||||
write(touchedfileindexfd, filepath.data(), filepath.length());
|
||||
write(touchedfileindexfd, "\n", 1);
|
||||
write(touched_fileindex_fd, filepath.data(), filepath.length());
|
||||
write(touched_fileindex_fd, "\n", 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -459,13 +521,13 @@ int state_monitor::write_touchedfileentry(std::string_view filepath)
|
||||
* Inserts a file into the list of new files created during this session.
|
||||
* This index is used in deleting new files during restore.
|
||||
*/
|
||||
int state_monitor::write_newfileentry(std::string_view filepath)
|
||||
int state_monitor::write_new_file_entry(std::string_view filepath)
|
||||
{
|
||||
std::string indexfile = ctx.deltadir + "/idxnew.idx";
|
||||
int fd = open(indexfile.c_str(), O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS);
|
||||
std::string index_file = ctx.deltadir + IDX_NEWFILES;
|
||||
int fd = open(index_file.c_str(), O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS);
|
||||
if (fd <= 0)
|
||||
{
|
||||
std::cerr << errno << ": Open failed " << indexfile << "\n";
|
||||
std::cerr << errno << ": Open failed " << index_file << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -479,27 +541,28 @@ int state_monitor::write_newfileentry(std::string_view filepath)
|
||||
|
||||
/**
|
||||
* Scans and removes the given filepath from the new files index.
|
||||
* This is called when a file added during this session gets deleted in the same session.
|
||||
*/
|
||||
void state_monitor::remove_newfileentry(std::string_view filepath)
|
||||
void state_monitor::remove_new_file_entry(std::string_view filepath)
|
||||
{
|
||||
filepath = filepath.substr(ctx.datadir.length(), filepath.length() - ctx.datadir.length());
|
||||
|
||||
// We create a copy of the new file index and transfer lines from first file
|
||||
// to the second file except the line matching the given filepath.
|
||||
|
||||
std::string indexfile = ctx.deltadir + "/idxnew.idx";
|
||||
std::string indexfile_tmp = ctx.deltadir + "/idxnew.idx.tmp";
|
||||
std::string index_file = ctx.deltadir + IDX_NEWFILES;
|
||||
std::string index_file_tmp = ctx.deltadir + IDX_NEWFILES + ".tmp";
|
||||
|
||||
std::ifstream infile(indexfile);
|
||||
std::ofstream outfile(indexfile_tmp);
|
||||
std::ifstream infile(index_file);
|
||||
std::ofstream outfile(index_file_tmp);
|
||||
|
||||
bool linestransferred = false;
|
||||
bool lines_transferred = false;
|
||||
for (std::string line; std::getline(infile, line);)
|
||||
{
|
||||
if (line != filepath) // Skip the file being removed.
|
||||
{
|
||||
outfile << line << "\n";
|
||||
linestransferred = true;
|
||||
lines_transferred = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,13 +570,13 @@ void state_monitor::remove_newfileentry(std::string_view filepath)
|
||||
outfile.close();
|
||||
|
||||
// Remove the old index.
|
||||
std::remove(indexfile.c_str());
|
||||
std::remove(index_file.c_str());
|
||||
|
||||
// If no lines transferred, delete the temp file as well.
|
||||
if (linestransferred)
|
||||
std::rename(indexfile_tmp.c_str(), indexfile.c_str());
|
||||
if (lines_transferred)
|
||||
std::rename(index_file_tmp.c_str(), index_file.c_str());
|
||||
else
|
||||
std::remove(indexfile_tmp.c_str());
|
||||
std::remove(index_file_tmp.c_str());
|
||||
}
|
||||
|
||||
} // namespace statefs
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _STATEFS_STATE_MONITOR_
|
||||
#define _STATEFS_STATE_MONITOR_
|
||||
#ifndef _HP_STATEFS_STATE_MONITOR_
|
||||
#define _HP_STATEFS_STATE_MONITOR_
|
||||
|
||||
#include <cstdint>
|
||||
#include <sys/types.h>
|
||||
@@ -12,40 +12,41 @@
|
||||
namespace statefs
|
||||
{
|
||||
|
||||
// Holds information about an original file in state that we are tracking.
|
||||
/**
|
||||
* Holds information about an original file in state that we are tracking.
|
||||
*/
|
||||
struct state_file_info
|
||||
{
|
||||
bool isnew;
|
||||
off_t original_length;
|
||||
std::unordered_set<uint32_t> cached_blockids;
|
||||
std::string filepath;
|
||||
int readfd;
|
||||
int cachefd;
|
||||
int indexfd;
|
||||
bool is_new; // Whether this is a new file created during this session.
|
||||
off_t original_length; // Original file length.
|
||||
std::unordered_set<uint32_t> cached_blockids; // Set of block ids cached during this session.
|
||||
std::string filepath; // Actual real path of the file. (not fuse path)
|
||||
int readfd; // fd used for reading the original file for caching.
|
||||
int cachefd; // fd for writing into the block cache file.
|
||||
int indexfd; // fd for writing into the block index file.
|
||||
};
|
||||
|
||||
// Invoked by fuse file system for relevent file system calls.
|
||||
/**
|
||||
* Invoked by fuse file system for relevent file system calls.
|
||||
*/
|
||||
class state_monitor
|
||||
{
|
||||
private:
|
||||
// Map of fd-->filepath
|
||||
std::unordered_map<int, std::string> fdpathmap;
|
||||
std::unordered_map<int, std::string> fd_path_map;
|
||||
|
||||
// Map of filepath-->fileinfo
|
||||
std::unordered_map<std::string, state_file_info> fileinfomap;
|
||||
|
||||
// Complete list of modified files during the session.
|
||||
std::unordered_set<std::string> touchedfiles;
|
||||
std::unordered_map<std::string, state_file_info> file_info_map;
|
||||
|
||||
// List of new cache sub directories created during the session.
|
||||
std::unordered_set<std::string> created_cachesubdirs;
|
||||
std::unordered_set<std::string> created_cache_subdirs;
|
||||
|
||||
// Mutex to synchronize parallel file system calls into our custom state tracking logic.
|
||||
std::mutex monitor_mutex;
|
||||
|
||||
// Holds the fd used to write into modified files index. This will be kept open for the entire
|
||||
// life of the state monitor.
|
||||
int touchedfileindexfd = 0;
|
||||
int touched_fileindex_fd = 0;
|
||||
|
||||
int extract_filepath(std::string &filepath, const int fd);
|
||||
int get_fd_filepath(std::string &filepath, const int fd);
|
||||
@@ -55,10 +56,10 @@ private:
|
||||
|
||||
int cache_blocks(state_file_info &fi, const off_t offset, const size_t length);
|
||||
int prepare_caching(state_file_info &fi);
|
||||
void close_cachingfds(state_file_info &fi);
|
||||
int write_touchedfileentry(std::string_view filepath);
|
||||
int write_newfileentry(std::string_view filepath);
|
||||
void remove_newfileentry(std::string_view filepath);
|
||||
void close_caching_fds(state_file_info &fi);
|
||||
int write_touched_file_entry(std::string_view filepath);
|
||||
int write_new_file_entry(std::string_view filepath);
|
||||
void remove_new_file_entry(std::string_view filepath);
|
||||
|
||||
public:
|
||||
statedir_context ctx;
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
void oncreate(const int fd);
|
||||
void onopen(const int inodefd, const int flags);
|
||||
void onwrite(const int fd, const off_t offset, const size_t length);
|
||||
void onrename(const std::string &oldfilepath, const std::string &newfilepath);
|
||||
void onrename(const std::string &old_filepath, const std::string &new_filepath);
|
||||
void ondelete(const std::string &filepath);
|
||||
void ontruncate(const int fd, const off_t newsize);
|
||||
void onclose(const int fd);
|
||||
|
||||
@@ -20,7 +20,7 @@ void state_restore::delete_newfiles()
|
||||
std::string filepath(ctx.datadir);
|
||||
filepath.append(file);
|
||||
|
||||
std::remove(filepath.c_str());
|
||||
remove(filepath.c_str());
|
||||
}
|
||||
|
||||
infile.close();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _STATEFS_STATE_RESTORE_
|
||||
#define _STATEFS_STATE_RESTORE_
|
||||
#ifndef _HP_STATEFS_STATE_RESTORE_
|
||||
#define _HP_STATEFS_STATE_RESTORE_
|
||||
|
||||
#include "../pchheader.hpp"
|
||||
#include "hasher.hpp"
|
||||
|
||||
357
src/statefs/state_store.cpp
Normal file
357
src/statefs/state_store.cpp
Normal file
@@ -0,0 +1,357 @@
|
||||
#include "../pchheader.hpp"
|
||||
#include "hasher.hpp"
|
||||
#include "state_common.hpp"
|
||||
#include "hashtree_builder.hpp"
|
||||
#include "state_store.hpp"
|
||||
#include "../hplog.hpp"
|
||||
#include "state_store.hpp"
|
||||
|
||||
namespace statefs
|
||||
{
|
||||
|
||||
// Map of modified/deleted files with updated blockids and hashes (if modified).
|
||||
std::unordered_map<std::string, std::map<uint32_t, hasher::B2H>> touched_files;
|
||||
|
||||
/**
|
||||
* Checks whether the given directory exists in the state data directory.
|
||||
*/
|
||||
bool is_dir_exists(const std::string &dir_relpath)
|
||||
{
|
||||
const std::string full_path = current_ctx.datadir + dir_relpath;
|
||||
return boost::filesystem::exists(full_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the hash list of the file system entries at a given directory.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int get_fs_entry_hashes(std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries, const std::string &dir_relpath, const hasher::B2H expected_hash)
|
||||
{
|
||||
// TODO: instead of iterating the data dir, we could simply query the hash tree directory
|
||||
// listing and get the hashes using the hardlink names straight away. But then we don't have
|
||||
// a way to get the file names. If we could implement a mechanism for that we could make this efficient.
|
||||
|
||||
if (expected_hash != hasher::B2H_empty)
|
||||
{
|
||||
// Check whether the existing block hash matches expected hash.
|
||||
const std::string dir_hash_path = current_ctx.hashtreedir + dir_relpath + DIRHASH_FNAME;
|
||||
|
||||
hasher::B2H existsing_hash;
|
||||
if (read_file_bytes(&existsing_hash, dir_hash_path.c_str(), 0, hasher::HASH_SIZE) == -1)
|
||||
return -1;
|
||||
|
||||
if (existsing_hash != expected_hash)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const std::string full_path = current_ctx.datadir + dir_relpath;
|
||||
for (const boost::filesystem::directory_entry &dentry : boost::filesystem::directory_iterator(full_path))
|
||||
{
|
||||
const boost::filesystem::path p = dentry.path();
|
||||
|
||||
p2p::state_fs_hash_entry fs_entry;
|
||||
fs_entry.is_file = !boost::filesystem::is_directory(p);
|
||||
|
||||
std::string fsentry_relpath = dir_relpath + p.filename().string();
|
||||
|
||||
// Read the first 32 bytes of the .bhmap file or dir.hash file.
|
||||
|
||||
std::string hash_path;
|
||||
|
||||
if (fs_entry.is_file)
|
||||
{
|
||||
hash_path = current_ctx.blockhashmapdir + fsentry_relpath + HASHMAP_EXT;
|
||||
}
|
||||
else
|
||||
{
|
||||
fsentry_relpath += "/";
|
||||
hash_path = current_ctx.hashtreedir + fsentry_relpath + DIRHASH_FNAME;
|
||||
// Skip the directory if it doesn't contain the dir.hash file.
|
||||
// By that we assume the directory is empty so we're not interested in it.
|
||||
if (!boost::filesystem::exists(hash_path))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (read_file_bytes(&fs_entry.hash, hash_path.c_str(), 0, hasher::HASH_SIZE) == -1)
|
||||
return -1;
|
||||
|
||||
fs_entries.emplace(fsentry_relpath, std::move(fs_entry));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the block hash map for a file.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int get_block_hash_map(std::vector<uint8_t> &vec, const std::string &file_relpath, const hasher::B2H expected_hash)
|
||||
{
|
||||
const std::string bhmap_path = current_ctx.blockhashmapdir + file_relpath + HASHMAP_EXT;
|
||||
|
||||
if (expected_hash != hasher::B2H_empty)
|
||||
{
|
||||
// Check whether the existing block hash matches expected hash.
|
||||
|
||||
if (!boost::filesystem::exists(bhmap_path) || read_file_bytes_to_end(vec, bhmap_path.c_str(), 0) == -1)
|
||||
return -1;
|
||||
|
||||
// Existing hash is the first 32 bytes of bhmap contents.
|
||||
hasher::B2H existing_hash = *reinterpret_cast<hasher::B2H *>(vec.data());
|
||||
if (existing_hash != expected_hash)
|
||||
return -1;
|
||||
|
||||
// Return the bhmap bytes without the first 32 bytes.
|
||||
vec.erase(vec.begin(), vec.begin() + hasher::HASH_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip the file root hash and get the rest of the bytes.
|
||||
if (boost::filesystem::exists(bhmap_path) && read_file_bytes_to_end(vec, bhmap_path.c_str(), hasher::HASH_SIZE) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the byte length of a file.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int get_file_length(const std::string &file_relpath)
|
||||
{
|
||||
std::string full_path = current_ctx.datadir + file_relpath;
|
||||
int fd = open(full_path.c_str(), O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
LOG_ERR << errno << "Open failed " << full_path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const off_t total_len = lseek(fd, 0, SEEK_END);
|
||||
close(fd);
|
||||
|
||||
return total_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the specified data block from a state file.
|
||||
* @return Number of bytes read on success. -1 on failure.
|
||||
*/
|
||||
int get_block(std::vector<uint8_t> &vec, const std::string &file_relpath, const uint32_t block_id, const hasher::B2H expected_hash)
|
||||
{
|
||||
// Check whether the existing block hash matches expected hash.
|
||||
if (expected_hash != hasher::B2H_empty)
|
||||
{
|
||||
std::string bhmap_path = current_ctx.blockhashmapdir + file_relpath + HASHMAP_EXT;
|
||||
hasher::B2H existing_hash = hasher::B2H_empty;
|
||||
|
||||
if (read_file_bytes(&existing_hash, bhmap_path.c_str(), (block_id + 1) * hasher::HASH_SIZE, hasher::HASH_SIZE) == -1)
|
||||
return -1;
|
||||
|
||||
if (existing_hash != expected_hash)
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string full_path = current_ctx.datadir + file_relpath;
|
||||
vec.resize(BLOCK_SIZE);
|
||||
int read_bytes = read_file_bytes(vec.data(), full_path.c_str(), block_id * BLOCK_SIZE, BLOCK_SIZE);
|
||||
|
||||
if (read_bytes == -1)
|
||||
return -1;
|
||||
|
||||
vec.resize(read_bytes);
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the specified directory in the state data directory.
|
||||
*/
|
||||
void create_dir(const std::string &dir_relpath)
|
||||
{
|
||||
const std::string full_path = current_ctx.datadir + dir_relpath;
|
||||
boost::filesystem::create_directories(full_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files within the specified state sub directory and marks the changes.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int delete_dir(const std::string &dir_relpath)
|
||||
{
|
||||
std::string full_dir_path = current_ctx.datadir + dir_relpath;
|
||||
|
||||
const boost::filesystem::directory_iterator itr_end;
|
||||
for (boost::filesystem::directory_iterator itr(full_dir_path); itr != itr_end; itr++)
|
||||
{
|
||||
boost::filesystem::path p = itr->path();
|
||||
|
||||
if (!boost::filesystem::is_directory(p))
|
||||
{
|
||||
if (!boost::filesystem::remove(p))
|
||||
return -1;
|
||||
|
||||
// Add the deleted file rel path to the touched files list.
|
||||
touched_files.emplace(
|
||||
get_relpath(p.string(), current_ctx.datadir),
|
||||
std::map<uint32_t, hasher::B2H>());
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, delete the directory itself.
|
||||
boost::filesystem::remove_all(full_dir_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified state file and marks the change.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int delete_file(const std::string &file_relpath)
|
||||
{
|
||||
std::string full_path = current_ctx.datadir + file_relpath;
|
||||
if (!boost::filesystem::remove(full_path))
|
||||
return -1;
|
||||
|
||||
touched_files.emplace(file_relpath, std::map<uint32_t, hasher::B2H>());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the specified state file to the specified length and marks the change.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int truncate_file(const std::string &file_relpath, const size_t newsize)
|
||||
{
|
||||
std::string full_path = current_ctx.datadir + file_relpath;
|
||||
int fd = open(full_path.c_str(), O_WRONLY | O_CREAT, FILE_PERMS);
|
||||
if (fd == -1)
|
||||
{
|
||||
LOG_ERR << errno << "Open failed " << full_path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = ftruncate(fd, newsize);
|
||||
close(fd);
|
||||
if (ret == -1)
|
||||
{
|
||||
LOG_ERR << errno << "Truncate failed " << full_path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified block to a file and marks the change.
|
||||
* @param file_relpath State data relative path of the file.
|
||||
* @param block_id Block id to replace/write.
|
||||
* @param buf The buffer containing data to be written.
|
||||
* @param len Length of the buffer.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int write_block(const std::string &file_relpath, const uint32_t block_id, const void *buf, const size_t len)
|
||||
{
|
||||
std::string full_path = current_ctx.datadir + file_relpath;
|
||||
int fd = open(full_path.c_str(), O_WRONLY | O_CREAT, FILE_PERMS);
|
||||
if (fd == -1)
|
||||
{
|
||||
LOG_ERR << errno << "Open failed " << full_path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const off_t offset = block_id * BLOCK_SIZE;
|
||||
int ret = pwrite(fd, buf, len, offset);
|
||||
close(fd);
|
||||
if (ret == -1)
|
||||
{
|
||||
LOG_ERR << errno << "Write failed " << full_path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hasher::B2H hash = hasher::hash(&offset, 8, buf, len);
|
||||
touched_files[file_relpath].emplace(block_id, hash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the latest hash tree with any changes recorded in touched files index.
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int compute_hash_tree(hasher::B2H &statehash, const bool force_all)
|
||||
{
|
||||
hashtree_builder htree_builder(current_ctx);
|
||||
|
||||
int ret = force_all ? htree_builder.generate(statehash, true) : htree_builder.generate(statehash, touched_files);
|
||||
|
||||
touched_files.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
//-----Private helper functions---------//
|
||||
|
||||
/**
|
||||
* Reads bytes from file into a buffer.
|
||||
* @param buf Buffer to fill with the read bytes.
|
||||
* @param filepath Full path to the file.
|
||||
* @param start Starting offset to read.
|
||||
* @param len Number of bytes to read.
|
||||
* @return Number of bytes read on successful read. -1 on failure.
|
||||
*/
|
||||
int read_file_bytes(void *buf, const char *filepath, const off_t start, const size_t len)
|
||||
{
|
||||
int fd = open(filepath, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
LOG_ERR << errno << "Open failed " << filepath;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read_bytes = pread(fd, buf, len, start);
|
||||
close(fd);
|
||||
if (read_bytes <= 0)
|
||||
{
|
||||
LOG_ERR << errno << "Read failed " << filepath;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads bytes from file into a vector. The vector size will be adjusted to the actual bytes read.
|
||||
* @param vec Vector to fill with the read bytes.
|
||||
* @param filepath Full path to the file.
|
||||
* @param start Starting offset to read.
|
||||
* @return Number of bytes read on successful read. -1 on failure.
|
||||
*/
|
||||
int read_file_bytes_to_end(std::vector<uint8_t> &vec, const char *filepath, const off_t start)
|
||||
{
|
||||
int fd = open(filepath, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
LOG_ERR << errno << "Open failed " << filepath;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const off_t total_len = lseek(fd, 0, SEEK_END);
|
||||
if (total_len == -1)
|
||||
return -1;
|
||||
|
||||
const size_t len = total_len - start;
|
||||
vec.resize(len);
|
||||
|
||||
int read_bytes = pread(fd, vec.data(), len, start);
|
||||
close(fd);
|
||||
if (read_bytes <= 0)
|
||||
{
|
||||
LOG_ERR << errno << "Read failed " << filepath;
|
||||
return -1;
|
||||
}
|
||||
vec.resize(read_bytes);
|
||||
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
} // namespace statefs
|
||||
35
src/statefs/state_store.hpp
Normal file
35
src/statefs/state_store.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef _HP_STATEFS_STATE_STORE_
|
||||
#define _HP_STATEFS_STATE_STORE_
|
||||
|
||||
#include "../pchheader.hpp"
|
||||
#include "../p2p/p2p.hpp"
|
||||
#include "hasher.hpp"
|
||||
|
||||
namespace statefs
|
||||
{
|
||||
|
||||
// Map of modified/deleted files with updated blockids and hashes (if modified).
|
||||
extern std::unordered_map<std::string, std::map<uint32_t, hasher::B2H>> touched_files;
|
||||
|
||||
bool is_dir_exists(const std::string &dir_relpath);
|
||||
int get_fs_entry_hashes(std::unordered_map<std::string, p2p::state_fs_hash_entry> &fs_entries, const std::string &dir_relpath, const hasher::B2H expected_hash);
|
||||
int get_block_hash_map(std::vector<uint8_t> &vec, const std::string &file_relpath, const hasher::B2H expected_hash);
|
||||
int get_file_length(const std::string &file_relpath);
|
||||
int get_block(std::vector<uint8_t> &vec, const std::string &file_relpath, const uint32_t block_id, const hasher::B2H expected_hash);
|
||||
void create_dir(const std::string &dir_relpath);
|
||||
int delete_dir(const std::string &dir_relpath);
|
||||
int delete_file(const std::string &file_relpath);
|
||||
int truncate_file(const std::string &file_relpath, const size_t newsize);
|
||||
int write_block(const std::string &file_relpath, const uint32_t block_id, const void *buf, const size_t len);
|
||||
int compute_hash_tree(hasher::B2H &statehash, const bool force_all = false);
|
||||
|
||||
/**
|
||||
* Private helper functions.
|
||||
*/
|
||||
|
||||
int read_file_bytes(void *buf, const char *filepath, const off_t start, const size_t len);
|
||||
int read_file_bytes_to_end(std::vector<uint8_t> &vec, const char *filepath, const off_t start);
|
||||
|
||||
} // namespace statefs
|
||||
|
||||
#endif
|
||||
@@ -27,6 +27,8 @@ constexpr uint8_t MIN_PEERMSG_VERSION = 1;
|
||||
// (Keeping this as int for effcient msg payload and comparison)
|
||||
constexpr uint8_t MIN_NPL_INPUT_VERSION = 1;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* FIFO hash set with a max size.
|
||||
*/
|
||||
|
||||
@@ -10,14 +10,15 @@ else
|
||||
sudo apt-get install -y nodejs
|
||||
fi
|
||||
|
||||
if [ -x "$(command -v fusermount3)" ]; then
|
||||
echo "FUSE already installed."
|
||||
else
|
||||
# if [ -x "$(command -v fusermount3)" ]; then
|
||||
# echo "FUSE already installed."
|
||||
# else
|
||||
echo "Installing FUSE..."
|
||||
sudo cp ./libfuse3.so.3 /usr/local/lib/
|
||||
sudo ldconfig
|
||||
sudo cp ./fusermount3 /usr/local/bin/
|
||||
fi
|
||||
# fi
|
||||
|
||||
|
||||
sudo rm -r ~/contract > /dev/null 2>&1
|
||||
./hpcore new ./contract
|
||||
@@ -26,3 +27,18 @@ openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout tlskey.pem -ou
|
||||
-subj "/C=AU/ST=ST/L=L/O=O/OU=OU/CN=localhost/emailAddress=hp@example" > /dev/null 2>&1
|
||||
popd > /dev/null 2>&1
|
||||
|
||||
sudo mkdir -p ./contract/statehist/0
|
||||
sudo mkdir -p ./contract/statehist/0/data
|
||||
|
||||
FILE=fuse-3.8.0.tar.xz
|
||||
FILE2=fuse-3.8.0
|
||||
if [ -f "$FILE" ]; then
|
||||
if [ -f "$FILE2" ]; then
|
||||
sudo cp -r ./fuse-3.8.0 ~/contract/statehist/0/data
|
||||
else
|
||||
sudo tar -xf fuse-3.8.0.tar.xz -C ~/contract/statehist/0/data
|
||||
fi
|
||||
else
|
||||
sudo wget https://github.com/libfuse/libfuse/releases/download/fuse-3.8.0/fuse-3.8.0.tar.xz -
|
||||
sudo tar -xf fuse-3.8.0.tar.xz -C ~/contract/statehist/0/data
|
||||
fi
|
||||
|
||||
@@ -13,7 +13,7 @@ if [ $mode = "new" ]; then
|
||||
sshpass -p $vmpass scp $hpcore/build/hpcore \
|
||||
$hpcore/build/hpstatemon \
|
||||
$hpcore/examples/echocontract/contract.js \
|
||||
/usr/local/lib/libfuse3.so.3 \
|
||||
/usr/local/lib/x86_64-linux-gnu/libfuse3.so.3 \
|
||||
/usr/local/bin/fusermount3 \
|
||||
./setup-hp.sh \
|
||||
geveo@$vmip:~/
|
||||
|
||||
Reference in New Issue
Block a user