diff --git a/README.md b/README.md index 81248341..ddd25d0a 100644 --- a/README.md +++ b/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). \ No newline at end of file diff --git a/makefile b/makefile index 4fcac85e..77a96895 100644 --- a/makefile +++ b/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' diff --git a/myctr/cfg/hp.cfg b/myctr/cfg/hp.cfg new file mode 100644 index 00000000..ae0bb37f --- /dev/null +++ b/myctr/cfg/hp.cfg @@ -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 +} \ No newline at end of file diff --git a/src/conf.cpp b/src/conf.cpp index 16ff4d28..d3e6ec7d 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -2,11 +2,14 @@ #include #include #include +#include +#include #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 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 writer(osw); + PrettyWriter 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 = 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; } diff --git a/src/conf.h b/src/conf.h index 8b88e780..b3135ad2 100644 --- a/src/conf.h +++ b/src/conf.h @@ -2,6 +2,7 @@ #define _HP_CONF_H_ #include "lib/rapidjson/document.h" +#include 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 peers; + vector 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 diff --git a/src/crypto.cpp b/src/crypto.cpp index 792d7edd..80a94b54 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -1,9 +1,7 @@ #include #include #include -#include #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 pubDecoded = base64_decode(b64KeyPair.publicKey); - vector privDecoded = base64_decode(b64KeyPair.privateKey); + vector pubDecoded = base64_decode(conf::cfg.pubkeyb64); + vector 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; diff --git a/src/crypto.h b/src/crypto.h index 5358d59e..fe9d3c42 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -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 diff --git a/src/main.cpp b/src/main.cpp index 987b69cf..b7cfcbf1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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; } -