mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
General code optmisations and cleanup. (#15)
Updated `version_compare` based on this issue: #13 Got rid of `replace_string_contents` helper func. Replaced #define macros with static consts. Moved comments from headers to source files.
This commit is contained in:
@@ -74,7 +74,7 @@ Code is divided into subsystems via namespaces. Some subsystems mentioned here a
|
||||
Handles contract configuration. Loads and holds the central configuration object. Used by most of the subsystems.
|
||||
|
||||
#### crypto
|
||||
Handles cryptographic activities. Wraps libsodium and offers convinience functions.
|
||||
Handles cryptographic activities. Wraps libsodium and offers convenience functions.
|
||||
|
||||
#### proc
|
||||
Handles contract process execution.
|
||||
|
||||
47
src/conf.cpp
47
src/conf.cpp
@@ -18,17 +18,16 @@ using namespace rapidjson;
|
||||
namespace conf
|
||||
{
|
||||
|
||||
// Global contract context struct exposed to the application.
|
||||
contract_ctx ctx;
|
||||
|
||||
// Global configuration struct exposed to the application.
|
||||
contract_config cfg;
|
||||
|
||||
int load_config();
|
||||
int save_config();
|
||||
int validate_config();
|
||||
int validate_contract_dir_paths();
|
||||
int is_schema_valid(Document &d);
|
||||
int binpair_to_b64();
|
||||
int b64pair_to_bin();
|
||||
|
||||
/**
|
||||
* Loads and initializes the contract config for execution. Must be called once during application startup.
|
||||
* @return 0 for success. -1 for failure.
|
||||
*/
|
||||
int init()
|
||||
{
|
||||
// The validations/loading needs to be in this order.
|
||||
@@ -42,6 +41,9 @@ int init()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and saves new signing keys in the contract config.
|
||||
*/
|
||||
int rekey()
|
||||
{
|
||||
// Load the contract config and re-save with the newly generated keys.
|
||||
@@ -61,6 +63,11 @@ int rekey()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new contract directory with the default contract config.
|
||||
* By the time this gets called, the 'ctx' struct must be populated.
|
||||
* This function makes use of the paths populated in the ctx.
|
||||
*/
|
||||
int create_contract()
|
||||
{
|
||||
if (boost::filesystem::exists(ctx.contractDir))
|
||||
@@ -97,6 +104,10 @@ int create_contract()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the contract context with directory paths based on provided base directory.
|
||||
* This is called after parsing HP command line arg in order to populate the ctx.
|
||||
*/
|
||||
void set_contract_dir_paths(string basedir)
|
||||
{
|
||||
if (basedir[basedir.size() - 1] == '/')
|
||||
@@ -109,9 +120,6 @@ void set_contract_dir_paths(string basedir)
|
||||
ctx.stateDir = basedir + "/state";
|
||||
}
|
||||
|
||||
|
||||
//------Private functions for this namespace (not exposed via header).
|
||||
|
||||
/**
|
||||
* Reads the config file on disk and populates the in-memory 'cfg' struct.
|
||||
*
|
||||
@@ -146,14 +154,19 @@ int load_config()
|
||||
}
|
||||
|
||||
// Check whether this contract complies with the min version requirement.
|
||||
string minversion = string(_HP_MIN_CONTRACT_VERSION_);
|
||||
if (util::version_compare(cfgversion, minversion) == -1)
|
||||
int verresult = util::version_compare(cfgversion, string(util::min_contract_version));
|
||||
if (verresult == -1)
|
||||
{
|
||||
cerr << "Contract version too old. Minimum "
|
||||
<< _HP_MIN_CONTRACT_VERSION_ << " required. "
|
||||
<< util::min_contract_version << " required. "
|
||||
<< cfgversion << " found.\n";
|
||||
return -1;
|
||||
}
|
||||
else if (verresult == -2)
|
||||
{
|
||||
cerr << "Malformed version string.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Load up the values into the struct.
|
||||
|
||||
@@ -196,7 +209,7 @@ int save_config()
|
||||
Document d;
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
d.AddMember("version", StringRef(_HP_VERSION_), allocator);
|
||||
d.AddMember("version", StringRef(util::hp_version), allocator);
|
||||
d.AddMember("pubkeyb64", StringRef(cfg.pubkeyb64.data()), allocator);
|
||||
d.AddMember("seckeyb64", StringRef(cfg.seckeyb64.data()), allocator);
|
||||
d.AddMember("binary", StringRef(cfg.binary.data()), allocator);
|
||||
@@ -289,8 +302,8 @@ int b64pair_to_bin()
|
||||
}
|
||||
|
||||
// Assign the cfg pubkey/seckey fields with the decoded strings.
|
||||
util::replace_string_contents(cfg.pubkey, (char *)decoded_pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
util::replace_string_contents(cfg.seckey, (char *)decoded_seckey, crypto_sign_SECRETKEYBYTES);
|
||||
cfg.pubkey = string((char *)decoded_pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
cfg.seckey = string((char *)decoded_seckey, crypto_sign_SECRETKEYBYTES);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
83
src/conf.hpp
83
src/conf.hpp
@@ -1,29 +1,22 @@
|
||||
#ifndef _HP_CONF_H_
|
||||
#define _HP_CONF_H_
|
||||
|
||||
// Hot Pocket version. Displayed on 'hotpocket version' and written to new contract configs.
|
||||
#define _HP_VERSION_ "0.1"
|
||||
|
||||
// Minimum compatible contract config version (this will be used to validate contract configs)
|
||||
#define _HP_MIN_CONTRACT_VERSION_ "0.1"
|
||||
|
||||
// Minimum compatible peer message version (this will be used to accept/reject incoming peer connections)
|
||||
// (Keeping this as int for effcient msg payload and comparison)
|
||||
#define _HP_MIN_PEERMSG_VERSION_ 1
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
|
||||
/**
|
||||
* Manages the central contract config and context structs.
|
||||
* Contains functions to contract config operations such as create/rekey/load.
|
||||
*/
|
||||
namespace conf
|
||||
{
|
||||
|
||||
// Holds contextual information of the currently loaded contract.
|
||||
// Holds contextual information about the currently loaded contract.
|
||||
struct contract_ctx
|
||||
{
|
||||
|
||||
string command; // The CLI command issued to launch HotPocket
|
||||
|
||||
string contractDir; // Contract base directory
|
||||
@@ -33,58 +26,62 @@ struct contract_ctx
|
||||
string configFile; // Full path to the contract config file
|
||||
};
|
||||
|
||||
//
|
||||
// Holds all the contract config values.
|
||||
struct contract_config
|
||||
{
|
||||
// Config elements which are initialized in memory (these are not directly loaded from the config file)
|
||||
|
||||
string pubkey; // Contract public key bytes
|
||||
string seckey; // Contract secret key bytes
|
||||
|
||||
string pubkey; // Contract public key bytes
|
||||
string seckey; // Contract secret key bytes
|
||||
|
||||
// Config elements which are loaded from the config file.
|
||||
|
||||
string pubkeyb64; // Contract base64 public key
|
||||
string seckeyb64; // Contract base64 secret key
|
||||
string binary; // Full path to the contract binary
|
||||
string binargs; // CLI arguments to pass to the contract binary
|
||||
string listenip; // The IPs to listen on for incoming connections
|
||||
vector<string> peers; // List of peers in the format "<ip address>:<port>"
|
||||
vector<string> unl; // Unique node list (list of base64 public keys)
|
||||
unsigned short peerport; // Listening port for peer connections
|
||||
int roundtime; // Consensus round time in ms
|
||||
unsigned short pubport; // Listening port for public user connections
|
||||
int pubmaxsize; // User message max size in bytes
|
||||
int pubmaxcpm; // User message rate
|
||||
string pubkeyb64; // Contract base64 public key
|
||||
string seckeyb64; // Contract base64 secret key
|
||||
string binary; // Full path to the contract binary
|
||||
string binargs; // CLI arguments to pass to the contract binary
|
||||
string listenip; // The IPs to listen on for incoming connections
|
||||
vector<string> peers; // List of peers in the format "<ip address>:<port>"
|
||||
vector<string> unl; // Unique node list (list of base64 public keys)
|
||||
unsigned short peerport; // Listening port for peer connections
|
||||
int roundtime; // Consensus round time in ms
|
||||
unsigned short pubport; // Listening port for public user connections
|
||||
int pubmaxsize; // User message max size in bytes
|
||||
int pubmaxcpm; // User message rate
|
||||
};
|
||||
|
||||
//Global contract context struct exposed to the application.
|
||||
// Global contract context struct exposed to the application.
|
||||
// Other modeuls will access context values via this.
|
||||
extern contract_ctx ctx;
|
||||
|
||||
//Global configuration struct exposed to the application.
|
||||
// Global configuration struct exposed to the application.
|
||||
// Other modeuls will access config values via this.
|
||||
extern contract_config cfg;
|
||||
|
||||
/**
|
||||
* Loads and initializes the contract config for execution. Must be called once during application startup.
|
||||
* @return 0 for success. -1 for failure.
|
||||
*/
|
||||
int init();
|
||||
|
||||
/**
|
||||
* Generates and saves new signing keys in the contract config.
|
||||
*/
|
||||
int rekey();
|
||||
|
||||
/**
|
||||
* Creates a new contract directory with the default contract config.
|
||||
*/
|
||||
int create_contract();
|
||||
|
||||
/**
|
||||
* Updates the contract context with directory paths based on provided base directory.
|
||||
*/
|
||||
void set_contract_dir_paths(string basedir);
|
||||
|
||||
//------Internal-use functions for this namespace.
|
||||
|
||||
int load_config();
|
||||
|
||||
int save_config();
|
||||
|
||||
int validate_config();
|
||||
|
||||
int validate_contract_dir_paths();
|
||||
|
||||
int is_schema_valid(Document &d);
|
||||
|
||||
int binpair_to_b64();
|
||||
|
||||
int b64pair_to_bin();
|
||||
|
||||
} // namespace conf
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,10 @@ using namespace std;
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes the crypto subsystem. Must be called once during application startup.
|
||||
* @return 0 for successful initialization. -1 for failure.
|
||||
*/
|
||||
int init()
|
||||
{
|
||||
if (sodium_init() < 0)
|
||||
@@ -20,6 +24,9 @@ int init()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signing key pair using libsodium and assigns them to the provided strings.
|
||||
*/
|
||||
void generate_signing_keys(string &pubkey, string &seckey)
|
||||
{
|
||||
//Generate key pair using libsodium default algorithm. (Currently using ed25519)
|
||||
@@ -28,10 +35,17 @@ void generate_signing_keys(string &pubkey, string &seckey)
|
||||
unsigned char seckeychars[crypto_sign_SECRETKEYBYTES];
|
||||
crypto_sign_keypair(pubkeychars, seckeychars);
|
||||
|
||||
util::replace_string_contents(pubkey, (char *)pubkeychars, crypto_sign_PUBLICKEYBYTES);
|
||||
util::replace_string_contents(seckey, (char *)seckeychars, crypto_sign_SECRETKEYBYTES);
|
||||
pubkey = string((char *)pubkeychars, crypto_sign_PUBLICKEYBYTES);
|
||||
seckey = string((char *)seckeychars, crypto_sign_SECRETKEYBYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signature bytes for a message.
|
||||
*
|
||||
* @param msg Message bytes to sign.
|
||||
* @param seckey Secret key bytes.
|
||||
* @return Signature bytes.
|
||||
*/
|
||||
string sign(const string &msg, const string &seckey)
|
||||
{
|
||||
//Generate the signature using libsodium.
|
||||
@@ -42,6 +56,13 @@ string sign(const string &msg, const string &seckey)
|
||||
return sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base64 signature string for a message.
|
||||
*
|
||||
* @param msg Base64 message string to sign.
|
||||
* @param seckey Base64 secret key string.
|
||||
* @return Base64 signature string.
|
||||
*/
|
||||
string sign_b64(const string &msg, const string &seckeyb64)
|
||||
{
|
||||
//Decode b64 string and generate the signature using libsodium.
|
||||
@@ -57,12 +78,28 @@ string sign_b64(const string &msg, const string &seckeyb64)
|
||||
return sigb64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the given signature bytes for the message.
|
||||
*
|
||||
* @param msg Message bytes.
|
||||
* @param sig Signature bytes.
|
||||
* @param pubkey Public key bytes.
|
||||
* @return 0 for successful verification. -1 for failure.
|
||||
*/
|
||||
int verify(const string &msg, const string &sig, const string &pubkey)
|
||||
{
|
||||
return crypto_sign_verify_detached(
|
||||
(unsigned char *)sig.data(), (unsigned char *)msg.data(), msg.length(), (unsigned char *)pubkey.data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the given base64 signature for the message.
|
||||
*
|
||||
* @param msg Base64 message string.
|
||||
* @param sig Base64 signature string.
|
||||
* @param pubkey Base64 secret key.
|
||||
* @return 0 for successful verification. -1 for failure.
|
||||
*/
|
||||
int verify_b64(const string &msg, const string &sigb64, const string &pubkeyb64)
|
||||
{
|
||||
//Decode b64 string and verify the signature using libsodium.
|
||||
|
||||
@@ -3,56 +3,23 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* Offers convenience functions for cryptographic operations wrapping libsodium.
|
||||
* These functions are used for contract config and user/peer message authentication.
|
||||
*/
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
/**
|
||||
* Initializes the crypto subsystem. Must be called once during application startup.
|
||||
* @return 0 for successful initialization. -1 for failure.
|
||||
*/
|
||||
int init();
|
||||
|
||||
/**
|
||||
* Generates a signing key pair using libsodium and assigns them to the provided strings.
|
||||
*/
|
||||
void generate_signing_keys(string &pubkey, string &seckey);
|
||||
|
||||
/**
|
||||
* Returns the signature bytes for a message.
|
||||
*
|
||||
* @param msg Message bytes to sign.
|
||||
* @param seckey Secret key bytes.
|
||||
* @return Signature bytes.
|
||||
*/
|
||||
string sign(const string &msg, const string &seckey);
|
||||
|
||||
/**
|
||||
* Returns the base64 signature string for a message.
|
||||
*
|
||||
* @param msg Base64 message string to sign.
|
||||
* @param seckey Base64 secret key string.
|
||||
* @return Base64 signature string.
|
||||
*/
|
||||
string sign_b64(const string &msg, const string &seckeyb64);
|
||||
|
||||
/**
|
||||
* Verifies the given signature bytes for the message.
|
||||
*
|
||||
* @param msg Message bytes.
|
||||
* @param sig Signature bytes.
|
||||
* @param pubkey Public key bytes.
|
||||
* @return 0 for successful verification. -1 for failure.
|
||||
*/
|
||||
int verify(const string &msg, const string &sig, const string &pubkey);
|
||||
|
||||
/**
|
||||
* Verifies the given base64 signature for the message.
|
||||
*
|
||||
* @param msg Base64 message string.
|
||||
* @param sig Base64 signature string.
|
||||
* @param pubkey Base64 secret key.
|
||||
* @return 0 for successful verification. -1 for failure.
|
||||
*/
|
||||
int verify_b64(const string &msg, const string &sigb64, const string &pubkeyb64);
|
||||
|
||||
} // namespace crypto
|
||||
|
||||
112
src/main.cpp
112
src/main.cpp
@@ -4,59 +4,27 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include "util.hpp"
|
||||
#include "conf.hpp"
|
||||
#include "crypto.hpp"
|
||||
#include "usr/usr.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int parse_cmd(int argc, char **argv);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (parse_cmd(argc, argv) != 0)
|
||||
return -1;
|
||||
|
||||
if (conf::ctx.command == "version")
|
||||
{
|
||||
cout << _HP_VERSION_ << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (crypto::init() != 0)
|
||||
return -1;
|
||||
|
||||
if (conf::ctx.command == "new")
|
||||
{
|
||||
if (conf::create_contract() != 0)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conf::ctx.command == "rekey")
|
||||
{
|
||||
if (conf::rekey() != 0)
|
||||
return -1;
|
||||
}
|
||||
else if (conf::ctx.command == "run")
|
||||
{
|
||||
if (conf::init() != 0 || usr::init() != 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << "exited normally\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses CLI args and extracts hot pocket command and parameters given.
|
||||
* HP command line accepts command and the contract directory(optional)
|
||||
*/
|
||||
int parse_cmd(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1) //We get working dir as an arg anyway. So we need to check for >1 args.
|
||||
{
|
||||
string command = argv[1];
|
||||
conf::ctx.command = command;
|
||||
if (command == "run" || command == "new" || command == "rekey")
|
||||
// We populate the global contract ctx with the detected command.
|
||||
conf::ctx.command = argv[1];
|
||||
|
||||
// For run/new/rekey, contract directory argument must be specified.
|
||||
|
||||
if (conf::ctx.command == "run" || conf::ctx.command == "new" || conf::ctx.command == "rekey")
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
@@ -64,18 +32,22 @@ int parse_cmd(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
// We inform the conf subsystem to populate the contract directory context values
|
||||
// based on the directory argument from the command line.
|
||||
conf::set_contract_dir_paths(argv[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (command == "version")
|
||||
else if (conf::ctx.command == "version")
|
||||
{
|
||||
if (argc == 2)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If all extractions fail display help message.
|
||||
|
||||
cerr << "Arguments mismatch.\n";
|
||||
cout << "Usage:\n";
|
||||
cout << "hpcore version\n";
|
||||
@@ -84,3 +56,55 @@ int parse_cmd(int argc, char **argv)
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Extract the CLI args
|
||||
// After this call conf::ctx must be populated.
|
||||
if (parse_cmd(argc, argv) != 0)
|
||||
return -1;
|
||||
|
||||
if (conf::ctx.command == "version")
|
||||
{
|
||||
// Print the version
|
||||
cout << util::hp_version << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This block is about contract operations (new/rekey/run)
|
||||
// All the contract operations will be executed on the contract directory specified
|
||||
// in the command line args. 'parse_cmd()' above takes care of populating the contexual directory paths.
|
||||
|
||||
// For any contract opreation to execute, we should init the crypto subsystem.
|
||||
if (crypto::init() != 0)
|
||||
return -1;
|
||||
|
||||
if (conf::ctx.command == "new")
|
||||
{
|
||||
// This will create a new contract with all the required files.
|
||||
if (conf::create_contract() != 0)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (conf::ctx.command == "rekey")
|
||||
{
|
||||
// This will generate new signing keys for the contract.
|
||||
if (conf::rekey() != 0)
|
||||
return -1;
|
||||
}
|
||||
else if (conf::ctx.command == "run")
|
||||
{
|
||||
// In order to host the contract we should init some required sub systems.
|
||||
if (conf::init() != 0 || usr::init() != 0)
|
||||
return -1;
|
||||
|
||||
// This will start hosting the contract and start consensus rounds.
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << "exited normally\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
18
src/proc.cpp
18
src/proc.cpp
@@ -19,11 +19,14 @@ namespace proc
|
||||
/**
|
||||
* Keeps the currently executing contract process id (if any)
|
||||
*/
|
||||
int contract_pid;
|
||||
int contract_pid = 0;
|
||||
|
||||
int write_to_stdin(ContractExecArgs &args);
|
||||
|
||||
int exec_contract(ContractExecArgs &args)
|
||||
/**
|
||||
* Executes the contract process and passes the specified arguments.
|
||||
*
|
||||
* @return 0 on successful process creation. -1 on failure or contract process is already running.
|
||||
*/
|
||||
int exec_contract(const ContractExecArgs &args)
|
||||
{
|
||||
if (is_contract_running())
|
||||
{
|
||||
@@ -73,7 +76,7 @@ int exec_contract(ContractExecArgs &args)
|
||||
* "unl":[ "pkb64", ... ]
|
||||
* }
|
||||
*/
|
||||
int write_to_stdin(ContractExecArgs &args)
|
||||
int write_to_stdin(const ContractExecArgs &args)
|
||||
{
|
||||
//Populate the json document with contract args.
|
||||
|
||||
@@ -81,7 +84,7 @@ int write_to_stdin(ContractExecArgs &args)
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
|
||||
d.AddMember("version", StringRef(_HP_VERSION_), allocator);
|
||||
d.AddMember("version", StringRef(util::hp_version), allocator);
|
||||
d.AddMember("pubkey", StringRef(conf::cfg.pubkeyb64.data()), allocator);
|
||||
d.AddMember("ts", args.timestamp, allocator);
|
||||
|
||||
@@ -137,6 +140,9 @@ int write_to_stdin(ContractExecArgs &args)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the contract process is running at this moment.
|
||||
*/
|
||||
bool is_contract_running()
|
||||
{
|
||||
if (contract_pid > 0)
|
||||
|
||||
23
src/proc.hpp
23
src/proc.hpp
@@ -8,6 +8,9 @@
|
||||
using namespace std;
|
||||
using namespace util;
|
||||
|
||||
/**
|
||||
* Contains helper functions regarding POSIX process execution and IPC between HP and SC.
|
||||
*/
|
||||
namespace proc
|
||||
{
|
||||
|
||||
@@ -16,9 +19,9 @@ namespace proc
|
||||
*/
|
||||
struct ContractExecArgs
|
||||
{
|
||||
map<string, contract_user> &users; // Map of authenticated contract users indexed by user pubkey.
|
||||
map<string, peer_node> &peers; // Map of connected peers indexed by node pubkey.
|
||||
uint64_t timestamp; // Current HotPocket timestamp.
|
||||
map<string, contract_user> &users; // Map of authenticated contract users indexed by user pubkey.
|
||||
map<string, peer_node> &peers; // Map of connected peers indexed by node pubkey.
|
||||
uint64_t timestamp; // Current HotPocket timestamp.
|
||||
|
||||
ContractExecArgs(map<string, contract_user> &_users, map<string, peer_node> &_peers, uint64_t _timestamp)
|
||||
: users(_users), peers(_peers)
|
||||
@@ -27,18 +30,14 @@ struct ContractExecArgs
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes the contract process and passes the specified arguments.
|
||||
*
|
||||
* @return 0 on successful process creation. -1 on failure or contract process is already running.
|
||||
*/
|
||||
int exec_contract(ContractExecArgs &args);
|
||||
int exec_contract(const ContractExecArgs &args);
|
||||
|
||||
/**
|
||||
* Checks whether the contract process is running at this moment.
|
||||
*/
|
||||
bool is_contract_running();
|
||||
|
||||
//------Internal-use functions for this namespace.
|
||||
|
||||
int write_to_stdin(const ContractExecArgs &args);
|
||||
|
||||
} // namespace proc
|
||||
|
||||
#endif
|
||||
@@ -30,6 +30,10 @@ map<string, contract_user> users;
|
||||
*/
|
||||
Document challenge_response_schemadoc;
|
||||
|
||||
/**
|
||||
* Initializes the usr subsystem. Must be called once during application startup.
|
||||
* @return 0 for successful initialization. -1 for failure.
|
||||
*/
|
||||
int init()
|
||||
{
|
||||
//We initialize the response schema doc from this json string so we can
|
||||
@@ -51,23 +55,37 @@ int init()
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs user challenge message json and the challenge string required for
|
||||
* initial user challenge handshake. This gets called when a user gets establishes
|
||||
* a web sockets connection to HP.
|
||||
*
|
||||
* @param msg String reference to copy the generated json message string into.
|
||||
* Message format:
|
||||
* {
|
||||
* "version": "<HP version>",
|
||||
* "type": "public_challenge",
|
||||
* "challenge": "<base64 challenge string>"
|
||||
* }
|
||||
* @param challenge String reference to copy the generated base64 challenge string into.
|
||||
*/
|
||||
void create_user_challenge(string &msg, string &challengeb64)
|
||||
{
|
||||
//Use libsodium to generate the random challenge bytes.
|
||||
unsigned char challenge_bytes[USER_CHALLENGE_LEN];
|
||||
randombytes_buf(challenge_bytes, USER_CHALLENGE_LEN);
|
||||
unsigned char challenge_bytes[user_challenge_len];
|
||||
randombytes_buf(challenge_bytes, user_challenge_len);
|
||||
|
||||
//We pass the b64 challenge string separately to the caller even though
|
||||
//we also include it in the challenge msg as well.
|
||||
|
||||
base64_encode(challenge_bytes, USER_CHALLENGE_LEN, challengeb64);
|
||||
base64_encode(challenge_bytes, user_challenge_len, challengeb64);
|
||||
|
||||
//Construct the challenge msg json.
|
||||
Document d;
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
d.AddMember("version", StringRef(_HP_VERSION_), allocator);
|
||||
d.AddMember("type", MSG_PUBLIC_CHALLENGE, allocator);
|
||||
d.AddMember("version", StringRef(util::hp_version), allocator);
|
||||
d.AddMember("type", StringRef(msg_public_challenge), allocator);
|
||||
d.AddMember("challenge", StringRef(challengeb64.data()), allocator);
|
||||
|
||||
StringBuffer buffer;
|
||||
@@ -76,6 +94,21 @@ void create_user_challenge(string &msg, string &challengeb64)
|
||||
msg = buffer.GetString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the user challenge response with the original challenge issued to the user
|
||||
* and the user public contained in the response.
|
||||
*
|
||||
* @param response The response bytes to verify. This will be parsed as json.
|
||||
* Accepted response format:
|
||||
* {
|
||||
* "type": "challenge_response",
|
||||
* "challenge": "<original base64 challenge the user received>",
|
||||
* "sig": "<Base64 signature of the challenge>",
|
||||
* "pubkey": "<Base64 public key of the user>"
|
||||
* }
|
||||
* @param original_challenge The original base64 challenge string issued to the user.
|
||||
* @return 0 if challenge response is verified. -1 if challenge not met or an error occurs.
|
||||
*/
|
||||
int verify_user_challenge_response(const string &response, const string &original_challenge, string &extracted_pubkeyb64)
|
||||
{
|
||||
//We load response raw bytes into json document and validate the schema.
|
||||
@@ -94,16 +127,14 @@ int verify_user_challenge_response(const string &response, const string &origina
|
||||
}
|
||||
|
||||
//Validate msg type.
|
||||
string type = d["type"].GetString();
|
||||
if (type != MSG_CHALLENGE_RESP)
|
||||
if (d["type"] != msg_challenge_resp)
|
||||
{
|
||||
cerr << "User challenge response type invalid. 'challenge_response' expeced.\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Compare the response challenge string with the original issued challenge.
|
||||
string challenge = d["challenge"].GetString();
|
||||
if (challenge != original_challenge)
|
||||
if (d["challenge"] != original_challenge.data())
|
||||
{
|
||||
cerr << "User challenge resposne: challenge mismatch.\n";
|
||||
return -1;
|
||||
@@ -121,6 +152,12 @@ int verify_user_challenge_response(const string &response, const string &origina
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified public key into the global user list.
|
||||
* This should get called after the challenge handshake is verified.
|
||||
*
|
||||
* @return 0 on successful additions. -1 on failure.
|
||||
*/
|
||||
int add_user(const string &pubkeyb64)
|
||||
{
|
||||
if (users.count(pubkeyb64) == 1)
|
||||
@@ -156,6 +193,12 @@ int add_user(const string &pubkeyb64)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified public key from the global user list.
|
||||
* This must get called when a user disconnects from HP.
|
||||
*
|
||||
* @return 0 on successful removals. -1 on failure.
|
||||
*/
|
||||
int remove_user(const string &pubkeyb64)
|
||||
{
|
||||
if (users.count(pubkeyb64) == 0)
|
||||
@@ -177,6 +220,12 @@ int remove_user(const string &pubkeyb64)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all per-user outputs produced by the contract process and store them in
|
||||
* the user buffer for later processing.
|
||||
*
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int read_contract_user_outputs()
|
||||
{
|
||||
//Read any outputs that has been written by the contract process
|
||||
@@ -199,7 +248,7 @@ int read_contract_user_outputs()
|
||||
read(fdout, data, bytes_available);
|
||||
|
||||
//Populate the user output buffer with new data
|
||||
util::replace_string_contents(user.outbuffer, data, bytes_available);
|
||||
user.outbuffer = string(data, bytes_available);
|
||||
|
||||
cout << "Read " + to_string(bytes_available) << " bytes into user output buffer. user:" + user.pubkeyb64 << endl;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
#ifndef _HP_USR_H_
|
||||
#define _HP_USR_H_
|
||||
|
||||
// Length of user random challenge bytes.
|
||||
#define USER_CHALLENGE_LEN 16
|
||||
|
||||
// Message type for the user challenge.
|
||||
#define MSG_PUBLIC_CHALLENGE "public_challenge"
|
||||
|
||||
// Message type for the user challenge response.
|
||||
#define MSG_CHALLENGE_RESP "challenge_response"
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@@ -24,72 +15,30 @@ using namespace util;
|
||||
namespace usr
|
||||
{
|
||||
|
||||
// Length of user random challenge bytes.
|
||||
static const int user_challenge_len = 16;
|
||||
|
||||
// Message type for the user challenge.
|
||||
static const char *msg_public_challenge = "public_challenge";
|
||||
|
||||
// Message type for the user challenge response.
|
||||
static const char *msg_challenge_resp = "challenge_response";
|
||||
|
||||
/**
|
||||
* Global authenticated (challenge-verified) user list.
|
||||
*/
|
||||
extern map<string, contract_user> users;
|
||||
|
||||
/**
|
||||
* Initializes the usr subsystem. Must be called once during application startup.
|
||||
* @return 0 for successful initialization. -1 for failure.
|
||||
*/
|
||||
int init();
|
||||
|
||||
/**
|
||||
* Constructs user challenge message json and the challenge string required for
|
||||
* initial user challenge handshake. This gets called when a user gets establishes
|
||||
* a web sockets connection to HP.
|
||||
*
|
||||
* @param msg String reference to copy the generated json message string into.
|
||||
* Message format:
|
||||
* {
|
||||
* "version": "<HP version>",
|
||||
* "type": "public_challenge",
|
||||
* "challenge": "<base64 challenge string>"
|
||||
* }
|
||||
* @param challenge String reference to copy the generated base64 challenge string into.
|
||||
*/
|
||||
void create_user_challenge(string &msg, string &challengeb64);
|
||||
|
||||
/**
|
||||
* Verifies the user challenge response with the original challenge issued to the user
|
||||
* and the user public contained in the response.
|
||||
*
|
||||
* @param response The response bytes to verify. This will be parsed as json.
|
||||
* Accepted response format:
|
||||
* {
|
||||
* "type": "challenge_response",
|
||||
* "challenge": "<original base64 challenge the user received>",
|
||||
* "sig": "<Base64 signature of the challenge>",
|
||||
* "pubkey": "<Base64 public key of the user>"
|
||||
* }
|
||||
* @param original_challenge The original base64 challenge string issued to the user.
|
||||
* @return 0 if challenge response is verified. -1 if challenge not met or an error occurs.
|
||||
*/
|
||||
int verify_user_challenge_response(const string &response, const string &original_challenge, string &extracted_pubkey);
|
||||
|
||||
/**
|
||||
* Adds the specified public key into the global user list.
|
||||
* This should get called after the challenge handshake is verified.
|
||||
*
|
||||
* @return 0 on successful additions. -1 on failure.
|
||||
*/
|
||||
int add_user(const string &pubkeyb64);
|
||||
|
||||
/**
|
||||
* Removes the specified public key from the global user list.
|
||||
* This must get called when a user disconnects from HP.
|
||||
*
|
||||
* @return 0 on successful removals. -1 on failure.
|
||||
*/
|
||||
int remove_user(const string &pubkeyb64);
|
||||
|
||||
/**
|
||||
* Read all per-user outputs produced by the contract process and store them in
|
||||
* the user buffer for later processing.
|
||||
*
|
||||
* @return 0 on success. -1 on failure.
|
||||
*/
|
||||
int read_contract_user_outputs();
|
||||
|
||||
} // namespace usr
|
||||
|
||||
67
src/util.cpp
67
src/util.cpp
@@ -1,18 +1,19 @@
|
||||
#include <string>
|
||||
#include <sodium.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace util
|
||||
{
|
||||
|
||||
void replace_string_contents(string &str, const char *bytes, size_t bytes_len)
|
||||
{
|
||||
if (str.length() > 0)
|
||||
str.clear();
|
||||
str.append(bytes, bytes_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes provided bytes to base64 string.
|
||||
*
|
||||
* @param bin Bytes to encode.
|
||||
* @param bin_len Bytes length.
|
||||
* @param encoded_string String reference to assign the base64 encoded output.
|
||||
*/
|
||||
int base64_encode(const unsigned char *bin, size_t bin_len, string &encoded_string)
|
||||
{
|
||||
// Get length of encoded result from sodium.
|
||||
@@ -29,10 +30,17 @@ int base64_encode(const unsigned char *bin, size_t bin_len, string &encoded_stri
|
||||
return -1;
|
||||
|
||||
// Assign the encoded char* onto the provided string reference.
|
||||
replace_string_contents(encoded_string, base64chars, base64_len);
|
||||
encoded_string = string(base64chars, base64_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes provided base64 string into bytes.
|
||||
*
|
||||
* @param base64_str Base64 string to decode.
|
||||
* @param decoded Decoded bytes.
|
||||
* @param decoded_len Decoded bytes length.
|
||||
*/
|
||||
int base64_decode(const string &base64_str, unsigned char *decoded, size_t decoded_len)
|
||||
{
|
||||
const char *b64_end;
|
||||
@@ -49,35 +57,34 @@ int base64_decode(const string &base64_str, unsigned char *decoded, size_t decod
|
||||
return 0;
|
||||
}
|
||||
|
||||
// v1 < v2 -> -1
|
||||
// v1 == v2 -> 0
|
||||
// v1 > v2 -> +1
|
||||
int version_compare(const string &v1, const string &v2)
|
||||
/**
|
||||
* Compare two version strings in the format of "1.12.3".
|
||||
* v1 < v2 -> returns -1
|
||||
* v1 == v2 -> returns 0
|
||||
* v1 > v2 -> returns +1
|
||||
* Error -> returns -2
|
||||
*/
|
||||
int version_compare(const string &x, const string &y)
|
||||
{
|
||||
size_t i = 0, j = 0;
|
||||
while (i < v1.length() || j < v2.length())
|
||||
istringstream ix(x), iy(y);
|
||||
while (ix.good() || iy.good())
|
||||
{
|
||||
int acc1 = 0, acc2 = 0;
|
||||
int cx = 0, cy = 0;
|
||||
ix >> cx;
|
||||
iy >> cy;
|
||||
|
||||
while (i < v1.length() && v1[i] != '.')
|
||||
{
|
||||
acc1 = acc1 * 10 + (v1[i] - '0');
|
||||
i++;
|
||||
}
|
||||
while (j < v2.length() && v2[j] != '.')
|
||||
{
|
||||
acc2 = acc2 * 10 + (v2[j] - '0');
|
||||
j++;
|
||||
}
|
||||
if ((!ix.eof() && !ix.good()) || (!iy.eof() && !iy.good()))
|
||||
return -2;
|
||||
|
||||
if (acc1 < acc2)
|
||||
if (cx > cy)
|
||||
return 1;
|
||||
if (cx < cy)
|
||||
return -1;
|
||||
if (acc1 > acc2)
|
||||
return +1;
|
||||
|
||||
++i;
|
||||
++j;
|
||||
ix.ignore();
|
||||
iy.ignore();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
58
src/util.hpp
58
src/util.hpp
@@ -6,19 +6,32 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* Contains helper functions and data structures used by multiple other subsystems.
|
||||
*/
|
||||
namespace util
|
||||
{
|
||||
|
||||
// Hot Pocket version. Displayed on 'hotpocket version' and written to new contract configs.
|
||||
static const char *hp_version = "0.1";
|
||||
|
||||
// Minimum compatible contract config version (this will be used to validate contract configs)
|
||||
static const char *min_contract_version = "0.1";
|
||||
|
||||
// Minimum compatible peer message version (this will be used to accept/reject incoming peer connections)
|
||||
// (Keeping this as int for effcient msg payload and comparison)
|
||||
static const int min_peermsg_version = 1;
|
||||
|
||||
/**
|
||||
* Holds information about an authenticated (challenge-verified) user
|
||||
* connected to the HotPocket node.
|
||||
*/
|
||||
struct contract_user
|
||||
{
|
||||
string pubkeyb64; // Base64 user public key
|
||||
int inpipe[2]; // Pipe to receive user input
|
||||
int outpipe[2]; // Pipe to receive output produced by the contract
|
||||
string outbuffer; // Holds the contract output to be processed by consensus rounds
|
||||
string pubkeyb64; // Base64 user public key
|
||||
int inpipe[2]; // Pipe to receive user input
|
||||
int outpipe[2]; // Pipe to receive output produced by the contract
|
||||
string outbuffer; // Holds the contract output to be processed by consensus rounds
|
||||
|
||||
contract_user(const string &_pubkeyb64, int _inpipe[2], int _outpipe[2])
|
||||
{
|
||||
@@ -35,9 +48,9 @@ struct contract_user
|
||||
*/
|
||||
struct peer_node
|
||||
{
|
||||
string pubkeyb64; // Base64 peer public key
|
||||
int inpipe[2]; // NPL pipe from HP to SC
|
||||
int outpipe[2]; // NPL pipe from SC to HP
|
||||
string pubkeyb64; // Base64 peer public key
|
||||
int inpipe[2]; // NPL pipe from HP to SC
|
||||
int outpipe[2]; // NPL pipe from SC to HP
|
||||
|
||||
peer_node(const string &_pubkeyb64, int _inpipe[2], int _outpipe[2])
|
||||
{
|
||||
@@ -49,41 +62,12 @@ struct peer_node
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes provided bytes to base64 string.
|
||||
*
|
||||
* @param bin Bytes to encode.
|
||||
* @param bin_len Bytes length.
|
||||
* @param encoded_string String reference to assign the base64 encoded output.
|
||||
*/
|
||||
int base64_encode(const unsigned char *bin, size_t bin_len, string &encoded_string);
|
||||
|
||||
/**
|
||||
* Decodes provided base64 string into bytes.
|
||||
*
|
||||
* @param base64_str Base64 string to decode.
|
||||
* @param decoded Decoded bytes.
|
||||
* @param decoded_len Decoded bytes length.
|
||||
*/
|
||||
int base64_decode(const string &base64_str, unsigned char *decoded, size_t decoded_len);
|
||||
|
||||
/**
|
||||
* Replaces contents of the given string with provided bytes.
|
||||
*
|
||||
* @param str String reference to replace contents.
|
||||
* @param bytes Bytes to write into the string.
|
||||
* @param bytes_len Bytes length.
|
||||
*/
|
||||
void replace_string_contents(string &str, const char* bytes, size_t bytes_len);
|
||||
|
||||
/**
|
||||
* Compare two version strings in the format of "1.12.3".
|
||||
* v1 < v2 -> returns -1
|
||||
* v1 == v2 -> returns 0
|
||||
* v1 > v2 -> returns +1
|
||||
*/
|
||||
int version_compare(const string &v1, const string &v2);
|
||||
|
||||
} // namespace usr
|
||||
} // namespace util
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user