Refactored user I/O with signed inputs and NUPs. (#53)

This commit is contained in:
Ravin Perera
2019-11-03 22:45:23 +05:30
committed by GitHub
parent cb364cc420
commit 83189556de
27 changed files with 958 additions and 596 deletions

View File

@@ -12,6 +12,7 @@ A C++ version of hotpocket designed for production envrionments, original protot
* Websockets - Boost|Beast https://github.com/boostorg/beast
* RapidJSON - http://rapidjson.org
* P2P Protocol - https://google.github.io/flatbuffers/
* TLS - https://www.openssl.org/
## Steps to setup Hot Pocket
@@ -26,9 +27,8 @@ Instructions are based on [this](https://libsodium.gitbook.io/doc/installation).
1. Download and extract Libsodium 1.0.18 from [here](https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz).
2. Navigate to the extracted libsodium directory in a terminal.
3. Run `./configure`
4. Run `make && make check`
5. Run `sudo make install`
3. Run `./configure && make && make check`
4. Run `sudo make install`
#### Install Boost
Following Instructions are based on Boost [getting started](https://www.boost.org/doc/libs/1_71_0/more/getting_started/unix-variants.html#prepare-to-use-a-boost-library-binary)
@@ -58,21 +58,20 @@ make
4. Run `sudo snap install flatbuffers --edge`
##### Compiling FlatBuffers message definitions
When you make a change to `message.fbc` defnition file, you need to run this:
Example: When you make a change to `p2pmsg_content_.fbc` defnition file, you need to run this:
`flatc -o src/p2p/ --gen-mutable --cpp src/p2p/message.fbs`
`flatc -o src/fbschema/ --gen-mutable --cpp src/fbschema/p2pmsg_content.fbs`
#### Install OpenSSL
1. Download and extract OpenSSL-1.1.1d source from [here](https://www.openssl.org/source/openssl-1.1.1d.tar.gz).
2. Navigate to the extracted directory.
3. Run `./config`
4. Run `make`
5. Run `make install`
3. Run `./config && make`
4. Run `sudo make install`
#### Run ldconfig
`sudo ldconfig`
This will update your library cache and avoid potential issues when running your compiled C++ program which links to newly installed libraries.
This will update your linker library cache and avoid potential issues when running your compiled C++ program which links to newly installed libraries.
#### Build and run Hot Pocket
1. Navigate to hotpocket repo root.

View File

@@ -41,7 +41,7 @@ function main() {
/* anatomy of a public challenge
{
hotpocket: 0.1,
version: '0.1',
type: 'public_challenge',
challenge: '<hex string>'
}
@@ -76,13 +76,14 @@ function main() {
// sign the challenge and send back the response
var sigbytes = sodium.crypto_sign_detached(m.challenge, keys.privateKey);
var response = {
version: '0.1',
type: 'challenge_response',
challenge: m.challenge,
sig: Buffer.from(sigbytes).toString('hex'),
pubkey: pkhex
}
console.log('Sending challenge response...');
console.log('Sending challenge response.');
ws.send(JSON.stringify(response))
// start listening for stdin
@@ -91,9 +92,29 @@ function main() {
output: process.stdout
});
// Capture user input from the console.
var input_pump = () => {
rl.question('', (answer) => {
ws.send(answer)
rl.question('\nProvide an input: ', (inp) => {
let inp_container = {
nonce: (new Date()).getTime().toString(),
input: Buffer.from(inp).toString('hex'),
maxledgerseqno: 9999999
}
let inp_container_bytes = JSON.stringify(inp_container);
let sig_bytes = sodium.crypto_sign_detached(inp_container_bytes, keys.privateKey);
let signed_inp_container = {
version: "0.1",
type: "contract_input",
content: inp_container_bytes.toString('hex'),
sig: Buffer.from(sig_bytes).toString('hex')
}
let msgtosend = JSON.stringify(signed_inp_container);
console.log("Sending message: " + msgtosend);
ws.send(msgtosend)
input_pump()
})
}

View File

@@ -48,7 +48,7 @@ function main() {
/* anatomy of a public challenge
{
hotpocket: 0.1,
version: '0.1',
type: 'public_challenge',
challenge: '<hex string>'
}
@@ -83,13 +83,14 @@ function main() {
// sign the challenge and send back the response
var sigbytes = sodium.crypto_sign_detached(m.challenge, keys.privateKey);
var response = {
version: '0.1',
type: 'challenge_response',
challenge: m.challenge,
sig: Buffer.from(sigbytes).toString('hex'),
pubkey: pkhex
}
console.log('Sending challenge response...');
console.log('Sending challenge response.');
ws.send(JSON.stringify(response));
setInterval(() => {

View File

@@ -2,8 +2,10 @@
#include "../pchheader.hpp"
#include "../conf.hpp"
#include "../usr/usr.hpp"
#include "../usr/user_input.hpp"
#include "../p2p/p2p.hpp"
#include "../fbschema/p2pmsg_helpers.hpp"
#include "../jsonschema/usrmsg_helpers.hpp"
#include "../p2p/peer_session_handler.hpp"
#include "../hplog.hpp"
#include "../crypto.hpp"
@@ -12,33 +14,19 @@
#include "cons.hpp"
namespace p2pmsg = fbschema::p2pmsg;
namespace jusrmsg = jsonschema::usrmsg;
namespace cons
{
consensus_context ctx;
/**
* Increment voting table counter.
*
* @param counter The counter map in which a vote should be incremented.
* @param candidate The candidate whose vote should be increased by 1.
*/
template <typename T>
void increment(std::map<T, int32_t> &counter, const T &candidate)
{
if (counter.count(candidate))
counter[candidate]++;
else
counter.try_emplace(candidate, 1);
}
int init()
{
//set start stage
ctx.stage = 0;
//load lcl detals from lcl history.
//load lcl details from lcl history.
const ledger_history ldr_hist = load_ledger();
ctx.led_seq_no = ldr_hist.led_seq_no;
ctx.lcl = ldr_hist.lcl;
@@ -56,8 +44,8 @@ void consensus()
ctx.time_now = util::get_epoch_milliseconds();
// Throughout consensus, we move over the incoming proposals collected via the network so far into
// the candidate proposal set (move and append). This is to have a private working set for the consensus and avoid
// threading conflicts with network incoming proposals.
// the candidate proposal set (move and append). This is to have a private working set for the consensus
// and avoid threading conflicts with network incoming proposals.
{
std::lock_guard<std::mutex> lock(p2p::collected_msgs.proposals_mutex);
ctx.candidate_proposals.splice(ctx.candidate_proposals.end(), p2p::collected_msgs.proposals);
@@ -69,9 +57,7 @@ void consensus()
bool self = p.pubkey == conf::cfg.pubkey;
std::cout << "[stage" << std::to_string(p.stage)
<< "] users:" << p.users.size()
<< " rinp:" << p.raw_inputs.size()
<< " hinp:" << p.hash_inputs.size()
<< " rout:" << p.raw_outputs.size()
<< " hout:" << p.hash_outputs.size()
<< " lcl:" << p.lcl
<< " self:" << self
@@ -99,17 +85,16 @@ void consensus()
}
}
// Transfer connected user data onto consensus candidate data.
populate_candidate_users_and_inputs();
// Broadcast non-unl proposals (NUP) containing inputs from locally connected users.
broadcast_nonunl_proposal();
util::sleep(conf::cfg.roundtime / 10);
// Verify and transfer user inputs from incoming NUPs onto consensus candidate data.
verify_and_populate_candidate_user_inputs();
// In stage 0 we create a novel proposal and broadcast it.
const p2p::proposal stg_prop = create_stage0_proposal();
if (broadcast_proposal(stg_prop) != 0)
{
// No peers to broadcast stage0 proposal (not even self). So we wait and try stage 0 again.
timewait_stage(true);
return;
}
broadcast_proposal(stg_prop);
}
else // Stage 1, 2, 3
{
@@ -173,28 +158,82 @@ void consensus()
// after a stage 0 novel proposal we will just busy wait for proposals
if (ctx.stage == 0)
std::this_thread::sleep_for(std::chrono::milliseconds(conf::cfg.roundtime / 100));
util::sleep(conf::cfg.roundtime / 100);
else
std::this_thread::sleep_for(std::chrono::milliseconds(conf::cfg.roundtime / 4));
util::sleep(conf::cfg.roundtime / 4);
}
/**
* Populate connected users and their inputs (if any) into consensus candidate data.
* Broadcasts any inputs from locally connected users via an NUP.
* @return 0 for successful broadcast. -1 for failure.
*/
void populate_candidate_users_and_inputs()
void broadcast_nonunl_proposal()
{
// Lock the connected user list until we do this operation.
std::lock_guard<std::mutex> lock(usr::ctx.users_mutex);
for (auto &[sid, con_user] : usr::ctx.users)
// Construct NUP.
p2p::nonunl_proposal nup;
std::lock_guard<std::mutex> lock(p2p::collected_msgs.nonunl_proposals_mutex);
for (auto &[sid, user] : usr::ctx.users)
{
// Populate the user into candidate user inputs map.
// We do this regardless of whether the user has any inputs or not.
std::list<usr::user_submitted_message> usermsgs;
usermsgs.splice(usermsgs.end(), user.submitted_inputs);
std::list<util::hash_buffer> &inplist = ctx.candidate_users[con_user.pubkey];
// Transfer the connected user's inputs (if any) to the candidate user's inputs list.
inplist.splice(inplist.end(), con_user.inputs);
// We should create an entry for each user pubkey, even if the user has no inputs. This is
// because this data map will be used to track connected users as well in addition to inputs.
nup.user_messages.try_emplace(user.pubkey, std::move(usermsgs));
}
p2p::peer_outbound_message msg(std::make_shared<flatbuffers::FlatBufferBuilder>(1024));
p2pmsg::create_msg_from_nonunl_proposal(msg.builder(), nup);
p2p::broadcast_message(msg);
LOG_DBG << "NUP sent." << " users:" << nup.user_messages.size();
}
/**
* Verifies the user signatures and populate non-expired user inputs from collected
* non-unl proposals (if any) into consensus candidate data.
*/
void verify_and_populate_candidate_user_inputs()
{
// Lock the list so any network activity is blocked.
std::lock_guard<std::mutex> lock(p2p::collected_msgs.nonunl_proposals_mutex);
for (const p2p::nonunl_proposal &p : p2p::collected_msgs.nonunl_proposals)
{
for (const auto &[pubkey, umsgs] : p.user_messages)
{
// Populate user list.
ctx.candidate_users.emplace(pubkey);
for (const usr::user_submitted_message &umsg : umsgs)
{
// Verify the signature of the message content.
if (crypto::verify(umsg.content, umsg.sig, pubkey) == 0)
{
// TODO: Also verify XRP payment token.
std::string nonce;
std::string input;
uint64_t maxledgerseqno;
jusrmsg::extract_input_container(nonce, input, maxledgerseqno, umsg.content);
// Ignore the input if our ledger has passed the input TTL.
if (maxledgerseqno > ctx.led_seq_no)
{
// Hash is prefixed with the nonce to support user-defined sort order.
std::string hash = std::move(nonce);
// Append the hash of the message signature to get the final hash.
hash.append(crypto::get_hash(umsg.sig));
ctx.candidate_user_inputs.try_emplace(
hash,
candidate_user_input(pubkey, std::move(input), maxledgerseqno));
}
}
}
}
}
p2p::collected_msgs.nonunl_proposals.clear();
}
p2p::proposal create_stage0_proposal()
@@ -206,39 +245,23 @@ p2p::proposal create_stage0_proposal()
stg_prop.stage = 0;
stg_prop.lcl = ctx.lcl;
// Populate the poposal with users list (user pubkey list) and their inputs.
for (auto [pubkey, inputs] : ctx.candidate_users)
{
// Add all the user connections we host.
// Populate the proposal with set of candidate user pubkeys.
for (const std::string &pubkey : ctx.candidate_users)
stg_prop.users.emplace(pubkey);
// Add all their pending inputs.
if (!inputs.empty())
{
std::vector<util::hash_buffer> inpvec;
for (util::hash_buffer &hashbuf : inputs)
inpvec.push_back(hashbuf); // Copy all hashbufs from candidate inputs into the proposal.
// We don't need candidate_users anymore, so clear it. It will be repopulated during next censensus round.
ctx.candidate_users.clear();
stg_prop.raw_inputs.emplace(pubkey, std::move(inpvec));
}
}
// Populate the proposal with hashes of user inputs.
for (const auto &[hash, cand_input] : ctx.candidate_user_inputs)
stg_prop.hash_inputs.emplace(hash);
// Populate the stg_prop with any contract outputs from previous round's stage 3.
for (auto &[pubkey, bufpair] : ctx.useriobufmap)
{
if (!bufpair.output.empty())
{
std::string rawoutput;
rawoutput.swap(bufpair.output);
stg_prop.raw_outputs.try_emplace(pubkey, util::hash_buffer(rawoutput, pubkey));
}
}
ctx.useriobufmap.clear();
// Populate the proposal with hashes of user outputs.
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 stg_prop again.
// todo: generate stg_prop hash and check with ctx.novel_proposal, we are sending same proposal again.
return stg_prop;
}
@@ -254,69 +277,27 @@ p2p::proposal create_stage123_proposal(vote_counter &votes)
// our peers or we will halt depending on level of consensus on the sides of the fork
stg_prop.lcl = ctx.lcl;
//todo:check lcl votes and wait for proposals
// Vote for rest of the proposal fields by looking at candidate proposals.
for (const p2p::proposal &cp : ctx.candidate_proposals)
{
// Vote for times.
// Everyone votes on an arbitrary time, as long as its within the round time and not in the future
// Everyone votes on an arbitrary time, as long as its within the round time and not in the future.
if (ctx.time_now > cp.time && (ctx.time_now - cp.time) < conf::cfg.roundtime)
increment(votes.time, cp.time);
// Vote for user connections
for (const std::string &user : cp.users)
increment(votes.users, user);
// Vote for user pubkeys.
for (const std::string &pubkey : cp.users)
increment(votes.users, pubkey);
// Vote for user inputs
// Vote for user inputs (hashes). Only vote for the inputs that are in our candidate_inputs set.
for (const std::string &hash : cp.hash_inputs)
if (ctx.candidate_user_inputs.count(hash) > 0)
increment(votes.inputs, hash);
// Proposals from stage 0 will have raw inputs (and their hashes) in them.
if (!cp.raw_inputs.empty())
{
for (auto &[pubkey, inputs] : cp.raw_inputs)
{
// Vote for the input hash.
for (util::hash_buffer input : inputs)
{
increment(votes.inputs, input.hash);
std::string inputbuffer;
inputbuffer.swap(input.buffer);
// Remember the actual input along with the hash for future use for apply-ledger.
ctx.possible_inputs.try_emplace(input.hash, std::make_pair(pubkey, inputbuffer));
}
}
}
// Proposals from stage 1, 2, 3 will have only input hashes in them.
else if (!cp.hash_inputs.empty())
{
for (const std::string &inputhash : cp.hash_inputs)
increment(votes.inputs, inputhash);
}
// Vote for contract outputs
// Proposals from stage 0 will have raw user outputs in them.
if (!cp.raw_outputs.empty())
{
for (auto [pubkey, output] : cp.raw_outputs)
{
// Vote for the hash.
increment<std::string>(votes.outputs, output.hash);
std::string outputbuf;
outputbuf.swap(output.buffer);
// Remember the actual output along with the hash for future use for apply-ledger and sending back to user.
ctx.possible_outputs.try_emplace(output.hash, std::make_pair(pubkey, outputbuf));
}
}
// Proposals from stage 1, 2, 3 will have hashed user outputs in them.
else if (!cp.hash_outputs.empty())
{
for (auto outputhash : cp.hash_outputs)
increment<std::string>(votes.outputs, outputhash);
}
// Vote for contract outputs (hashes). Only vote for the outputs that are in our candidate_outputs set.
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
}
@@ -328,14 +309,14 @@ p2p::proposal create_stage123_proposal(vote_counter &votes)
// if we're at proposal stage 1 we'll accept any input and connection that has 1 or more vote.
// Add user connections which have votes over stage threshold to proposal.
for (auto &[userpubkey, numvotes] : votes.users)
if (numvotes >= vote_threshold || (numvotes > 0 && ctx.stage == 1))
stg_prop.users.emplace(userpubkey);
// Add user pubkeys which have votes over stage threshold to proposal.
for (auto &[pubkey, numvotes] : votes.users)
if (numvotes >= vote_threshold || (ctx.stage == 1 && numvotes > 0))
stg_prop.users.emplace(pubkey);
// Add inputs which have votes over stage threshold to proposal.
for (auto &[hash, numvotes] : votes.inputs)
if (numvotes >= vote_threshold || (numvotes > 0 && ctx.stage == 1))
if (numvotes >= vote_threshold || (ctx.stage == 1 && numvotes > 0))
stg_prop.hash_inputs.emplace(hash);
// Add outputs which have votes over stage threshold to proposal.
@@ -363,33 +344,16 @@ p2p::proposal create_stage123_proposal(vote_counter &votes)
* Broadcasts the given proposal to all connected peers.
* @return 0 on success. -1 if no peers to broadcast.
*/
int broadcast_proposal(const p2p::proposal &p)
void broadcast_proposal(const p2p::proposal &p)
{
p2p::peer_outbound_message msg(std::make_shared<flatbuffers::FlatBufferBuilder>(1024));
p2pmsg::create_msg_from_proposal(msg.builder(), p);
{
//Broadcast while locking the peer_connections.
std::lock_guard<std::mutex> lock(p2p::peer_connections_mutex);
if (p2p::peer_connections.size() == 0)
{
LOG_DBG << "No peers to broadcast";
return -1;
}
for (auto &[k, session] : p2p::peer_connections)
session->send(msg);
}
p2p::broadcast_message(msg);
LOG_DBG << "Proposed [stage" << std::to_string(p.stage)
<< "] users:" << p.users.size()
<< " rinp:" << p.raw_inputs.size()
<< " hinp:" << p.hash_inputs.size()
<< " rout:" << p.raw_outputs.size()
<< " hout:" << p.hash_outputs.size();
return 0;
}
/**
@@ -520,7 +484,7 @@ void timewait_stage(bool reset)
if (reset)
ctx.stage = 0;
std::this_thread::sleep_for(std::chrono::milliseconds(conf::cfg.roundtime / 100));
util::sleep(conf::cfg.roundtime / 100);
}
/**
@@ -529,112 +493,167 @@ void timewait_stage(bool reset)
*/
void apply_ledger(const p2p::proposal &cons_prop)
{
// todo:write lcl.
ctx.led_seq_no++;
ctx.lcl = cons::save_ledger(cons_prop, ctx.led_seq_no);
// Send any output from the previous consensus round to users.
// After the current ledger seq no is updated, we remove any newly expired inputs from candidate set.
{
auto itr = ctx.candidate_user_inputs.begin();
while (itr != ctx.candidate_user_inputs.end())
{
if (itr->second.maxledgerseqno <= ctx.led_seq_no)
ctx.candidate_user_inputs.erase(itr++);
else
++itr;
}
}
// 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.)
proc::contract_bufmap_t useriobufmap;
feed_inputs_to_contract_bufmap(useriobufmap, cons_prop);
run_contract_binary(cons_prop.time, useriobufmap);
extract_outputs_from_contract_bufmap(useriobufmap);
}
/**
* Dispatch any consensus-reached outputs to matching users if they are connected to us locally.
* @param cons_prop The proposal that achieved consensus.
*/
void dispatch_user_outputs(const p2p::proposal &cons_prop)
{
std::lock_guard<std::mutex> lock(usr::ctx.users_mutex);
for (const std::string &hash : cons_prop.hash_outputs)
{
auto itr = ctx.possible_outputs.find(hash);
bool hashfound = (itr != ctx.possible_outputs.end());
auto cu_itr = ctx.candidate_user_outputs.find(hash);
bool hashfound = (cu_itr != ctx.candidate_user_outputs.end());
if (!hashfound)
{
// There's no possiblity for this to happen.
LOG_ERR << "Output required but wasn't in our possible output dict, this will potentially cause desync.";
LOG_ERR << "Output required but wasn't in our candidate outputs map, this will potentially cause desync.";
// todo: consider fatal
}
else
{
// Send outputs to users.
auto &[pubkey, output] = itr->second;
std::string outputtosend;
outputtosend.swap(output);
// Send matching outputs to locally connected users.
candidate_user_output &cand_output = cu_itr->second;
// Find the user session by user pubkey.
auto sess_itr = usr::ctx.sessionids.find(cand_output.userpubkey);
if (sess_itr != usr::ctx.sessionids.end()) // match found
{
std::lock_guard<std::mutex> lock(usr::ctx.users_mutex);
// Find the user by session id.
const std::string sessionid = usr::ctx.sessionids[pubkey];
auto itr = usr::ctx.users.find(sessionid);
if (itr != usr::ctx.users.end())
auto user_itr = usr::ctx.users.find(sess_itr->second); // sess_itr->second is the session id.
if (user_itr != usr::ctx.users.end()) // match found
{
const usr::connected_user &user = itr->second;
std::string outputtosend;
outputtosend.swap(cand_output.output);
usr::user_outbound_message outmsg(std::move(outputtosend));
const usr::connected_user &user = user_itr->second;
user.session->send(std::move(outmsg));
}
}
}
}
// now we can safely clear our candidate outputs.
ctx.candidate_user_outputs.clear();
}
// now we can safely clear our outputs.
ctx.possible_outputs.empty();
//todo:check state against the winning / canonical state
//and act accordingly (rollback, ask state from peer, etc.)
//create input to feed to binary contract run
/**
* 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.
* @param cons_prop The proposal that achieved consensus.
*/
void feed_inputs_to_contract_bufmap(proc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop)
{
// Populate the buf map with all currently connected users regardless of whether they have inputs or not.
// This is in case the contract wanted to emit some data to a user without needing any input.
for (const std::string &pubkey : cons_prop.users)
bufmap.try_emplace(pubkey, proc::contract_iobuf_pair());
for (const std::string &hash : cons_prop.hash_inputs)
{
auto itr = ctx.possible_inputs.find(hash);
bool hashfound = (itr != ctx.possible_inputs.end());
// For each consensus input hash, we need to find the actual input content to feed the contract.
auto itr = ctx.candidate_user_inputs.find(hash);
bool hashfound = (itr != ctx.candidate_user_inputs.end());
if (!hashfound)
{
// There's no possiblity for this to happen.
LOG_ERR << "input required but wasn't in our possible input dict, this will potentially cause desync";
// todo: consider fatal
LOG_ERR << "input required but wasn't in our candidate inputs map, this will potentially cause desync.";
// TODO: consider fatal
}
else
{
// Prepare ctx.useriobufmap with user inputs to feed to the contract.
// Populate the input content into the bufmap.
const std::string &pubkey = itr->second.first;
std::string rawinput = itr->second.second;
candidate_user_input &cand_input = itr->second;
std::string inputtofeed;
inputtofeed.swap(rawinput);
inputtofeed.swap(cand_input.input);
proc::contract_iobuf_pair &bufpair = ctx.useriobufmap[pubkey];
proc::contract_iobuf_pair &bufpair = bufmap[cand_input.userpubkey];
bufpair.inputs.push_back(std::move(inputtofeed));
// Remove the input from the candidate set because we no longer need it.
ctx.candidate_user_inputs.erase(itr);
}
}
ctx.possible_inputs.clear();
run_contract_binary(cons_prop.time);
// Remove entries from candidate inputs that made their way into a closed ledger
auto cu_itr = ctx.candidate_users.begin();
while (cu_itr != ctx.candidate_users.end())
{
// Delete any ledger inputs for this user.
std::list<util::hash_buffer> &inputs = cu_itr->second;
auto inp_itr = inputs.begin();
while (inp_itr != inputs.end())
{
// Delete the input from the list, if it was part of consensus proposal.
if (cons_prop.hash_inputs.count(inp_itr->hash))
inputs.erase(inp_itr++);
else
++inp_itr;
}
// Delete the user from the list if there are no more unprocessed inputs.
if (cu_itr->second.empty())
ctx.candidate_users.erase(cu_itr++);
else
++cu_itr;
}
}
void run_contract_binary(int64_t time_now)
/**
* Reads any outputs the contract has produced on the provided buf map and transfers them to candidate outputs
* for the next consensus round.
* @param bufmap The contract bufmap containing the outputs produced by the contract.
*/
void extract_outputs_from_contract_bufmap(proc::contract_bufmap_t &bufmap)
{
// todo:implement proper data structures to exchange npl and hpsc bufs
proc::contract_bufmap_t nplbufs;
for (auto &[pubkey, bufpair] : bufmap)
{
if (!bufpair.output.empty())
{
std::string output;
output.swap(bufpair.output);
std::string hash = crypto::get_hash(pubkey, output);
ctx.candidate_user_outputs.try_emplace(
std::move(hash),
candidate_user_output(pubkey, std::move(output)));
}
}
}
/**
* Executes the smart contract with the specified time and provided I/O buf maps.
* @param time_now The time that must be passed on to the contract.
* @param useriobufmap The contract bufmap which holds user I/O buffers.
*/
void run_contract_binary(int64_t time_now, proc::contract_bufmap_t &useriobufmap)
{
// todo:implement exchange of npl and hpsc bufs
proc::contract_bufmap_t nplbufmap;
proc::contract_iobuf_pair hpscbufpair;
proc::ContractExecArgs eargs(time_now, ctx.useriobufmap, nplbufs, hpscbufpair);
proc::contract_exec_args eargs(time_now, useriobufmap, nplbufmap, hpscbufpair);
proc::exec_contract(eargs);
}
/**
* Increment voting table counter.
* @param counter The counter map in which a vote should be incremented.
* @param candidate The candidate whose vote should be increased by 1.
*/
template <typename T>
void increment(std::map<T, int32_t> &counter, const T &candidate)
{
if (counter.count(candidate))
counter[candidate]++;
else
counter.try_emplace(candidate, 1);
}
} // namespace cons

View File

@@ -4,6 +4,7 @@
#include "../pchheader.hpp"
#include "../proc.hpp"
#include "../p2p/p2p.hpp"
#include "../usr/user_input.hpp"
namespace cons
{
@@ -15,13 +16,58 @@ static const float STAGE2_THRESHOLD = 0.65;
//stage 3 vote threshold
static const float STAGE3_THRESHOLD = 0.8;
/**
* Represents a contract input that takes part in consensus.
*/
struct candidate_user_input
{
std::string userpubkey;
std::string input;
uint64_t maxledgerseqno;
candidate_user_input(std::string userpubkey, std::string input, uint64_t maxledgerseqno)
{
this->userpubkey = std::move(userpubkey);
this->input = std::move(input);
this->maxledgerseqno = maxledgerseqno;
}
};
/**
* Represents a contract output that takes part in consensus.
*/
struct candidate_user_output
{
std::string userpubkey;
std::string output;
candidate_user_output(std::string userpubkey, std::string output)
{
this->userpubkey = std::move(userpubkey);
this->output = std::move(output);
}
};
/**
* This is used to store consensus information
*/
struct consensus_context
{
// The set of proposals that are being collected as consensus stages are progressing.
std::list<p2p::proposal> candidate_proposals;
std::unordered_map<std::string, std::list<util::hash_buffer>> candidate_users;
// Set of user pubkeys that is said to be connected to the cluster. This will be cleared in each round.
std::unordered_set<std::string> candidate_users;
// Map of candidate user inputs with input hash as map key. Inputs will stay here until they
// achieve consensus or expire (due to maxledgerseqno). Input hash is globally unique among inputs
// from all users. We will use this map to feed inputs into the contract once consensus is achieved.
std::unordered_map<std::string, candidate_user_input> candidate_user_inputs;
// Map of outputs generated by the contract with output hash is the map key. Outputs will stay
// here until the end of the current consensus round. Output hash is globally unique among outputs for
// all users. We will use this map to distribute outputs back to connected users once consensus is achieved.
std::unordered_map<std::string, candidate_user_output> candidate_user_outputs;
uint8_t stage;
uint64_t novel_proposal_time;
@@ -30,11 +76,6 @@ struct consensus_context
uint64_t led_seq_no;
std::string novel_proposal;
std::map<std::string, std::pair<const std::string, std::string>> possible_inputs;
std::map<std::string, std::pair<const std::string, std::string>> possible_outputs;
std::unordered_map<std::string, proc::contract_iobuf_pair> useriobufmap;
int32_t next_sleep;
};
@@ -54,25 +95,36 @@ int init();
void consensus();
void apply_ledger(const p2p::proposal &proposal);
void broadcast_nonunl_proposal();
float_t get_stage_threshold(uint8_t stage);
void timewait_stage(bool reset);
void populate_candidate_users_and_inputs();
void verify_and_populate_candidate_user_inputs();
p2p::proposal create_stage0_proposal();
p2p::proposal create_stage123_proposal(vote_counter &votes);
int broadcast_proposal(const p2p::proposal &p);
void broadcast_proposal(const p2p::proposal &p);
void check_majority_stage(bool &is_desync, bool &should_reset, uint8_t &majority_stage, vote_counter &votes);
void check_lcl_votes(bool &is_desync, bool &should_request_history, std::string &majority_lcl, vote_counter &votes);
void run_contract_binary(int64_t time);
float_t get_stage_threshold(uint8_t stage);
void timewait_stage(bool reset);
void apply_ledger(const p2p::proposal &proposal);
void dispatch_user_outputs(const p2p::proposal &cons_prop);
void feed_inputs_to_contract_bufmap(proc::contract_bufmap_t &bufmap, const p2p::proposal &cons_prop);
void extract_outputs_from_contract_bufmap(proc::contract_bufmap_t &bufmap);
void run_contract_binary(int64_t time_now, proc::contract_bufmap_t &useriobufmap);
template <typename T>
void increment(std::map<T, int32_t> &counter, const T &candidate);
} // namespace cons

View File

@@ -138,16 +138,41 @@ int verify_hex(std::string_view msg, std::string_view sighex, std::string_view p
*/
std::string get_hash(std::string_view data)
{
unsigned char hashchars[crypto_generichash_BYTES];
std::string hash;
hash.resize(crypto_generichash_blake2b_BYTES);
crypto_generichash_blake2b(
hashchars,
sizeof hashchars,
reinterpret_cast<unsigned char *>(hash.data()),
hash.length(),
reinterpret_cast<const unsigned char *>(data.data()),
data.length(),
NULL, 0);
return std::string(reinterpret_cast<char *>(hashchars), crypto_generichash_blake2b_BYTES);
return hash;
}
/**
* Generates blake2b hash for the given set of strings using stream hashing.
*/
std::string get_hash(std::string_view s1, std::string_view s2)
{
std::string hash;
hash.resize(crypto_generichash_blake2b_BYTES);
// Init stream hashing.
crypto_generichash_blake2b_state state;
crypto_generichash_blake2b_init(&state, NULL, 0, hash.length());
crypto_generichash_blake2b_update(&state, reinterpret_cast<const unsigned char *>(s1.data()), s1.length());
crypto_generichash_blake2b_update(&state, reinterpret_cast<const unsigned char *>(s2.data()), s2.length());
// Get the final hash.
crypto_generichash_blake2b_final(
&state,
reinterpret_cast<unsigned char *>(hash.data()),
hash.length());
return hash;
}
} // namespace crypto

View File

@@ -31,6 +31,8 @@ int verify_hex(std::string_view msg, std::string_view sighex, std::string_view p
std::string get_hash(std::string_view data);
std::string get_hash(std::string_view s1, std::string_view s2);
} // namespace crypto
#endif

View File

@@ -25,10 +25,9 @@ std::string_view flatbuff_bytes_to_sv(const flatbuffers::Vector<uint8_t> *buffer
/**
* Returns set from Flatbuffer vector of ByteArrays.
*/
const std::unordered_set<std::string> flatbuf_bytearrayvector_to_stringlist(const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *fbvec)
const std::set<std::string> flatbuf_bytearrayvector_to_stringlist(const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *fbvec)
{
std::unordered_set<std::string> set;
set.reserve(fbvec->size());
std::set<std::string> set;
for (auto el : *fbvec)
set.emplace(std::string(flatbuff_bytes_to_sv(el->array())));
return set;
@@ -63,7 +62,7 @@ sv_to_flatbuff_bytes(flatbuffers::FlatBufferBuilder &builder, std::string_view s
* Returns Flatbuffer vector of ByteArrays from given set of strings.
*/
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>>
stringlist_to_flatbuf_bytearrayvector(flatbuffers::FlatBufferBuilder &builder, const std::unordered_set<std::string> &set)
stringlist_to_flatbuf_bytearrayvector(flatbuffers::FlatBufferBuilder &builder, const std::set<std::string> &set)
{
std::vector<flatbuffers::Offset<ByteArray>> fbvec;
fbvec.reserve(set.size());

View File

@@ -17,7 +17,7 @@ std::string_view flatbuff_bytes_to_sv(const uint8_t *data, flatbuffers::uoffset_
std::string_view flatbuff_bytes_to_sv(const flatbuffers::Vector<uint8_t> *buffer);
const std::unordered_set<std::string>
const std::set<std::string>
flatbuf_bytearrayvector_to_stringlist(const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *fbvec);
const std::unordered_map<std::string, const std::string>
@@ -29,7 +29,7 @@ const flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
sv_to_flatbuff_bytes(flatbuffers::FlatBufferBuilder &builder, std::string_view sv);
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>>
stringlist_to_flatbuf_bytearrayvector(flatbuffers::FlatBufferBuilder &builder, const std::unordered_set<std::string> &set);
stringlist_to_flatbuf_bytearrayvector(flatbuffers::FlatBufferBuilder &builder, const std::set<std::string> &set);
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>>
stringmap_to_flatbuf_bytepairvector(flatbuffers::FlatBufferBuilder &builder, const std::unordered_map<std::string, const std::string> &map);

View File

@@ -2,30 +2,32 @@
include "common_schema.fbs";
namespace fbschema.p2pmsg;
table RawInputList { //Pubkey bytes with an array of key value pairs.
pubkey:[ubyte];
inputs:[BytesKeyValuePair];
table UserSubmittedMessage {
content:[ubyte];
signature:[ubyte];
}
table RawOutput { //Pubkey bytes with a output key value pair.
table UserSubmittedMessageGroup {
pubkey:[ubyte];
output:BytesKeyValuePair;
messages:[UserSubmittedMessage];
}
union Message { Proposal_Message, Npl_Message } //message content type
union Message { NonUnl_Proposal_Message, Proposal_Message, Npl_Message } //message content type
table Content {
message:Message;
}
table NonUnl_Proposal_Message {
usermessages:[UserSubmittedMessageGroup];
}
table Proposal_Message { //Proposal type message schema
stage:uint8;
time:uint64;
lcl:[ubyte];
users: [ByteArray];
raw_inputs: [RawInputList]; //stage 0 inputs (hash and raw value)
users:[ByteArray];
hash_inputs:[ByteArray]; //stage > 0 inputs (hash of stage 0 inputs)
raw_outputs: [RawOutput]; //stage 0 outputs (hash and raw value)
hash_outputs:[ByteArray]; //stage > 0 outputs (hash of stage 0 outputs)
state: State;
}

View File

@@ -1,8 +1,8 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_P2PMSGCONTENT_FBSCHEMA_P2PMSG_
#define FLATBUFFERS_GENERATED_P2PMSGCONTENT_FBSCHEMA_P2PMSG_
#ifndef FLATBUFFERS_GENERATED_P2PMSGCONTENT_FBSCHEMA_P2PMSG_H_
#define FLATBUFFERS_GENERATED_P2PMSGCONTENT_FBSCHEMA_P2PMSG_H_
#include "flatbuffers/flatbuffers.h"
@@ -11,12 +11,14 @@
namespace fbschema {
namespace p2pmsg {
struct RawInputList;
struct UserSubmittedMessage;
struct RawOutput;
struct UserSubmittedMessageGroup;
struct Content;
struct NonUnl_Proposal_Message;
struct Proposal_Message;
struct Npl_Message;
@@ -27,15 +29,17 @@ struct State;
enum Message {
Message_NONE = 0,
Message_Proposal_Message = 1,
Message_Npl_Message = 2,
Message_NonUnl_Proposal_Message = 1,
Message_Proposal_Message = 2,
Message_Npl_Message = 3,
Message_MIN = Message_NONE,
Message_MAX = Message_Npl_Message
};
inline const Message (&EnumValuesMessage())[3] {
inline const Message (&EnumValuesMessage())[4] {
static const Message values[] = {
Message_NONE,
Message_NonUnl_Proposal_Message,
Message_Proposal_Message,
Message_Npl_Message
};
@@ -45,6 +49,7 @@ inline const Message (&EnumValuesMessage())[3] {
inline const char * const *EnumNamesMessage() {
static const char * const names[] = {
"NONE",
"NonUnl_Proposal_Message",
"Proposal_Message",
"Npl_Message",
nullptr
@@ -62,6 +67,10 @@ template<typename T> struct MessageTraits {
static const Message enum_value = Message_NONE;
};
template<> struct MessageTraits<NonUnl_Proposal_Message> {
static const Message enum_value = Message_NonUnl_Proposal_Message;
};
template<> struct MessageTraits<Proposal_Message> {
static const Message enum_value = Message_Proposal_Message;
};
@@ -73,10 +82,80 @@ template<> struct MessageTraits<Npl_Message> {
bool VerifyMessage(flatbuffers::Verifier &verifier, const void *obj, Message type);
bool VerifyMessageVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
struct RawInputList FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
struct UserSubmittedMessage FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_CONTENT = 4,
VT_SIGNATURE = 6
};
const flatbuffers::Vector<uint8_t> *content() const {
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_CONTENT);
}
flatbuffers::Vector<uint8_t> *mutable_content() {
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_CONTENT);
}
const flatbuffers::Vector<uint8_t> *signature() const {
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_SIGNATURE);
}
flatbuffers::Vector<uint8_t> *mutable_signature() {
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_SIGNATURE);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_CONTENT) &&
verifier.VerifyVector(content()) &&
VerifyOffset(verifier, VT_SIGNATURE) &&
verifier.VerifyVector(signature()) &&
verifier.EndTable();
}
};
struct UserSubmittedMessageBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_content(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> content) {
fbb_.AddOffset(UserSubmittedMessage::VT_CONTENT, content);
}
void add_signature(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> signature) {
fbb_.AddOffset(UserSubmittedMessage::VT_SIGNATURE, signature);
}
explicit UserSubmittedMessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
UserSubmittedMessageBuilder &operator=(const UserSubmittedMessageBuilder &);
flatbuffers::Offset<UserSubmittedMessage> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<UserSubmittedMessage>(end);
return o;
}
};
inline flatbuffers::Offset<UserSubmittedMessage> CreateUserSubmittedMessage(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> content = 0,
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> signature = 0) {
UserSubmittedMessageBuilder builder_(_fbb);
builder_.add_signature(signature);
builder_.add_content(content);
return builder_.Finish();
}
inline flatbuffers::Offset<UserSubmittedMessage> CreateUserSubmittedMessageDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<uint8_t> *content = nullptr,
const std::vector<uint8_t> *signature = nullptr) {
auto content__ = content ? _fbb.CreateVector<uint8_t>(*content) : 0;
auto signature__ = signature ? _fbb.CreateVector<uint8_t>(*signature) : 0;
return fbschema::p2pmsg::CreateUserSubmittedMessage(
_fbb,
content__,
signature__);
}
struct UserSubmittedMessageGroup FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_PUBKEY = 4,
VT_INPUTS = 6
VT_MESSAGES = 6
};
const flatbuffers::Vector<uint8_t> *pubkey() const {
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_PUBKEY);
@@ -84,133 +163,64 @@ struct RawInputList FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
flatbuffers::Vector<uint8_t> *mutable_pubkey() {
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_PUBKEY);
}
const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *inputs() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_INPUTS);
const flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessage>> *messages() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessage>> *>(VT_MESSAGES);
}
flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *mutable_inputs() {
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *>(VT_INPUTS);
flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessage>> *mutable_messages() {
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessage>> *>(VT_MESSAGES);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_PUBKEY) &&
verifier.VerifyVector(pubkey()) &&
VerifyOffset(verifier, VT_INPUTS) &&
verifier.VerifyVector(inputs()) &&
verifier.VerifyVectorOfTables(inputs()) &&
VerifyOffset(verifier, VT_MESSAGES) &&
verifier.VerifyVector(messages()) &&
verifier.VerifyVectorOfTables(messages()) &&
verifier.EndTable();
}
};
struct RawInputListBuilder {
struct UserSubmittedMessageGroupBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_pubkey(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey) {
fbb_.AddOffset(RawInputList::VT_PUBKEY, pubkey);
fbb_.AddOffset(UserSubmittedMessageGroup::VT_PUBKEY, pubkey);
}
void add_inputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> inputs) {
fbb_.AddOffset(RawInputList::VT_INPUTS, inputs);
void add_messages(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessage>>> messages) {
fbb_.AddOffset(UserSubmittedMessageGroup::VT_MESSAGES, messages);
}
explicit RawInputListBuilder(flatbuffers::FlatBufferBuilder &_fbb)
explicit UserSubmittedMessageGroupBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
RawInputListBuilder &operator=(const RawInputListBuilder &);
flatbuffers::Offset<RawInputList> Finish() {
UserSubmittedMessageGroupBuilder &operator=(const UserSubmittedMessageGroupBuilder &);
flatbuffers::Offset<UserSubmittedMessageGroup> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<RawInputList>(end);
auto o = flatbuffers::Offset<UserSubmittedMessageGroup>(end);
return o;
}
};
inline flatbuffers::Offset<RawInputList> CreateRawInputList(
inline flatbuffers::Offset<UserSubmittedMessageGroup> CreateUserSubmittedMessageGroup(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>> inputs = 0) {
RawInputListBuilder builder_(_fbb);
builder_.add_inputs(inputs);
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessage>>> messages = 0) {
UserSubmittedMessageGroupBuilder builder_(_fbb);
builder_.add_messages(messages);
builder_.add_pubkey(pubkey);
return builder_.Finish();
}
inline flatbuffers::Offset<RawInputList> CreateRawInputListDirect(
inline flatbuffers::Offset<UserSubmittedMessageGroup> CreateUserSubmittedMessageGroupDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<uint8_t> *pubkey = nullptr,
const std::vector<flatbuffers::Offset<fbschema::BytesKeyValuePair>> *inputs = nullptr) {
const std::vector<flatbuffers::Offset<UserSubmittedMessage>> *messages = nullptr) {
auto pubkey__ = pubkey ? _fbb.CreateVector<uint8_t>(*pubkey) : 0;
auto inputs__ = inputs ? _fbb.CreateVector<flatbuffers::Offset<fbschema::BytesKeyValuePair>>(*inputs) : 0;
return fbschema::p2pmsg::CreateRawInputList(
auto messages__ = messages ? _fbb.CreateVector<flatbuffers::Offset<UserSubmittedMessage>>(*messages) : 0;
return fbschema::p2pmsg::CreateUserSubmittedMessageGroup(
_fbb,
pubkey__,
inputs__);
}
struct RawOutput FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_PUBKEY = 4,
VT_OUTPUT = 6
};
const flatbuffers::Vector<uint8_t> *pubkey() const {
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_PUBKEY);
}
flatbuffers::Vector<uint8_t> *mutable_pubkey() {
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_PUBKEY);
}
const fbschema::BytesKeyValuePair *output() const {
return GetPointer<const fbschema::BytesKeyValuePair *>(VT_OUTPUT);
}
fbschema::BytesKeyValuePair *mutable_output() {
return GetPointer<fbschema::BytesKeyValuePair *>(VT_OUTPUT);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_PUBKEY) &&
verifier.VerifyVector(pubkey()) &&
VerifyOffset(verifier, VT_OUTPUT) &&
verifier.VerifyTable(output()) &&
verifier.EndTable();
}
};
struct RawOutputBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_pubkey(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey) {
fbb_.AddOffset(RawOutput::VT_PUBKEY, pubkey);
}
void add_output(flatbuffers::Offset<fbschema::BytesKeyValuePair> output) {
fbb_.AddOffset(RawOutput::VT_OUTPUT, output);
}
explicit RawOutputBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
RawOutputBuilder &operator=(const RawOutputBuilder &);
flatbuffers::Offset<RawOutput> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<RawOutput>(end);
return o;
}
};
inline flatbuffers::Offset<RawOutput> CreateRawOutput(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey = 0,
flatbuffers::Offset<fbschema::BytesKeyValuePair> output = 0) {
RawOutputBuilder builder_(_fbb);
builder_.add_output(output);
builder_.add_pubkey(pubkey);
return builder_.Finish();
}
inline flatbuffers::Offset<RawOutput> CreateRawOutputDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<uint8_t> *pubkey = nullptr,
flatbuffers::Offset<fbschema::BytesKeyValuePair> output = 0) {
auto pubkey__ = pubkey ? _fbb.CreateVector<uint8_t>(*pubkey) : 0;
return fbschema::p2pmsg::CreateRawOutput(
_fbb,
pubkey__,
output);
messages__);
}
struct Content FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
@@ -228,6 +238,9 @@ struct Content FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
return GetPointer<const void *>(VT_MESSAGE);
}
template<typename T> const T *message_as() const;
const NonUnl_Proposal_Message *message_as_NonUnl_Proposal_Message() const {
return message_type() == Message_NonUnl_Proposal_Message ? static_cast<const NonUnl_Proposal_Message *>(message()) : nullptr;
}
const Proposal_Message *message_as_Proposal_Message() const {
return message_type() == Message_Proposal_Message ? static_cast<const Proposal_Message *>(message()) : nullptr;
}
@@ -246,6 +259,10 @@ struct Content FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
}
};
template<> inline const NonUnl_Proposal_Message *Content::message_as<NonUnl_Proposal_Message>() const {
return message_as_NonUnl_Proposal_Message();
}
template<> inline const Proposal_Message *Content::message_as<Proposal_Message>() const {
return message_as_Proposal_Message();
}
@@ -285,17 +302,69 @@ inline flatbuffers::Offset<Content> CreateContent(
return builder_.Finish();
}
struct NonUnl_Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_USERMESSAGES = 4
};
const flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>> *usermessages() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>> *>(VT_USERMESSAGES);
}
flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>> *mutable_usermessages() {
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>> *>(VT_USERMESSAGES);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_USERMESSAGES) &&
verifier.VerifyVector(usermessages()) &&
verifier.VerifyVectorOfTables(usermessages()) &&
verifier.EndTable();
}
};
struct NonUnl_Proposal_MessageBuilder {
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_usermessages(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>>> usermessages) {
fbb_.AddOffset(NonUnl_Proposal_Message::VT_USERMESSAGES, usermessages);
}
explicit NonUnl_Proposal_MessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
NonUnl_Proposal_MessageBuilder &operator=(const NonUnl_Proposal_MessageBuilder &);
flatbuffers::Offset<NonUnl_Proposal_Message> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<NonUnl_Proposal_Message>(end);
return o;
}
};
inline flatbuffers::Offset<NonUnl_Proposal_Message> CreateNonUnl_Proposal_Message(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>>> usermessages = 0) {
NonUnl_Proposal_MessageBuilder builder_(_fbb);
builder_.add_usermessages(usermessages);
return builder_.Finish();
}
inline flatbuffers::Offset<NonUnl_Proposal_Message> CreateNonUnl_Proposal_MessageDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<flatbuffers::Offset<UserSubmittedMessageGroup>> *usermessages = nullptr) {
auto usermessages__ = usermessages ? _fbb.CreateVector<flatbuffers::Offset<UserSubmittedMessageGroup>>(*usermessages) : 0;
return fbschema::p2pmsg::CreateNonUnl_Proposal_Message(
_fbb,
usermessages__);
}
struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_STAGE = 4,
VT_TIME = 6,
VT_LCL = 8,
VT_USERS = 10,
VT_RAW_INPUTS = 12,
VT_HASH_INPUTS = 14,
VT_RAW_OUTPUTS = 16,
VT_HASH_OUTPUTS = 18,
VT_STATE = 20
VT_HASH_INPUTS = 12,
VT_HASH_OUTPUTS = 14,
VT_STATE = 16
};
uint8_t stage() const {
return GetField<uint8_t>(VT_STAGE, 0);
@@ -321,24 +390,12 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *mutable_users() {
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *>(VT_USERS);
}
const flatbuffers::Vector<flatbuffers::Offset<RawInputList>> *raw_inputs() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<RawInputList>> *>(VT_RAW_INPUTS);
}
flatbuffers::Vector<flatbuffers::Offset<RawInputList>> *mutable_raw_inputs() {
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<RawInputList>> *>(VT_RAW_INPUTS);
}
const flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *hash_inputs() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *>(VT_HASH_INPUTS);
}
flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *mutable_hash_inputs() {
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *>(VT_HASH_INPUTS);
}
const flatbuffers::Vector<flatbuffers::Offset<RawOutput>> *raw_outputs() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<RawOutput>> *>(VT_RAW_OUTPUTS);
}
flatbuffers::Vector<flatbuffers::Offset<RawOutput>> *mutable_raw_outputs() {
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<RawOutput>> *>(VT_RAW_OUTPUTS);
}
const flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *hash_outputs() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>> *>(VT_HASH_OUTPUTS);
}
@@ -360,15 +417,9 @@ struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
VerifyOffset(verifier, VT_USERS) &&
verifier.VerifyVector(users()) &&
verifier.VerifyVectorOfTables(users()) &&
VerifyOffset(verifier, VT_RAW_INPUTS) &&
verifier.VerifyVector(raw_inputs()) &&
verifier.VerifyVectorOfTables(raw_inputs()) &&
VerifyOffset(verifier, VT_HASH_INPUTS) &&
verifier.VerifyVector(hash_inputs()) &&
verifier.VerifyVectorOfTables(hash_inputs()) &&
VerifyOffset(verifier, VT_RAW_OUTPUTS) &&
verifier.VerifyVector(raw_outputs()) &&
verifier.VerifyVectorOfTables(raw_outputs()) &&
VerifyOffset(verifier, VT_HASH_OUTPUTS) &&
verifier.VerifyVector(hash_outputs()) &&
verifier.VerifyVectorOfTables(hash_outputs()) &&
@@ -393,15 +444,9 @@ struct Proposal_MessageBuilder {
void add_users(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> users) {
fbb_.AddOffset(Proposal_Message::VT_USERS, users);
}
void add_raw_inputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawInputList>>> raw_inputs) {
fbb_.AddOffset(Proposal_Message::VT_RAW_INPUTS, raw_inputs);
}
void add_hash_inputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> hash_inputs) {
fbb_.AddOffset(Proposal_Message::VT_HASH_INPUTS, hash_inputs);
}
void add_raw_outputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawOutput>>> raw_outputs) {
fbb_.AddOffset(Proposal_Message::VT_RAW_OUTPUTS, raw_outputs);
}
void add_hash_outputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> hash_outputs) {
fbb_.AddOffset(Proposal_Message::VT_HASH_OUTPUTS, hash_outputs);
}
@@ -426,18 +471,14 @@ inline flatbuffers::Offset<Proposal_Message> CreateProposal_Message(
uint64_t time = 0,
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> users = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawInputList>>> raw_inputs = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> hash_inputs = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawOutput>>> raw_outputs = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbschema::ByteArray>>> hash_outputs = 0,
flatbuffers::Offset<State> state = 0) {
Proposal_MessageBuilder builder_(_fbb);
builder_.add_time(time);
builder_.add_state(state);
builder_.add_hash_outputs(hash_outputs);
builder_.add_raw_outputs(raw_outputs);
builder_.add_hash_inputs(hash_inputs);
builder_.add_raw_inputs(raw_inputs);
builder_.add_users(users);
builder_.add_lcl(lcl);
builder_.add_stage(stage);
@@ -450,16 +491,12 @@ inline flatbuffers::Offset<Proposal_Message> CreateProposal_MessageDirect(
uint64_t time = 0,
const std::vector<uint8_t> *lcl = nullptr,
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *users = nullptr,
const std::vector<flatbuffers::Offset<RawInputList>> *raw_inputs = nullptr,
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *hash_inputs = nullptr,
const std::vector<flatbuffers::Offset<RawOutput>> *raw_outputs = nullptr,
const std::vector<flatbuffers::Offset<fbschema::ByteArray>> *hash_outputs = nullptr,
flatbuffers::Offset<State> state = 0) {
auto lcl__ = lcl ? _fbb.CreateVector<uint8_t>(*lcl) : 0;
auto users__ = users ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*users) : 0;
auto raw_inputs__ = raw_inputs ? _fbb.CreateVector<flatbuffers::Offset<RawInputList>>(*raw_inputs) : 0;
auto hash_inputs__ = hash_inputs ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*hash_inputs) : 0;
auto raw_outputs__ = raw_outputs ? _fbb.CreateVector<flatbuffers::Offset<RawOutput>>(*raw_outputs) : 0;
auto hash_outputs__ = hash_outputs ? _fbb.CreateVector<flatbuffers::Offset<fbschema::ByteArray>>(*hash_outputs) : 0;
return fbschema::p2pmsg::CreateProposal_Message(
_fbb,
@@ -467,9 +504,7 @@ inline flatbuffers::Offset<Proposal_Message> CreateProposal_MessageDirect(
time,
lcl__,
users__,
raw_inputs__,
hash_inputs__,
raw_outputs__,
hash_outputs__,
state);
}
@@ -775,6 +810,10 @@ inline bool VerifyMessage(flatbuffers::Verifier &verifier, const void *obj, Mess
case Message_NONE: {
return true;
}
case Message_NonUnl_Proposal_Message: {
auto ptr = reinterpret_cast<const NonUnl_Proposal_Message *>(obj);
return verifier.VerifyTable(ptr);
}
case Message_Proposal_Message: {
auto ptr = reinterpret_cast<const Proposal_Message *>(obj);
return verifier.VerifyTable(ptr);
@@ -836,4 +875,4 @@ inline void FinishSizePrefixedContentBuffer(
} // namespace p2pmsg
} // namespace fbschema
#endif // FLATBUFFERS_GENERATED_P2PMSGCONTENT_FBSCHEMA_P2PMSG_
#endif // FLATBUFFERS_GENERATED_P2PMSGCONTENT_FBSCHEMA_P2PMSG_H_

View File

@@ -139,6 +139,21 @@ int validate_and_extract_content(const Content **content_ref, const uint8_t *con
return 0;
}
/**
* Creates a non-unl proposal stuct from the given non-unl proposal message.
* @param The Flatbuffer non-unl poporal received from the peer.
* @return A non-unl proposal struct representing the message.
*/
const p2p::nonunl_proposal create_nonunl_proposal_from_msg(const NonUnl_Proposal_Message &msg, uint64_t timestamp)
{
p2p::nonunl_proposal nup;
if (msg.usermessages())
nup.user_messages = flatbuf_usermsgsmap_to_usermsgsmap(msg.usermessages());
return nup;
}
/**
* Creates a proposal stuct from the given proposal message.
* @param The Flatbuffer poporal received from the peer.
@@ -159,15 +174,9 @@ const p2p::proposal create_proposal_from_msg(const Proposal_Message &msg, const
if (msg.users())
p.users = flatbuf_bytearrayvector_to_stringlist(msg.users());
if (msg.raw_inputs())
p.raw_inputs = flatbuf_rawinputs_to_hashbuffermap(msg.raw_inputs());
if (msg.hash_inputs())
p.hash_inputs = flatbuf_bytearrayvector_to_stringlist(msg.hash_inputs());
if (msg.raw_outputs())
p.raw_outputs = flatbuf_rawoutputs_to_hashbuffermap(msg.raw_outputs());
if (msg.hash_outputs())
p.hash_outputs = flatbuf_bytearrayvector_to_stringlist(msg.hash_outputs());
@@ -176,6 +185,23 @@ const p2p::proposal create_proposal_from_msg(const Proposal_Message &msg, const
//---Message creation helpers---//
void create_msg_from_nonunl_proposal(flatbuffers::FlatBufferBuilder &container_builder, const p2p::nonunl_proposal &nup)
{
flatbuffers::FlatBufferBuilder builder(1024);
flatbuffers::Offset<NonUnl_Proposal_Message> nupmsg =
CreateNonUnl_Proposal_Message(
builder,
usermsgsmap_to_flatbuf_usermsgsmap(builder, nup.user_messages));
flatbuffers::Offset<Content> message = CreateContent(builder, Message_NonUnl_Proposal_Message, nupmsg.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, false);
}
/**
* Ctreat proposal peer message from the given proposal struct.
* @param container_builder Flatbuffer builder for the container message.
@@ -186,7 +212,6 @@ void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder,
// todo:get a average propsal message size and allocate content builder based on that.
flatbuffers::FlatBufferBuilder builder(1024);
// Create dummy propsal message
flatbuffers::Offset<Proposal_Message> proposal =
CreateProposal_Message(
builder,
@@ -194,9 +219,7 @@ void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder,
p.time,
sv_to_flatbuff_bytes(builder, p.lcl),
stringlist_to_flatbuf_bytearrayvector(builder, p.users),
hashbuffermap_to_flatbuf_rawinputs(builder, p.raw_inputs),
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_inputs),
hashbuffermap_to_flatbuf_rawoutputs(builder, p.raw_outputs),
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_outputs));
flatbuffers::Offset<Content> message = CreateContent(builder, Message_Proposal_Message, proposal.Union());
@@ -249,48 +272,24 @@ void create_containermsg_from_content(
//---Conversion helpers from flatbuffers data types to std data types---//
/**
* Returns a hash buffer map from Flatbuffer proposal raw inputs.
*/
const std::unordered_map<std::string, const std::vector<util::hash_buffer>>
flatbuf_rawinputs_to_hashbuffermap(const flatbuffers::Vector<flatbuffers::Offset<RawInputList>> *fbvec)
const std::unordered_map<std::string, const std::list<usr::user_submitted_message>>
flatbuf_usermsgsmap_to_usermsgsmap(const flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>> *fbvec)
{
std::unordered_map<std::string, const std::vector<util::hash_buffer>> map;
std::unordered_map<std::string, const std::list<usr::user_submitted_message>> map;
map.reserve(fbvec->size());
for (const RawInputList *user : *fbvec)
for (const UserSubmittedMessageGroup *group : *fbvec)
{
std::vector<util::hash_buffer> bufvec;
bufvec.reserve(user->inputs()->size());
std::list<usr::user_submitted_message> msglist;
for (auto input : *user->inputs())
for (auto msg : *group->messages())
{
// Create hash_buffer object and manually assign the hash from the input.
util::hash_buffer buf(flatbuff_bytes_to_sv(input->value())); //input->value() is the raw input.
buf.hash = flatbuff_bytes_to_sv(input->key()); //input->key() is the hash of the input.
bufvec.push_back(buf);
msglist.push_back(usr::user_submitted_message(
flatbuff_bytes_to_sv(msg->content()),
flatbuff_bytes_to_sv(msg->signature())
));
}
map.emplace(flatbuff_bytes_to_sv(user->pubkey()), std::move(bufvec));
}
return map;
}
/**
* Returns a hash buffer map from Flatbuffer proposal raw outputs.
*/
const std::unordered_map<std::string, util::hash_buffer>
flatbuf_rawoutputs_to_hashbuffermap(const flatbuffers::Vector<flatbuffers::Offset<RawOutput>> *fbvec)
{
std::unordered_map<std::string, util::hash_buffer> map;
map.reserve(fbvec->size());
for (const RawOutput *user : *fbvec)
{
// Create hash_buffer object and manually assign the hash from the output.
util::hash_buffer buf(flatbuff_bytes_to_sv(user->output()->value())); //output->value() is the raw output.
buf.hash = flatbuff_bytes_to_sv(user->output()->key()); //output->key() is the hash of the output.
map.emplace(flatbuff_bytes_to_sv(user->pubkey()), std::move(buf));
map.emplace(flatbuff_bytes_to_sv(group->pubkey()), std::move(msglist));
}
return map;
}
@@ -298,50 +297,26 @@ flatbuf_rawoutputs_to_hashbuffermap(const flatbuffers::Vector<flatbuffers::Offse
//---Conversion helpers from std data types to flatbuffers data types---//
//---These are used in constructing Flatbuffer messages using builders---//
/**
* Returns Flatbuffer vector of RawInputs from a given map of hash buffer lists.
*/
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawInputList>>>
hashbuffermap_to_flatbuf_rawinputs(flatbuffers::FlatBufferBuilder &builder, const std::unordered_map<std::string, const std::vector<util::hash_buffer>> &map)
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>>>
usermsgsmap_to_flatbuf_usermsgsmap(flatbuffers::FlatBufferBuilder &builder, const std::unordered_map<std::string, const std::list<usr::user_submitted_message>> &map)
{
std::vector<flatbuffers::Offset<RawInputList>> fbvec;
std::vector<flatbuffers::Offset<UserSubmittedMessageGroup>> fbvec;
fbvec.reserve(map.size());
for (auto const &[pubkey, bufvec] : map)
for (auto const &[pubkey, msglist] : map)
{
std::vector<flatbuffers::Offset<BytesKeyValuePair>> fbinputsvec;
for (const util::hash_buffer &buf : bufvec)
std::vector<flatbuffers::Offset<UserSubmittedMessage>> fbmsgsvec;
for (const usr::user_submitted_message &msg : msglist)
{
fbinputsvec.push_back(CreateBytesKeyValuePair(
fbmsgsvec.push_back(CreateUserSubmittedMessage(
builder,
sv_to_flatbuff_bytes(builder, buf.hash),
sv_to_flatbuff_bytes(builder, buf.buffer)));
sv_to_flatbuff_bytes(builder, msg.content),
sv_to_flatbuff_bytes(builder, msg.sig)));
}
fbvec.push_back(CreateRawInputList(
fbvec.push_back(CreateUserSubmittedMessageGroup(
builder,
sv_to_flatbuff_bytes(builder, pubkey),
builder.CreateVector(fbinputsvec)));
}
return builder.CreateVector(fbvec);
}
/**
* Returns Flatbuffer vector of RawOutputs from a given map of hash buffers.
*/
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawOutput>>>
hashbuffermap_to_flatbuf_rawoutputs(flatbuffers::FlatBufferBuilder &builder, const std::unordered_map<std::string, util::hash_buffer> &map)
{
std::vector<flatbuffers::Offset<RawOutput>> fbvec;
fbvec.reserve(map.size());
for (auto const &[pubkey, buf] : map)
{
fbvec.push_back(CreateRawOutput(
builder,
sv_to_flatbuff_bytes(builder, pubkey),
CreateBytesKeyValuePair(
builder,
sv_to_flatbuff_bytes(builder, buf.hash),
sv_to_flatbuff_bytes(builder, buf.buffer))));
builder.CreateVector(fbmsgsvec)));
}
return builder.CreateVector(fbvec);
}

View File

@@ -21,10 +21,14 @@ int validate_container_trust(const Container *container);
int validate_and_extract_content(const Content **content_ref, const uint8_t *content_ptr, flatbuffers::uoffset_t content_size);
const p2p::nonunl_proposal create_nonunl_proposal_from_msg(const NonUnl_Proposal_Message &msg, uint64_t timestamp);
const p2p::proposal create_proposal_from_msg(const Proposal_Message &msg, const flatbuffers::Vector<uint8_t> *pubkey, uint64_t timestamp);
//---Message creation helpers---//
void create_msg_from_nonunl_proposal(flatbuffers::FlatBufferBuilder &container_builder, const p2p::nonunl_proposal &nup);
void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, const p2p::proposal &p);
void create_containermsg_from_content(
@@ -32,19 +36,13 @@ void create_containermsg_from_content(
//---Conversion helpers from flatbuffers data types to std data types---//
const std::unordered_map<std::string, const std::vector<util::hash_buffer>>
flatbuf_rawinputs_to_hashbuffermap(const flatbuffers::Vector<flatbuffers::Offset<RawInputList>> *fbvec);
const std::unordered_map<std::string, util::hash_buffer>
flatbuf_rawoutputs_to_hashbuffermap(const flatbuffers::Vector<flatbuffers::Offset<RawOutput>> *fbvec);
const std::unordered_map<std::string, const std::list<usr::user_submitted_message>>
flatbuf_usermsgsmap_to_usermsgsmap(const flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>> *fbvec);
//---Conversion helpers from std data types to flatbuffers data types---//
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawInputList>>>
hashbuffermap_to_flatbuf_rawinputs(flatbuffers::FlatBufferBuilder &builder, const std::unordered_map<std::string, const std::vector<util::hash_buffer>> &map);
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RawOutput>>>
hashbuffermap_to_flatbuf_rawoutputs(flatbuffers::FlatBufferBuilder &builder, const std::unordered_map<std::string, util::hash_buffer> &map);
const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<UserSubmittedMessageGroup>>>
usermsgsmap_to_flatbuf_usermsgsmap(flatbuffers::FlatBufferBuilder &builder, const std::unordered_map<std::string, const std::list<usr::user_submitted_message>> &map);
} // namespace fbschema::p2pmsg

View File

@@ -7,21 +7,9 @@
namespace jsonschema::usrmsg
{
static const char *SCHEMA_VERSION = "0.1";
// These fields are used on json messages response validation.
static const char *FLD_TYPE = "type";
static const char *FLD_CHALLENGE = "challenge";
static const char *FLD_SIG = "sig";
static const char *FLD_PUBKEY = "pubkey";
static const char *FLD_INPUT = "input";
static const char *FLD_CONTENT = "content";
static const char *FLD_NONCE = "nonce";
// Message types
static const char *MSGTYPE_CHALLENGE = "public_challenge";
static const char *MSGTYPE_CHALLENGE_RESP = "challenge_response";
static const char *MSGTYPE_CONTRACT_INPUT = "contract_input";
// Separators
static const char *SEP_COMMA = "\",\"";
static const char *SEP_COLON = "\":\"";
// Length of user random challenge bytes.
static const size_t CHALLENGE_LEN = 16;
@@ -58,15 +46,17 @@ void create_user_challenge(std::string &msg, std::string &challengehex)
// Only Hot Pocket version number is variable length. Therefore message size is roughly 95 bytes
// so allocating 128bits for heap padding.
msg.reserve(128);
msg.append("{\"version\":\"")
msg.append("{\"")
.append(FLD_VERSION)
.append(SEP_COLON)
.append(SCHEMA_VERSION)
.append("\",\"")
.append(SEP_COMMA)
.append(FLD_TYPE)
.append("\":\"")
.append(SEP_COLON)
.append(MSGTYPE_CHALLENGE)
.append("\",\"")
.append(SEP_COMMA)
.append(FLD_CHALLENGE)
.append("\":\"")
.append(SEP_COLON)
.append(challengehex)
.append("\"}");
}
@@ -79,6 +69,7 @@ void create_user_challenge(std::string &msg, std::string &challengehex)
* @param response The response bytes to verify. This will be parsed as json.
* Accepted response format:
* {
* "version": "<protocol version>"
* "type": "challenge_response",
* "challenge": "<original hex challenge the user received>",
* "sig": "<hex signature of the challenge>",
@@ -89,43 +80,35 @@ void create_user_challenge(std::string &msg, std::string &challengehex)
*/
int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string_view response, std::string_view original_challenge)
{
// We load response raw bytes into json document.
rapidjson::Document d;
// Because we project the response message directly from the binary socket buffer in a zero-copy manner, the response
// string is not null terminated. 'kParseStopWhenDoneFlag' avoids rapidjson error in this case.
d.Parse<rapidjson::kParseStopWhenDoneFlag>(response.data());
if (d.HasParseError())
{
LOG_INFO << "Challenge response json parsing failed.";
if (parse_user_message(d, response) != 0)
return -1;
}
// Validate msg type.
if (!d.HasMember(FLD_TYPE) || d[FLD_TYPE] != MSGTYPE_CHALLENGE_RESP)
if (d[FLD_TYPE] != MSGTYPE_CHALLENGE_RESP)
{
LOG_INFO << "User challenge response type invalid. 'challenge_response' expected.";
LOG_DBG << "User challenge response type invalid. 'challenge_response' expected.";
return -1;
}
// Compare the response challenge string with the original issued challenge.
if (!d.HasMember(FLD_CHALLENGE) || d[FLD_CHALLENGE] != original_challenge.data())
{
LOG_INFO << "User challenge response challenge invalid.";
LOG_DBG << "User challenge response challenge invalid.";
return -1;
}
// Check for the 'sig' field existence.
if (!d.HasMember(FLD_SIG) || !d[FLD_SIG].IsString())
{
LOG_INFO << "User challenge response signature invalid.";
LOG_DBG << "User challenge response signature invalid.";
return -1;
}
// Check for the 'pubkey' field existence.
if (!d.HasMember(FLD_PUBKEY) || !d[FLD_PUBKEY].IsString())
{
LOG_INFO << "User challenge response public key invalid.";
LOG_DBG << "User challenge response public key invalid.";
return -1;
}
@@ -136,7 +119,7 @@ int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string
util::getsv(d[FLD_SIG]),
pubkeysv) != 0)
{
LOG_INFO << "User challenge response signature verification failed.";
LOG_DBG << "User challenge response signature verification failed.";
return -1;
}
@@ -145,4 +128,138 @@ int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string
return 0;
}
/**
* Extracts a signed input container message sent by user.
*
* @param extracted_content The content extracted from the message.
* @param extracted_sig The binary signature extracted from the message.
* @param d The json document holding the input container.
* Accepted signed input container format:
* {
* "version": "<protocol version>"
* "type": "contract_input",
* "content": "<hex encoded input container message>",
* "sig": "<hex encoded signature of the content>"
* }
* @return 0 on successful extraction. -1 for failure.
*/
int extract_signed_input_container(
std::string &extracted_content, std::string &extracted_sig, const rapidjson::Document &d)
{
if (!d.HasMember(FLD_CONTENT) || !d.HasMember(FLD_SIG))
{
LOG_DBG << "User signed input required fields missing.";
return -1;
}
if (!d[FLD_CONTENT].IsString() || !d[FLD_SIG].IsString())
{
LOG_DBG << "User signed input invaid field values.";
return -1;
}
// Verify the signature of the content.
const std::string content(d[FLD_CONTENT].GetString(), d[FLD_CONTENT].GetStringLength());
const std::string_view sighex(d[FLD_SIG].GetString(), d[FLD_SIG].GetStringLength());
std::string sig;
sig.resize(crypto_sign_ed25519_BYTES);
util::hex2bin(reinterpret_cast<unsigned char *>(sig.data()), sig.length(), sighex);
extracted_content = std::move(content);
extracted_sig = std::move(sig);
return 0;
}
/**
* Extract the individual components of a given input container json.
* @param nonce The extracted nonce.
* @param input The extracted input.
* @param max_ledger_seqno The extracted max ledger sequence no.
* @param contentjson The json string containing the input container message.
* {
* "nonce": "<random string with optional sorted order>",
* "input": "<hex encoded contract input content>",
* "maxledgerseqno": 4562712334
* }
* @return 0 on succesful extraction. -1 on failure.
*/
int extract_input_container(std::string &nonce, std::string &input, uint64_t &max_ledger_seqno, std::string_view contentjson)
{
rapidjson::Document d;
d.Parse(contentjson.data());
if (d.HasParseError())
{
LOG_DBG << "User input container json parsing failed.";
return -1;
}
if (!d.HasMember(FLD_NONCE) || !d.HasMember(FLD_INPUT) || !d.HasMember(FLD_MAX_LED_SEQ))
{
LOG_DBG << "User input container required fields missing.";
return -1;
}
if (!d[FLD_NONCE].IsString() || !d[FLD_INPUT].IsString() || !d[FLD_MAX_LED_SEQ].IsUint64())
{
LOG_DBG << "User input container invaid field values.";
return -1;
}
rapidjson::Value &inputval = d[FLD_INPUT];
std::string_view inputhex(inputval.GetString(), inputval.GetStringLength());
// Convert hex input to binary.
input.resize(inputhex.length() / 2);
if (util::hex2bin(
reinterpret_cast<unsigned char *>(input.data()),
input.length(),
inputhex) != 0)
{
LOG_DBG << "Contract input format invalid.";
return -1;
}
nonce = d[FLD_NONCE].GetString();
max_ledger_seqno = d[FLD_MAX_LED_SEQ].GetUint64();
return 0;
}
/**
* Parses a json message sent by a user.
* @param d RapidJson document to which the parsed json should be loaded.
* @param message The message to parse.
* @return 0 on successful parsing. -1 for failure.
*/
int parse_user_message(rapidjson::Document &d, std::string_view message)
{
// We load response raw bytes into json document.
// Because we project the response message directly from the binary socket buffer in a zero-copy manner, the response
// string is not null terminated. 'kParseStopWhenDoneFlag' avoids rapidjson error in this case.
d.Parse<rapidjson::kParseStopWhenDoneFlag>(message.data());
if (d.HasParseError())
{
LOG_DBG << "User json message parsing failed.";
return -1;
}
// Check existence of msg type field.
if (!d.HasMember(FLD_VERSION) || !d[FLD_VERSION].IsString())
{
LOG_DBG << "User json message 'version' missing or invalid.";
return -1;
}
// Check existence of msg type field.
if (!d.HasMember(FLD_TYPE) || !d[FLD_TYPE].IsString())
{
LOG_DBG << "User json message 'type' missing or invalid.";
return -1;
}
return 0;
}
} // namespace jsonschema::usrmsg

View File

@@ -5,9 +5,35 @@
namespace jsonschema::usrmsg
{
static const char *SCHEMA_VERSION = "0.1";
// These fields are used on json messages response validation.
static const char *FLD_VERSION = "version";
static const char *FLD_TYPE = "type";
static const char *FLD_CHALLENGE = "challenge";
static const char *FLD_SIG = "sig";
static const char *FLD_PUBKEY = "pubkey";
static const char *FLD_INPUT = "input";
static const char *FLD_MAX_LED_SEQ = "maxledgerseqno";
static const char *FLD_CONTENT = "content";
static const char *FLD_NONCE = "nonce";
// Message types
static const char *MSGTYPE_CHALLENGE = "public_challenge";
static const char *MSGTYPE_CHALLENGE_RESP = "challenge_response";
static const char *MSGTYPE_CONTRACT_INPUT = "contract_input";
void create_user_challenge(std::string &msg, std::string &challengehex);
int verify_user_challenge_response(std::string &extracted_pubkeyhex, std::string_view response, std::string_view original_challenge);
int extract_signed_input_container(std::string &extracted_content, std::string &extracted_sig, const rapidjson::Document &d);
int extract_input_container(std::string &nonce, std::string &input, uint64_t &max_ledger_seqno, std::string_view contentjson);
int parse_user_message(rapidjson::Document &d, std::string_view message);
} // namespace jsonschema::usrmsg
#endif

View File

@@ -116,7 +116,7 @@ void peer_connection_watchdog()
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
util::sleep(200);
}
}
@@ -150,4 +150,22 @@ bool is_message_duplicate(std::string_view message)
return true;
}
/**
* Broadcasts the given message to all currently connected outbound peers.
*/
void broadcast_message(peer_outbound_message msg)
{
if (p2p::peer_connections.size() == 0)
{
LOG_DBG << "No peers to broadcast (not even self). Waiting until at least one peer connects.";
while (p2p::peer_connections.size() == 0)
util::sleep(100);
}
//Broadcast while locking the peer_connections.
std::lock_guard<std::mutex> lock(p2p::peer_connections_mutex);
for (auto &[k, session] : p2p::peer_connections)
session->send(msg);
}
} // namespace p2p

View File

@@ -3,6 +3,7 @@
#include "../pchheader.hpp"
#include "../sock/socket_session.hpp"
#include "../usr/user_input.hpp"
#include "peer_session_handler.hpp"
namespace p2p
@@ -15,17 +16,23 @@ struct proposal
uint64_t time;
uint8_t stage;
std::string lcl;
std::unordered_set<std::string> users;
std::unordered_map<std::string, const std::vector<util::hash_buffer>> raw_inputs;
std::unordered_set<std::string> hash_inputs;
std::unordered_map<std::string, util::hash_buffer> raw_outputs;
std::unordered_set<std::string> hash_outputs;
std::set<std::string> users;
std::set<std::string> hash_inputs;
std::set<std::string> hash_outputs;
};
struct nonunl_proposal
{
std::unordered_map<std::string, const std::list<usr::user_submitted_message>> user_messages;
};
struct message_collection
{
std::list<proposal> proposals;
std::mutex proposals_mutex; // Mutex for proposals access race conditions.
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.
};
/**
@@ -48,6 +55,8 @@ void peer_connection_watchdog();
bool is_message_duplicate(std::string_view message);
void broadcast_message(peer_outbound_message msg);
} // namespace p2p
#endif

View File

@@ -70,6 +70,13 @@ void peer_session_handler::on_message(sock::socket_session<peer_outbound_message
collected_msgs.proposals.push_back(
p2pmsg::create_proposal_from_msg(*content->message_as_Proposal_Message(), container->pubkey(), container->timestamp()));
}
else if (content_message_type == p2pmsg::Message_NonUnl_Proposal_Message) //message is a non-unl proposal message
{
std::lock_guard<std::mutex> lock(collected_msgs.nonunl_proposals_mutex); // Insert non-unl proposal with lock.
collected_msgs.nonunl_proposals.push_back(
p2pmsg::create_nonunl_proposal_from_msg(*content->message_as_NonUnl_Proposal_Message(), container->timestamp()));
}
else if (content_message_type == p2pmsg::Message_Npl_Message) //message is a NPL message
{
const p2pmsg::Npl_Message *npl = content->message_as_Npl_Message();

View File

@@ -27,6 +27,7 @@
#include <boost/log/utility/setup/file.hpp>
#include <boost/thread/thread.hpp>
#include <chrono>
#include <cstdarg>
#include <cstdio>
#include <fcntl.h>
#include <fstream>

View File

@@ -36,7 +36,7 @@ __pid_t contract_pid;
*
* @return 0 on successful process creation. -1 on failure or contract process is already running.
*/
int exec_contract(const ContractExecArgs &args)
int exec_contract(const contract_exec_args &args)
{
// Write any hp input messages to hp->sc pipe.
if (write_contract_hp_inputs(args) != 0)
@@ -162,7 +162,7 @@ int await_contract_execution()
* "unl":[ "pkhex", ... ]
* }
*/
int write_contract_args(const ContractExecArgs &args)
int write_contract_args(const contract_exec_args &args)
{
// Populate the json string with contract args.
// We don't use a JSON parser here because it's lightweight to contrstuct the
@@ -230,7 +230,7 @@ int write_contract_args(const ContractExecArgs &args)
/**
* Writes any hp input messages to the contract.
*/
int write_contract_hp_inputs(const ContractExecArgs &args)
int write_contract_hp_inputs(const contract_exec_args &args)
{
if (create_and_write_iopipes(hpscfds, args.hpscbufs.inputs) != 0)
{
@@ -246,7 +246,7 @@ int write_contract_hp_inputs(const ContractExecArgs &args)
*
* @return 0 on success. -1 on failure.
*/
int read_contract_hp_outputs(const ContractExecArgs &args)
int read_contract_hp_outputs(const contract_exec_args &args)
{
// Clear the input buffers because we are sure the contract has finished reading from
// that mapped memory portion.

View File

@@ -35,7 +35,7 @@ typedef std::unordered_map<std::string, contract_iobuf_pair> contract_bufmap_t;
/**
* Holds information that should be passed into the contract process.
*/
struct ContractExecArgs
struct contract_exec_args
{
// Map of user I/O buffers (map key: user binary public key).
// The value is a pair holding consensus-verified inputs and contract-generated outputs.
@@ -52,7 +52,7 @@ struct ContractExecArgs
// Current HotPocket timestamp.
int64_t timestamp;
ContractExecArgs(
contract_exec_args(
int64_t _timestamp,
contract_bufmap_t &_userbufs,
contract_bufmap_t &_nplbufs,
@@ -65,17 +65,17 @@ struct ContractExecArgs
}
};
int exec_contract(const ContractExecArgs &args);
int exec_contract(const contract_exec_args &args);
int await_contract_execution();
//------Internal-use functions for this namespace.
int write_contract_args(const ContractExecArgs &args);
int write_contract_args(const contract_exec_args &args);
int write_contract_hp_inputs(const ContractExecArgs &args);
int write_contract_hp_inputs(const contract_exec_args &args);
int read_contract_hp_outputs(const ContractExecArgs &args);
int read_contract_hp_outputs(const contract_exec_args &args);
// Common helper functions

32
src/usr/user_input.hpp Normal file
View File

@@ -0,0 +1,32 @@
#ifndef _HP_USR_USER_INPUT_
#define _HP_USR_USER_INPUT_
#include "../pchheader.hpp"
namespace usr
{
/**
* Represents a signed contract input message a network user has submitted.
*/
struct user_submitted_message
{
std::string content;
std::string sig;
user_submitted_message(std::string content, std::string sig)
{
this->content = std::move(content);
this->sig = std::move(sig);
}
user_submitted_message(std::string_view content, std::string_view sig)
{
this->content = content;
this->sig = sig;
}
};
} // namespace usr
#endif

View File

@@ -44,7 +44,7 @@ void user_session_handler::on_message(
// Meaning we have previously issued a challenge to the client,
if (session->flags[util::SESSION_FLAG::USER_CHALLENGE_ISSUED])
{
if (verify_challenge(message, session))
if (verify_challenge(message, session) == 0)
return;
}
// Check whether this session belongs to an authenticated (challenge-verified) user.
@@ -58,12 +58,20 @@ void user_session_handler::on_message(
{
// This is an authed user.
connected_user &user = itr->second;
handle_user_message(user, message);
return;
if (handle_user_message(user, message) == 0)
return;
LOG_DBG << "Bad message from user " << session->uniqueid;
// TODO: Increase session bad message count.
}
else
{
LOG_DBG << "User session id not found: " << session->uniqueid;
}
}
// If for any reason we reach this point, we should drop the connection.
// If for any reason we reach this point, we should drop the connection because none of the
// valid cases match.
session->close();
LOG_INFO << "Dropped the user connection " << session->address << ":" << session->port;
}
@@ -77,14 +85,11 @@ void user_session_handler::on_close(sock::socket_session<user_outbound_message>
// Session is awaiting challenge response.
if (session->flags[util::SESSION_FLAG::USER_CHALLENGE_ISSUED])
{
ctx.pending_challenges.erase(session->uniqueid);
}
// Session belongs to an authed user.
else if (session->flags[util::SESSION_FLAG::USER_AUTHED])
{
remove_user(session->uniqueid);
}
LOG_INFO << "User disconnected " << session->uniqueid;
}

View File

@@ -1,6 +1,4 @@
#include "../pchheader.hpp"
#include "usr.hpp"
#include "user_session_handler.hpp"
#include "../jsonschema/usrmsg_helpers.hpp"
#include "../sock/socket_server.hpp"
#include "../sock/socket_session_handler.hpp"
@@ -8,6 +6,9 @@
#include "../conf.hpp"
#include "../crypto.hpp"
#include "../hplog.hpp"
#include "usr.hpp"
#include "user_session_handler.hpp"
#include "user_input.hpp"
namespace jusrmsg = jsonschema::usrmsg;
@@ -43,14 +44,20 @@ std::string issue_challenge(const std::string sessionid)
return msgstr;
}
bool verify_challenge(std::string_view message, sock::socket_session<user_outbound_message> *session)
/**
* Verifies the given message for a previously issued user challenge.
* @param message Challenge response.
* @param session The socket session that received the response.
* @return 0 for successful verification. -1 for failure.
*/
int verify_challenge(std::string_view message, sock::socket_session<user_outbound_message> *session)
{
// The received message must be the challenge response. We need to verify it.
auto itr = ctx.pending_challenges.find(session->uniqueid);
if (itr == ctx.pending_challenges.end())
{
LOG_DBG << "No challenge found for the session " << session->uniqueid;
return false;
return -1;
}
std::string userpubkeyhex;
@@ -81,7 +88,7 @@ bool verify_challenge(std::string_view message, sock::socket_session<user_outbou
LOG_INFO << "User connection " << session->uniqueid << " authenticated. Public key "
<< userpubkeyhex;
return true;
return 0;
}
else
{
@@ -93,18 +100,44 @@ bool verify_challenge(std::string_view message, sock::socket_session<user_outbou
LOG_INFO << "Challenge verification failed " << session->uniqueid;
}
return false;
return -1;
}
void handle_user_message(connected_user &user, std::string_view message)
/**
* Processes a message sent by a connected user. This will be invoked by web socket on_message handler.
* @param user The authenticated user who sent the message.
* @param message The message sent by user.
* @return 0 on successful processing. -1 for failure.
*/
int handle_user_message(connected_user &user, std::string_view message)
{
rapidjson::Document d;
if (jusrmsg::parse_user_message(d, message) == 0)
{
std::lock_guard<std::mutex> lock(ctx.users_mutex);
//Add to the hashed input buffer list.
user.inputs.push_back(util::hash_buffer(message, user.pubkey));
// Message is a contract input message.
if (d[jusrmsg::FLD_TYPE] == jusrmsg::MSGTYPE_CONTRACT_INPUT)
{
std::string contentjson;
std::string sig;
if (jusrmsg::extract_signed_input_container(contentjson, sig, d) == 0)
{
std::lock_guard<std::mutex> lock(ctx.users_mutex);
//Add to the submitted input list.
user.submitted_inputs.push_back(user_submitted_message(
std::move(contentjson),
std::move(sig)));
return 0;
}
}
else
{
LOG_DBG << "Invalid user message type: " << d[jusrmsg::FLD_TYPE].GetString();
}
}
LOG_DBG << "Collected " << message.length() << " bytes from user";
// Bad message.
return -1;
}
/**

View File

@@ -5,6 +5,7 @@
#include "../util.hpp"
#include "../sock/socket_session.hpp"
#include "user_session_handler.hpp"
#include "user_input.hpp"
/**
* Maintains the global user list with pending input outputs and manages user connections.
@@ -21,16 +22,16 @@ struct connected_user
// User binary public key
const std::string pubkey;
// Holds the unprocessed user inputs (and the hashes) collected from websocket.
std::list<util::hash_buffer> inputs;
// Holds the unprocessed user inputs collected from websocket.
std::list<user_submitted_message> submitted_inputs;
// Holds the websocket session of this user.
// We don't need to own the session object since the lifetime of user and session are coupled.
sock::socket_session<user_outbound_message> *session;
/**
* @session
* @param _pubkey The public key of the user in binary format.
* @param session The web socket session the user is connected to.
* @param pubkey The public key of the user in binary format.
*/
connected_user(sock::socket_session<user_outbound_message> *session, std::string_view pubkey)
: pubkey(pubkey)
@@ -85,9 +86,9 @@ int init();
std::string issue_challenge(const std::string sessionid);
bool verify_challenge(std::string_view message, sock::socket_session<user_outbound_message> *session);
int verify_challenge(std::string_view message, sock::socket_session<user_outbound_message> *session);
void handle_user_message(connected_user &user, std::string_view message);
int handle_user_message(connected_user &user, std::string_view message);
int add_user(sock::socket_session<user_outbound_message> *session, const std::string &pubkey);

View File

@@ -59,6 +59,14 @@ int64_t get_epoch_milliseconds()
.count();
}
/**
* Sleeps the current thread for specified no. of milliseconds.
*/
void sleep(uint64_t milliseconds)
{
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
/**
* Compare two version strings in the format of "1.12.3".
* v1 < v2 -> returns -1

View File

@@ -49,39 +49,12 @@ int hex2bin(unsigned char *decoded, size_t decoded_len, std::string_view hex_str
int64_t get_epoch_milliseconds();
void sleep(uint64_t milliseconds);
int version_compare(const std::string &x, const std::string &y);
std::string_view getsv(const rapidjson::Value &v);
/**
* Represents a data buffer which calculates the hash of the buffer.
*/
struct hash_buffer
{
std::string hash;
std::string buffer;
hash_buffer(std::string_view data)
{
buffer = data;
}
hash_buffer(std::string_view data, std::string_view hashprefix)
{
buffer = data;
std::string timestr = std::to_string(get_epoch_milliseconds());
std::string stringtohash;
stringtohash.reserve(hashprefix.length() + buffer.length() + timestr.length());
stringtohash.append(hashprefix);
stringtohash.append(buffer);
stringtohash.append(timestr);
hash = crypto::get_hash(stringtohash);
}
};
} // namespace util
#endif