mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Added contract-based config file. (#4)
Added contract-based config file and command line flow. Added contract creation and rekey support.
This commit is contained in:
17
README.md
17
README.md
@@ -10,3 +10,20 @@ A C++ version of hotpocket designed for production envrionments, original protot
|
||||
* Websockets - Boost|Beast https://github.com/boostorg/beast
|
||||
* RapidJSON - http://rapidjson.org
|
||||
* Protocol - https://github.com/protocolbuffers/protobuf
|
||||
|
||||
### Installing Boost
|
||||
Instructions are based on https://www.boost.org/doc/libs/1_71_0/more/getting_started/unix-variants.html#prepare-to-use-a-boost-library-binary
|
||||
|
||||
1. Download and extract boost package from [here](https://www.boost.org/users/history/version_1_71_0.html).
|
||||
2. Navigate to the extracted boost directory in a terminal.
|
||||
3. Run `./bootstrap.sh`
|
||||
4. Run `sudo ./b2 install` (This will compile and install boost libraries into your `/usr/local/lib`)
|
||||
5. Run `sudo ldconfig` (This will update your library cache and avoid potential issues when running your compiled C++ program which links to newly installed boost libraries)
|
||||
|
||||
## Running hotpocket
|
||||
1. navigate to the src root.
|
||||
2. Run `make`
|
||||
3. Run `./build/hpcore new ~/mycontract`. This will initialize a new contract directory `mycontract` in your home directory.
|
||||
4. Take a look at `~/mycontract/cfg/hp.cfg`. This is your new contract config file. You can modify it according to your contract hosting requirements.
|
||||
5. Run `./build/hpcore rekey ~/mycontract` to generate new public/private key pair.
|
||||
6. Run `./build/hpcore run ~/mycontract` to run your smart contract (to do).
|
||||
2
makefile
2
makefile
@@ -1,4 +1,4 @@
|
||||
all:
|
||||
mkdir -p build
|
||||
g++ src/*.cpp -lsodium -std=c++17 -o build/hpcore
|
||||
g++ src/*.cpp -lsodium -lboost_system -lboost_filesystem -std=c++17 -o build/hpcore
|
||||
echo 'build successful, binary in '`pwd`'/build/hpcore'
|
||||
|
||||
14
myctr/cfg/hp.cfg
Normal file
14
myctr/cfg/hp.cfg
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"pubkeyb64": "QJobte+YoDi0jfkp1yrARYudB0E4Bo5RQ1YJpaqkSPs=",
|
||||
"seckeyb64": "CfjHaSvWG3JbHqJxsj9prDZiw8tEzwkYlfciKsOJ3uhAmhu175igOLSN+SnXKsBFi50HQTgGjlFDVgmlqqRI+w==",
|
||||
"binary": "",
|
||||
"binargs": "",
|
||||
"listenip": "0.0.0.0",
|
||||
"peers": [],
|
||||
"unl": [],
|
||||
"peerport": 22860,
|
||||
"roundtime": 1000,
|
||||
"pubport": 8080,
|
||||
"pubmaxsize": 65536,
|
||||
"pubmaxcpm": 100
|
||||
}
|
||||
178
src/conf.cpp
178
src/conf.cpp
@@ -2,11 +2,14 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "lib/rapidjson/document.h"
|
||||
#include "lib/rapidjson/istreamwrapper.h"
|
||||
#include "lib/rapidjson/ostreamwrapper.h"
|
||||
#include "lib/rapidjson/writer.h"
|
||||
#include "lib/rapidjson/prettywriter.h"
|
||||
#include "conf.h"
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
@@ -14,47 +17,172 @@ using namespace rapidjson;
|
||||
namespace conf
|
||||
{
|
||||
|
||||
static const char * configPath;
|
||||
ContractCtx ctx;
|
||||
ContractConfig cfg;
|
||||
|
||||
string get_full_path(const char *filename)
|
||||
void load_config()
|
||||
{
|
||||
string fullpath = configPath;
|
||||
fullpath += filename;
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
void load(const char *filename, Document &d)
|
||||
{
|
||||
ifstream ifs(get_full_path(filename));
|
||||
ifstream ifs(ctx.configFile);
|
||||
IStreamWrapper isw(ifs);
|
||||
|
||||
Document d;
|
||||
d.ParseStream(isw);
|
||||
|
||||
cfg.pubkeyb64 = d["pubkeyb64"].GetString();
|
||||
cfg.seckeyb64 = d["seckeyb64"].GetString();
|
||||
cfg.binary = d["binary"].GetString();
|
||||
cfg.binargs = d["binargs"].GetString();
|
||||
cfg.listenip = d["listenip"].GetString();
|
||||
|
||||
cfg.peers.clear();
|
||||
for (auto &v : d["peers"].GetArray())
|
||||
cfg.peers.push_back(v.GetString());
|
||||
|
||||
cfg.unl.clear();
|
||||
for (auto &v : d["unl"].GetArray())
|
||||
cfg.unl.push_back(v.GetString());
|
||||
|
||||
cfg.peerport = d["peerport"].GetInt();
|
||||
cfg.roundtime = d["roundtime"].GetInt();
|
||||
cfg.pubport = d["pubport"].GetInt();
|
||||
cfg.pubmaxsize = d["pubmaxsize"].GetInt();
|
||||
cfg.pubmaxcpm = d["pubmaxcpm"].GetInt();
|
||||
}
|
||||
|
||||
void save(const char *filename, Document &d)
|
||||
void save_config()
|
||||
{
|
||||
ofstream ofs(get_full_path(filename));
|
||||
Document d;
|
||||
d.SetObject();
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
d.AddMember("pubkeyb64", StringRef(cfg.pubkeyb64.c_str()), allocator);
|
||||
d.AddMember("seckeyb64", StringRef(cfg.seckeyb64.c_str()), allocator);
|
||||
d.AddMember("binary", StringRef(cfg.binary.c_str()), allocator);
|
||||
d.AddMember("binargs", StringRef(cfg.binargs.c_str()), allocator);
|
||||
d.AddMember("listenip", StringRef(cfg.listenip.c_str()), allocator);
|
||||
|
||||
Value peers(kArrayType);
|
||||
d.AddMember("peers", peers, allocator);
|
||||
for (int i = 0; i < cfg.peers.size(); i++)
|
||||
{
|
||||
Value v;
|
||||
v.SetString(StringRef(cfg.peers[i].c_str()), allocator);
|
||||
peers.PushBack(v, allocator);
|
||||
}
|
||||
|
||||
Value unl(kArrayType);
|
||||
d.AddMember("unl", unl, allocator);
|
||||
for (int i = 0; i < cfg.unl.size(); i++)
|
||||
{
|
||||
Value v;
|
||||
v.SetString(StringRef(cfg.unl[i].c_str()), allocator);
|
||||
unl.PushBack(v, allocator);
|
||||
}
|
||||
|
||||
d.AddMember("peerport", cfg.peerport, allocator);
|
||||
d.AddMember("roundtime", cfg.roundtime, allocator);
|
||||
d.AddMember("pubport", cfg.pubport, allocator);
|
||||
d.AddMember("pubmaxsize", cfg.pubmaxsize, allocator);
|
||||
d.AddMember("pubmaxcpm", cfg.pubmaxcpm, allocator);
|
||||
|
||||
ofstream ofs(ctx.configFile);
|
||||
OStreamWrapper osw(ofs);
|
||||
|
||||
Writer<OStreamWrapper> writer(osw);
|
||||
PrettyWriter<OStreamWrapper> writer(osw);
|
||||
d.Accept(writer);
|
||||
}
|
||||
|
||||
string get_exec_path()
|
||||
int parse_cmd(int argc, char **argv)
|
||||
{
|
||||
char buf[PATH_MAX + 1];
|
||||
if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) == -1)
|
||||
throw string("readlink() failed");
|
||||
string str(buf);
|
||||
return str.substr(0, str.rfind('/') + 1);
|
||||
if (argc == 3) //We get working dir as an arg anyway. So we need to check for 1+2 args.
|
||||
{
|
||||
string command(argv[1]);
|
||||
if (command == "run" || command == "new" || command == "rekey")
|
||||
{
|
||||
ctx.command = command;
|
||||
string dir = argv[2];
|
||||
if (dir[dir.size() - 1] == '/')
|
||||
dir = dir.substr(0, dir.size() - 1);
|
||||
|
||||
ctx.contractDir = dir;
|
||||
ctx.configDir = dir + "/cfg";
|
||||
ctx.configFile = ctx.configDir + "/hp.cfg";
|
||||
ctx.histDir = dir + "/hist";
|
||||
ctx.stateDir = dir + "/state";
|
||||
ctx.binDir = dir + "/bin";
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Invalid command. 'run | new | rekey' expected.\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Argument count mismatch.\n";
|
||||
}
|
||||
|
||||
cout << "Usage: hpcore <command> <contract dir> (command = run | new |rekey)\n";
|
||||
cout << "Example: hpcore run ~/mycontract\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init()
|
||||
int create_contract()
|
||||
{
|
||||
string execPath = get_exec_path();
|
||||
char * confPathChar = (char *)malloc(execPath.size() + 1);
|
||||
strcpy(confPathChar, &execPath[0]);
|
||||
configPath = confPathChar;
|
||||
struct stat dirInfo;
|
||||
|
||||
if (boost::filesystem::exists(ctx.contractDir))
|
||||
{
|
||||
cerr << "Contract dir already exists.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
boost::filesystem::create_directories(ctx.configDir);
|
||||
boost::filesystem::create_directories(ctx.binDir);
|
||||
boost::filesystem::create_directories(ctx.histDir);
|
||||
boost::filesystem::create_directories(ctx.stateDir);
|
||||
|
||||
//Create config file with default settings.
|
||||
cfg.listenip = "0.0.0.0";
|
||||
cfg.peerport = 22860;
|
||||
cfg.roundtime = 1000;
|
||||
cfg.pubport = 8080;
|
||||
cfg.pubmaxsize = 65536;
|
||||
cfg.pubmaxcpm = 100;
|
||||
save_config();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rekey()
|
||||
{
|
||||
//Clear the keys and save the config. crpyto::init will automatically init the keys.
|
||||
cfg.pubkeyb64 = "";
|
||||
cfg.seckeyb64 = "";
|
||||
save_config();
|
||||
}
|
||||
|
||||
int init(int argc, char **argv)
|
||||
{
|
||||
if (!parse_cmd(argc, argv))
|
||||
return 0;
|
||||
|
||||
if (ctx.command == "new")
|
||||
{
|
||||
if (!create_contract())
|
||||
return 0;
|
||||
load_config();
|
||||
}
|
||||
else if (ctx.command == "rekey")
|
||||
{
|
||||
load_config();
|
||||
rekey();
|
||||
}
|
||||
else if (ctx.command == "run")
|
||||
{
|
||||
load_config();
|
||||
//TO DO: Contract run logic.
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
38
src/conf.h
38
src/conf.h
@@ -2,6 +2,7 @@
|
||||
#define _HP_CONF_H_
|
||||
|
||||
#include "lib/rapidjson/document.h"
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace rapidjson;
|
||||
@@ -9,9 +10,40 @@ using namespace rapidjson;
|
||||
namespace conf
|
||||
{
|
||||
|
||||
int init();
|
||||
void load(const char *filename, Document &d);
|
||||
void save(const char *filename, Document &d);
|
||||
struct ContractCtx
|
||||
{
|
||||
string command;
|
||||
string contractDir;
|
||||
string histDir;
|
||||
string stateDir;
|
||||
string binDir;
|
||||
string configDir;
|
||||
string configFile;
|
||||
};
|
||||
|
||||
struct ContractConfig
|
||||
{
|
||||
string pubkeyb64;
|
||||
string seckeyb64;
|
||||
unsigned char* pubkey;
|
||||
unsigned char* seckey;
|
||||
string binary;
|
||||
string binargs;
|
||||
string listenip;
|
||||
vector<string> peers;
|
||||
vector<string> unl;
|
||||
int peerport;
|
||||
int roundtime;
|
||||
int pubport;
|
||||
int pubmaxsize;
|
||||
int pubmaxcpm;
|
||||
};
|
||||
|
||||
extern ContractCtx ctx;
|
||||
extern ContractConfig cfg;
|
||||
int init(int argc, char **argv);
|
||||
void load_config();
|
||||
void save_config();
|
||||
|
||||
} // namespace conf
|
||||
|
||||
|
||||
106
src/crypto.cpp
106
src/crypto.cpp
@@ -1,9 +1,7 @@
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <sodium.h>
|
||||
#include <fstream>
|
||||
#include "base64.h"
|
||||
#include "lib/rapidjson/document.h"
|
||||
#include "conf.h"
|
||||
#include "crypto.h"
|
||||
|
||||
@@ -12,88 +10,32 @@ using namespace rapidjson;
|
||||
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
static const char CFG_FILE[] = "keys.cfg";
|
||||
|
||||
//Struct used for storing in JSON
|
||||
struct KeyPairB64
|
||||
{
|
||||
const char *publicKey;
|
||||
const char *privateKey;
|
||||
};
|
||||
|
||||
//Struct used for crypto operations
|
||||
struct KeyPairCrypto
|
||||
{
|
||||
unsigned char *publicKey;
|
||||
unsigned char *privateKey;
|
||||
};
|
||||
|
||||
static KeyPairB64 b64KeyPair;
|
||||
static KeyPairCrypto cryptoKeyPair;
|
||||
|
||||
unsigned long long get_sig_len()
|
||||
{
|
||||
return crypto_sign_BYTES;
|
||||
}
|
||||
|
||||
void sign(const unsigned char *msg, unsigned long long msg_len, unsigned char *sig)
|
||||
void sign(const unsigned char *msg, unsigned long long msg_len, unsigned char *sig, const unsigned char *seckey)
|
||||
{
|
||||
crypto_sign_detached(sig, NULL, msg, msg_len, cryptoKeyPair.privateKey);
|
||||
crypto_sign_detached(sig, NULL, msg, msg_len, seckey);
|
||||
}
|
||||
|
||||
bool verify(const unsigned char *msg, unsigned long long msg_len, const unsigned char *sig)
|
||||
bool verify(const unsigned char *msg, unsigned long long msg_len, const unsigned char *sig, const unsigned char *pubkey)
|
||||
{
|
||||
int result = crypto_sign_verify_detached(sig, msg, msg_len, cryptoKeyPair.publicKey);
|
||||
int result = crypto_sign_verify_detached(sig, msg, msg_len, pubkey);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
void load_keys_b64()
|
||||
{
|
||||
Document d;
|
||||
conf::load(CFG_FILE, d);
|
||||
if (!d.IsNull())
|
||||
{
|
||||
b64KeyPair.publicKey = d["public"].GetString();
|
||||
b64KeyPair.privateKey = d["private"].GetString();
|
||||
}
|
||||
else
|
||||
{
|
||||
b64KeyPair.publicKey = NULL;
|
||||
b64KeyPair.privateKey = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void save_keys_b64()
|
||||
{
|
||||
Document d;
|
||||
d.SetObject();
|
||||
|
||||
Document::AllocatorType &allocator = d.GetAllocator();
|
||||
d.AddMember("public", StringRef(b64KeyPair.publicKey), allocator);
|
||||
d.AddMember("private", StringRef(b64KeyPair.privateKey), allocator);
|
||||
conf::save(CFG_FILE, d);
|
||||
}
|
||||
|
||||
void cryptopair_to_b64()
|
||||
{
|
||||
string b64PubKey = base64_encode(cryptoKeyPair.publicKey, crypto_sign_PUBLICKEYBYTES);
|
||||
string b64PrivKey = base64_encode(cryptoKeyPair.privateKey, crypto_sign_SECRETKEYBYTES);
|
||||
|
||||
char *b64PubKeyChar = (char *)malloc(b64PubKey.size() + 1);
|
||||
char *b64PrivKeyChar = (char *)malloc(b64PrivKey.size() + 1);
|
||||
|
||||
strcpy(b64PubKeyChar, &b64PubKey[0]);
|
||||
strcpy(b64PrivKeyChar, &b64PrivKey[0]);
|
||||
|
||||
b64KeyPair.publicKey = b64PubKeyChar;
|
||||
b64KeyPair.privateKey = b64PrivKeyChar;
|
||||
conf::cfg.pubkeyb64 = base64_encode(conf::cfg.pubkey, crypto_sign_PUBLICKEYBYTES);
|
||||
conf::cfg.seckeyb64 = base64_encode(conf::cfg.seckey, crypto_sign_SECRETKEYBYTES);
|
||||
}
|
||||
|
||||
void b64pair_to_crypto()
|
||||
{
|
||||
vector<unsigned char> pubDecoded = base64_decode(b64KeyPair.publicKey);
|
||||
vector<unsigned char> privDecoded = base64_decode(b64KeyPair.privateKey);
|
||||
vector<unsigned char> pubDecoded = base64_decode(conf::cfg.pubkeyb64);
|
||||
vector<unsigned char> privDecoded = base64_decode(conf::cfg.seckeyb64);
|
||||
|
||||
unsigned char *pubDecodedBytes = (unsigned char *)malloc(pubDecoded.size());
|
||||
unsigned char *privDecodedBytes = (unsigned char *)malloc(privDecoded.size());
|
||||
@@ -107,18 +49,27 @@ void b64pair_to_crypto()
|
||||
privDecodedBytes[i] = privDecoded[i];
|
||||
}
|
||||
|
||||
cryptoKeyPair.publicKey = pubDecodedBytes;
|
||||
cryptoKeyPair.privateKey = privDecodedBytes;
|
||||
if (conf::cfg.pubkey != NULL)
|
||||
free(conf::cfg.pubkey);
|
||||
|
||||
if (conf::cfg.seckey != NULL)
|
||||
free(conf::cfg.seckey);
|
||||
|
||||
conf::cfg.pubkey = pubDecodedBytes;
|
||||
conf::cfg.seckey = privDecodedBytes;
|
||||
}
|
||||
|
||||
void generate_crypto_keys()
|
||||
{
|
||||
unsigned char *pubKey = (unsigned char *)malloc(crypto_sign_PUBLICKEYBYTES);
|
||||
unsigned char *privKey = (unsigned char *)malloc(crypto_sign_SECRETKEYBYTES);
|
||||
crypto_sign_keypair(pubKey, privKey);
|
||||
if (conf::cfg.pubkey != NULL)
|
||||
free(conf::cfg.pubkey);
|
||||
|
||||
cryptoKeyPair.publicKey = pubKey;
|
||||
cryptoKeyPair.privateKey = privKey;
|
||||
if (conf::cfg.seckey != NULL)
|
||||
free(conf::cfg.seckey);
|
||||
|
||||
conf::cfg.pubkey = (unsigned char *)malloc(crypto_sign_PUBLICKEYBYTES);
|
||||
conf::cfg.seckey = (unsigned char *)malloc(crypto_sign_SECRETKEYBYTES);
|
||||
crypto_sign_keypair(conf::cfg.pubkey, conf::cfg.seckey);
|
||||
}
|
||||
|
||||
int init()
|
||||
@@ -129,20 +80,17 @@ int init()
|
||||
return 0;
|
||||
}
|
||||
|
||||
load_keys_b64();
|
||||
|
||||
//If any keys are missing generate a new pair and save to file.
|
||||
if (!b64KeyPair.publicKey || !b64KeyPair.privateKey)
|
||||
if (conf::cfg.pubkeyb64.empty() || conf::cfg.seckeyb64.empty())
|
||||
{
|
||||
cout << "Keys not found. Generating.\n";
|
||||
cout << "Generating new keys.\n";
|
||||
generate_crypto_keys();
|
||||
cryptopair_to_b64();
|
||||
save_keys_b64();
|
||||
conf::save_config();
|
||||
}
|
||||
else
|
||||
{
|
||||
b64pair_to_crypto();
|
||||
cout << "Keys loaded from file.\n";
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace crypto
|
||||
|
||||
int init();
|
||||
unsigned long long get_sig_len();
|
||||
void sign(const unsigned char *msg, unsigned long long msg_len, unsigned char *sig);
|
||||
bool verify(const unsigned char *msg, unsigned long long msg_len, const unsigned char *sig);
|
||||
void sign(const unsigned char *msg, unsigned long long msg_len, unsigned char *sig, const unsigned char *seckey);
|
||||
bool verify(const unsigned char *msg, unsigned long long msg_len, const unsigned char *sig, const unsigned char *pubkey);
|
||||
|
||||
} // namespace crypto
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ using namespace std;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (!conf::init() || !crypto::init())
|
||||
if (!(conf::init(argc, argv) && crypto::init()))
|
||||
{
|
||||
cerr << "Init error\n";
|
||||
return -1;
|
||||
@@ -20,9 +20,9 @@ int main(int argc, char **argv)
|
||||
//Example sign and verification.
|
||||
unsigned char msg[10] = "hotpocket";
|
||||
unsigned char *sig = new unsigned char[crypto::get_sig_len()];
|
||||
crypto::sign(msg, 10, sig);
|
||||
crypto::sign(msg, 10, sig, conf::cfg.seckey);
|
||||
|
||||
bool isValid = crypto::verify(msg, 10, sig);
|
||||
bool isValid = crypto::verify(msg, 10, sig, conf::cfg.pubkey);
|
||||
if (isValid)
|
||||
cout << "Signature verified.\n";
|
||||
else
|
||||
@@ -31,4 +31,3 @@ int main(int argc, char **argv)
|
||||
cout << "exited normally\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user