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:
Ravidu Lashan
2019-12-13 10:20:41 +05:30
committed by GitHub
parent b506b34b4f
commit 1238e96423
48 changed files with 2534 additions and 448 deletions

View File

@@ -1,4 +1,5 @@
**/**
!build/hpcore
!build/hpstatemon
!libfuse3.so.3
!libfuse3.so.3
!fusermount3

View File

@@ -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)

View File

@@ -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

View File

@@ -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

Binary file not shown.

12
reset.sh Executable file
View 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

View File

@@ -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.

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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
View 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

View 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

View File

@@ -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.
*/

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -6,6 +6,7 @@ table Ledger {
seq_no:uint64;
time:uint64;
lcl:[ubyte];
state:[ubyte];
users: [ByteArray];
inputs: [ByteArray];
outputs: [ByteArray];

View File

@@ -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__);

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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()));
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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];

View File

@@ -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);
};

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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]);
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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();

View File

@@ -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
View 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

View 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

View File

@@ -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.
*/

View File

@@ -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

View File

@@ -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:~/