mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Basic consensus implementation (#45)
Consensus for user connections, user inputs, contract outputs and time.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -9,4 +9,5 @@ release/**
|
||||
**/Makefile
|
||||
**/CMakeCache.txt
|
||||
**/cmake_install.cmake
|
||||
**/CMakeFiles/**
|
||||
**/CMakeFiles/**
|
||||
hpcluster
|
||||
@@ -1,40 +1,43 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(HPCore)
|
||||
|
||||
add_definitions("-std=c++17" "-DBOOST_LOG_DYN_LINK")
|
||||
find_package(Boost REQUIRED COMPONENTS system log)
|
||||
add_definitions("-std=c++17")
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY build)
|
||||
set(CMAKE_BUILD_TYPE "MinSizeRel" FORCE)
|
||||
|
||||
add_executable(hpcore
|
||||
src/sock/socket_client.cpp
|
||||
src/sock/socket_server.cpp
|
||||
src/sock/socket_session.cpp
|
||||
src/p2p/peer_message_handler.cpp
|
||||
src/p2p/peer_session_handler.cpp
|
||||
src/p2p/p2p.cpp
|
||||
src/usr/user_session_handler.cpp
|
||||
src/usr/usr.cpp
|
||||
src/cons/cons.cpp
|
||||
src/util.cpp
|
||||
src/crypto.cpp
|
||||
src/conf.cpp
|
||||
src/hplog.cpp
|
||||
src/proc.cpp
|
||||
src/usr/user_session_handler.cpp
|
||||
src/usr/usr.cpp
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
add_custom_target(release
|
||||
COMMAND cmake -DCMAKE_BUILD_TYPE=RELEASE .
|
||||
COMMAND make hpcore
|
||||
add_custom_target(docker
|
||||
COMMAND strip ./build/hpcore
|
||||
COMMAND docker build -t hpcore:latest .
|
||||
)
|
||||
set_target_properties(release PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
set_target_properties(docker PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
add_dependencies(docker hpcore)
|
||||
|
||||
target_link_libraries(hpcore
|
||||
libsodium.a
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_LOG_LIBRARY}
|
||||
${Boost_LOG_SETUP_LIBRARY}
|
||||
libboost_system.a
|
||||
libboost_thread.a
|
||||
libboost_log.a
|
||||
libboost_log_setup.a
|
||||
libboost_filesystem.a
|
||||
stdc++fs
|
||||
pthread
|
||||
crypto
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
FROM ubuntu:bionic
|
||||
# We are going with NodeJs debian docker image because sample contracts need NodeJs to run.
|
||||
# Otherwise, hpcore itself can run on any docker image like ubuntu or debian without NodeJs.
|
||||
FROM node:10.17.0-buster-slim
|
||||
|
||||
# hpcore binary is copied to /hp directory withtin the docker image.
|
||||
WORKDIR /hp
|
||||
COPY ./build/hpcore .
|
||||
ENTRYPOINT ["/hp/hpcore"]
|
||||
118
cluster-create.sh
Executable file
118
cluster-create.sh
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Generate contract sub-directories within this script directory for the given no. of cluster nodes.
|
||||
# Usage (to generate 8-node cluster): ./cluster.sh 8
|
||||
|
||||
# Validate the node count arg.
|
||||
if [ -n "$1" ] && [ "$1" -eq "$1" ] 2>/dev/null; then
|
||||
echo "Generating a Hot Pocket cluster of ${1} node(s)..."
|
||||
else
|
||||
echo "Error: Please provide number of nodes."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Delete and recreate 'hpcluster' directory.
|
||||
rm -r hpcluster > /dev/null 2>&1
|
||||
mkdir hpcluster
|
||||
clusterloc="./hpcluster"
|
||||
|
||||
pushd $clusterloc > /dev/null 2>&1
|
||||
|
||||
# Create contract directories for all nodes in the cluster.
|
||||
ncount=$1
|
||||
for (( i=0; i<$ncount; i++ ))
|
||||
do
|
||||
|
||||
let n=$i+1
|
||||
let peerport=22860+$n
|
||||
let pubport=8080+$n
|
||||
|
||||
# Create contract dir named "node<i>"
|
||||
../build/hpcore new "node${n}" > /dev/null 2>&1
|
||||
|
||||
pushd ./node$n/cfg > /dev/null 2>&1
|
||||
|
||||
# Use NodeJs to manipulate HP json configuration.
|
||||
|
||||
mv hp.cfg tmp.json # nodejs needs file extension to be .json
|
||||
|
||||
# Collect each node pubkey and peer ports for later processing.
|
||||
|
||||
pubkeys[i]=$(node -p "require('./tmp.json').pubkeyhex")
|
||||
|
||||
# During hosting we use docker virtual dns instead of IP address.
|
||||
# So each node is reachable via 'node<id>' name.
|
||||
peers[i]="node${n}:${peerport}"
|
||||
|
||||
# Update contract config.
|
||||
node -p "JSON.stringify({...require('./tmp.json'), \
|
||||
binary: '/usr/local/bin/node', \
|
||||
binargs: './bin/contract.js', \
|
||||
peerport: ${peerport}, \
|
||||
pubport: ${pubport}, \
|
||||
roundtime: 10000,
|
||||
loglevel: 'debug'
|
||||
}, null, 2)" > hp.cfg
|
||||
rm tmp.json
|
||||
|
||||
# Generate ssl certs
|
||||
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem \
|
||||
-subj "/C=AU/ST=ST/L=L/O=O/OU=OU/CN=localhost/emailAddress=hpnode${n}@example" > /dev/null 2>&1
|
||||
popd > /dev/null 2>&1
|
||||
|
||||
# Copy the contract executable.
|
||||
mkdir ./node$n/bin
|
||||
cp ../examples/echocontract/contract.js ./node$n/bin/contract.js
|
||||
done
|
||||
|
||||
# Function to generate JSON array string while skiping a given index.
|
||||
function joinarr {
|
||||
arrname=$1[@]
|
||||
arr=("${!arrname}")
|
||||
skip=$2
|
||||
|
||||
j=0
|
||||
str="["
|
||||
for (( i=0; i<$ncount; i++ ))
|
||||
do
|
||||
let prevlast=$ncount-2
|
||||
if [ "$i" != "$skip" ]
|
||||
then
|
||||
str="$str'${arr[i]}'"
|
||||
|
||||
if [ $j -lt $prevlast ]
|
||||
then
|
||||
str="$str,"
|
||||
fi
|
||||
let j=j+1
|
||||
fi
|
||||
done
|
||||
str="$str]"
|
||||
|
||||
echo $str
|
||||
}
|
||||
|
||||
# Loop through all nodes hp.cfg and inject peer and unl lists (skip self node).
|
||||
for (( j=0; j<$ncount; j++ ))
|
||||
do
|
||||
let n=$j+1
|
||||
mypeers=$(joinarr peers $j)
|
||||
myunl=$(joinarr pubkeys $j)
|
||||
|
||||
pushd ./node$n/cfg > /dev/null 2>&1
|
||||
mv hp.cfg tmp.json # nodejs needs file extension to be .json
|
||||
node -p "JSON.stringify({...require('./tmp.json'),peers:${mypeers},unl:${myunl}}, null, 2)" > hp.cfg
|
||||
rm tmp.json
|
||||
popd > /dev/null 2>&1
|
||||
done
|
||||
|
||||
popd > /dev/null 2>&1
|
||||
|
||||
# Create docker virtual network named 'hpnet'
|
||||
# All nodes will communicate with each other via this network.
|
||||
docker network create --driver bridge hpnet > /dev/null 2>&1
|
||||
|
||||
echo "Cluster generated at ${clusterloc}"
|
||||
echo "Use \"./cluster-start.sh <nodeid>\" to run each node."
|
||||
|
||||
exit 0
|
||||
27
cluster-start.sh
Executable file
27
cluster-start.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Runs the specified node contract with hpcore docker image.
|
||||
# This script assumes you already have the hpcore docker image and 'hpnet' virtual docker network.
|
||||
# Usage (to run the node no. 1): ./start.sh 1
|
||||
|
||||
# Validate the node count arg.
|
||||
if [ -n "$1" ] && [ "$1" -eq "$1" ] 2>/dev/null; then
|
||||
echo "Starting docker container..."
|
||||
else
|
||||
echo "Error: Please provide node ID to run."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
clusterloc=$(pwd)/hpcluster
|
||||
n=$1
|
||||
|
||||
let pubport=8080+$n
|
||||
# let peerport=22860+$n #Uncomment if peer port needs to be exposed to host.
|
||||
|
||||
# Mount the node<id> contract directory into hpcore docker container and run.
|
||||
# We specify --network=hpnet so all nodes will communicate via 'hpnet' docker virtual network.
|
||||
# We specify --name for each node so it will be the virtual dns name for each node.
|
||||
docker run --rm -t -i --network=hpnet --name=node${n} \
|
||||
-p ${pubport}:${pubport} \
|
||||
--mount type=bind,source=${clusterloc}/node${n},target=/contract \
|
||||
hpcore:latest run /contract
|
||||
@@ -4,8 +4,8 @@ process.on('uncaughtException', (err) => {
|
||||
const fs = require('fs')
|
||||
|
||||
let input = fs.readFileSync(0, 'utf8');
|
||||
console.log("===Sample contract started===");
|
||||
console.log("Contract args received from hp: " + input);
|
||||
//console.log("===Sample contract started===");
|
||||
//console.log("Contract args received from hp: " + input);
|
||||
|
||||
let hpargs = JSON.parse(input);
|
||||
|
||||
@@ -22,9 +22,9 @@ Object.keys(hpargs.usrfd).forEach(function (key, index) {
|
||||
|
||||
let hpinput = fs.readFileSync(hpargs.hpfd[0], 'utf8');
|
||||
if (hpinput.length > 0) {
|
||||
console.log("Input received from hp:");
|
||||
console.log(hpinput);
|
||||
//console.log("Input received from hp:");
|
||||
//console.log(hpinput);
|
||||
fs.writeSync(hpargs.hpfd[1], "Echoing: " + hpinput);
|
||||
}
|
||||
|
||||
console.log("===Sample contract ended===");
|
||||
//console.log("===Sample contract ended===");
|
||||
@@ -93,7 +93,7 @@ function main() {
|
||||
|
||||
var input_pump = () => {
|
||||
rl.question('', (answer) => {
|
||||
ws.send(answer + "\n")
|
||||
ws.send(answer)
|
||||
input_pump()
|
||||
})
|
||||
}
|
||||
|
||||
10
src/conf.cpp
10
src/conf.cpp
@@ -38,6 +38,14 @@ int init()
|
||||
if (validate_contract_dir_paths() != 0 || load_config() != 0 || validate_config() != 0)
|
||||
return -1;
|
||||
|
||||
// Append self peer to peer list.
|
||||
std::string portstr = std::to_string(cfg.peerport);
|
||||
std::string peerid = "0.0.0.0:" + portstr;
|
||||
cfg.peers.emplace(std::move(peerid), std::make_pair("0.0.0.0", portstr));
|
||||
|
||||
// Append self pubkey to unl list.
|
||||
cfg.unl.emplace(cfg.pubkey);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -457,7 +465,7 @@ int validate_contract_dir_paths()
|
||||
{
|
||||
std::cout << path << " does not exist. Please provide self-signed certificates. Can generate using command\n"
|
||||
<< "openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem\n"
|
||||
<< "and add it to " + ctx.configDir;
|
||||
<< "and add it to " + ctx.configDir << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
500
src/cons/cons.cpp
Normal file
500
src/cons/cons.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <math.h>
|
||||
#include <thread>
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
#include "../conf.hpp"
|
||||
#include "../usr/usr.hpp"
|
||||
#include "../p2p/p2p.hpp"
|
||||
#include "../p2p/peer_message_handler.hpp"
|
||||
#include "../p2p/peer_session_handler.hpp"
|
||||
#include "../hplog.hpp"
|
||||
#include "../crypto.hpp"
|
||||
#include "../proc.hpp"
|
||||
#include "cons.hpp"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
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 void
|
||||
// threading conflicts with network incoming proposals list.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p2p::collected_msgs.proposals_mutex);
|
||||
ctx.candidate_proposals.splice(ctx.candidate_proposals.end(), p2p::collected_msgs.proposals);
|
||||
}
|
||||
|
||||
if (ctx.stage == 0)
|
||||
{
|
||||
// Stage 0 means begining of a consensus round.
|
||||
|
||||
// Remove any useless candidate proposals so we'll have a cleaner proposal set to look at
|
||||
// when we transition to stage 1.
|
||||
auto itr = ctx.candidate_proposals.begin();
|
||||
while (itr != ctx.candidate_proposals.end())
|
||||
{
|
||||
// Remove any proposal from previous round's stage 3.
|
||||
// Remove any proposal from self (pubkey match).
|
||||
// todo: check the state of these to ensure we're running consensus ledger
|
||||
if (itr->stage == 3 || conf::cfg.pubkey == itr->pubkey)
|
||||
ctx.candidate_proposals.erase(itr++);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
else // Stage 1, 2, 3
|
||||
{
|
||||
// Initialize vote counters
|
||||
vote_counter votes;
|
||||
|
||||
// check if we're ahead/behind of consensus
|
||||
bool is_desync, reset_to_stage0;
|
||||
int8_t majority_stage;
|
||||
check_majority_stage(is_desync, reset_to_stage0, majority_stage, votes);
|
||||
if (is_desync)
|
||||
{
|
||||
timewait_stage(reset_to_stage0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Remove all candidate proposals that are behind our current stage.
|
||||
auto itr = ctx.candidate_proposals.begin();
|
||||
while (itr != ctx.candidate_proposals.end())
|
||||
{
|
||||
if (itr->stage < ctx.stage)
|
||||
ctx.candidate_proposals.erase(itr++);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
|
||||
if (ctx.stage == 3)
|
||||
{
|
||||
apply_ledger(stg_prop);
|
||||
|
||||
// We have finished a consensus round (all 4 stages).
|
||||
LOG_DBG << "****Stage 3 consensus reached****";
|
||||
}
|
||||
}
|
||||
|
||||
// We have finished a consensus stage.
|
||||
|
||||
// Transition to next stage.
|
||||
ctx.stage = (ctx.stage + 1) % 4;
|
||||
|
||||
// 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));
|
||||
else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(conf::cfg.roundtime / 4));
|
||||
}
|
||||
|
||||
p2p::proposal create_stage0_proposal()
|
||||
{
|
||||
// The proposal we are going to emit in stage 0.
|
||||
p2p::proposal stg_prop;
|
||||
stg_prop.time = ctx.time_now;
|
||||
stg_prop.timestamp = ctx.time_now;
|
||||
ctx.novel_proposal_time = ctx.time_now;
|
||||
stg_prop.stage = 0;
|
||||
stg_prop.lcl = ctx.lcl;
|
||||
|
||||
// Populate the stg_prop with users list (user pubkey list) and their inputs.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(usr::users_mutex);
|
||||
for (auto &[sid, user] : usr::users)
|
||||
{
|
||||
// add all the user connections we host
|
||||
stg_prop.users.emplace(user.pubkey);
|
||||
|
||||
// and all their pending messages
|
||||
if (!user.inbuffer.empty())
|
||||
{
|
||||
std::string input;
|
||||
input.swap(user.inbuffer);
|
||||
stg_prop.raw_inputs.try_emplace(user.pubkey, std::move(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate the stg_prop with any contract outputs from previous round's stage 3.
|
||||
for (auto &[pubkey, bufpair] : ctx.local_userbuf)
|
||||
{
|
||||
if (!bufpair.second.empty()) // bufpair.second is the output buffer.
|
||||
{
|
||||
std::string rawoutput;
|
||||
rawoutput.swap(bufpair.second);
|
||||
|
||||
stg_prop.raw_outputs.try_emplace(pubkey, std::move(rawoutput));
|
||||
}
|
||||
}
|
||||
ctx.local_userbuf.clear();
|
||||
|
||||
// todo: set propsal states
|
||||
// todo: generate stg_prop hash and check with ctx.novel_proposal, we are sending same stg_prop again.
|
||||
|
||||
return stg_prop;
|
||||
}
|
||||
|
||||
p2p::proposal create_stage123_proposal(vote_counter &votes)
|
||||
{
|
||||
// The proposal to be emited at the end of this stage.
|
||||
p2p::proposal stg_prop;
|
||||
stg_prop.timestamp = ctx.time_now;
|
||||
stg_prop.stage = ctx.stage;
|
||||
|
||||
// we always vote for our current lcl 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;
|
||||
|
||||
//todo:check lcl votes and wait for proposals
|
||||
|
||||
// Vote for rest of the proposal fields
|
||||
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
|
||||
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 inputs
|
||||
|
||||
// Proposals from stage 0 will have raw inputs in them.
|
||||
if (!cp.raw_inputs.empty())
|
||||
{
|
||||
for (auto &[pubkey, input] : cp.raw_inputs)
|
||||
{
|
||||
// Hash the pubkey+input.
|
||||
std::string str_to_hash;
|
||||
str_to_hash.reserve(pubkey.size() + input.size());
|
||||
str_to_hash.append(pubkey);
|
||||
str_to_hash.append(input);
|
||||
std::string hash = crypto::sha_512_hash(str_to_hash, "INP", 3);
|
||||
|
||||
// Vote for the hash.
|
||||
increment(votes.inputs, hash);
|
||||
|
||||
// Remember the actual input along with the hash for future use for apply-ledger.
|
||||
ctx.possible_inputs.try_emplace(
|
||||
std::move(hash),
|
||||
std::make_pair(pubkey, input));
|
||||
}
|
||||
}
|
||||
// Proposals from stage 1, 2, 3 will have hashed inputs in them.
|
||||
else if (!cp.hash_inputs.empty())
|
||||
{
|
||||
for (const std::string &inputhash : cp.hash_inputs)
|
||||
increment(votes.inputs, inputhash);
|
||||
}
|
||||
|
||||
// Vote for user outputs
|
||||
|
||||
// Proposals from stage 0 will have raw user outputs in them.
|
||||
if (!cp.raw_outputs.empty())
|
||||
{
|
||||
for (auto &[pubkey, output] : cp.raw_outputs)
|
||||
{
|
||||
// Hash the pubkey+input.
|
||||
std::string str_to_hash;
|
||||
str_to_hash.reserve(pubkey.size() + output.size());
|
||||
str_to_hash.append(pubkey);
|
||||
str_to_hash.append(output);
|
||||
std::string hash = crypto::sha_512_hash(str_to_hash, "OUT", 3);
|
||||
|
||||
// Vote for the hash.
|
||||
increment<std::string>(votes.outputs, hash);
|
||||
|
||||
// Remember the actual output along with the hash for future use for apply-ledger.
|
||||
ctx.possible_outputs.try_emplace(
|
||||
std::move(hash),
|
||||
std::make_pair(pubkey, output));
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// todo: repeat above for state
|
||||
}
|
||||
|
||||
float_t vote_threshold = get_stage_threshold(ctx.stage);
|
||||
|
||||
// todo: check if inputs being proposed by another node are actually spoofed inputs
|
||||
// from a user locally connected to this node.
|
||||
|
||||
// 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 inputs which have votes over stage threshold to proposal.
|
||||
for (auto &[hash, numvotes] : votes.inputs)
|
||||
if (numvotes >= vote_threshold || (numvotes > 0 && ctx.stage == 1))
|
||||
stg_prop.hash_inputs.emplace(hash);
|
||||
|
||||
// Add outputs which have votes over stage threshold to proposal.
|
||||
for (auto &[hash, numvotes] : votes.outputs)
|
||||
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;
|
||||
for (auto [time, numvotes] : votes.time)
|
||||
{
|
||||
if (numvotes > highest_votes)
|
||||
{
|
||||
highest_votes = numvotes;
|
||||
stg_prop.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
return stg_prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the given proposal to all connected peers.
|
||||
* @return 0 on success. -1 if not peers to broadcast.
|
||||
*/
|
||||
int broadcast_proposal(const p2p::proposal &p)
|
||||
{
|
||||
p2p::peer_outbound_message msg(std::make_shared<flatbuffers::FlatBufferBuilder>(1024));
|
||||
p2p::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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether our current stage is ahead or behind of the majority stage.
|
||||
*/
|
||||
void check_majority_stage(bool &is_desync, bool &should_reset, int8_t &majority_stage, vote_counter &votes)
|
||||
{
|
||||
// Stage votes.
|
||||
for (const p2p::proposal &cp : ctx.candidate_proposals)
|
||||
{
|
||||
// Vote stages if only proposal lcl is match with node's last consensus lcl
|
||||
if (cp.lcl == ctx.lcl)
|
||||
increment(votes.stage, cp.stage);
|
||||
|
||||
// todo:vote for lcl checking condtion
|
||||
}
|
||||
|
||||
majority_stage = -1;
|
||||
is_desync = false;
|
||||
|
||||
int32_t highest_votes = 0;
|
||||
for (const auto [stage, votes] : votes.stage)
|
||||
{
|
||||
if (votes > highest_votes)
|
||||
{
|
||||
highest_votes = votes;
|
||||
majority_stage = stage;
|
||||
}
|
||||
}
|
||||
|
||||
if (majority_stage < ctx.stage - 1)
|
||||
{
|
||||
should_reset = (ctx.time_now - ctx.novel_proposal_time) < floor(conf::cfg.roundtime / 4);
|
||||
is_desync = true;
|
||||
|
||||
LOG_DBG << "Stage desync (Reset:" << should_reset << "). Node stage:" << std::to_string(ctx.stage)
|
||||
<< " is ahead of majority stage:" << std::to_string(majority_stage);
|
||||
}
|
||||
else if (majority_stage > ctx.stage - 1)
|
||||
{
|
||||
should_reset = true;
|
||||
is_desync = true;
|
||||
|
||||
LOG_DBG << "Stage desync (Reset:" << should_reset << "). Node stage:" << std::to_string(ctx.stage)
|
||||
<< " is behind majority stage:" << std::to_string(majority_stage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the consensus percentage threshold for the specified stage.
|
||||
* @param stage The consensus stage [1, 2, 3]
|
||||
*/
|
||||
float_t get_stage_threshold(int8_t stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case 1:
|
||||
return cons::STAGE1_THRESHOLD * conf::cfg.unl.size();
|
||||
case 2:
|
||||
return cons::STAGE2_THRESHOLD * conf::cfg.unl.size();
|
||||
case 3:
|
||||
return cons::STAGE3_THRESHOLD * conf::cfg.unl.size();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void timewait_stage(bool reset)
|
||||
{
|
||||
if (reset)
|
||||
ctx.stage = 0;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(conf::cfg.roundtime / 100));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize the ledger after consensus.
|
||||
* @param cons_prop The proposal that reached consensus.
|
||||
*/
|
||||
void apply_ledger(const p2p::proposal &cons_prop)
|
||||
{
|
||||
// todo:write lcl.
|
||||
|
||||
// Send any output from the previous consensus round to users.
|
||||
for (const std::string &hash : cons_prop.hash_outputs)
|
||||
{
|
||||
auto itr = ctx.possible_outputs.find(hash);
|
||||
bool hashfound = (itr != ctx.possible_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.";
|
||||
// todo: consider fatal
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send outputs to users.
|
||||
auto &[pubkey, output] = itr->second;
|
||||
std::string outputtosend;
|
||||
outputtosend.swap(output);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(usr::users_mutex);
|
||||
|
||||
// Find the user by session id.
|
||||
const std::string sessionid = usr::sessionids[pubkey];
|
||||
auto itr = usr::users.find(sessionid);
|
||||
if (itr != usr::users.end())
|
||||
{
|
||||
const usr::connected_user &user = itr->second;
|
||||
usr::user_outbound_message outmsg(std::move(outputtosend));
|
||||
user.session->send(std::move(outmsg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
//todo:remove entries from pending inputs that made their way into a closed ledger
|
||||
for (const std::string &hash : cons_prop.hash_inputs)
|
||||
{
|
||||
auto itr = ctx.possible_inputs.find(hash);
|
||||
bool hashfound = (itr != ctx.possible_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
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare ctx.local_userbuf with user inputs to feed to the contract.
|
||||
|
||||
const std::string &pubkey = itr->second.first;
|
||||
std::string rawinput = itr->second.second;
|
||||
|
||||
std::string inputtofeed;
|
||||
inputtofeed.swap(rawinput);
|
||||
|
||||
std::pair<std::string, std::string> bufpair;
|
||||
bufpair.first = std::move(inputtofeed);
|
||||
ctx.local_userbuf.try_emplace(pubkey, std::move(bufpair));
|
||||
}
|
||||
}
|
||||
|
||||
run_contract_binary(cons_prop.time);
|
||||
}
|
||||
|
||||
void run_contract_binary(int64_t time_now)
|
||||
{
|
||||
std::pair<std::string, std::string> hpscbufpair;
|
||||
std::unordered_map<std::string, std::pair<std::string, std::string>> nplbufs;
|
||||
|
||||
proc::ContractExecArgs eargs(time_now, ctx.local_userbuf, nplbufs, hpscbufpair);
|
||||
proc::exec_contract(eargs);
|
||||
}
|
||||
|
||||
} // namespace cons
|
||||
72
src/cons/cons.hpp
Normal file
72
src/cons/cons.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef _HP_CONS_H_
|
||||
#define _HP_CONS_H_
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <ctime>
|
||||
#include "../p2p/p2p.hpp"
|
||||
|
||||
namespace cons
|
||||
{
|
||||
|
||||
//stage 1 vote threshold
|
||||
static const float STAGE1_THRESHOLD = 0.5;
|
||||
//stage 2 vote threshold
|
||||
static const float STAGE2_THRESHOLD = 0.65;
|
||||
//stage 3 vote threshold
|
||||
static const float STAGE3_THRESHOLD = 0.8;
|
||||
|
||||
/**
|
||||
* This is used to store consensus information
|
||||
*/
|
||||
struct consensus_context
|
||||
{
|
||||
std::list<p2p::proposal> candidate_proposals;
|
||||
|
||||
int8_t stage;
|
||||
int64_t novel_proposal_time;
|
||||
int64_t time_now;
|
||||
std::string lcl;
|
||||
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, std::pair<std::string, std::string>> local_userbuf;
|
||||
|
||||
int32_t next_sleep;
|
||||
};
|
||||
|
||||
struct vote_counter
|
||||
{
|
||||
std::map<int8_t, int32_t> stage;
|
||||
std::map<int64_t, int32_t> time;
|
||||
std::map<std::string, int32_t> lcl;
|
||||
std::map<std::string, int32_t> users;
|
||||
std::map<std::string, int32_t> inputs;
|
||||
std::map<std::string, int32_t> outputs;
|
||||
};
|
||||
|
||||
extern consensus_context ctx;
|
||||
|
||||
void consensus();
|
||||
|
||||
void apply_ledger(const p2p::proposal &proposal);
|
||||
|
||||
float_t get_stage_threshold(int8_t stage);
|
||||
|
||||
void timewait_stage(bool reset);
|
||||
|
||||
p2p::proposal create_stage0_proposal();
|
||||
|
||||
p2p::proposal create_stage123_proposal(vote_counter &votes);
|
||||
|
||||
int broadcast_proposal(const p2p::proposal &p);
|
||||
|
||||
void check_majority_stage(bool &is_desync, bool &should_reset, int8_t &majority_stage, vote_counter &votes);
|
||||
|
||||
void run_contract_binary(std::int64_t time);
|
||||
|
||||
} // namespace cons
|
||||
|
||||
#endif
|
||||
@@ -132,25 +132,6 @@ int verify_hex(std::string_view msg, std::string_view sighex, std::string_view p
|
||||
decoded_pubkey + 1); // +1 to skip prefix byte.
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SHA 512 hash for message prepend with prefix before hashing.
|
||||
*
|
||||
* @param msg message string.
|
||||
* @param prefix prefix char array.
|
||||
* @param char_length length of prefix char array.
|
||||
* @return SHA 512 hash.
|
||||
*/
|
||||
std::string sha_512_hash(const std::string &msg, const char *prefix, size_t char_length)
|
||||
{
|
||||
std::string payload;
|
||||
payload.reserve(char_length + msg.size());
|
||||
payload.append(prefix);
|
||||
payload.append(msg);
|
||||
unsigned char hashchars[crypto_hash_sha512_BYTES];
|
||||
crypto_hash_sha512(hashchars, (unsigned char *)payload.data(), payload.length());
|
||||
return std::string((char *)hashchars, crypto_hash_sha512_BYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SHA 512 hash for message prepend with prefix before hashing.
|
||||
*
|
||||
|
||||
@@ -29,8 +29,6 @@ int verify(std::string_view msg, std::string_view sig, std::string_view pubkey);
|
||||
|
||||
int verify_hex(std::string_view msg, std::string_view sighex, std::string_view pubkeyhex);
|
||||
|
||||
std::string sha_512_hash(const std::string &msg, const char *prefix, size_t char_length);
|
||||
|
||||
std::string sha_512_hash(std::string_view msg, const char *prefix, size_t char_length);
|
||||
|
||||
} // namespace crypto
|
||||
|
||||
@@ -79,12 +79,12 @@ void init()
|
||||
|
||||
// This will make every new launch of Hot Pocket to start a new log file number.
|
||||
// It will scan existing log files matching the pattern and find the next number.
|
||||
keywords::scan_method = sinks::file::scan_matching,
|
||||
keywords::scan_method = sinks::file::scan_matching
|
||||
|
||||
#ifndef NDEBUG
|
||||
// We enable auto_flush to immediately get the logs onto the file. Otherwise it takes time
|
||||
// for buffered logs to reach the file. This impacts performance. So enabled only in debug build.
|
||||
keywords::auto_flush = true
|
||||
, keywords::auto_flush = true
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
56
src/main.cpp
56
src/main.cpp
@@ -13,9 +13,9 @@
|
||||
#include "conf.hpp"
|
||||
#include "crypto.hpp"
|
||||
#include "usr/usr.hpp"
|
||||
#include "p2p/p2p.hpp"
|
||||
#include "proc.hpp"
|
||||
#include "hplog.hpp"
|
||||
#include "cons/cons.hpp"
|
||||
|
||||
/**
|
||||
* Parses CLI args and extracts hot pocket command and parameters given.
|
||||
@@ -124,65 +124,21 @@ int main(int argc, char **argv)
|
||||
|
||||
hplog::init();
|
||||
|
||||
if (usr::init() != 0)
|
||||
if (p2p::init() != 0)
|
||||
return -1;
|
||||
|
||||
if (p2p::init() != 0)
|
||||
if (usr::init() != 0)
|
||||
return -1;
|
||||
|
||||
// After initializing primary subsystems, register the SIGINT handler.
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
// This will start hosting the contract and start consensus rounds.
|
||||
// TODO
|
||||
cons::ctx.stage = 0;
|
||||
cons::ctx.lcl = "static_lcl";
|
||||
|
||||
while (true)
|
||||
{
|
||||
sleep(2);
|
||||
|
||||
// Test code to execute contract and collect outputs.
|
||||
std::unordered_map<std::string, std::pair<std::string, std::string>> userbufs;
|
||||
for (auto &[sid, user] : usr::users)
|
||||
{
|
||||
std::pair<std::string, std::string> bufpair;
|
||||
std::string inputtosend;
|
||||
inputtosend.swap(user.inbuffer);
|
||||
bufpair.first = std::move(inputtosend);
|
||||
|
||||
userbufs.emplace(user.pubkey, std::move(bufpair));
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> hpscbufpair;
|
||||
hpscbufpair.first = "{msg:'Message from HP'}";
|
||||
|
||||
std::unordered_map<std::string, std::pair<std::string, std::string>> nplbufs;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
std::pair<std::string, std::string> bufpair;
|
||||
nplbufs.emplace("aaa", std::move(bufpair));
|
||||
}
|
||||
|
||||
proc::ContractExecArgs eargs(123123345, userbufs, nplbufs, hpscbufpair);
|
||||
proc::exec_contract(eargs);
|
||||
|
||||
for (auto &[pubkey, bufpair] : userbufs)
|
||||
{
|
||||
if (!bufpair.second.empty())
|
||||
{
|
||||
// Find the user session id by the pubkey.
|
||||
const std::string sessionid = usr::sessionids[pubkey];
|
||||
|
||||
// Find the user by session id.
|
||||
auto itr = usr::users.find(sessionid);
|
||||
const usr::connected_user &user = itr->second;
|
||||
user.session->send(std::move(bufpair.second));
|
||||
}
|
||||
}
|
||||
|
||||
if (!hpscbufpair.second.empty())
|
||||
LOG_DBG << "Message from SC: " << hpscbufpair.second;
|
||||
|
||||
userbufs.clear();
|
||||
cons::consensus();
|
||||
}
|
||||
|
||||
// Free resources.
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
//IDL file for p2p message content schema.
|
||||
namespace p2p;
|
||||
|
||||
table StringKeyValuePair { //flatbuff equivalent for dictionary/hashmap for <byte>
|
||||
table BytesKeyValuePair { //flatbuff equivalent for dictionary/hashmap for <byte>
|
||||
key:[ubyte];
|
||||
value:string;
|
||||
value:[ubyte];
|
||||
}
|
||||
|
||||
table ByteArray { //To represent list of byte arrays
|
||||
array:[ubyte];
|
||||
}
|
||||
|
||||
union Message { Proposal, Npl } //message content type
|
||||
union Message { Proposal_Message, Npl_Message } //message content type
|
||||
|
||||
table Content {
|
||||
message:Message;
|
||||
}
|
||||
|
||||
table Proposal { //Proposal type message schema
|
||||
table Proposal_Message { //Proposal type message schema
|
||||
pubkey:[ubyte];
|
||||
timestamp:uint64;
|
||||
stage: int8;
|
||||
time:uint64;
|
||||
lcl:[ubyte];
|
||||
connections: [ByteArray];
|
||||
raw_inputs: [StringKeyValuePair]; //stage 0 inputs
|
||||
hash_inputs:[string]; //stage > 0 inputs (hash of satge 0 inputs)
|
||||
raw_outputs: [StringKeyValuePair]; //stage 0 outputs
|
||||
hash_outputs:[string]; //stage > 0 outputs (hash of satge 0 outputs)
|
||||
users: [ByteArray];
|
||||
raw_inputs: [BytesKeyValuePair]; //stage 0 inputs
|
||||
hash_inputs:[ByteArray]; //stage > 0 inputs (hash of stage 0 inputs)
|
||||
raw_outputs: [BytesKeyValuePair]; //stage 0 outputs
|
||||
hash_outputs:[ByteArray]; //stage > 0 outputs (hash of stage 0 outputs)
|
||||
state: State;
|
||||
}
|
||||
|
||||
table Npl { //NPL type message schema
|
||||
table Npl_Message { //NPL type message schema
|
||||
pubkey:[ubyte];
|
||||
timestamp:uint64;
|
||||
data:[ubyte];
|
||||
@@ -38,16 +38,16 @@ table Npl { //NPL type message schema
|
||||
}
|
||||
|
||||
table StateDifference { //Represent state difference by tracking created,updated and deleted state files.
|
||||
created: [StringKeyValuePair]; //list of { fn => hash }
|
||||
updated: [StringKeyValuePair];
|
||||
deleted: [StringKeyValuePair];
|
||||
created: [BytesKeyValuePair]; //list of { fn => hash }
|
||||
updated: [BytesKeyValuePair];
|
||||
deleted: [BytesKeyValuePair];
|
||||
}
|
||||
|
||||
table State {
|
||||
previous: [ubyte]; // hash of the previous state
|
||||
current: [ubyte]; // hash of the current state
|
||||
difference: StateDifference;
|
||||
patch: [StringKeyValuePair]; // fn -> bsdiff patch going from previous state to new state
|
||||
patch: [BytesKeyValuePair]; // fn -> bsdiff patch going from previous state to new state
|
||||
}
|
||||
|
||||
root_type Content; //root type for message content
|
||||
@@ -8,15 +8,15 @@
|
||||
|
||||
namespace p2p {
|
||||
|
||||
struct StringKeyValuePair;
|
||||
struct BytesKeyValuePair;
|
||||
|
||||
struct ByteArray;
|
||||
|
||||
struct Content;
|
||||
|
||||
struct Proposal;
|
||||
struct Proposal_Message;
|
||||
|
||||
struct Npl;
|
||||
struct Npl_Message;
|
||||
|
||||
struct StateDifference;
|
||||
|
||||
@@ -24,17 +24,17 @@ struct State;
|
||||
|
||||
enum Message {
|
||||
Message_NONE = 0,
|
||||
Message_Proposal = 1,
|
||||
Message_Npl = 2,
|
||||
Message_Proposal_Message = 1,
|
||||
Message_Npl_Message = 2,
|
||||
Message_MIN = Message_NONE,
|
||||
Message_MAX = Message_Npl
|
||||
Message_MAX = Message_Npl_Message
|
||||
};
|
||||
|
||||
inline const Message (&EnumValuesMessage())[3] {
|
||||
static const Message values[] = {
|
||||
Message_NONE,
|
||||
Message_Proposal,
|
||||
Message_Npl
|
||||
Message_Proposal_Message,
|
||||
Message_Npl_Message
|
||||
};
|
||||
return values;
|
||||
}
|
||||
@@ -42,15 +42,15 @@ inline const Message (&EnumValuesMessage())[3] {
|
||||
inline const char * const *EnumNamesMessage() {
|
||||
static const char * const names[] = {
|
||||
"NONE",
|
||||
"Proposal",
|
||||
"Npl",
|
||||
"Proposal_Message",
|
||||
"Npl_Message",
|
||||
nullptr
|
||||
};
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameMessage(Message e) {
|
||||
if (e < Message_NONE || e > Message_Npl) return "";
|
||||
if (e < Message_NONE || e > Message_Npl_Message) return "";
|
||||
const size_t index = static_cast<size_t>(e);
|
||||
return EnumNamesMessage()[index];
|
||||
}
|
||||
@@ -59,18 +59,18 @@ template<typename T> struct MessageTraits {
|
||||
static const Message enum_value = Message_NONE;
|
||||
};
|
||||
|
||||
template<> struct MessageTraits<Proposal> {
|
||||
static const Message enum_value = Message_Proposal;
|
||||
template<> struct MessageTraits<Proposal_Message> {
|
||||
static const Message enum_value = Message_Proposal_Message;
|
||||
};
|
||||
|
||||
template<> struct MessageTraits<Npl> {
|
||||
static const Message enum_value = Message_Npl;
|
||||
template<> struct MessageTraits<Npl_Message> {
|
||||
static const Message enum_value = Message_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 StringKeyValuePair FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
struct BytesKeyValuePair FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_KEY = 4,
|
||||
VT_VALUE = 6
|
||||
@@ -81,60 +81,60 @@ struct StringKeyValuePair FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
flatbuffers::Vector<uint8_t> *mutable_key() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_KEY);
|
||||
}
|
||||
const flatbuffers::String *value() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_VALUE);
|
||||
const flatbuffers::Vector<uint8_t> *value() const {
|
||||
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_VALUE);
|
||||
}
|
||||
flatbuffers::String *mutable_value() {
|
||||
return GetPointer<flatbuffers::String *>(VT_VALUE);
|
||||
flatbuffers::Vector<uint8_t> *mutable_value() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_VALUE);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_KEY) &&
|
||||
verifier.VerifyVector(key()) &&
|
||||
VerifyOffset(verifier, VT_VALUE) &&
|
||||
verifier.VerifyString(value()) &&
|
||||
verifier.VerifyVector(value()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct StringKeyValuePairBuilder {
|
||||
struct BytesKeyValuePairBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_key(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> key) {
|
||||
fbb_.AddOffset(StringKeyValuePair::VT_KEY, key);
|
||||
fbb_.AddOffset(BytesKeyValuePair::VT_KEY, key);
|
||||
}
|
||||
void add_value(flatbuffers::Offset<flatbuffers::String> value) {
|
||||
fbb_.AddOffset(StringKeyValuePair::VT_VALUE, value);
|
||||
void add_value(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> value) {
|
||||
fbb_.AddOffset(BytesKeyValuePair::VT_VALUE, value);
|
||||
}
|
||||
explicit StringKeyValuePairBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
explicit BytesKeyValuePairBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
StringKeyValuePairBuilder &operator=(const StringKeyValuePairBuilder &);
|
||||
flatbuffers::Offset<StringKeyValuePair> Finish() {
|
||||
BytesKeyValuePairBuilder &operator=(const BytesKeyValuePairBuilder &);
|
||||
flatbuffers::Offset<BytesKeyValuePair> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<StringKeyValuePair>(end);
|
||||
auto o = flatbuffers::Offset<BytesKeyValuePair>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<StringKeyValuePair> CreateStringKeyValuePair(
|
||||
inline flatbuffers::Offset<BytesKeyValuePair> CreateBytesKeyValuePair(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> key = 0,
|
||||
flatbuffers::Offset<flatbuffers::String> value = 0) {
|
||||
StringKeyValuePairBuilder builder_(_fbb);
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> value = 0) {
|
||||
BytesKeyValuePairBuilder builder_(_fbb);
|
||||
builder_.add_value(value);
|
||||
builder_.add_key(key);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<StringKeyValuePair> CreateStringKeyValuePairDirect(
|
||||
inline flatbuffers::Offset<BytesKeyValuePair> CreateBytesKeyValuePairDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<uint8_t> *key = nullptr,
|
||||
const char *value = nullptr) {
|
||||
const std::vector<uint8_t> *value = nullptr) {
|
||||
auto key__ = key ? _fbb.CreateVector<uint8_t>(*key) : 0;
|
||||
auto value__ = value ? _fbb.CreateString(value) : 0;
|
||||
return p2p::CreateStringKeyValuePair(
|
||||
auto value__ = value ? _fbb.CreateVector<uint8_t>(*value) : 0;
|
||||
return p2p::CreateBytesKeyValuePair(
|
||||
_fbb,
|
||||
key__,
|
||||
value__);
|
||||
@@ -208,11 +208,11 @@ struct Content FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
return GetPointer<const void *>(VT_MESSAGE);
|
||||
}
|
||||
template<typename T> const T *message_as() const;
|
||||
const Proposal *message_as_Proposal() const {
|
||||
return message_type() == Message_Proposal ? static_cast<const Proposal *>(message()) : nullptr;
|
||||
const Proposal_Message *message_as_Proposal_Message() const {
|
||||
return message_type() == Message_Proposal_Message ? static_cast<const Proposal_Message *>(message()) : nullptr;
|
||||
}
|
||||
const Npl *message_as_Npl() const {
|
||||
return message_type() == Message_Npl ? static_cast<const Npl *>(message()) : nullptr;
|
||||
const Npl_Message *message_as_Npl_Message() const {
|
||||
return message_type() == Message_Npl_Message ? static_cast<const Npl_Message *>(message()) : nullptr;
|
||||
}
|
||||
void *mutable_message() {
|
||||
return GetPointer<void *>(VT_MESSAGE);
|
||||
@@ -226,12 +226,12 @@ struct Content FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
}
|
||||
};
|
||||
|
||||
template<> inline const Proposal *Content::message_as<Proposal>() const {
|
||||
return message_as_Proposal();
|
||||
template<> inline const Proposal_Message *Content::message_as<Proposal_Message>() const {
|
||||
return message_as_Proposal_Message();
|
||||
}
|
||||
|
||||
template<> inline const Npl *Content::message_as<Npl>() const {
|
||||
return message_as_Npl();
|
||||
template<> inline const Npl_Message *Content::message_as<Npl_Message>() const {
|
||||
return message_as_Npl_Message();
|
||||
}
|
||||
|
||||
struct ContentBuilder {
|
||||
@@ -265,14 +265,14 @@ inline flatbuffers::Offset<Content> CreateContent(
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct Proposal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
struct Proposal_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PUBKEY = 4,
|
||||
VT_TIMESTAMP = 6,
|
||||
VT_STAGE = 8,
|
||||
VT_TIME = 10,
|
||||
VT_LCL = 12,
|
||||
VT_CONNECTIONS = 14,
|
||||
VT_USERS = 14,
|
||||
VT_RAW_INPUTS = 16,
|
||||
VT_HASH_INPUTS = 18,
|
||||
VT_RAW_OUTPUTS = 20,
|
||||
@@ -309,35 +309,35 @@ struct Proposal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
flatbuffers::Vector<uint8_t> *mutable_lcl() {
|
||||
return GetPointer<flatbuffers::Vector<uint8_t> *>(VT_LCL);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *connections() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_CONNECTIONS);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *users() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_USERS);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *mutable_connections() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_CONNECTIONS);
|
||||
flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *mutable_users() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_USERS);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *raw_inputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_RAW_INPUTS);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *raw_inputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_RAW_INPUTS);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *mutable_raw_inputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_RAW_INPUTS);
|
||||
flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *mutable_raw_inputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_RAW_INPUTS);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *hash_inputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_HASH_INPUTS);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *hash_inputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_HASH_INPUTS);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *mutable_hash_inputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_HASH_INPUTS);
|
||||
flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *mutable_hash_inputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_HASH_INPUTS);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *raw_outputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_RAW_OUTPUTS);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *raw_outputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_RAW_OUTPUTS);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *mutable_raw_outputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_RAW_OUTPUTS);
|
||||
flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *mutable_raw_outputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_RAW_OUTPUTS);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *hash_outputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_HASH_OUTPUTS);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *hash_outputs() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_HASH_OUTPUTS);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *mutable_hash_outputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_HASH_OUTPUTS);
|
||||
flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *mutable_hash_outputs() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *>(VT_HASH_OUTPUTS);
|
||||
}
|
||||
const State *state() const {
|
||||
return GetPointer<const State *>(VT_STATE);
|
||||
@@ -354,89 +354,89 @@ struct Proposal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
VerifyField<uint64_t>(verifier, VT_TIME) &&
|
||||
VerifyOffset(verifier, VT_LCL) &&
|
||||
verifier.VerifyVector(lcl()) &&
|
||||
VerifyOffset(verifier, VT_CONNECTIONS) &&
|
||||
verifier.VerifyVector(connections()) &&
|
||||
verifier.VerifyVectorOfTables(connections()) &&
|
||||
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.VerifyVectorOfStrings(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.VerifyVectorOfStrings(hash_outputs()) &&
|
||||
verifier.VerifyVectorOfTables(hash_outputs()) &&
|
||||
VerifyOffset(verifier, VT_STATE) &&
|
||||
verifier.VerifyTable(state()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ProposalBuilder {
|
||||
struct Proposal_MessageBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_pubkey(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey) {
|
||||
fbb_.AddOffset(Proposal::VT_PUBKEY, pubkey);
|
||||
fbb_.AddOffset(Proposal_Message::VT_PUBKEY, pubkey);
|
||||
}
|
||||
void add_timestamp(uint64_t timestamp) {
|
||||
fbb_.AddElement<uint64_t>(Proposal::VT_TIMESTAMP, timestamp, 0);
|
||||
fbb_.AddElement<uint64_t>(Proposal_Message::VT_TIMESTAMP, timestamp, 0);
|
||||
}
|
||||
void add_stage(int8_t stage) {
|
||||
fbb_.AddElement<int8_t>(Proposal::VT_STAGE, stage, 0);
|
||||
fbb_.AddElement<int8_t>(Proposal_Message::VT_STAGE, stage, 0);
|
||||
}
|
||||
void add_time(uint64_t time) {
|
||||
fbb_.AddElement<uint64_t>(Proposal::VT_TIME, time, 0);
|
||||
fbb_.AddElement<uint64_t>(Proposal_Message::VT_TIME, time, 0);
|
||||
}
|
||||
void add_lcl(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl) {
|
||||
fbb_.AddOffset(Proposal::VT_LCL, lcl);
|
||||
fbb_.AddOffset(Proposal_Message::VT_LCL, lcl);
|
||||
}
|
||||
void add_connections(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> connections) {
|
||||
fbb_.AddOffset(Proposal::VT_CONNECTIONS, connections);
|
||||
void add_users(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> users) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_USERS, users);
|
||||
}
|
||||
void add_raw_inputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> raw_inputs) {
|
||||
fbb_.AddOffset(Proposal::VT_RAW_INPUTS, raw_inputs);
|
||||
void add_raw_inputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> raw_inputs) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_RAW_INPUTS, raw_inputs);
|
||||
}
|
||||
void add_hash_inputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> hash_inputs) {
|
||||
fbb_.AddOffset(Proposal::VT_HASH_INPUTS, hash_inputs);
|
||||
void add_hash_inputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> hash_inputs) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_HASH_INPUTS, hash_inputs);
|
||||
}
|
||||
void add_raw_outputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> raw_outputs) {
|
||||
fbb_.AddOffset(Proposal::VT_RAW_OUTPUTS, raw_outputs);
|
||||
void add_raw_outputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> raw_outputs) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_RAW_OUTPUTS, raw_outputs);
|
||||
}
|
||||
void add_hash_outputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> hash_outputs) {
|
||||
fbb_.AddOffset(Proposal::VT_HASH_OUTPUTS, hash_outputs);
|
||||
void add_hash_outputs(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> hash_outputs) {
|
||||
fbb_.AddOffset(Proposal_Message::VT_HASH_OUTPUTS, hash_outputs);
|
||||
}
|
||||
void add_state(flatbuffers::Offset<State> state) {
|
||||
fbb_.AddOffset(Proposal::VT_STATE, state);
|
||||
fbb_.AddOffset(Proposal_Message::VT_STATE, state);
|
||||
}
|
||||
explicit ProposalBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
explicit Proposal_MessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
ProposalBuilder &operator=(const ProposalBuilder &);
|
||||
flatbuffers::Offset<Proposal> Finish() {
|
||||
Proposal_MessageBuilder &operator=(const Proposal_MessageBuilder &);
|
||||
flatbuffers::Offset<Proposal_Message> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<Proposal>(end);
|
||||
auto o = flatbuffers::Offset<Proposal_Message>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Proposal> CreateProposal(
|
||||
inline flatbuffers::Offset<Proposal_Message> CreateProposal_Message(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey = 0,
|
||||
uint64_t timestamp = 0,
|
||||
int8_t stage = 0,
|
||||
uint64_t time = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> connections = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> raw_inputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> hash_inputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> raw_outputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> hash_outputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> users = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> raw_inputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> hash_inputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> raw_outputs = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ByteArray>>> hash_outputs = 0,
|
||||
flatbuffers::Offset<State> state = 0) {
|
||||
ProposalBuilder builder_(_fbb);
|
||||
Proposal_MessageBuilder builder_(_fbb);
|
||||
builder_.add_time(time);
|
||||
builder_.add_timestamp(timestamp);
|
||||
builder_.add_state(state);
|
||||
@@ -444,41 +444,41 @@ inline flatbuffers::Offset<Proposal> CreateProposal(
|
||||
builder_.add_raw_outputs(raw_outputs);
|
||||
builder_.add_hash_inputs(hash_inputs);
|
||||
builder_.add_raw_inputs(raw_inputs);
|
||||
builder_.add_connections(connections);
|
||||
builder_.add_users(users);
|
||||
builder_.add_lcl(lcl);
|
||||
builder_.add_pubkey(pubkey);
|
||||
builder_.add_stage(stage);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<Proposal> CreateProposalDirect(
|
||||
inline flatbuffers::Offset<Proposal_Message> CreateProposal_MessageDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<uint8_t> *pubkey = nullptr,
|
||||
uint64_t timestamp = 0,
|
||||
int8_t stage = 0,
|
||||
uint64_t time = 0,
|
||||
const std::vector<uint8_t> *lcl = nullptr,
|
||||
const std::vector<flatbuffers::Offset<ByteArray>> *connections = nullptr,
|
||||
const std::vector<flatbuffers::Offset<StringKeyValuePair>> *raw_inputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<flatbuffers::String>> *hash_inputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<StringKeyValuePair>> *raw_outputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<flatbuffers::String>> *hash_outputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<ByteArray>> *users = nullptr,
|
||||
const std::vector<flatbuffers::Offset<BytesKeyValuePair>> *raw_inputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<ByteArray>> *hash_inputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<BytesKeyValuePair>> *raw_outputs = nullptr,
|
||||
const std::vector<flatbuffers::Offset<ByteArray>> *hash_outputs = nullptr,
|
||||
flatbuffers::Offset<State> state = 0) {
|
||||
auto pubkey__ = pubkey ? _fbb.CreateVector<uint8_t>(*pubkey) : 0;
|
||||
auto lcl__ = lcl ? _fbb.CreateVector<uint8_t>(*lcl) : 0;
|
||||
auto connections__ = connections ? _fbb.CreateVector<flatbuffers::Offset<ByteArray>>(*connections) : 0;
|
||||
auto raw_inputs__ = raw_inputs ? _fbb.CreateVector<flatbuffers::Offset<StringKeyValuePair>>(*raw_inputs) : 0;
|
||||
auto hash_inputs__ = hash_inputs ? _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*hash_inputs) : 0;
|
||||
auto raw_outputs__ = raw_outputs ? _fbb.CreateVector<flatbuffers::Offset<StringKeyValuePair>>(*raw_outputs) : 0;
|
||||
auto hash_outputs__ = hash_outputs ? _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*hash_outputs) : 0;
|
||||
return p2p::CreateProposal(
|
||||
auto users__ = users ? _fbb.CreateVector<flatbuffers::Offset<ByteArray>>(*users) : 0;
|
||||
auto raw_inputs__ = raw_inputs ? _fbb.CreateVector<flatbuffers::Offset<BytesKeyValuePair>>(*raw_inputs) : 0;
|
||||
auto hash_inputs__ = hash_inputs ? _fbb.CreateVector<flatbuffers::Offset<ByteArray>>(*hash_inputs) : 0;
|
||||
auto raw_outputs__ = raw_outputs ? _fbb.CreateVector<flatbuffers::Offset<BytesKeyValuePair>>(*raw_outputs) : 0;
|
||||
auto hash_outputs__ = hash_outputs ? _fbb.CreateVector<flatbuffers::Offset<ByteArray>>(*hash_outputs) : 0;
|
||||
return p2p::CreateProposal_Message(
|
||||
_fbb,
|
||||
pubkey__,
|
||||
timestamp,
|
||||
stage,
|
||||
time,
|
||||
lcl__,
|
||||
connections__,
|
||||
users__,
|
||||
raw_inputs__,
|
||||
hash_inputs__,
|
||||
raw_outputs__,
|
||||
@@ -486,7 +486,7 @@ inline flatbuffers::Offset<Proposal> CreateProposalDirect(
|
||||
state);
|
||||
}
|
||||
|
||||
struct Npl FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
struct Npl_Message FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_PUBKEY = 4,
|
||||
VT_TIMESTAMP = 6,
|
||||
@@ -530,40 +530,40 @@ struct Npl FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
}
|
||||
};
|
||||
|
||||
struct NplBuilder {
|
||||
struct Npl_MessageBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_pubkey(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey) {
|
||||
fbb_.AddOffset(Npl::VT_PUBKEY, pubkey);
|
||||
fbb_.AddOffset(Npl_Message::VT_PUBKEY, pubkey);
|
||||
}
|
||||
void add_timestamp(uint64_t timestamp) {
|
||||
fbb_.AddElement<uint64_t>(Npl::VT_TIMESTAMP, timestamp, 0);
|
||||
fbb_.AddElement<uint64_t>(Npl_Message::VT_TIMESTAMP, timestamp, 0);
|
||||
}
|
||||
void add_data(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data) {
|
||||
fbb_.AddOffset(Npl::VT_DATA, data);
|
||||
fbb_.AddOffset(Npl_Message::VT_DATA, data);
|
||||
}
|
||||
void add_lcl(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl) {
|
||||
fbb_.AddOffset(Npl::VT_LCL, lcl);
|
||||
fbb_.AddOffset(Npl_Message::VT_LCL, lcl);
|
||||
}
|
||||
explicit NplBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
explicit Npl_MessageBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
: fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
NplBuilder &operator=(const NplBuilder &);
|
||||
flatbuffers::Offset<Npl> Finish() {
|
||||
Npl_MessageBuilder &operator=(const Npl_MessageBuilder &);
|
||||
flatbuffers::Offset<Npl_Message> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = flatbuffers::Offset<Npl>(end);
|
||||
auto o = flatbuffers::Offset<Npl_Message>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline flatbuffers::Offset<Npl> CreateNpl(
|
||||
inline flatbuffers::Offset<Npl_Message> CreateNpl_Message(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey = 0,
|
||||
uint64_t timestamp = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> lcl = 0) {
|
||||
NplBuilder builder_(_fbb);
|
||||
Npl_MessageBuilder builder_(_fbb);
|
||||
builder_.add_timestamp(timestamp);
|
||||
builder_.add_lcl(lcl);
|
||||
builder_.add_data(data);
|
||||
@@ -571,7 +571,7 @@ inline flatbuffers::Offset<Npl> CreateNpl(
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline flatbuffers::Offset<Npl> CreateNplDirect(
|
||||
inline flatbuffers::Offset<Npl_Message> CreateNpl_MessageDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<uint8_t> *pubkey = nullptr,
|
||||
uint64_t timestamp = 0,
|
||||
@@ -580,7 +580,7 @@ inline flatbuffers::Offset<Npl> CreateNplDirect(
|
||||
auto pubkey__ = pubkey ? _fbb.CreateVector<uint8_t>(*pubkey) : 0;
|
||||
auto data__ = data ? _fbb.CreateVector<uint8_t>(*data) : 0;
|
||||
auto lcl__ = lcl ? _fbb.CreateVector<uint8_t>(*lcl) : 0;
|
||||
return p2p::CreateNpl(
|
||||
return p2p::CreateNpl_Message(
|
||||
_fbb,
|
||||
pubkey__,
|
||||
timestamp,
|
||||
@@ -594,23 +594,23 @@ struct StateDifference FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
VT_UPDATED = 6,
|
||||
VT_DELETED = 8
|
||||
};
|
||||
const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *created() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_CREATED);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *created() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_CREATED);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *mutable_created() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_CREATED);
|
||||
flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *mutable_created() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_CREATED);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *updated() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_UPDATED);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *updated() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_UPDATED);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *mutable_updated() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_UPDATED);
|
||||
flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *mutable_updated() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_UPDATED);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *deleted() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_DELETED);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *deleted() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_DELETED);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *mutable_deleted() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_DELETED);
|
||||
flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *mutable_deleted() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_DELETED);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
@@ -630,13 +630,13 @@ struct StateDifference FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
struct StateDifferenceBuilder {
|
||||
flatbuffers::FlatBufferBuilder &fbb_;
|
||||
flatbuffers::uoffset_t start_;
|
||||
void add_created(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> created) {
|
||||
void add_created(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> created) {
|
||||
fbb_.AddOffset(StateDifference::VT_CREATED, created);
|
||||
}
|
||||
void add_updated(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> updated) {
|
||||
void add_updated(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> updated) {
|
||||
fbb_.AddOffset(StateDifference::VT_UPDATED, updated);
|
||||
}
|
||||
void add_deleted(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> deleted) {
|
||||
void add_deleted(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> deleted) {
|
||||
fbb_.AddOffset(StateDifference::VT_DELETED, deleted);
|
||||
}
|
||||
explicit StateDifferenceBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
@@ -653,9 +653,9 @@ struct StateDifferenceBuilder {
|
||||
|
||||
inline flatbuffers::Offset<StateDifference> CreateStateDifference(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> created = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> updated = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> deleted = 0) {
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> created = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> updated = 0,
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> deleted = 0) {
|
||||
StateDifferenceBuilder builder_(_fbb);
|
||||
builder_.add_deleted(deleted);
|
||||
builder_.add_updated(updated);
|
||||
@@ -665,12 +665,12 @@ inline flatbuffers::Offset<StateDifference> CreateStateDifference(
|
||||
|
||||
inline flatbuffers::Offset<StateDifference> CreateStateDifferenceDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<flatbuffers::Offset<StringKeyValuePair>> *created = nullptr,
|
||||
const std::vector<flatbuffers::Offset<StringKeyValuePair>> *updated = nullptr,
|
||||
const std::vector<flatbuffers::Offset<StringKeyValuePair>> *deleted = nullptr) {
|
||||
auto created__ = created ? _fbb.CreateVector<flatbuffers::Offset<StringKeyValuePair>>(*created) : 0;
|
||||
auto updated__ = updated ? _fbb.CreateVector<flatbuffers::Offset<StringKeyValuePair>>(*updated) : 0;
|
||||
auto deleted__ = deleted ? _fbb.CreateVector<flatbuffers::Offset<StringKeyValuePair>>(*deleted) : 0;
|
||||
const std::vector<flatbuffers::Offset<BytesKeyValuePair>> *created = nullptr,
|
||||
const std::vector<flatbuffers::Offset<BytesKeyValuePair>> *updated = nullptr,
|
||||
const std::vector<flatbuffers::Offset<BytesKeyValuePair>> *deleted = nullptr) {
|
||||
auto created__ = created ? _fbb.CreateVector<flatbuffers::Offset<BytesKeyValuePair>>(*created) : 0;
|
||||
auto updated__ = updated ? _fbb.CreateVector<flatbuffers::Offset<BytesKeyValuePair>>(*updated) : 0;
|
||||
auto deleted__ = deleted ? _fbb.CreateVector<flatbuffers::Offset<BytesKeyValuePair>>(*deleted) : 0;
|
||||
return p2p::CreateStateDifference(
|
||||
_fbb,
|
||||
created__,
|
||||
@@ -703,11 +703,11 @@ struct State FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
||||
StateDifference *mutable_difference() {
|
||||
return GetPointer<StateDifference *>(VT_DIFFERENCE);
|
||||
}
|
||||
const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *patch() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_PATCH);
|
||||
const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *patch() const {
|
||||
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_PATCH);
|
||||
}
|
||||
flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *mutable_patch() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>> *>(VT_PATCH);
|
||||
flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *mutable_patch() {
|
||||
return GetPointer<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *>(VT_PATCH);
|
||||
}
|
||||
bool Verify(flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) &&
|
||||
@@ -736,7 +736,7 @@ struct StateBuilder {
|
||||
void add_difference(flatbuffers::Offset<StateDifference> difference) {
|
||||
fbb_.AddOffset(State::VT_DIFFERENCE, difference);
|
||||
}
|
||||
void add_patch(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<StringKeyValuePair>>> patch) {
|
||||
void add_patch(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> patch) {
|
||||
fbb_.AddOffset(State::VT_PATCH, patch);
|
||||
}
|
||||
explicit StateBuilder(flatbuffers::FlatBufferBuilder &_fbb)
|
||||
@@ -756,7 +756,7 @@ inline flatbuffers::Offset<State> CreateState(
|
||||
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<StringKeyValuePair>>> patch = 0) {
|
||||
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>>> patch = 0) {
|
||||
StateBuilder builder_(_fbb);
|
||||
builder_.add_patch(patch);
|
||||
builder_.add_difference(difference);
|
||||
@@ -770,10 +770,10 @@ inline flatbuffers::Offset<State> CreateStateDirect(
|
||||
const std::vector<uint8_t> *previous = nullptr,
|
||||
const std::vector<uint8_t> *current = nullptr,
|
||||
flatbuffers::Offset<StateDifference> difference = 0,
|
||||
const std::vector<flatbuffers::Offset<StringKeyValuePair>> *patch = nullptr) {
|
||||
const std::vector<flatbuffers::Offset<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<StringKeyValuePair>>(*patch) : 0;
|
||||
auto patch__ = patch ? _fbb.CreateVector<flatbuffers::Offset<BytesKeyValuePair>>(*patch) : 0;
|
||||
return p2p::CreateState(
|
||||
_fbb,
|
||||
previous__,
|
||||
@@ -787,12 +787,12 @@ inline bool VerifyMessage(flatbuffers::Verifier &verifier, const void *obj, Mess
|
||||
case Message_NONE: {
|
||||
return true;
|
||||
}
|
||||
case Message_Proposal: {
|
||||
auto ptr = reinterpret_cast<const Proposal *>(obj);
|
||||
case Message_Proposal_Message: {
|
||||
auto ptr = reinterpret_cast<const Proposal_Message *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
case Message_Npl: {
|
||||
auto ptr = reinterpret_cast<const Npl *>(obj);
|
||||
case Message_Npl_Message: {
|
||||
auto ptr = reinterpret_cast<const Npl_Message *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
default: return false;
|
||||
|
||||
@@ -11,10 +11,17 @@ namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
|
||||
|
||||
namespace p2p
|
||||
{
|
||||
|
||||
/**
|
||||
* Holds all the messages until they are processed by consensus.
|
||||
*/
|
||||
message_collection collected_msgs;
|
||||
|
||||
/**
|
||||
* Peer connections exposing to the application
|
||||
*/
|
||||
std::unordered_map<std::string, sock::socket_session<peer_outbound_message> *> peer_connections;
|
||||
std::mutex peer_connections_mutex; // Mutex for peer connections access race conditions.
|
||||
|
||||
/**
|
||||
* Peer session handler instance. This instance's methods will be fired for any peer socket activity.
|
||||
@@ -47,7 +54,7 @@ std::thread peer_thread;
|
||||
*/
|
||||
sock::session_options sess_opts;
|
||||
|
||||
std::map<std::string, time_t> recent_peer_msghash;
|
||||
std::map<std::string, int64_t> recent_peer_msghash;
|
||||
|
||||
int init()
|
||||
{
|
||||
@@ -103,72 +110,4 @@ void peer_connection_watchdog()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the incoming p2p message. Check for message version, timestamp and signature.
|
||||
*
|
||||
* @param message binary message content.
|
||||
* @param signature binary message signature.
|
||||
* @param pubkey binary public key of message originating node.
|
||||
* @param timestamp message timestamp.
|
||||
* @param version message timestamp.
|
||||
* @return whether message is validated or not.
|
||||
*/
|
||||
bool validate_peer_message(std::string_view message, std::string_view signature, std::string_view pubkey, time_t timestamp, uint16_t version)
|
||||
{
|
||||
//Validation are prioritzed base on expensiveness of validation.
|
||||
//i.e - signature validation is done at the end.
|
||||
|
||||
std::time_t time_now = std::time(nullptr);
|
||||
|
||||
//check protocol version of message whether it is greater than minimum supported protocol version.
|
||||
if (version < util::MIN_PEERMSG_VERSION)
|
||||
{
|
||||
LOG_DBG << "Recieved message is from unsupported version";
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate if the message is not from a node of current node's unl list.
|
||||
if (!conf::cfg.unl.count(pubkey.data()))
|
||||
{
|
||||
LOG_DBG << "pubkey verification failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
//check message timestamp. < timestamp now - 4* round time.
|
||||
/*todo:this might change to check only current stage related. (Base on how consensus algorithm implementation take shape)
|
||||
check message stage is for valid stage(node's current consensus stage - 1)
|
||||
*/
|
||||
if (timestamp < (time_now - conf::cfg.roundtime * 4))
|
||||
{
|
||||
LOG_DBG << "Recieved message from peer is old";
|
||||
return false;
|
||||
}
|
||||
|
||||
//verify message signature.
|
||||
//this should be the last validation since this is bit expensive
|
||||
auto signature_verified = crypto::verify(message, signature, pubkey);
|
||||
|
||||
if (signature_verified != 0)
|
||||
{
|
||||
LOG_DBG << "Signature verification failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// After signature is verified, get message hash and see wheteher
|
||||
// message is already recieved -> abandon if duplicate.
|
||||
auto messageHash = crypto::sha_512_hash(message, "PEERMSG", 7);
|
||||
|
||||
if (recent_peer_msghash.count(messageHash) == 0)
|
||||
{
|
||||
recent_peer_msghash.try_emplace(std::move(messageHash), timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DBG << "Duplicate message";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace p2p
|
||||
@@ -1,21 +1,51 @@
|
||||
#ifndef _HP_P2P_H_
|
||||
#define _HP_P2P_H_
|
||||
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include "../sock/socket_session.hpp"
|
||||
#include "peer_session_handler.hpp"
|
||||
|
||||
namespace p2p
|
||||
{
|
||||
|
||||
struct proposal
|
||||
{
|
||||
std::string pubkey;
|
||||
int64_t timestamp;
|
||||
int64_t time;
|
||||
int8_t stage;
|
||||
std::string lcl;
|
||||
std::unordered_set<std::string> users;
|
||||
std::unordered_map<std::string, const std::string> raw_inputs;
|
||||
std::unordered_set<std::string> hash_inputs;
|
||||
std::unordered_map<std::string, const std::string> raw_outputs;
|
||||
std::unordered_set<std::string> hash_outputs;
|
||||
};
|
||||
|
||||
struct message_collection
|
||||
{
|
||||
std::list<proposal> proposals;
|
||||
std::mutex proposals_mutex; // Mutex for proposals access race conditions.
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds all the messages until they are processed by consensus.
|
||||
*/
|
||||
extern message_collection collected_msgs;
|
||||
|
||||
/**
|
||||
* This is used to store active peer connections mapped by the unique key of socket session
|
||||
*/
|
||||
extern std::unordered_map< std::string, sock::socket_session<peer_outbound_message> *> peer_connections;
|
||||
extern std::unordered_map<std::string, sock::socket_session<peer_outbound_message> *> peer_connections;
|
||||
extern std::mutex peer_connections_mutex; // Mutex for peer connections access race conditions.
|
||||
|
||||
/**
|
||||
* This is used to store hash of recent peer messages: messagehash -> timestamp of message
|
||||
*/
|
||||
extern std::map<std::string, time_t> recent_peer_msghash;
|
||||
extern std::map<std::string, int64_t> recent_peer_msghash;
|
||||
|
||||
int init();
|
||||
|
||||
@@ -24,8 +54,6 @@ void start_peer_connections();
|
||||
|
||||
void peer_connection_watchdog();
|
||||
|
||||
bool validate_peer_message(std::string_view message, std::string_view signature, std::string_view pubkey, time_t timestamp, uint16_t version);
|
||||
|
||||
} // namespace p2p
|
||||
|
||||
#endif
|
||||
346
src/p2p/peer_message_handler.cpp
Normal file
346
src/p2p/peer_message_handler.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
#include <string>
|
||||
#include "../conf.hpp"
|
||||
#include "../crypto.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "../hplog.hpp"
|
||||
#include "peer_message_handler.hpp"
|
||||
#include "message_content_generated.h"
|
||||
#include "message_container_generated.h"
|
||||
|
||||
namespace p2p
|
||||
{
|
||||
|
||||
/**
|
||||
* This section contains Flatbuffer message reading/writing helpers.
|
||||
* These helpers are mainly used by peer_session_handler.
|
||||
*
|
||||
* All Flatbuffer peer messages are 'Container' messages. 'Container' message is a bucket
|
||||
* which some common headers (version, singature etc..) and the message 'Content' (Proposal, NPL etc..).
|
||||
*
|
||||
* Therefore, when constructing peer messages, we have to first construct 'Content' message and then
|
||||
* place the 'Content' inside a 'Conatiner. 'Content' and 'Container' messages are constructed using
|
||||
* Flatbuffer builders.
|
||||
*
|
||||
* Reading is also 2 steps because of this. We have first interprit the 'Container' message from the
|
||||
* received data and then interprit the 'Content' portion of it separately to read the actual content.
|
||||
*/
|
||||
|
||||
//---Message validation and reading helpers---/
|
||||
|
||||
/**
|
||||
* Verifies Conatiner message structure and outputs faltbuffer Container pointer to access the given buffer.
|
||||
*
|
||||
* @param container_ref A pointer reference to assign the pointer to the Container object.
|
||||
* @param container_bud The buffer containing the data that should validated and interpreted
|
||||
* via the container pointer.
|
||||
* @return 0 on successful verification. -1 for failure.
|
||||
*/
|
||||
int validate_and_extract_container(const Container **container_ref, std::string_view container_buf)
|
||||
{
|
||||
//Accessing message buffer
|
||||
const uint8_t *container_buf_ptr = reinterpret_cast<const uint8_t *>(container_buf.data());
|
||||
size_t container_buf_size = container_buf.length();
|
||||
|
||||
//Defining Flatbuffer verifier (default max depth = 64, max_tables = 1000000,)
|
||||
flatbuffers::Verifier container_verifier(container_buf_ptr, container_buf_size);
|
||||
|
||||
//Verify container message using flatbuffer verifier
|
||||
if (!VerifyContainerBuffer(container_verifier))
|
||||
{
|
||||
LOG_DBG << "Flatbuffer verify: Bad container.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Get message container
|
||||
const Container *container = GetContainer(container_buf_ptr);
|
||||
|
||||
//check protocol version of message whether it is greater than minimum supported protocol version.
|
||||
const uint16_t version = container->version();
|
||||
if (version < util::MIN_PEERMSG_VERSION)
|
||||
{
|
||||
LOG_DBG << "Recieved message is from unsupported protocol version (" << version << ")";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Assign container and content out params.
|
||||
*container_ref = container;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the Content message structure and outputs faltbuffer Content pointer to access the given buffer.
|
||||
*
|
||||
* @param content_ref A pointer reference to assign the pointer to the Content object.
|
||||
* @param content_ptr Pointer to the the buffer containing the data that should validated and interpreted
|
||||
* via the container pointer.
|
||||
* @param content_size Data buffer size.
|
||||
* @return 0 on successful verification. -1 for failure.
|
||||
*/
|
||||
int validate_and_extract_content(const Content **content_ref, const uint8_t *content_ptr, flatbuffers::uoffset_t content_size)
|
||||
{
|
||||
//Defining Flatbuffer verifier for message content verification.
|
||||
//Since content is also serialised by using Flatbuffer we can verify it using Flatbuffer.
|
||||
flatbuffers::Verifier content_verifier(content_ptr, content_size);
|
||||
|
||||
//verify content message using flatbuffer verifier.
|
||||
if (!VerifyContainerBuffer(content_verifier))
|
||||
{
|
||||
LOG_DBG << "Flatbuffer verify: Bad content.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
*content_ref = GetContent(content_ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the incoming p2p message content on several criteria.
|
||||
*
|
||||
* @param message Message content data buffer.
|
||||
* @param signature Binary message signature.
|
||||
* @param pubkey Binary public key of message originating node.
|
||||
* @param timestamp Message timestamp.
|
||||
* @param version Message protocol version.
|
||||
* @return 0 on successful validation. -1 for failure.
|
||||
*/
|
||||
int validate_content_message(std::string_view message, std::string_view signature, std::string_view pubkey, int64_t timestamp)
|
||||
{
|
||||
//Validation are prioritzed base on expensiveness of validation.
|
||||
//i.e - signature validation is done at the end.
|
||||
|
||||
int64_t time_now = util::get_epoch_milliseconds();
|
||||
|
||||
// validate if the message is not from a node of current node's unl list.
|
||||
if (!conf::cfg.unl.count(pubkey.data()))
|
||||
{
|
||||
LOG_DBG << "pubkey verification failed";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check message timestamp. < timestamp now - 4* round time.
|
||||
/*todo:this might change to check only current stage related. (Base on how consensus algorithm implementation take shape)
|
||||
check message stage is for valid stage(node's current consensus stage - 1)
|
||||
*/
|
||||
if (timestamp < (time_now - conf::cfg.roundtime * 4))
|
||||
{
|
||||
LOG_DBG << "Recieved message from peer is old";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//verify message signature.
|
||||
//this should be the last validation since this is bit expensive
|
||||
auto signature_verified = crypto::verify(message, signature, pubkey);
|
||||
|
||||
if (signature_verified != 0)
|
||||
{
|
||||
LOG_DBG << "Signature verification failed";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// After signature is verified, get message hash and see wheteher
|
||||
// message is already recieved -> abandon if duplicate.
|
||||
// auto messageHash = crypto::sha_512_hash(message, "PEERMSG", 7);
|
||||
|
||||
// if (recent_peer_msghash.count(messageHash) == 0)
|
||||
// {
|
||||
// recent_peer_msghash.try_emplace(std::move(messageHash), timestamp);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// LOG_DBG << "Duplicate message";
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a proposal stuct from the given proposal message.
|
||||
* @param The Flatbuffer poporal received from the peer.
|
||||
* @return A proposal struct representing the message.
|
||||
*/
|
||||
const proposal create_proposal_from_msg(const Proposal_Message &msg)
|
||||
{
|
||||
proposal p;
|
||||
|
||||
if (msg.pubkey())
|
||||
p.pubkey = flatbuff_bytes_to_sv(msg.pubkey());
|
||||
|
||||
p.time = msg.time();
|
||||
p.timestamp = msg.timestamp();
|
||||
p.stage = msg.stage();
|
||||
|
||||
if (msg.lcl())
|
||||
p.lcl = flatbuff_bytes_to_sv(msg.lcl());
|
||||
|
||||
if (msg.users())
|
||||
p.users = flatbuf_bytearrayvector_to_stringlist(msg.users());
|
||||
|
||||
if (msg.raw_inputs())
|
||||
p.raw_inputs = flatbuf_pairvector_to_stringmap(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_pairvector_to_stringmap(msg.raw_outputs());
|
||||
|
||||
if (msg.hash_outputs())
|
||||
p.hash_outputs = flatbuf_bytearrayvector_to_stringlist(msg.hash_outputs());
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
//---Message creation helpers---//
|
||||
|
||||
/**
|
||||
* Ctreat proposal peer message from the given proposal struct.
|
||||
* @param container_builder Flatbuffer builder for the container message.
|
||||
* @param p The proposal struct to be placed in the container message.
|
||||
*/
|
||||
void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, const proposal &p)
|
||||
{
|
||||
// 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,
|
||||
sv_to_flatbuff_bytes(builder, conf::cfg.pubkey),
|
||||
p.timestamp,
|
||||
p.stage,
|
||||
p.time,
|
||||
sv_to_flatbuff_bytes(builder, p.lcl),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.users),
|
||||
stringmap_to_flatbuf_bytepairvector(builder, p.raw_inputs),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_inputs),
|
||||
stringmap_to_flatbuf_bytepairvector(builder, p.raw_outputs),
|
||||
stringlist_to_flatbuf_bytearrayvector(builder, p.hash_outputs));
|
||||
|
||||
flatbuffers::Offset<Content> message = CreateContent(builder, Message_Proposal_Message, proposal.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param content_builder The Flatbuffer builder containing the content message that should be placed
|
||||
* inside the container message.
|
||||
*/
|
||||
void create_containermsg_from_content(
|
||||
flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder)
|
||||
{
|
||||
uint8_t *content_buf = content_builder.GetBufferPointer();
|
||||
flatbuffers::uoffset_t content_size = content_builder.GetSize();
|
||||
|
||||
// Create container message content from serialised content from previous step.
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> content = container_builder.CreateVector(content_buf, content_size);
|
||||
|
||||
// Sign message content with this node's private key.
|
||||
std::string_view content_to_sign(reinterpret_cast<const char *>(content_buf), content_size);
|
||||
std::string sig = crypto::sign(content_to_sign, conf::cfg.seckey);
|
||||
|
||||
flatbuffers::Offset<Container> container_message = CreateContainer(
|
||||
container_builder,
|
||||
util::PEERMSG_VERSION,
|
||||
sv_to_flatbuff_bytes(container_builder, sig), //signature field
|
||||
content);
|
||||
|
||||
// Finish building message container to get serialised message.
|
||||
container_builder.Finish(container_message);
|
||||
}
|
||||
|
||||
//---Conversion helpers from flatbuffers data types to std data types---//
|
||||
|
||||
/**
|
||||
* Returns string_view from flat buffer data pointer and length.
|
||||
*/
|
||||
std::string_view flatbuff_bytes_to_sv(const uint8_t *data, flatbuffers::uoffset_t length)
|
||||
{
|
||||
const char *signature_content_str = reinterpret_cast<const char *>(data);
|
||||
return std::string_view(signature_content_str, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns return 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 set from Flatbuffer vector of ByteArrays.
|
||||
*/
|
||||
const std::unordered_set<std::string> flatbuf_bytearrayvector_to_stringlist(const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *fbvec)
|
||||
{
|
||||
std::unordered_set<std::string> set;
|
||||
set.reserve(fbvec->size());
|
||||
for (auto el : *fbvec)
|
||||
set.emplace(std::string(flatbuff_bytes_to_sv(el->array())));
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map from Flatbuffer vector of key value pairs.
|
||||
*/
|
||||
const std::unordered_map<std::string, const std::string>
|
||||
flatbuf_pairvector_to_stringmap(const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *fbvec)
|
||||
{
|
||||
std::unordered_map<std::string, const std::string> map;
|
||||
map.reserve(fbvec->size());
|
||||
for (auto el : *fbvec)
|
||||
map.emplace(flatbuff_bytes_to_sv(el->key()), flatbuff_bytes_to_sv(el->value()));
|
||||
return map;
|
||||
}
|
||||
|
||||
//---Conversion helpers from std data types to flatbuffers data types---//
|
||||
//---These are used in constructing Flatbuffer messages using builders---//
|
||||
|
||||
/**
|
||||
* Returns Flatbuffer bytes vector from string_view.
|
||||
*/
|
||||
const flatbuffers::Offset<flatbuffers::Vector<uint8_t>>
|
||||
sv_to_flatbuff_bytes(flatbuffers::FlatBufferBuilder &builder, std::string_view sv)
|
||||
{
|
||||
return builder.CreateVector(reinterpret_cast<const uint8_t *>(sv.data()), sv.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
std::vector<flatbuffers::Offset<ByteArray>> fbvec;
|
||||
fbvec.reserve(set.size());
|
||||
for (std::string_view str : set)
|
||||
fbvec.push_back(CreateByteArray(builder, sv_to_flatbuff_bytes(builder, str)));
|
||||
return builder.CreateVector(fbvec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Flatbuffer vector of key value pairs from given map.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
std::vector<flatbuffers::Offset<BytesKeyValuePair>> fbvec;
|
||||
fbvec.reserve(map.size());
|
||||
for (auto const &[key, value] : map)
|
||||
{
|
||||
fbvec.push_back(CreateBytesKeyValuePair(
|
||||
builder,
|
||||
sv_to_flatbuff_bytes(builder, key),
|
||||
sv_to_flatbuff_bytes(builder, value)));
|
||||
}
|
||||
return builder.CreateVector(fbvec);
|
||||
}
|
||||
|
||||
} // namespace p2p
|
||||
56
src/p2p/peer_message_handler.hpp
Normal file
56
src/p2p/peer_message_handler.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef _HP_PEER_MESSAGE_HANDLER_H_
|
||||
#define _HP_PEER_MESSAGE_HANDLER_H_
|
||||
|
||||
#include <string>
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
#include "message_content_generated.h"
|
||||
#include "message_container_generated.h"
|
||||
#include "p2p.hpp"
|
||||
|
||||
namespace p2p
|
||||
{
|
||||
/**
|
||||
* This section contains Flatbuffer message reading/writing helpers.
|
||||
*/
|
||||
|
||||
//---Message validation and reading helpers---/
|
||||
|
||||
int validate_and_extract_container(const Container **container_ref, std::string_view container_buf);
|
||||
|
||||
int validate_and_extract_content(const Content **content_ref, const uint8_t *content_ptr, flatbuffers::uoffset_t content_size);
|
||||
|
||||
int validate_content_message(std::string_view message, std::string_view signature, std::string_view pubkey, int64_t timestamp);
|
||||
|
||||
const proposal create_proposal_from_msg(const Proposal_Message &msg);
|
||||
|
||||
//---Message creation helpers---//
|
||||
|
||||
void create_msg_from_proposal(flatbuffers::FlatBufferBuilder &container_builder, const proposal &p);
|
||||
|
||||
void create_containermsg_from_content(
|
||||
flatbuffers::FlatBufferBuilder &container_builder, const flatbuffers::FlatBufferBuilder &content_builder);
|
||||
|
||||
//---Conversion helpers from flatbuffers data types to std data types---//
|
||||
|
||||
std::string_view flatbuff_bytes_to_sv(const uint8_t *data, flatbuffers::uoffset_t length);
|
||||
|
||||
std::string_view flatbuff_bytes_to_sv(const flatbuffers::Vector<uint8_t> *buffer);
|
||||
|
||||
const std::unordered_set<std::string> flatbuf_bytearrayvector_to_stringlist(const flatbuffers::Vector<flatbuffers::Offset<ByteArray>> *fbvec);
|
||||
|
||||
const std::unordered_map<std::string, const std::string> flatbuf_pairvector_to_stringmap(const flatbuffers::Vector<flatbuffers::Offset<BytesKeyValuePair>> *fbvec);
|
||||
|
||||
//---Conversion helpers from std data types to flatbuffers data types---//
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
} // namespace p2p
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "../hplog.hpp"
|
||||
#include "p2p.hpp"
|
||||
#include "peer_session_handler.hpp"
|
||||
#include "peer_message_handler.hpp"
|
||||
#include "message_content_generated.h"
|
||||
#include "message_container_generated.h"
|
||||
|
||||
@@ -32,70 +33,6 @@ std::string_view peer_outbound_message::buffer()
|
||||
(*fbbuilder_ptr).GetSize());
|
||||
}
|
||||
|
||||
//private method used to create a proposal message with dummy data.
|
||||
//Will be similiar to consensus proposal creation in each stage.
|
||||
const std::string create_message(flatbuffers::FlatBufferBuilder &container_builder)
|
||||
{
|
||||
//todo:get a average propsal message size and allocate builder based on that.
|
||||
/*
|
||||
* todo: create custom vector allocator for protobuff in order to avoid copying buffer to string.
|
||||
* includes overidding socket_session send method to support this as well.
|
||||
*/
|
||||
flatbuffers::FlatBufferBuilder builder(1024);
|
||||
std::time_t timestamp = std::time(nullptr);
|
||||
uint8_t stage = 0;
|
||||
|
||||
std::string pubkey = conf::cfg.pubkey;
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> pubkey_b = builder.CreateVector((uint8_t *)pubkey.data(), pubkey.size());
|
||||
|
||||
//create dummy propsal message
|
||||
flatbuffers::Offset<Proposal> proposal = CreateProposal(builder, pubkey_b, timestamp, stage, timestamp);
|
||||
flatbuffers::Offset<Content> message = CreateContent(builder, Message_Proposal, proposal.Union());
|
||||
builder.Finish(message); //finished building message content to get serialised content.
|
||||
|
||||
//Get serialized/packed message content pointer and size.
|
||||
uint8_t *buf = builder.GetBufferPointer();
|
||||
flatbuffers::uoffset_t size = builder.GetSize();
|
||||
|
||||
//Get a binary string_view for the serialised message content.
|
||||
const char *content_str = reinterpret_cast<const char *>(buf);
|
||||
std::string_view message_content(content_str, size);
|
||||
|
||||
//create container message content from serialised content from previous step.
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> content = container_builder.CreateVector(buf, size);
|
||||
|
||||
//Sign message content with node's private key.
|
||||
std::string sig = crypto::sign(message_content, conf::cfg.seckey);
|
||||
char *sig_buf = sig.data();
|
||||
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> signature = container_builder.CreateVector((uint8_t *)sig_buf, sig.size()); //include signature to message
|
||||
|
||||
flatbuffers::Offset<Container> container_message = CreateContainer(container_builder, util::MIN_PEERMSG_VERSION, signature, content);
|
||||
container_builder.Finish(container_message); //finished building message container to get serialised message.
|
||||
|
||||
flatbuffers::uoffset_t buf_size = container_builder.GetSize();
|
||||
uint8_t *message_buf = container_builder.GetBufferPointer();
|
||||
|
||||
//todo: should return buffer_pointer to socket.
|
||||
return std::string((char *)message_buf, buf_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method to return string_view from flat buffer data pointer and length.
|
||||
*/
|
||||
std::string_view flatbuff_bytes_to_sv(const uint8_t *data, flatbuffers::uoffset_t length)
|
||||
{
|
||||
const char *signature_content_str = reinterpret_cast<const char *>(data);
|
||||
return std::string_view(signature_content_str, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method to return 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets hit every time a peer connects to HP via the peer port (configured in contract config).
|
||||
*/
|
||||
@@ -105,15 +42,20 @@ void peer_session_handler::on_connect(sock::socket_session<peer_outbound_message
|
||||
{
|
||||
// We init the session unique id to associate with the challenge.
|
||||
session->init_uniqueid();
|
||||
peer_connections.insert(std::make_pair(session->uniqueid, session));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p2p::peer_connections_mutex);
|
||||
peer_connections.insert(std::make_pair(session->uniqueid, session));
|
||||
}
|
||||
LOG_DBG << "Adding peer to list: " << session->uniqueid;
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: set container builder defualt builder size to combination of serialized content length + signature length(which is fixed)
|
||||
peer_outbound_message msg(std::make_shared<flatbuffers::FlatBufferBuilder>(1024));
|
||||
std::string message = create_message(msg.builder());
|
||||
session->send(msg);
|
||||
// peer_outbound_message msg(std::make_shared<flatbuffers::FlatBufferBuilder>(1024));
|
||||
|
||||
// proposal p;
|
||||
// create_msg_from_proposal(msg.builder(), p);
|
||||
// session->send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,93 +63,66 @@ void peer_session_handler::on_connect(sock::socket_session<peer_outbound_message
|
||||
//validate and handle each type of peer messages.
|
||||
void peer_session_handler::on_message(sock::socket_session<peer_outbound_message> *session, std::string_view message)
|
||||
{
|
||||
//Accessing message buffer
|
||||
const uint8_t *container_pointer = reinterpret_cast<const uint8_t *>(message.data());
|
||||
size_t container_length = message.length();
|
||||
const Container *container;
|
||||
if (validate_and_extract_container(&container, message) != 0)
|
||||
return;
|
||||
|
||||
//Defining Flatbuffer verifier (default max depth = 64, max_tables = 1000000,)
|
||||
flatbuffers::Verifier container_verifier(container_pointer, container_length);
|
||||
//Get serialised message content.
|
||||
const flatbuffers::Vector<uint8_t> *container_content = container->content();
|
||||
|
||||
//Verify container message using flatbuffer verifier
|
||||
if (VerifyContainerBuffer(container_verifier))
|
||||
//Accessing message content and size.
|
||||
const uint8_t *content_ptr = container_content->Data();
|
||||
flatbuffers::uoffset_t content_size = container_content->size();
|
||||
|
||||
const Content *content;
|
||||
if (validate_and_extract_content(&content, content_ptr, content_size) != 0)
|
||||
return;
|
||||
|
||||
p2p::Message content_message_type = content->message_type(); //i.e - proposal, npl, state request, state response, etc
|
||||
|
||||
if (content_message_type == Message_Proposal_Message) //message is a proposal message
|
||||
{
|
||||
//Get message container
|
||||
const p2p::Container *container = GetContainer(container_pointer);
|
||||
const uint16_t version = container->version();
|
||||
const Proposal_Message *proposalmsg = content->message_as_Proposal_Message();
|
||||
|
||||
//validate message for malleability, timeliness, signature and prune recieving messages.
|
||||
bool val_result = validate_content_message(
|
||||
flatbuff_bytes_to_sv(content_ptr, content_size),
|
||||
flatbuff_bytes_to_sv(container->signature()),
|
||||
flatbuff_bytes_to_sv(proposalmsg->pubkey()),
|
||||
proposalmsg->timestamp());
|
||||
|
||||
//Get serialised message content.
|
||||
const flatbuffers::Vector<uint8_t> *container_content = container->content();
|
||||
|
||||
//Accessing message content and size.
|
||||
const uint8_t *content_pointer = container_content->Data();
|
||||
flatbuffers::uoffset_t content_size = container_content->size();
|
||||
|
||||
//Defining Flatbuffer verifier for content message verification.
|
||||
//Since content is also serialised by using Filterbuf we can verify it using Filterbuffer.
|
||||
flatbuffers::Verifier content_verifier(content_pointer, content_size);
|
||||
|
||||
//verify content message conent using flatbuffer verifier.
|
||||
if (VerifyContainerBuffer(content_verifier))
|
||||
if (val_result == 0)
|
||||
{
|
||||
//Get message content.
|
||||
const Content *content = GetContent(content_pointer);
|
||||
p2p::Message content_message_type = content->message_type(); //i.e - proposal, npl, state request, state response, etc
|
||||
|
||||
if (content_message_type == Message_Proposal) //message is a proposal message
|
||||
{
|
||||
const Proposal *proposal = content->message_as_Proposal();
|
||||
uint64_t timestamp = proposal->timestamp();
|
||||
|
||||
//Get public key of message originating node.
|
||||
std::string_view message_pubkey = flatbuff_bytes_to_sv(proposal->pubkey());
|
||||
|
||||
//Get signature from container message.
|
||||
std::string_view message_signature = flatbuff_bytes_to_sv(container->signature());
|
||||
|
||||
std::string_view message_content = flatbuff_bytes_to_sv(content_pointer, content_size);
|
||||
|
||||
//validate message for malleability, timeliness, signature and prune recieving messages.
|
||||
bool validated = p2p::validate_peer_message(message_content, message_signature, message_pubkey, timestamp, version);
|
||||
if (validated)
|
||||
{
|
||||
//if validated send message to consensus.
|
||||
//if validated broadcast message.
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DBG << "Message validation failed";
|
||||
}
|
||||
}
|
||||
else if (content_message_type == Message_Npl) //message is a proposal message
|
||||
{
|
||||
const Npl *npl = content->message_as_Npl();
|
||||
// execute npl logic here.
|
||||
//broadcast message.
|
||||
}
|
||||
else
|
||||
{
|
||||
//warn received invalid message from peer.
|
||||
LOG_DBG << "Received invalid message type from peer";
|
||||
//remove/penalize node who sent the message.
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(collected_msgs.proposals_mutex);
|
||||
collected_msgs.proposals.push_back(create_proposal_from_msg(*proposalmsg));
|
||||
}
|
||||
else
|
||||
{
|
||||
//warn bad message content.
|
||||
LOG_DBG << "Bad message content";
|
||||
LOG_DBG << "Message content field validation failed";
|
||||
}
|
||||
}
|
||||
else if (content_message_type == Message_Npl_Message) //message is a NPL message
|
||||
{
|
||||
const Npl_Message *npl = content->message_as_Npl_Message();
|
||||
// execute npl logic here.
|
||||
//broadcast message.
|
||||
}
|
||||
else
|
||||
{
|
||||
//warn bad messages from peer.
|
||||
LOG_DBG << "Bad message from peer";
|
||||
//warn received invalid message from peer.
|
||||
LOG_DBG << "Received invalid message type from peer";
|
||||
//TODO: remove/penalize node who sent the message.
|
||||
}
|
||||
}
|
||||
|
||||
//peer session on message callback method
|
||||
void peer_session_handler::on_close(sock::socket_session<peer_outbound_message> *session)
|
||||
{
|
||||
LOG_DBG << "on_closing peer :" << session->uniqueid;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p2p::peer_connections_mutex);
|
||||
peer_connections.erase(session->uniqueid);
|
||||
}
|
||||
LOG_DBG << "Peer disonnected: " << session->uniqueid;
|
||||
}
|
||||
|
||||
} // namespace p2p
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef _HP_P2P_SESSION_H_
|
||||
#define _HP_P2P_SESSION_H_
|
||||
#ifndef _HP_PEER_SESSION_HANDLER_H_
|
||||
#define _HP_PEER_SESSION_HANDLER_H_
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
|
||||
@@ -40,10 +40,10 @@ struct ContractExecArgs
|
||||
std::pair<std::string, std::string> &hpscbufs;
|
||||
|
||||
// Current HotPocket timestamp.
|
||||
uint64_t timestamp;
|
||||
int64_t timestamp;
|
||||
|
||||
ContractExecArgs(
|
||||
uint64_t _timestamp,
|
||||
int64_t _timestamp,
|
||||
contract_bufmap &_userbufs,
|
||||
contract_bufmap &_nplbufs,
|
||||
std::pair<std::string, std::string> &_hpscbufs) :
|
||||
|
||||
@@ -27,7 +27,7 @@ user_outbound_message::user_outbound_message(std::string &&_msg)
|
||||
// Returns the buffer that should be written to the socket.
|
||||
std::string_view user_outbound_message::buffer()
|
||||
{
|
||||
return std::string_view(msg.data(), msg.size());
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ ssl::context ctx{ssl::context::tlsv13};
|
||||
* Map key: User socket session id (<ip:port>)
|
||||
*/
|
||||
std::unordered_map<std::string, usr::connected_user> users;
|
||||
std::mutex users_mutex; // Mutex for users access race conditions.
|
||||
|
||||
/**
|
||||
* Holds set of connected user session ids and public keys for lookups.
|
||||
@@ -219,7 +220,10 @@ int add_user(sock::socket_session<user_outbound_message> *session, const std::st
|
||||
return -1;
|
||||
}
|
||||
|
||||
users.emplace(sessionid, usr::connected_user(session, pubkey));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(users_mutex);
|
||||
users.emplace(sessionid, usr::connected_user(session, pubkey));
|
||||
}
|
||||
|
||||
// Populate sessionid map so we can lookup by user pubkey.
|
||||
sessionids[pubkey] = sessionid;
|
||||
@@ -246,7 +250,11 @@ int remove_user(const std::string &sessionid)
|
||||
|
||||
usr::connected_user &user = itr->second;
|
||||
|
||||
sessionids.erase(user.pubkey);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(users_mutex);
|
||||
sessionids.erase(user.pubkey);
|
||||
}
|
||||
|
||||
users.erase(itr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <cstdio>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include "../util.hpp"
|
||||
#include "../sock/socket_session.hpp"
|
||||
#include "user_session_handler.hpp"
|
||||
@@ -45,6 +46,7 @@ struct connected_user
|
||||
* Map key: User socket session id (<ip:port>)
|
||||
*/
|
||||
extern std::unordered_map<std::string, usr::connected_user> users;
|
||||
extern std::mutex users_mutex; // Mutex for users access race conditions.
|
||||
|
||||
/**
|
||||
* Keep track of verification-pending challenges issued to newly connected users.
|
||||
|
||||
11
src/util.cpp
11
src/util.cpp
@@ -1,6 +1,7 @@
|
||||
#include <string>
|
||||
#include <sodium.h>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
namespace util
|
||||
@@ -52,6 +53,16 @@ int hex2bin(unsigned char *decodedbuf, size_t decodedbuf_len, std::string_view h
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current time in UNIX epoch milliseconds.
|
||||
*/
|
||||
int64_t get_epoch_milliseconds()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two version strings in the format of "1.12.3".
|
||||
* v1 < v2 -> returns -1
|
||||
|
||||
@@ -14,6 +14,9 @@ namespace util
|
||||
// Hot Pocket version. Displayed on 'hotpocket version' and written to new contract configs.
|
||||
static const char *HP_VERSION = "0.1";
|
||||
|
||||
// Current version of the peer message protocol.
|
||||
static const int PEERMSG_VERSION = 1;
|
||||
|
||||
// Minimum compatible contract config version (this will be used to validate contract configs)
|
||||
static const char *MIN_CONTRACT_VERSION = "0.1";
|
||||
|
||||
@@ -37,6 +40,8 @@ int bin2hex(std::string &encoded_string, const unsigned char *bin, size_t bin_le
|
||||
|
||||
int hex2bin(unsigned char *decoded, size_t decoded_len, std::string_view hex_str);
|
||||
|
||||
int64_t get_epoch_milliseconds();
|
||||
|
||||
int version_compare(const std::string &x, const std::string &y);
|
||||
|
||||
std::string_view getsv(const rapidjson::Value &v);
|
||||
|
||||
Reference in New Issue
Block a user