From 50c7663984d125479a7e7c3d7e261d2ae51b045f Mon Sep 17 00:00:00 2001 From: Oleh Date: Tue, 20 Apr 2021 19:00:59 +0300 Subject: [PATCH 001/100] update documentation --- docs/run-in-hardware-mode.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/run-in-hardware-mode.md b/docs/run-in-hardware-mode.md index 0f7c4e68..0e32911c 100644 --- a/docs/run-in-hardware-mode.md +++ b/docs/run-in-hardware-mode.md @@ -52,6 +52,7 @@ sgxwallet operates on the following network ports: - 1027 (http for initial SSL certification signing) - 1028 (localhost for admin ) - 1029 (http only operation) +- 1030 (localhost for informational requests) If operating with a firewall, please make sure these ports are open so clients are able to connect to the server. @@ -62,7 +63,7 @@ If operating with a firewall, please make sure these ports are open so clients a - \-s Sign client certificate without human confirmation - \-d Turn on debug output - \-v Verbose mode: turn on debug output -- \-vv Detailed verbose mode: turn on debug and trace outputs +- \-V Detailed verbose mode: turn on debug and trace outputs - \-n Launch SGXWalletServer using http (not https) - \-b Restore from back up (you will need to enter backup key) - \-y Do not ask user to acknowledge receipt of backup key From b60b5e999f8eb61bdb668abd4a4f9f27798cf79c Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 3 Jun 2021 16:41:13 +0300 Subject: [PATCH 002/100] SKALE-3951 add new message types to ZMQ server --- Makefile.am | 15 +- ServerInit.cpp | 2 +- TestUtils.cpp | 2 +- TestUtils.h | 4 +- ZMQMessage.cpp | 170 ---------- ZMQMessage.h | 85 ----- sgxwall.cpp | 2 +- testw.cpp | 4 +- .../BLSSignReqMessage.cpp | 0 .../BLSSignReqMessage.h | 0 .../BLSSignRspMessage.cpp | 0 .../BLSSignRspMessage.h | 0 .../ECDSASignReqMessage.cpp | 0 .../ECDSASignReqMessage.h | 0 .../ECDSASignRspMessage.cpp | 0 .../ECDSASignRspMessage.h | 0 ServerWorker.cpp => zmq_src/ServerWorker.cpp | 0 ServerWorker.h => zmq_src/ServerWorker.h | 0 ZMQClient.cpp => zmq_src/ZMQClient.cpp | 0 ZMQClient.h => zmq_src/ZMQClient.h | 0 zmq_src/ZMQMessage.cpp | 311 ++++++++++++++++++ zmq_src/ZMQMessage.h | 126 +++++++ ZMQServer.cpp => zmq_src/ZMQServer.cpp | 0 ZMQServer.h => zmq_src/ZMQServer.h | 0 24 files changed, 453 insertions(+), 268 deletions(-) delete mode 100644 ZMQMessage.cpp delete mode 100644 ZMQMessage.h rename BLSSignReqMessage.cpp => zmq_src/BLSSignReqMessage.cpp (100%) rename BLSSignReqMessage.h => zmq_src/BLSSignReqMessage.h (100%) rename BLSSignRspMessage.cpp => zmq_src/BLSSignRspMessage.cpp (100%) rename BLSSignRspMessage.h => zmq_src/BLSSignRspMessage.h (100%) rename ECDSASignReqMessage.cpp => zmq_src/ECDSASignReqMessage.cpp (100%) rename ECDSASignReqMessage.h => zmq_src/ECDSASignReqMessage.h (100%) rename ECDSASignRspMessage.cpp => zmq_src/ECDSASignRspMessage.cpp (100%) rename ECDSASignRspMessage.h => zmq_src/ECDSASignRspMessage.h (100%) rename ServerWorker.cpp => zmq_src/ServerWorker.cpp (100%) rename ServerWorker.h => zmq_src/ServerWorker.h (100%) rename ZMQClient.cpp => zmq_src/ZMQClient.cpp (100%) rename ZMQClient.h => zmq_src/ZMQClient.h (100%) create mode 100644 zmq_src/ZMQMessage.cpp create mode 100644 zmq_src/ZMQMessage.h rename ZMQServer.cpp => zmq_src/ZMQServer.cpp (100%) rename ZMQServer.h => zmq_src/ZMQServer.h (100%) diff --git a/Makefile.am b/Makefile.am index 907550df..a5043fb9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,7 +70,10 @@ bin_PROGRAMS = sgxwallet testw sgx_util ## have to be explicitly listed -COMMON_SRC = SGXException.cpp ExitHandler.cpp ZMQClient.cpp BLSSignRspMessage.cpp ECDSASignRspMessage.cpp ECDSASignReqMessage.cpp BLSSignReqMessage.cpp ZMQMessage.cpp ZMQServer.cpp ServerWorker.cpp InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp \ +COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/BLSSignRspMessage.cpp \ + zmq_src/ECDSASignRspMessage.cpp zmq_src/ECDSASignReqMessage.cpp zmq_src/BLSSignReqMessage.cpp \ + zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp zmq_src/ServerWorker.cpp \ + InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp \ SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp \ DKGCrypto.cpp ServerInit.cpp BLSPrivateKeyShareSGX.cpp LevelDB.cpp ServerDataChecker.cpp SEKManager.cpp \ third_party/intel/sgx_stub.c third_party/intel/sgx_detect_linux.c third_party/intel/create_enclave.c third_party/intel/oc_alloc.c \ @@ -110,18 +113,20 @@ sgxwallet_LDADD=-l$(SGX_URTS_LIB) -l$(SGX_UAE_SERVICE_LIB) -LlibBLS/deps/deps_in -ljsonrpccpp-stub -ljsonrpccpp-server -ljsonrpccpp-client -ljsonrpccpp-common -ljsoncpp -lmicrohttpd \ -lboost_system -lboost_thread -lgnutls -lgcrypt -lidn2 -lcurl -lssl -lcrypto -lz -lpthread -lstdc++fs - testw_SOURCES=testw.cpp $(COMMON_SRC) nodist_testw_SOURCES=${nodist_sgxwallet_SOURCES} EXTRA_testw_DEPENDENCIES=${EXTRA_sgxwallet_DEPENDENCIES} -testw_LDADD= ${sgxwallet_LDADD} +testw_LDADD=${sgxwallet_LDADD} -sgx_util_SOURCES= SGXException.cpp ExitHandler.cpp InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp sgx_util.cpp stubclient.cpp LevelDB.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp +sgx_util_SOURCES=SGXException.cpp ExitHandler.cpp InvalidStateException.cpp Exception.cpp \ + InvalidArgumentException.cpp Log.cpp sgx_util.cpp stubclient.cpp LevelDB.cpp \ + SGXRegistrationServer.cpp CSRManagerServer.cpp sgx_util_LDADD=-LlibBLS/deps/deps_inst/x86_or_x64/lib -Lleveldb/build -LlibBLS/build \ -LlibBLS/build/libff/libff \ -Llibzmq/build/lib/ \ -l:libzmq.a \ -l:libbls.a -l:libleveldb.a \ - -l:libff.a -lgmp -ljsonrpccpp-stub -ljsonrpccpp-server -ljsonrpccpp-client -ljsonrpccpp-common -ljsoncpp -lmicrohttpd -lgnutls -lgcrypt -lidn2 -lcurl -lssl -lcrypto -lz -lpthread -ldl + -l:libff.a -lgmp -ljsonrpccpp-stub -ljsonrpccpp-server -ljsonrpccpp-client -ljsonrpccpp-common \ + -ljsoncpp -lmicrohttpd -lgnutls -lgcrypt -lidn2 -lcurl -lssl -lcrypto -lz -lpthread -ldl diff --git a/ServerInit.cpp b/ServerInit.cpp index 99e1211e..ce9f5682 100644 --- a/ServerInit.cpp +++ b/ServerInit.cpp @@ -58,7 +58,7 @@ #include "BLSCrypto.h" #include "ServerInit.h" #include "SGXException.h" -#include "ZMQServer.h" +#include "zmq_src/ZMQServer.h" #include "SGXWalletServer.hpp" uint32_t enclaveLogLevel = 0; diff --git a/TestUtils.cpp b/TestUtils.cpp index 50814563..41d824e4 100644 --- a/TestUtils.cpp +++ b/TestUtils.cpp @@ -42,7 +42,7 @@ #include "SGXWalletServer.hpp" #include "catch.hpp" -#include "ZMQClient.h" +#include "zmq_src/ZMQClient.h" #include "BLSSigShare.h" #include "BLSSigShareSet.h" #include "BLSPublicKeyShare.h" diff --git a/TestUtils.h b/TestUtils.h index 29830434..169f3687 100644 --- a/TestUtils.h +++ b/TestUtils.h @@ -27,8 +27,6 @@ #include #include #include -// #include -// #include #include #include "sgxwallet_common.h" #include "third_party/intel/create_enclave.h" @@ -41,7 +39,7 @@ #include #include "stubclient.h" #include -#include "ZMQClient.h" +#include "zmq_src/ZMQClient.h" #include "abstractstubserver.h" using namespace std; diff --git a/ZMQMessage.cpp b/ZMQMessage.cpp deleted file mode 100644 index b7605f7c..00000000 --- a/ZMQMessage.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - Copyright (C) 2020 SKALE Labs - - This file is part of skale-consensus. - - skale-consensus is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - skale-consensus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with skale-consensus. If not, see . - - @file ZMQMessage.cpp - @author Stan Kladko - @date 2020 -*/ - -#include "common.h" -#include "sgxwallet_common.h" -#include -#include -#include - -#include "ZMQClient.h" -#include "SGXWalletServer.hpp" -#include "BLSSignReqMessage.h" -#include "BLSSignRspMessage.h" -#include "ECDSASignReqMessage.h" -#include "ECDSASignRspMessage.h" -#include "ZMQMessage.h" - - -uint64_t ZMQMessage::getUint64Rapid(const char *_name) { - CHECK_STATE(_name); - CHECK_STATE(d->HasMember(_name)); - const rapidjson::Value &a = (*d)[_name]; - CHECK_STATE(a.IsUint64()); - return a.GetUint64(); -}; - -string ZMQMessage::getStringRapid(const char *_name) { - CHECK_STATE(_name); - CHECK_STATE(d->HasMember(_name)); - CHECK_STATE((*d)[_name].IsString()); - return (*d)[_name].GetString(); -}; - - - - -shared_ptr ZMQMessage::parse(const char *_msg, - size_t _size, bool _isRequest, - bool _verifySig) { - - CHECK_STATE(_msg); - CHECK_STATE2(_size > 5, ZMQ_INVALID_MESSAGE_SIZE); - // CHECK NULL TERMINATED - CHECK_STATE(_msg[_size] == 0); - CHECK_STATE2(_msg[_size - 1] == '}', ZMQ_INVALID_MESSAGE); - CHECK_STATE2(_msg[0] == '{', ZMQ_INVALID_MESSAGE); - - auto d = make_shared(); - - d->Parse(_msg); - - CHECK_STATE2(!d->HasParseError(), ZMQ_COULD_NOT_PARSE); - CHECK_STATE2(d->IsObject(), ZMQ_COULD_NOT_PARSE); - - CHECK_STATE2(d->HasMember("type"), ZMQ_NO_TYPE_IN_MESSAGE); - CHECK_STATE2((*d)["type"].IsString(), ZMQ_NO_TYPE_IN_MESSAGE); - string type = (*d)["type"].GetString(); - - if (_verifySig) { - CHECK_STATE2(d->HasMember("cert"),ZMQ_NO_CERT_IN_MESSAGE); - CHECK_STATE2(d->HasMember("msgSig"), ZMQ_NO_SIG_IN_MESSAGE); - CHECK_STATE2((*d)["cert"].IsString(), ZMQ_NO_CERT_IN_MESSAGE); - CHECK_STATE2((*d)["msgSig"].IsString(), ZMQ_NO_SIG_IN_MESSAGE); - - auto cert = make_shared((*d)["cert"].GetString()); - string hash = cryptlite::sha256::hash_hex(*cert); - - auto filepath = "/tmp/sgx_wallet_cert_hash_" + hash; - - std::ofstream outFile(filepath); - - outFile << *cert; - - outFile.close(); - - static recursive_mutex m; - - EVP_PKEY *publicKey = nullptr; - - { - lock_guard lock(m); - - if (!verifiedCerts.exists(*cert)) { - CHECK_STATE(SGXWalletServer::verifyCert(filepath)); - auto handles = ZMQClient::readPublicKeyFromCertStr(*cert); - CHECK_STATE(handles.first); - CHECK_STATE(handles.second); - verifiedCerts.put(*cert, handles); - remove(cert->c_str()); - } - - publicKey = verifiedCerts.get(*cert).first; - - CHECK_STATE(publicKey); - - auto msgSig = make_shared((*d)["msgSig"].GetString()); - - d->RemoveMember("msgSig"); - - rapidjson::StringBuffer buffer; - - rapidjson::Writer w(buffer); - - d->Accept(w); - - auto msgToVerify = buffer.GetString(); - - ZMQClient::verifySig(publicKey,msgToVerify, *msgSig ); - - } - } - - - shared_ptr result; - - if (_isRequest) { - return buildRequest(type, d); - } else { - return buildResponse(type, d); - } -} - -shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr _d) { - if (_type == ZMQMessage::BLS_SIGN_REQ) { - return make_shared(_d); - } else if (_type == ZMQMessage::ECDSA_SIGN_REQ) { - return - make_shared(_d); - } else { - BOOST_THROW_EXCEPTION(SGXException(-301, "Incorrect zmq message type: " + - string(_type))); - } -} - -shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr _d) { - if (_type == ZMQMessage::BLS_SIGN_RSP) { - return - make_shared(_d); - } else if (_type == ZMQMessage::ECDSA_SIGN_RSP) { - return - make_shared(_d); - } else { - BOOST_THROW_EXCEPTION(InvalidStateException("Incorrect zmq message request type: " + string(_type), - __CLASS_NAME__) - ); - } -} - -cache::lru_cache> -ZMQMessage::verifiedCerts(256); \ No newline at end of file diff --git a/ZMQMessage.h b/ZMQMessage.h deleted file mode 100644 index e2ce6a87..00000000 --- a/ZMQMessage.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright (C) 2018-2019 SKALE Labs - - This file is part of skale-consensus. - - skale-consensus is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - skale-consensus is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with skale-consensus. If not, see . - - @file ZMQMessage.h - @author Stan Kladko - @date 2018 -*/ - -#pragma once - - -#include -#include - -#include -#include -#include -#include -#include - -#include "third_party/lrucache.hpp" - -#include "abstractstubserver.h" - -#include "document.h" -#include "stringbuffer.h" -#include "writer.h" - -#include "SGXException.h" - -using namespace std; - -class ZMQMessage { - - shared_ptr d; - - - static cache::lru_cache> verifiedCerts; - -protected: - - -public: - - - static constexpr const char *BLS_SIGN_REQ = "BLSSignReq"; - static constexpr const char *BLS_SIGN_RSP = "BLSSignRsp"; - static constexpr const char *ECDSA_SIGN_REQ = "ECDSASignReq"; - static constexpr const char *ECDSA_SIGN_RSP = "ECDSASignRsp"; - - explicit ZMQMessage(shared_ptr &_d) : d(_d) { - }; - - string getStringRapid(const char *_name); - - uint64_t getUint64Rapid(const char *_name); - - uint64_t getStatus() { - return getUint64Rapid("status"); - } - - static shared_ptr parse(const char* _msg, size_t _size, bool _isRequest, - bool _verifySig); - - static shared_ptr buildRequest(string& type, shared_ptr _d); - static shared_ptr buildResponse(string& type, shared_ptr _d); - - virtual Json::Value process() = 0; - -}; \ No newline at end of file diff --git a/sgxwall.cpp b/sgxwall.cpp index 18c0c6be..798da45b 100644 --- a/sgxwall.cpp +++ b/sgxwall.cpp @@ -36,7 +36,7 @@ #include "TestUtils.h" -#include "ZMQServer.h" +#include "zmq_src/ZMQServer.h" #include "testw.h" #include "sgxwall.h" diff --git a/testw.cpp b/testw.cpp index 593c1b4a..151398a0 100644 --- a/testw.cpp +++ b/testw.cpp @@ -57,8 +57,8 @@ #include "SGXRegistrationServer.h" #include "SGXWalletServer.h" -#include "ZMQClient.h" -#include "ZMQServer.h" +#include "zmq_src/ZMQClient.h" +#include "zmq_src/ZMQServer.h" #include "sgxwallet.h" #include "TestUtils.h" #include "testw.h" diff --git a/BLSSignReqMessage.cpp b/zmq_src/BLSSignReqMessage.cpp similarity index 100% rename from BLSSignReqMessage.cpp rename to zmq_src/BLSSignReqMessage.cpp diff --git a/BLSSignReqMessage.h b/zmq_src/BLSSignReqMessage.h similarity index 100% rename from BLSSignReqMessage.h rename to zmq_src/BLSSignReqMessage.h diff --git a/BLSSignRspMessage.cpp b/zmq_src/BLSSignRspMessage.cpp similarity index 100% rename from BLSSignRspMessage.cpp rename to zmq_src/BLSSignRspMessage.cpp diff --git a/BLSSignRspMessage.h b/zmq_src/BLSSignRspMessage.h similarity index 100% rename from BLSSignRspMessage.h rename to zmq_src/BLSSignRspMessage.h diff --git a/ECDSASignReqMessage.cpp b/zmq_src/ECDSASignReqMessage.cpp similarity index 100% rename from ECDSASignReqMessage.cpp rename to zmq_src/ECDSASignReqMessage.cpp diff --git a/ECDSASignReqMessage.h b/zmq_src/ECDSASignReqMessage.h similarity index 100% rename from ECDSASignReqMessage.h rename to zmq_src/ECDSASignReqMessage.h diff --git a/ECDSASignRspMessage.cpp b/zmq_src/ECDSASignRspMessage.cpp similarity index 100% rename from ECDSASignRspMessage.cpp rename to zmq_src/ECDSASignRspMessage.cpp diff --git a/ECDSASignRspMessage.h b/zmq_src/ECDSASignRspMessage.h similarity index 100% rename from ECDSASignRspMessage.h rename to zmq_src/ECDSASignRspMessage.h diff --git a/ServerWorker.cpp b/zmq_src/ServerWorker.cpp similarity index 100% rename from ServerWorker.cpp rename to zmq_src/ServerWorker.cpp diff --git a/ServerWorker.h b/zmq_src/ServerWorker.h similarity index 100% rename from ServerWorker.h rename to zmq_src/ServerWorker.h diff --git a/ZMQClient.cpp b/zmq_src/ZMQClient.cpp similarity index 100% rename from ZMQClient.cpp rename to zmq_src/ZMQClient.cpp diff --git a/ZMQClient.h b/zmq_src/ZMQClient.h similarity index 100% rename from ZMQClient.h rename to zmq_src/ZMQClient.h diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp new file mode 100644 index 00000000..608877df --- /dev/null +++ b/zmq_src/ZMQMessage.cpp @@ -0,0 +1,311 @@ +/* + Copyright (C) 2020 SKALE Labs + + This file is part of skale-consensus. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file ZMQMessage.cpp + @author Stan Kladko + @date 2020 +*/ + +#include "common.h" +#include "sgxwallet_common.h" +#include +#include +#include + +#include "ZMQClient.h" +#include "SGXWalletServer.hpp" +#include "BLSSignReqMessage.h" +#include "BLSSignRspMessage.h" +#include "ECDSASignReqMessage.h" +#include "ECDSASignRspMessage.h" +#include "ZMQMessage.h" + + +uint64_t ZMQMessage::getUint64Rapid(const char *_name) { + CHECK_STATE(_name); + CHECK_STATE(d->HasMember(_name)); + const rapidjson::Value &a = (*d)[_name]; + CHECK_STATE(a.IsUint64()); + return a.GetUint64(); +}; + +string ZMQMessage::getStringRapid(const char *_name) { + CHECK_STATE(_name); + CHECK_STATE(d->HasMember(_name)); + CHECK_STATE((*d)[_name].IsString()); + return (*d)[_name].GetString(); +}; + +shared_ptr ZMQMessage::parse(const char *_msg, + size_t _size, bool _isRequest, + bool _verifySig) { + + CHECK_STATE(_msg); + CHECK_STATE2(_size > 5, ZMQ_INVALID_MESSAGE_SIZE); + // CHECK NULL TERMINATED + CHECK_STATE(_msg[_size] == 0); + CHECK_STATE2(_msg[_size - 1] == '}', ZMQ_INVALID_MESSAGE); + CHECK_STATE2(_msg[0] == '{', ZMQ_INVALID_MESSAGE); + + auto d = make_shared(); + + d->Parse(_msg); + + CHECK_STATE2(!d->HasParseError(), ZMQ_COULD_NOT_PARSE); + CHECK_STATE2(d->IsObject(), ZMQ_COULD_NOT_PARSE); + + CHECK_STATE2(d->HasMember("type"), ZMQ_NO_TYPE_IN_MESSAGE); + CHECK_STATE2((*d)["type"].IsString(), ZMQ_NO_TYPE_IN_MESSAGE); + string type = (*d)["type"].GetString(); + + if (_verifySig) { + CHECK_STATE2(d->HasMember("cert"),ZMQ_NO_CERT_IN_MESSAGE); + CHECK_STATE2(d->HasMember("msgSig"), ZMQ_NO_SIG_IN_MESSAGE); + CHECK_STATE2((*d)["cert"].IsString(), ZMQ_NO_CERT_IN_MESSAGE); + CHECK_STATE2((*d)["msgSig"].IsString(), ZMQ_NO_SIG_IN_MESSAGE); + + auto cert = make_shared((*d)["cert"].GetString()); + string hash = cryptlite::sha256::hash_hex(*cert); + + auto filepath = "/tmp/sgx_wallet_cert_hash_" + hash; + + std::ofstream outFile(filepath); + + outFile << *cert; + + outFile.close(); + + static recursive_mutex m; + + EVP_PKEY *publicKey = nullptr; + + { + lock_guard lock(m); + + if (!verifiedCerts.exists(*cert)) { + CHECK_STATE(SGXWalletServer::verifyCert(filepath)); + auto handles = ZMQClient::readPublicKeyFromCertStr(*cert); + CHECK_STATE(handles.first); + CHECK_STATE(handles.second); + verifiedCerts.put(*cert, handles); + remove(cert->c_str()); + } + + publicKey = verifiedCerts.get(*cert).first; + + CHECK_STATE(publicKey); + + auto msgSig = make_shared((*d)["msgSig"].GetString()); + + d->RemoveMember("msgSig"); + + rapidjson::StringBuffer buffer; + + rapidjson::Writer w(buffer); + + d->Accept(w); + + auto msgToVerify = buffer.GetString(); + + ZMQClient::verifySig(publicKey,msgToVerify, *msgSig ); + + } + } + + shared_ptr result; + + if (_isRequest) { + return buildRequest(type, d); + } else { + return buildResponse(type, d); + } +} + +shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr _d) { + Requests r; + try { + int t = requests.at( _type.c_str() ); + r = static_cast(t); + } catch ( std::out_of_range& ) { + BOOST_THROW_EXCEPTION(SGXException(-301, "Incorrect zmq message type: " + string(_type))); + } + + shared_ptr ret = nullptr; + + switch (r) { + case ENUM_BLS_SIGN_REQ: + ret = make_shared(_d); + break; + case ENUM_ECDSA_SIGN_REQ: + ret = make_shared(_d); + break; + case ENUM_IMPORT_BLS_REQ: + ret = make_shared(_d); + break; + case ENUM_IMPORT_ECDSA_REQ: + ret = make_shared(_d); + break; + case ENUM_GENERATE_ECDSA_REQ: + ret = make_shared(_d); + break; + case ENUM_GET_PUBLIC_ECDSA_REQ: + ret = make_shared(_d); + break; + case ENUM_GENERATE_DKG_POLY_REQ: + ret = make_shared(_d); + break; + case ENUM_GET_VV_REQ: + ret = make_shared(_d); + break; + case ENUM_GET_SECRET_SHARE_REQ: + ret = make_shared(_d); + break; + case ENUM_DKG_VERIFY_REQ: + ret = make_shared(_d); + break; + case ENUM_CREATE_BLS_PRIVATE_REQ: + ret = make_shared(_d); + break; + case ENUM_GET_BLS_PUBLIC_REQ: + ret = make_shared(_d); + break; + case ENUM_GET_ALL_BLS_PUBLIC_REQ: + ret = make_shared(_d); + break; + case ENUM_COMPLAINT_RESPONSE_REQ: + ret = make_shared(_d); + break; + case ENUM_MULT_G2_REQ: + ret = make_shared(_d); + break; + case ENUM_IS_POLY_EXISTS_REQ: + ret = make_shared(_d); + break; + case ENUM_GET_SERVER_STATUS_REQ: + ret = make_shared(_d); + break; + case ENUM_GET_SERVER_VERSION_REQ: + ret = make_shared(_d); + break; + case ENUM_DELETE_BLS_KEY_REQ: + ret = make_shared(_d); + break; + default: + break; + } + + return ret; +} + +shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr _d) { + Responses r; + try { + int t = responses.at( _type.c_str() ); + r = static_cast(t); + } catch ( std::out_of_range& ) { + BOOST_THROW_EXCEPTION(InvalidStateException("Incorrect zmq message request type: " + string(_type), + __CLASS_NAME__) + ); + } + + shared_ptr ret = nullptr; + + switch (r) { + case ENUM_BLS_SIGN_RSP: + ret = make_shared(_d); + break; + case ENUM_ECDSA_SIGN_RSP: + ret = make_shared(_d); + break; + case ENUM_IMPORT_BLS_RSP: + ret = make_shared(_d); + break; + case ENUM_IMPORT_ECDSA_RSP: + ret = make_shared(_d); + break; + case ENUM_GENERATE_ECDSA_RSP: + ret = make_shared(_d); + break; + case ENUM_GET_PUBLIC_ECDSA_RSP: + ret = make_shared(_d); + break; + case ENUM_GENERATE_DKG_POLY_RSP: + ret = make_shared(_d); + break; + case ENUM_GET_VV_RSP: + ret = make_shared(_d); + break; + case ENUM_GET_SECRET_SHARE_RSP: + ret = make_shared(_d); + break; + case ENUM_DKG_VERIFY_RSP: + ret = make_shared(_d); + break; + case ENUM_CREATE_BLS_PRIVATE_RSP: + ret = make_shared(_d); + break; + case ENUM_GET_BLS_PUBLIC_RSP: + ret = make_shared(_d); + break; + case ENUM_GET_ALL_BLS_PUBLIC_RSP: + ret = make_shared(_d); + break; + case ENUM_COMPLAINT_RESPONSE_RSP: + ret = make_shared(_d); + break; + case ENUM_MULT_G2_RSP: + ret = make_shared(_d); + break; + case ENUM_IS_POLY_EXISTS_RSP: + ret = make_shared(_d); + break; + case ENUM_GET_SERVER_STATUS_RSP: + ret = make_shared(_d); + break; + case ENUM_GET_SERVER_VERSION_RSP: + ret = make_shared(_d); + break; + case ENUM_DELETE_BLS_KEY_RSP: + ret = make_shared(_d); + break; + default: + break; + } + + return ret; +} + +cache::lru_cache> ZMQMessage::verifiedCerts(256); + +const std::map ZMQMessage::responses { + {BLS_SIGN_REQ, 0}, {ECDSA_SIGN_REQ, 1}, {IMPORT_BLS_REQ, 2}, {IMPORT_ECDSA_REQ, 3}, + {GENERATE_ECDSA_REQ, 4}, {GET_PUBLIC_ECDSA_REQ, 5}, {GENERATE_DKG_POLY_REQ, 6}, + {GET_VV_REQ, 7}, {GET_SECRET_SHARE_REQ, 8}, {DKG_VERIFY_REQ, 9}, + {CREATE_BLS_PRIVATE_REQ, 10}, {GET_BLS_PUBLIC_REQ, 11}, {GET_ALL_BLS_PUBLIC_REQ, 12}, + {COMPLAINT_RESPONSE_REQ, 13}, {MULT_G2_REQ, 14}, {IS_POLY_EXISTS_REQ, 15}, + {GET_SERVER_STATUS_REQ, 16}, {GET_SERVER_VERSION_REQ, 17}, {DELETE_BLS_KEY_REQ, 18} +}; + +const std::map ZMQMessage::requests { + {BLS_SIGN_RSP, 0}, {ECDSA_SIGN_RSP, 1}, {IMPORT_BLS_RSP, 2}, {IMPORT_ECDSA_RSP, 3}, + {GENERATE_ECDSA_RSP, 4}, {GET_PUBLIC_ECDSA_RSP, 5}, {GENERATE_DKG_POLY_RSP, 6}, + {GET_VV_RSP, 7}, {GET_SECRET_SHARE_RSP, 8}, {DKG_VERIFY_RSP, 9}, + {CREATE_BLS_PRIVATE_RSP, 10}, {GET_BLS_PUBLIC_RSP, 11}, {GET_ALL_BLS_PUBLIC_RSP, 12}, + {COMPLAINT_RESPONSE_RSP, 13}, {MULT_G2_RSP, 14}, {IS_POLY_EXISTS_RSP, 15}, + {GET_SERVER_STATUS_RSP, 16}, {GET_SERVER_VERSION_RSP, 17}, {DELETE_BLS_KEY_RSP, 18} +}; diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h new file mode 100644 index 00000000..c3a21e42 --- /dev/null +++ b/zmq_src/ZMQMessage.h @@ -0,0 +1,126 @@ +/* + Copyright (C) 2018-2019 SKALE Labs + + This file is part of skale-consensus. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file ZMQMessage.h + @author Stan Kladko + @date 2018 +*/ + +#pragma once + + +#include +#include + +#include +#include +#include +#include +#include + +#include "third_party/lrucache.hpp" + +#include "abstractstubserver.h" + +#include "document.h" +#include "stringbuffer.h" +#include "writer.h" + +#include "SGXException.h" + +using namespace std; + +class ZMQMessage { + + shared_ptr d; + + + static cache::lru_cache> verifiedCerts; + +public: + + static constexpr const char *BLS_SIGN_REQ = "BLSSignReq"; + static constexpr const char *BLS_SIGN_RSP = "BLSSignRsp"; + static constexpr const char *ECDSA_SIGN_REQ = "ECDSASignReq"; + static constexpr const char *ECDSA_SIGN_RSP = "ECDSASignRsp"; + static constexpr const char *IMPORT_BLS_REQ = "importBLSReq"; + static constexpr const char *IMPORT_BLS_RSP = "importBLSRps"; + static constexpr const char *IMPORT_ECDSA_REQ = "importECDSAReq"; + static constexpr const char *IMPORT_ECDSA_RSP = "importECDSARsp"; + static constexpr const char *GENERATE_ECDSA_REQ = "generateECDSAReq"; + static constexpr const char *GENERATE_ECDSA_RSP = "generateECDSARsp"; + static constexpr const char *GET_PUBLIC_ECDSA_REQ = "getPublicECDSAReq"; + static constexpr const char *GET_PUBLIC_ECDSA_RSP = "getPublicECDSARsp"; + static constexpr const char *GENERATE_DKG_POLY_REQ = "generateDKGPolyReq"; + static constexpr const char *GENERATE_DKG_POLY_RSP = "generateDKGPolyRsp"; + static constexpr const char *GET_VV_REQ = "getVerificationVectorReq"; + static constexpr const char *GET_VV_RSP = "getVerificationVectorRsp"; + static constexpr const char *GET_SECRET_SHARE_REQ = "getSecretShareReq"; + static constexpr const char *GET_SECRET_SHARE_RSP = "getSecretShareRsp"; + static constexpr const char *DKG_VERIFY_REQ = "dkgVerificationReq"; + static constexpr const char *DKG_VERIFY_RSP = "dkgVerificationRsp"; + static constexpr const char *CREATE_BLS_PRIVATE_REQ = "createBLSPrivateReq"; + static constexpr const char *CREATE_BLS_PRIVATE_RSP = "createBLSPrivateRsp"; + static constexpr const char *GET_BLS_PUBLIC_REQ = "getBLSPublicReq"; + static constexpr const char *GET_BLS_PUBLIC_RSP = "getBLSPublicRsp"; + static constexpr const char *GET_ALL_BLS_PUBLIC_REQ = "getAllBLSPublicReq"; + static constexpr const char *GET_ALL_BLS_PUBLIC_RSP = "getAllBLSPublicRsp"; + static constexpr const char *COMPLAINT_RESPONSE_REQ = "complaintResponseReq"; + static constexpr const char *COMPLAINT_RESPONSE_RSP = "complaintResponseRsp"; + static constexpr const char *MULT_G2_REQ = "multG2Req"; + static constexpr const char *MULT_G2_RSP = "multG2Rsp"; + static constexpr const char *IS_POLY_EXISTS_REQ = "isPolyExistsReq"; + static constexpr const char *IS_POLY_EXISTS_RSP = "isPolyExistsRsp"; + static constexpr const char *GET_SERVER_STATUS_REQ = "getServerStatusReq"; + static constexpr const char *GET_SERVER_STATUS_RSP = "getServerStatusRsp"; + static constexpr const char *GET_SERVER_VERSION_REQ = "getServerVersionReq"; + static constexpr const char *GET_SERVER_VERSION_RSP = "getServerVersionRsp"; + static constexpr const char *DELETE_BLS_KEY_REQ = "deleteBLSKeyReq"; + static constexpr const char *DELETE_BLS_KEY_RSP = "deleteBLSKeyRsp"; + + static const std::map requests; + static const std::map responses; + + enum Requests { ENUM_BLS_SIGN_REQ, ENUM_ECDSA_SIGN_REQ, ENUM_IMPORT_BLS_REQ, ENUM_IMPORT_ECDSA_REQ, ENUM_GENERATE_ECDSA_REQ, ENUM_GET_PUBLIC_ECDSA_REQ, + ENUM_GENERATE_DKG_POLY_REQ, ENUM_GET_VV_REQ, ENUM_GET_SECRET_SHARE_REQ, ENUM_DKG_VERIFY_REQ, ENUM_CREATE_BLS_PRIVATE_REQ, + ENUM_GET_BLS_PUBLIC_REQ, ENUM_GET_ALL_BLS_PUBLIC_REQ, ENUM_COMPLAINT_RESPONSE_REQ, ENUM_MULT_G2_REQ, ENUM_IS_POLY_EXISTS_REQ, + ENUM_GET_SERVER_STATUS_REQ, ENUM_GET_SERVER_VERSION_REQ, ENUM_DELETE_BLS_KEY_REQ }; + enum Responses { ENUM_BLS_SIGN_RSP, ENUM_ECDSA_SIGN_RSP, ENUM_IMPORT_BLS_RSP, ENUM_IMPORT_ECDSA_RSP, ENUM_GENERATE_ECDSA_RSP, ENUM_GET_PUBLIC_ECDSA_RSP, + ENUM_GENERATE_DKG_POLY_RSP, ENUM_GET_VV_RSP, ENUM_GET_SECRET_SHARE_RSP, ENUM_DKG_VERIFY_RSP, ENUM_CREATE_BLS_PRIVATE_RSP, + ENUM_GET_BLS_PUBLIC_RSP, ENUM_GET_ALL_BLS_PUBLIC_RSP, ENUM_COMPLAINT_RESPONSE_RSP, ENUM_MULT_G2_RSP, ENUM_IS_POLY_EXISTS_RSP, + ENUM_GET_SERVER_STATUS_RSP, ENUM_GET_SERVER_VERSION_RSP, ENUM_DELETE_BLS_KEY_RSP }; + + explicit ZMQMessage(shared_ptr &_d) : d(_d) {}; + + string getStringRapid(const char *_name); + + uint64_t getUint64Rapid(const char *_name); + + uint64_t getStatus() { + return getUint64Rapid("status"); + } + + static shared_ptr parse(const char* _msg, size_t _size, bool _isRequest, + bool _verifySig); + + static shared_ptr buildRequest(string& type, shared_ptr _d); + static shared_ptr buildResponse(string& type, shared_ptr _d); + + virtual Json::Value process() = 0; + +}; diff --git a/ZMQServer.cpp b/zmq_src/ZMQServer.cpp similarity index 100% rename from ZMQServer.cpp rename to zmq_src/ZMQServer.cpp diff --git a/ZMQServer.h b/zmq_src/ZMQServer.h similarity index 100% rename from ZMQServer.h rename to zmq_src/ZMQServer.h From 303625eb47d27702ef140e0bcb183426d2048317 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 3 Jun 2021 17:13:47 +0300 Subject: [PATCH 003/100] SKALE-4161 format code --- TestUtils.cpp | 8 -------- testw.cpp | 45 +++---------------------------------------- zmq_src/ZMQClient.cpp | 18 +---------------- zmq_src/ZMQClient.h | 3 +-- zmq_src/ZMQServer.h | 3 --- 5 files changed, 5 insertions(+), 72 deletions(-) diff --git a/TestUtils.cpp b/TestUtils.cpp index 41d824e4..e43fffe1 100644 --- a/TestUtils.cpp +++ b/TestUtils.cpp @@ -73,7 +73,6 @@ string TestUtils::stringFromFr(libff::alt_bn128_Fr &el) { return string(arr); } - string TestUtils::convertDecToHex(string dec, int numBytes) { mpz_t num; mpz_init(num); @@ -190,7 +189,6 @@ void TestUtils::sendRPCRequest() { CHECK_STATE(sig["status"].asInt() == 0); } - CHECK_STATE(ethKeys[i]["status"] == 0); string polyName = "POLY:SCHAIN_ID:" + to_string(schainID) + ":NODE_ID:" + to_string(i) + ":DKG_ID:" + to_string(dkgID); @@ -250,7 +248,6 @@ void TestUtils::sendRPCRequest() { publicShares["publicShares"][i] = pubShares[i]; } - Json::Value blsPublicKeys; for (int i6 = 0; i6 <= testCount; i6++) { @@ -263,7 +260,6 @@ void TestUtils::sendRPCRequest() { string blsName = "BLS_KEY" + polyNames[i].substr(4); string secretShare = secretShares[i]["secretShare"].asString(); - auto response = c.createBLSPrivateKey(blsName, ethKeys[i]["keyName"].asString(), polyNames[i], secShares[i], t, n); CHECK_STATE(response["status"] == 0); @@ -399,14 +395,10 @@ void TestUtils::sendRPCRequestV2() { sigShareSet.merge(); } - void TestUtils::sendRPCRequestZMQ() { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); - - - int n = 16, t = 16; Json::Value ethKeys[n]; Json::Value verifVects[n]; diff --git a/testw.cpp b/testw.cpp index 151398a0..91bccae7 100644 --- a/testw.cpp +++ b/testw.cpp @@ -65,7 +65,6 @@ #define PRINT_SRC_LINE cerr << "Executing line " << to_string(__LINE__) << endl; - using namespace jsonrpc; using namespace std; @@ -97,7 +96,6 @@ class TestFixtureHTTPS { } }; - class TestFixtureZMQSign { public: TestFixtureZMQSign() { @@ -112,7 +110,6 @@ class TestFixtureZMQSign { } }; - class TestFixtureNoResetFromBackup { public: TestFixtureNoResetFromBackup() { @@ -127,7 +124,6 @@ class TestFixtureNoResetFromBackup { } }; - class TestFixtureNoReset { public: TestFixtureNoReset() { @@ -161,7 +157,6 @@ TEST_CASE_METHOD(TestFixture, "ECDSA AES keygen and signature test", "[ecdsa-aes vector signatureS(BUF_LEN, 0); uint8_t signatureV = 0; - for (int i = 0; i < 50; i++) { PRINT_SRC_LINE status = trustedEcdsaSign(eid, &errStatus, errMsg.data(), encrPrivKey.data(), encLen, @@ -174,7 +169,6 @@ TEST_CASE_METHOD(TestFixture, "ECDSA AES keygen and signature test", "[ecdsa-aes } - TEST_CASE_METHOD(TestFixture, "ECDSA AES key gen", "[ecdsa-aes-key-gen]") { vector errMsg(BUF_LEN, 0); int errStatus = 0; @@ -191,7 +185,6 @@ TEST_CASE_METHOD(TestFixture, "ECDSA AES key gen", "[ecdsa-aes-key-gen]") { REQUIRE(errStatus == SGX_SUCCESS); } - TEST_CASE_METHOD(TestFixture, "ECDSA AES get public key", "[ecdsa-aes-get-pub-key]") { int errStatus = 0; vector errMsg(BUF_LEN, 0); @@ -219,7 +212,6 @@ TEST_CASE_METHOD(TestFixture, "ECDSA AES get public key", "[ecdsa-aes-get-pub-ke REQUIRE(errStatus == SGX_SUCCESS); } - /* Do later TEST_CASE_METHOD("BLS key encrypt/decrypt", "[bls-key-encrypt-decrypt]") { resetDB(); @@ -251,7 +243,6 @@ TEST_CASE_METHOD("BLS key encrypt/decrypt", "[bls-key-encrypt-decrypt]") { */ - string genECDSAKeyAPI(StubClient &_c) { Json::Value genKey = _c.generateECDSAKey(); CHECK_STATE(genKey["status"].asInt() == 0); @@ -281,10 +272,8 @@ TEST_CASE_METHOD(TestFixture, "ECDSA key gen API", "[ecdsa-key-gen-api]") { auto keyName = genECDSAKeyAPI(c); - Json::Value sig = c.ecdsaSignMessageHash(10, keyName, SAMPLE_HASH); - for (int i = 0; i <= 20; i++) { try { PRINT_SRC_LINE @@ -308,7 +297,6 @@ TEST_CASE_METHOD(TestFixture, "BLS key encrypt", "[bls-key-encrypt]") { sleep(3); } - TEST_CASE_METHOD(TestFixture, "DKG AES gen test", "[dkg-aes-gen]") { vector encryptedDKGSecret(BUF_LEN, 0); vector errMsg(BUF_LEN, 0); @@ -333,7 +321,6 @@ TEST_CASE_METHOD(TestFixture, "DKG AES gen test", "[dkg-aes-gen]") { sleep(3); } - TEST_CASE_METHOD(TestFixture, "DKG AES public shares test", "[dkg-aes-pub-shares]") { vector encryptedDKGSecret(BUF_LEN, 0); vector errMsg(BUF_LEN, 0); @@ -443,7 +430,6 @@ TEST_CASE_METHOD(TestFixture, "DKG AES encrypted secret shares version 2 test", REQUIRE(errStatus == SGX_SUCCESS); } - /* * ( "verification test", "[verify]" ) { @@ -459,11 +445,6 @@ TEST_CASE_METHOD(TestFixture, "DKG AES encrypted secret shares version 2 test", }*/ - - - - - TEST_CASE_METHOD(TestFixture, "DKG_BLS test", "[dkg-bls]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); @@ -506,7 +487,6 @@ TEST_CASE_METHOD(TestFixture, "DKG_BLS V2 test", "[dkg-bls-v2]") { TestUtils::doDKGV2(c, 16, 5, ecdsaKeyNames, blsKeyNames, schainID, dkgID); } - TEST_CASE_METHOD(TestFixture, "DKG_BLS ZMQ test", "[dkgblszmq]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); @@ -594,7 +574,6 @@ TEST_CASE_METHOD(TestFixture, "Get ServerVersion", "[get-server-version]") { sleep(3); } - TEST_CASE_METHOD(TestFixtureHTTPS, "Cert request sign", "[cert-sign]") { PRINT_SRC_LINE @@ -617,15 +596,12 @@ TEST_CASE_METHOD(TestFixtureHTTPS, "Cert request sign", "[cert-sign]") { REQUIRE(result["status"] == 0); - PRINT_SRC_LINE result = SGXRegistrationServer::getServer()->SignCertificate("Haha"); REQUIRE(result["status"] != 0); } - - TEST_CASE_METHOD(TestFixture, "DKG API V2 test", "[dkg-api-v2]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); @@ -705,8 +681,6 @@ TEST_CASE_METHOD(TestFixture, "PolyExists test", "[dkg-poly-exists]") { REQUIRE(!polyDoesNotExist["IsExist"].asBool()); } - - TEST_CASE_METHOD(TestFixture, "AES_DKG V2 test", "[aes-dkg-v2]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); @@ -831,8 +805,7 @@ TEST_CASE_METHOD(TestFixture, "AES_DKG V2 test", "[aes-dkg-v2]") { string hash = SAMPLE_HASH; - auto hash_arr = make_shared < array < uint8_t, - 32 > > (); + auto hash_arr = make_shared < array < uint8_t, 32 > > (); uint64_t binLen; @@ -846,8 +819,7 @@ TEST_CASE_METHOD(TestFixture, "AES_DKG V2 test", "[aes-dkg-v2]") { string endName = polyNames[i].substr(4); string blsName = "BLS_KEY" + polyNames[i].substr(4); auto response = c.createBLSPrivateKeyV2(blsName, ethKeys[i]["keyName"].asString(), polyNames[i], secShares[i], - t, - n); + t, n); REQUIRE(response["status"] == 0); PRINT_SRC_LINE @@ -902,8 +874,6 @@ TEST_CASE_METHOD(TestFixture, "AES encrypt/decrypt", "[aes-encrypt-decrypt]") { sleep(3); } - - TEST_CASE_METHOD(TestFixture, "Many threads ecdsa dkg v2 bls", "[many-threads-crypto-v2]") { vector threads; int num_threads = 4; @@ -916,8 +886,6 @@ TEST_CASE_METHOD(TestFixture, "Many threads ecdsa dkg v2 bls", "[many-threads-cr } } - - TEST_CASE_METHOD(TestFixture, "First run", "[first-run]") { HttpClient client(RPC_ENDPOINT); @@ -935,8 +903,6 @@ TEST_CASE_METHOD(TestFixture, "First run", "[first-run]") { } sleep(3); - - } TEST_CASE_METHOD(TestFixtureNoReset, "Second run", "[second-run]") { @@ -959,7 +925,6 @@ TEST_CASE_METHOD(TestFixtureNoReset, "Second run", "[second-run]") { } } - TEST_CASE_METHOD(TestFixtureZMQSign, "ZMQ-ecdsa", "[zmq-ecdsa]") { HttpClient htp(RPC_ENDPOINT); @@ -977,10 +942,8 @@ TEST_CASE_METHOD(TestFixtureZMQSign, "ZMQ-ecdsa", "[zmq-ecdsa]") { int end = 10000000; string sh = string(SAMPLE_HASH); - std::vector workers; - PRINT_SRC_LINE for (int j = 0; j < 2; j++) { @@ -1003,6 +966,4 @@ TEST_CASE_METHOD(TestFixtureZMQSign, "ZMQ-ecdsa", "[zmq-ecdsa]") { } - -TEST_CASE_METHOD(TestFixtureNoResetFromBackup, "Backup restore", "[backup-restore]") { -} +TEST_CASE_METHOD(TestFixtureNoResetFromBackup, "Backup restore", "[backup-restore]") {} diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 6e05f00e..9dd9b2dc 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -57,12 +57,10 @@ shared_ptr ZMQClient::doRequestReply(Json::Value &_req) { string reqStr = fastWriter.write(_req); - reqStr = reqStr.substr(0, reqStr.size() - 1); CHECK_STATE(reqStr.front() == '{'); CHECK_STATE(reqStr.at(reqStr.size() - 1) == '}'); - auto resultStr = doZmqRequestReply(reqStr); try { @@ -71,7 +69,6 @@ shared_ptr ZMQClient::doRequestReply(Json::Value &_req) { CHECK_STATE(resultStr.front() == '{') CHECK_STATE(resultStr.back() == '}') - return ZMQMessage::parse(resultStr.c_str(), resultStr.size(), false, false); } catch (std::exception &e) { spdlog::error(string("Error in doRequestReply:") + e.what()); @@ -80,11 +77,8 @@ shared_ptr ZMQClient::doRequestReply(Json::Value &_req) { spdlog::error("Error in doRequestReply"); throw; } - - } - string ZMQClient::doZmqRequestReply(string &_req) { stringstream request; @@ -134,8 +128,6 @@ string ZMQClient::readFileIntoString(const string &_fileName) { return str; } - - void ZMQClient::verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _sig) { CHECK_STATE(_pubkey); @@ -167,8 +159,6 @@ void ZMQClient::verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _ /* First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the * signature. Length is returned in slen */ - - CHECK_STATE2(EVP_DigestVerifyFinal(mdctx, binSig.data(), binLen) == 1, ZMQ_COULD_NOT_VERIFY_SIG); @@ -177,7 +167,6 @@ void ZMQClient::verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _ return; } - string ZMQClient::signString(EVP_PKEY* _pkey, const string& _str) { CHECK_STATE(_pkey); @@ -186,8 +175,6 @@ string ZMQClient::signString(EVP_PKEY* _pkey, const string& _str) { static std::regex r("\\s+"); auto msgToSign = std::regex_replace(_str, r, ""); - - EVP_MD_CTX *mdctx = NULL; int ret = 0; unsigned char *signature = NULL; @@ -196,10 +183,8 @@ string ZMQClient::signString(EVP_PKEY* _pkey, const string& _str) { CHECK_STATE(mdctx = EVP_MD_CTX_create()); - CHECK_STATE((EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, _pkey) == 1)); - CHECK_STATE(EVP_DigestSignUpdate(mdctx, msgToSign.c_str(), msgToSign.size()) == 1); /* First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the @@ -331,7 +316,6 @@ string ZMQClient::ecdsaSignMessageHash(int base, const std::string &keyName, con return result->getSignature(); } - uint64_t ZMQClient::getProcessID() { return syscall(__NR_gettid); -} \ No newline at end of file +} diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 2c2e6b7e..fe8e410f 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -92,7 +92,7 @@ class ZMQClient { static string signString(EVP_PKEY* _pkey, const string& _str); - static void verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _sig); + static void verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _sig); string blsSignMessageHash(const std::string &keyShareName, const std::string &messageHash, int t, int n); @@ -101,5 +101,4 @@ class ZMQClient { }; - #endif //SGXWALLET_ZMQCLIENT_H diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index e4f654c8..d6c028ff 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -66,8 +66,6 @@ class ZMQServer { static void initZMQServer(bool _checkSignature); static void exitZMQServer(); - - private: shared_ptr ctx_; shared_ptr frontend; @@ -81,5 +79,4 @@ class ZMQServer { }; - #endif //SGXWALLET_ZMQServer_H From 0ac631cc40657eb2d025706941861940fe92c1c5 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 3 Jun 2021 17:27:43 +0300 Subject: [PATCH 004/100] SKALE-3951 fix license headers --- zmq_src/BLSSignReqMessage.cpp | 25 ++++++++++++++++++++++--- zmq_src/BLSSignReqMessage.h | 8 ++++---- zmq_src/BLSSignRspMessage.cpp | 10 +++++----- zmq_src/BLSSignRspMessage.h | 8 ++++---- zmq_src/ECDSASignReqMessage.cpp | 8 ++++---- zmq_src/ECDSASignReqMessage.h | 8 ++++---- zmq_src/ECDSASignRspMessage.cpp | 8 ++++---- zmq_src/ECDSASignRspMessage.h | 8 ++++---- zmq_src/ServerWorker.h | 1 + zmq_src/ZMQClient.cpp | 8 ++++---- zmq_src/ZMQClient.h | 8 ++++---- zmq_src/ZMQMessage.cpp | 8 ++++---- 12 files changed, 64 insertions(+), 44 deletions(-) diff --git a/zmq_src/BLSSignReqMessage.cpp b/zmq_src/BLSSignReqMessage.cpp index 303b8dc3..9a47011b 100644 --- a/zmq_src/BLSSignReqMessage.cpp +++ b/zmq_src/BLSSignReqMessage.cpp @@ -1,6 +1,25 @@ -// -// Created by kladko on 15.12.20. -// +/* + Copyright (C) 2018-2019 SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file BLSSignReqMessage.cpp + @author Stan Kladko + @date 2021 +*/ #include "BLSSignReqMessage.h" #include "SGXWalletServer.hpp" diff --git a/zmq_src/BLSSignReqMessage.h b/zmq_src/BLSSignReqMessage.h index 3c0b6912..528aefb8 100644 --- a/zmq_src/BLSSignReqMessage.h +++ b/zmq_src/BLSSignReqMessage.h @@ -1,20 +1,20 @@ /* Copyright (C) 2018-2019 SKALE Labs - This file is part of libBLS. + This file is part of sgxwallet. - libBLS is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - libBLS is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with libBLS. If not, see . + along with sgxwallet. If not, see . @file BLSReqSignMessage.h @author Stan Kladko diff --git a/zmq_src/BLSSignRspMessage.cpp b/zmq_src/BLSSignRspMessage.cpp index 7a662e9a..e30f0eda 100644 --- a/zmq_src/BLSSignRspMessage.cpp +++ b/zmq_src/BLSSignRspMessage.cpp @@ -1,22 +1,22 @@ /* Copyright (C) 2018-2019 SKALE Labs - This file is part of libBLS. + This file is part of sgxwallet. - libBLS is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - libBLS is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with libBLS. If not, see . + along with sgxwallet. If not, see . - @file BLSRspSignMessage.h + @file BLSRspSignMessage.cpp @author Stan Kladko @date 2020 */ diff --git a/zmq_src/BLSSignRspMessage.h b/zmq_src/BLSSignRspMessage.h index abf1f9d3..fc733b60 100644 --- a/zmq_src/BLSSignRspMessage.h +++ b/zmq_src/BLSSignRspMessage.h @@ -1,20 +1,20 @@ /* Copyright (C) 2018-2019 SKALE Labs - This file is part of libBLS. + This file is part of sgxwallet. - libBLS is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - libBLS is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with libBLS. If not, see . + along with sgxwallet. If not, see . @file BLSRspSignMessage.h @author Stan Kladko diff --git a/zmq_src/ECDSASignReqMessage.cpp b/zmq_src/ECDSASignReqMessage.cpp index 8e99be35..f7ac1752 100644 --- a/zmq_src/ECDSASignReqMessage.cpp +++ b/zmq_src/ECDSASignReqMessage.cpp @@ -1,20 +1,20 @@ /* Copyright (C) 2018-2019 SKALE Labs - This file is part of libBLS. + This file is part of sgxwallet. - libBLS is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - libBLS is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with libBLS. If not, see . + along with sgxwallet. If not, see . @file ECDSASignReqMessage.cpp @author Stan Kladko diff --git a/zmq_src/ECDSASignReqMessage.h b/zmq_src/ECDSASignReqMessage.h index 722fc75b..b95a885d 100644 --- a/zmq_src/ECDSASignReqMessage.h +++ b/zmq_src/ECDSASignReqMessage.h @@ -1,20 +1,20 @@ /* Copyright (C) 2018- SKALE Labs - This file is part of libBLS. + This file is part of sgxwallet. - libBLS is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - libBLS is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with libBLS. If not, see . + along with sgxwallet. If not, see . @file ECDSAReqSignMessage.h @author Stan Kladko diff --git a/zmq_src/ECDSASignRspMessage.cpp b/zmq_src/ECDSASignRspMessage.cpp index c8fb7ece..eeb7202b 100644 --- a/zmq_src/ECDSASignRspMessage.cpp +++ b/zmq_src/ECDSASignRspMessage.cpp @@ -1,20 +1,20 @@ /* Copyright (C) 2018- SKALE Labs - This file is part of libBLS. + This file is part of sgxwallet. - libBLS is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - libBLS is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with libBLS. If not, see . + along with sgxwallet. If not, see . @file ECDSARspSignMessage.cpp @author Stan Kladko diff --git a/zmq_src/ECDSASignRspMessage.h b/zmq_src/ECDSASignRspMessage.h index 24bcc951..3c8f7989 100644 --- a/zmq_src/ECDSASignRspMessage.h +++ b/zmq_src/ECDSASignRspMessage.h @@ -1,20 +1,20 @@ /* Copyright (C) 2018- SKALE Labs - This file is part of libBLS. + This file is part of sgxwallet. - libBLS is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - libBLS is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with libBLS. If not, see . + along with sgxwallet. If not, see . @file ECDSARspSignMessage.h @author Stan Kladko diff --git a/zmq_src/ServerWorker.h b/zmq_src/ServerWorker.h index 6f2a914b..3f1a83f4 100644 --- a/zmq_src/ServerWorker.h +++ b/zmq_src/ServerWorker.h @@ -20,6 +20,7 @@ @author Stan Kladko @date 2021 */ + #ifndef SGXWALLET_SERVERWORKER_H #define SGXWALLET_SERVERWORKER_H diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 9dd9b2dc..5e965189 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -1,20 +1,20 @@ /* Copyright (C) 2018-2019 SKALE Labs - This file is part of skale-consensus. + This file is part of sgxwallet. - skale-consensus is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - skale-consensus is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with skale-consensus. If not, see . + along with sgxwallet. If not, see . @file ZMQClient.cpp @author Stan Kladko diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index fe8e410f..45d6136f 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -1,20 +1,20 @@ /* Copyright (C) 2018-2019 SKALE Labs - This file is part of skale-consensus. + This file is part of sgxwallet. - skale-consensus is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - skale-consensus is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with skale-consensus. If not, see . + along with sgxwallet. If not, see . @file ZMQClient.h @author Stan Kladko diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 608877df..f0eaff41 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -1,20 +1,20 @@ /* Copyright (C) 2020 SKALE Labs - This file is part of skale-consensus. + This file is part of sgxwallet. - skale-consensus is free software: you can redistribute it and/or modify + sgxwallet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - skale-consensus is distributed in the hope that it will be useful, + sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with skale-consensus. If not, see . + along with sgxwallet. If not, see . @file ZMQMessage.cpp @author Stan Kladko From 877c416aaeb5841bf05fdf8fa46fad84a5ef26bc Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 3 Jun 2021 17:35:52 +0300 Subject: [PATCH 005/100] SKALE-3951 format code --- ServerInit.cpp | 4 ---- zmq_src/ServerWorker.cpp | 5 ----- zmq_src/ZMQClient.cpp | 1 - zmq_src/ZMQClient.h | 11 ----------- 4 files changed, 21 deletions(-) diff --git a/ServerInit.cpp b/ServerInit.cpp index ce9f5682..3892fde7 100644 --- a/ServerInit.cpp +++ b/ServerInit.cpp @@ -103,10 +103,8 @@ void initUserSpace() { } - uint64_t initEnclave() { - #ifndef SGX_HW_SIM unsigned long support; support = get_sgx_support(); @@ -161,7 +159,6 @@ uint64_t initEnclave() { return SGX_SUCCESS; } - void initAll(uint32_t _logLevel, bool _checkCert, bool _checkZMQSig, bool _autoSign, bool _generateTestKeys) { @@ -237,5 +234,4 @@ void exitAll() { CSRManagerServer::exitServer(); SGXInfoServer::exitServer(); ZMQServer::exitZMQServer(); - } diff --git a/zmq_src/ServerWorker.cpp b/zmq_src/ServerWorker.cpp index a372e5df..533b7c4f 100644 --- a/zmq_src/ServerWorker.cpp +++ b/zmq_src/ServerWorker.cpp @@ -53,8 +53,6 @@ ServerWorker::ServerWorker(zmq::context_t &_ctx, int sock_type, bool _checkSigna zmq_setsockopt(*worker, ZMQ_LINGER, &linger, sizeof(linger)); }; - - void ServerWorker::doOneServerLoop() noexcept { string replyStr; @@ -68,7 +66,6 @@ void ServerWorker::doOneServerLoop() noexcept { try { - zmq_pollitem_t items[1]; items[0].socket = *worker; items[0].events = ZMQ_POLLIN; @@ -154,9 +151,7 @@ void ServerWorker::work() { spdlog::info("Exited worker thread {}", index); } - void ServerWorker::requestExit() { isExitRequested.exchange(true); spdlog::info("Closed worker socket {}", index); } - diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 5e965189..547f664d 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -121,7 +121,6 @@ string ZMQClient::doZmqRequestReply(string &_req) { } } - string ZMQClient::readFileIntoString(const string &_fileName) { ifstream t(_fileName); string str((istreambuf_iterator(t)), istreambuf_iterator()); diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 45d6136f..61667543 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -21,9 +21,6 @@ @date 2021 */ - - - #ifndef SGXWALLET_ZMQCLIENT_H #define SGXWALLET_ZMQCLIENT_H @@ -40,28 +37,21 @@ #include #include "ZMQMessage.h" - - - #define REQUEST_TIMEOUT 10000 // msecs, (> 1000!) class ZMQClient { - - private: EVP_PKEY* pkey = 0; EVP_PKEY* pubkey = 0; X509* x509Cert = 0; - bool sign = true; string certFileName = ""; string certKeyName = ""; string certificate = ""; string key = ""; - recursive_mutex mutex; zmq::context_t ctx; @@ -82,7 +72,6 @@ class ZMQClient { public: - ZMQClient(const string &ip, uint16_t port, bool _sign, const string& _certPathName, const string& _certKeyName); From 003b4fca10d7e3c48ae3db0090e50085e8fe2a4e Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 15:04:55 +0300 Subject: [PATCH 006/100] SKALE-3951 add req methods --- zmq_src/BLSSignReqMessage.cpp | 36 -------------------------- zmq_src/BLSSignReqMessage.h | 38 ---------------------------- zmq_src/ECDSASignReqMessage.cpp | 38 ---------------------------- zmq_src/ECDSASignReqMessage.h | 40 ----------------------------- zmq_src/ZMQClient.cpp | 3 +-- zmq_src/ZMQMessage.cpp | 45 +++++++++++++++++++-------------- zmq_src/ZMQMessage.h | 3 ++- 7 files changed, 29 insertions(+), 174 deletions(-) delete mode 100644 zmq_src/BLSSignReqMessage.cpp delete mode 100644 zmq_src/BLSSignReqMessage.h delete mode 100644 zmq_src/ECDSASignReqMessage.cpp delete mode 100644 zmq_src/ECDSASignReqMessage.h diff --git a/zmq_src/BLSSignReqMessage.cpp b/zmq_src/BLSSignReqMessage.cpp deleted file mode 100644 index 9a47011b..00000000 --- a/zmq_src/BLSSignReqMessage.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright (C) 2018-2019 SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file BLSSignReqMessage.cpp - @author Stan Kladko - @date 2021 -*/ - -#include "BLSSignReqMessage.h" -#include "SGXWalletServer.hpp" - - -Json::Value BLSSignReqMessage::process() { - auto keyName = getStringRapid("keyShareName"); - auto hash = getStringRapid("messageHash"); - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); - auto result = SGXWalletServer::blsSignMessageHashImpl(keyName, hash, t, n); - result["type"] = ZMQMessage::BLS_SIGN_RSP; - return result; -} \ No newline at end of file diff --git a/zmq_src/BLSSignReqMessage.h b/zmq_src/BLSSignReqMessage.h deleted file mode 100644 index 528aefb8..00000000 --- a/zmq_src/BLSSignReqMessage.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright (C) 2018-2019 SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file BLSReqSignMessage.h - @author Stan Kladko - @date 2020 -*/ - -#ifndef SGXWALLET_BLSSIGNREQMSG_H -#define SGXWALLET_BLSSIGNREQMSG_H - -#include "ZMQMessage.h" - -class BLSSignReqMessage : public ZMQMessage { -public: - BLSSignReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; - - virtual Json::Value process(); - -}; - - -#endif //SGXWALLET_BLSSIGNREQMSG_H diff --git a/zmq_src/ECDSASignReqMessage.cpp b/zmq_src/ECDSASignReqMessage.cpp deleted file mode 100644 index f7ac1752..00000000 --- a/zmq_src/ECDSASignReqMessage.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright (C) 2018-2019 SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file ECDSASignReqMessage.cpp - @author Stan Kladko - @date 2020 -*/ - - -#include "SGXWalletServer.hpp" - -#include "ECDSASignReqMessage.h" - - - -Json::Value ECDSASignReqMessage::process() { - auto base = getUint64Rapid("base"); - auto keyName = getStringRapid("keyName"); - auto hash = getStringRapid("messageHash"); - auto result = SGXWalletServer::ecdsaSignMessageHashImpl(base, keyName, hash); - result["type"] = ZMQMessage::ECDSA_SIGN_RSP; - return result; -} \ No newline at end of file diff --git a/zmq_src/ECDSASignReqMessage.h b/zmq_src/ECDSASignReqMessage.h deleted file mode 100644 index b95a885d..00000000 --- a/zmq_src/ECDSASignReqMessage.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright (C) 2018- SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file ECDSAReqSignMessage.h - @author Stan Kladko - @date 2020 -*/ - -#ifndef SGXWALLET_ECDSASIGNREQMESSAGE_H -#define SGXWALLET_ECDSASIGNREQMESSAGE_H - -#include "ZMQMessage.h" - -class ECDSASignReqMessage : public ZMQMessage { -public: - - ECDSASignReqMessage(shared_ptr &_d) : ZMQMessage(_d) {}; - - virtual Json::Value process(); - - -}; - - -#endif //SGXWALLET_ECDSASIGNREQMESSAGE_H diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 547f664d..2c009cf3 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -33,9 +33,8 @@ #include "sgxwallet_common.h" #include "common.h" #include "BLSCrypto.h" -#include "BLSSignReqMessage.h" +#include "ReqMessage.h" #include "BLSSignRspMessage.h" -#include "ECDSASignReqMessage.h" #include "ECDSASignRspMessage.h" #include "ZMQClient.h" diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index f0eaff41..4ad3f544 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -29,9 +29,8 @@ #include "ZMQClient.h" #include "SGXWalletServer.hpp" -#include "BLSSignReqMessage.h" +#include "ReqMessage.h" #include "BLSSignRspMessage.h" -#include "ECDSASignReqMessage.h" #include "ECDSASignRspMessage.h" #include "ZMQMessage.h" @@ -44,6 +43,14 @@ uint64_t ZMQMessage::getUint64Rapid(const char *_name) { return a.GetUint64(); }; +Json::Value ZMQMessage::getJsonArray(const char *_name) { + CHECK_STATE(_name); + CHECK_STATE(d->HasMember(_name)); + const rapidjson::Value &a = (*d)[_name]; + CHECK_STATE(a.IsArray()); + return a.GetUint64(); +} + string ZMQMessage::getStringRapid(const char *_name) { CHECK_STATE(_name); CHECK_STATE(d->HasMember(_name)); @@ -155,55 +162,55 @@ shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr (_d); break; case ENUM_IMPORT_BLS_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_IMPORT_ECDSA_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GENERATE_ECDSA_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_PUBLIC_ECDSA_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GENERATE_DKG_POLY_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_VV_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_SECRET_SHARE_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_DKG_VERIFY_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_CREATE_BLS_PRIVATE_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_BLS_PUBLIC_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_ALL_BLS_PUBLIC_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_COMPLAINT_RESPONSE_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_MULT_G2_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_IS_POLY_EXISTS_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_SERVER_STATUS_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_SERVER_VERSION_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_DELETE_BLS_KEY_REQ: - ret = make_shared(_d); + ret = make_shared(_d); break; default: break; diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index c3a21e42..52bae86a 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -49,7 +49,6 @@ class ZMQMessage { shared_ptr d; - static cache::lru_cache> verifiedCerts; public: @@ -111,6 +110,8 @@ class ZMQMessage { uint64_t getUint64Rapid(const char *_name); + Json::Value getJsonArray(const char *_name); + uint64_t getStatus() { return getUint64Rapid("status"); } From c2e1e4f1a55faa9bff37abd9d71b2c3ca78e8517 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 15:18:50 +0300 Subject: [PATCH 007/100] SKALE-3951 add req methods --- Makefile.am | 2 +- zmq_src/ReqMessage.cpp | 186 +++++++++++++++++++++++++++++++++++++++++ zmq_src/ReqMessage.h | 181 +++++++++++++++++++++++++++++++++++++++ zmq_src/ZMQMessage.cpp | 12 ++- 4 files changed, 379 insertions(+), 2 deletions(-) create mode 100644 zmq_src/ReqMessage.cpp create mode 100644 zmq_src/ReqMessage.h diff --git a/Makefile.am b/Makefile.am index a5043fb9..1f019e81 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,7 +71,7 @@ bin_PROGRAMS = sgxwallet testw sgx_util COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/BLSSignRspMessage.cpp \ - zmq_src/ECDSASignRspMessage.cpp zmq_src/ECDSASignReqMessage.cpp zmq_src/BLSSignReqMessage.cpp \ + zmq_src/ECDSASignRspMessage.cpp zmq_src/ReqMessage.cpp \ zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp zmq_src/ServerWorker.cpp \ InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp \ SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp \ diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp new file mode 100644 index 00000000..c7bf0028 --- /dev/null +++ b/zmq_src/ReqMessage.cpp @@ -0,0 +1,186 @@ +/* + Copyright (C) 2018- SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file ReqMessage.cpp + @author Oleh Nikolaiev + @date 2021 +*/ + + +#include "SGXWalletServer.hpp" + +#include "ReqMessage.h" + + +Json::Value ECDSASignReqMessage::process() { + auto base = getUint64Rapid("base"); + auto keyName = getStringRapid("keyName"); + auto hash = getStringRapid("messageHash"); + auto result = SGXWalletServer::ecdsaSignMessageHashImpl(base, keyName, hash); + result["type"] = ZMQMessage::ECDSA_SIGN_RSP; + return result; +} + +Json::Value BLSSignReqMessage::process() { + auto keyName = getStringRapid("keyShareName"); + auto hash = getStringRapid("messageHash"); + auto t = getUint64Rapid("t"); + auto n = getUint64Rapid("n"); + auto result = SGXWalletServer::blsSignMessageHashImpl(keyName, hash, t, n); + result["type"] = ZMQMessage::BLS_SIGN_RSP; + return result; +} + +Json::Value importBLSReqMessage::process() { + auto keyName = getStringRapid("keyShareName"); + auto keyShare = getStringRapid("keyShare"); + auto result = SGXWalletServer::importBLSKeyShareImpl(keyShare, keyName); + result["type"] = ZMQMessage::IMPORT_BLS_RSP; + return result; +} + +Json::Value importECDSAReqMessage::process() { + auto keyName = getStringRapid("keyName"); + auto key = getStringRapid("key"); + auto result = SGXWalletServer::importECDSAKeyImpl(key, keyName); + result["type"] = ZMQMessage::IMPORT_ECDSA_RSP; + return result; +} + +Json::Value generateECDSAReqMessage::process() { + auto result = SGXWalletServer::generateECDSAKeyImpl(); + result["type"] = ZMQMessage::GENERATE_ECDSA_RSP; + return result; +} + +Json::Value getPublicECDSAReqMessage::process() { + auto keyName = getStringRapid("keyName"); + auto result = SGXWalletServer::getPublicECDSAKeyImpl(keyName); + result["type"] = ZMQMessage::GET_PUBLIC_ECDSA_RSP; + return result; +} + +Json::Value generateDKGPolyReqMessage::process() { + auto polyName = getStringRapid("polyName"); + auto t = getUint64Rapid("t"); + auto result = SGXWalletServer::generateDKGPolyImpl(polyName, t); + result["type"] = ZMQMessage::GENERATE_DKG_POLY_RSP; + return result; +} + +Json::Value getVerificationVectorReqMessage::process() { + auto polyName = getStringRapid("polyName"); + auto t = getUint64Rapid("t"); + auto n = getUint64Rapid("n"); + auto result = SGXWalletServer::getVerificationVectorImpl(polyName, t, n); + result["type"] = ZMQMessage::GET_VV_RSP; + return result; +} + +Json::Value getSecretShareReqMessage::process() { + auto polyName = getStringRapid("polyName"); + auto t = getUint64Rapid("t"); + auto n = getUint64Rapid("n"); + auto pubKeys = getJsonArray("publicKeys"); + auto result = SGXWalletServer::getSecretShareImpl(polyName, pubKeys, t, n); + result["type"] = ZMQMessage::GET_SECRET_SHARE_RSP; + return result; +} + +Json::Value dkgVerificationReqMessage::process() { + auto ethKeyName = getStringRapid("ethKeyName"); + auto t = getUint64Rapid("t"); + auto n = getUint64Rapid("n"); + auto idx = getUint64Rapid("index"); + auto pubShares = getStringRapid("publicShares"); + auto secretShare = getStringRapid("secretShare"); + auto result = SGXWalletServer::dkgVerificationImpl(pubShares, ethKeyName, secretShare, t, n, idx); + result["type"] = ZMQMessage::DKG_VERIFY_RSP; + return result; +} + +Json::Value createBLSPrivateKeyReqMessage::process() { + auto blsKeyName = getStringRapid("blsKeyName"); + auto ethKeyName = getStringRapid("ethKeyName"); + auto polyName = getStringRapid("polyName"); + auto secretShare = getStringRapid("secretShare"); + auto t = getUint64Rapid("t"); + auto n = getUint64Rapid("n"); + auto result = SGXWalletServer::createBLSPrivateKeyImpl(blsKeyName, ethKeyName, polyName, secretShare, t, n); + result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; + return result; +} + +Json::Value getBLSPublicReqMessage::process() { + auto blsKeyName = getStringRapid("blsKeyName"); + auto result = SGXWalletServer::getBLSPublicKeyShareImpl(blsKeyName); + result["type"] = ZMQMessage::GET_BLS_PUBLIC_RSP; + return result; +} + +Json::Value getAllBLSPublicKeysReqMessage::process() { + auto t = getUint64Rapid("t"); + auto n = getUint64Rapid("n"); + auto pubShares = getJsonArray("publicShares"); + auto result = SGXWalletServer::calculateAllBLSPublicKeysImpl(pubShares, t, n); + result["type"] = ZMQMessage::GET_ALL_BLS_PUBLIC_RSP; + return result; +} + +Json::Value complaintResponseReqMessage::process() { + auto polyName = getStringRapid("polyName"); + auto t = getUint64Rapid("t"); + auto n = getUint64Rapid("n"); + auto idx = getUint64Rapid("ind"); + auto result = SGXWalletServer::complaintResponseImpl(polyName, t, n, idx); + result["type"] = ZMQMessage::COMPLAINT_RESPONSE_RSP; + return result; +} + +Json::Value multG2ReqMessage::process() { + auto x = getStringRapid("x"); + auto result = SGXWalletServer::multG2Impl(x); + result["type"] = ZMQMessage::MULT_G2_RSP; + return result; +} + +Json::Value isPolyExistsReqMessage::process() { + auto polyName = getStringRapid("polyName"); + auto result = SGXWalletServer::isPolyExistsImpl(polyName); + result["type"] = ZMQMessage::IS_POLY_EXISTS_RSP; + return result; +} + +Json::Value getServerStatusReqMessage::process() { + auto result = SGXWalletServer::getServerStatusImpl(); + result["type"] = ZMQMessage::GET_SERVER_STATUS_RSP; + return result; +} + +Json::Value getServerVersionReqMessage::process() { + auto result = SGXWalletServer::getServerVersionImpl(); + result["type"] = ZMQMessage::GET_SERVER_VERSION_RSP; + return result; +} + +Json::Value deleteBLSKeyReqMessage::process() { + auto blsKeyName = getStringRapid("blsKeyName"); + auto result = SGXWalletServer::deleteBlsKeyImpl(blsKeyName); + result["type"] = ZMQMessage::DELETE_BLS_KEY_RSP; + return result; +} diff --git a/zmq_src/ReqMessage.h b/zmq_src/ReqMessage.h new file mode 100644 index 00000000..252313aa --- /dev/null +++ b/zmq_src/ReqMessage.h @@ -0,0 +1,181 @@ +/* + Copyright (C) 2018- SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file ReqMessage.h + @author Oleh Nikolaiev + @date 2021 +*/ + +#ifndef SGXWALLET_REQMESSAGE_H +#define SGXWALLET_REQMESSAGE_H + +#include "ZMQMessage.h" + +class ECDSASignReqMessage : public ZMQMessage { +public: + + ECDSASignReqMessage(shared_ptr &_d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class BLSSignReqMessage : public ZMQMessage { +public: + BLSSignReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class importBLSReqMessage : public ZMQMessage { +public: + importBLSReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class importECDSAReqMessage : public ZMQMessage { +public: + importECDSAReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class generateECDSAReqMessage : public ZMQMessage { +public: + generateECDSAReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getPublicECDSAReqMessage : public ZMQMessage { +public: + getPublicECDSAReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class generateDKGPolyReqMessage : public ZMQMessage { +public: + generateDKGPolyReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getVerificationVectorReqMessage : public ZMQMessage { +public: + getVerificationVectorReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getSecretShareReqMessage : public ZMQMessage { +public: + getSecretShareReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class dkgVerificationReqMessage : public ZMQMessage { +public: + dkgVerificationReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class createBLSPrivateKeyReqMessage : public ZMQMessage { +public: + createBLSPrivateKeyReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getBLSPublicReqMessage : public ZMQMessage { +public: + getBLSPublicReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getAllBLSPublicKeysReqMessage : public ZMQMessage { +public: + getAllBLSPublicKeysReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class complaintResponseReqMessage : public ZMQMessage { +public: + complaintResponseReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class multG2ReqMessage : public ZMQMessage { +public: + multG2ReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class isPolyExistsReqMessage : public ZMQMessage { +public: + isPolyExistsReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getServerStatusReqMessage : public ZMQMessage { +public: + getServerStatusReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getServerVersionReqMessage : public ZMQMessage { +public: + getServerVersionReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class deleteBLSKeyReqMessage : public ZMQMessage { +public: + deleteBLSKeyReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + +#endif //SGXWALLET_REQMESSAGE_H diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 4ad3f544..1d12f980 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -48,7 +48,17 @@ Json::Value ZMQMessage::getJsonArray(const char *_name) { CHECK_STATE(d->HasMember(_name)); const rapidjson::Value &a = (*d)[_name]; CHECK_STATE(a.IsArray()); - return a.GetUint64(); + + rapidjson::StringBuffer buffer; + rapidjson::Writer< rapidjson::StringBuffer > writer(buffer); + a.Accept(writer); + std::string strRequest = buffer.GetString(); + + Json::Reader reader; + Json::Value root; + reader.parse(strRequest, root, false); + + return root; } string ZMQMessage::getStringRapid(const char *_name) { From d9b3ba361745a9013cdc4c96d24fd5a9736b6090 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 15:24:47 +0300 Subject: [PATCH 008/100] SKALE-3951 add req methods --- zmq_src/ReqMessage.cpp | 4 ++-- zmq_src/ZMQMessage.cpp | 2 +- zmq_src/ZMQMessage.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index c7bf0028..b4fc4c9c 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -96,7 +96,7 @@ Json::Value getSecretShareReqMessage::process() { auto polyName = getStringRapid("polyName"); auto t = getUint64Rapid("t"); auto n = getUint64Rapid("n"); - auto pubKeys = getJsonArray("publicKeys"); + auto pubKeys = getJsonValueRapid("publicKeys"); auto result = SGXWalletServer::getSecretShareImpl(polyName, pubKeys, t, n); result["type"] = ZMQMessage::GET_SECRET_SHARE_RSP; return result; @@ -136,7 +136,7 @@ Json::Value getBLSPublicReqMessage::process() { Json::Value getAllBLSPublicKeysReqMessage::process() { auto t = getUint64Rapid("t"); auto n = getUint64Rapid("n"); - auto pubShares = getJsonArray("publicShares"); + auto pubShares = getJsonValueRapid("publicShares"); auto result = SGXWalletServer::calculateAllBLSPublicKeysImpl(pubShares, t, n); result["type"] = ZMQMessage::GET_ALL_BLS_PUBLIC_RSP; return result; diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 1d12f980..84256016 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -43,7 +43,7 @@ uint64_t ZMQMessage::getUint64Rapid(const char *_name) { return a.GetUint64(); }; -Json::Value ZMQMessage::getJsonArray(const char *_name) { +Json::Value ZMQMessage::getJsonValueRapid(const char *_name) { CHECK_STATE(_name); CHECK_STATE(d->HasMember(_name)); const rapidjson::Value &a = (*d)[_name]; diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index 52bae86a..fcb62b46 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -110,7 +110,7 @@ class ZMQMessage { uint64_t getUint64Rapid(const char *_name); - Json::Value getJsonArray(const char *_name); + Json::Value getJsonValueRapid(const char *_name); uint64_t getStatus() { return getUint64Rapid("status"); From 784064c3794f40f848b13caeab6dadfe69a4f425 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 15:57:31 +0300 Subject: [PATCH 009/100] SKALE-3951 add rsp methods --- Makefile.am | 7 +- zmq_src/RspMessage.cpp | 112 ++++++++++++++++++++++++ zmq_src/RspMessage.h | 187 +++++++++++++++++++++++++++++++++++++++++ zmq_src/ZMQClient.h | 2 +- zmq_src/ZMQMessage.cpp | 37 ++++---- 5 files changed, 321 insertions(+), 24 deletions(-) create mode 100644 zmq_src/RspMessage.cpp create mode 100644 zmq_src/RspMessage.h diff --git a/Makefile.am b/Makefile.am index 1f019e81..631d3cb3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,14 +70,13 @@ bin_PROGRAMS = sgxwallet testw sgx_util ## have to be explicitly listed -COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/BLSSignRspMessage.cpp \ - zmq_src/ECDSASignRspMessage.cpp zmq_src/ReqMessage.cpp \ +COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/RspMessage.cpp zmq_src/ReqMessage.cpp \ zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp zmq_src/ServerWorker.cpp \ InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp \ SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp \ DKGCrypto.cpp ServerInit.cpp BLSPrivateKeyShareSGX.cpp LevelDB.cpp ServerDataChecker.cpp SEKManager.cpp \ - third_party/intel/sgx_stub.c third_party/intel/sgx_detect_linux.c third_party/intel/create_enclave.c third_party/intel/oc_alloc.c \ - ECDSAImpl.c TestUtils.cpp sgxwallet.c SGXInfoServer.cpp ECDSACrypto.cpp + third_party/intel/sgx_stub.c third_party/intel/sgx_detect_linux.c third_party/intel/create_enclave.c \ + third_party/intel/oc_alloc.c ECDSAImpl.c TestUtils.cpp sgxwallet.c SGXInfoServer.cpp ECDSACrypto.cpp COMMON_ENCLAVE_SRC = secure_enclave_u.c secure_enclave_u.h sgxwallet_SOURCES = sgxwall.cpp $(COMMON_SRC) diff --git a/zmq_src/RspMessage.cpp b/zmq_src/RspMessage.cpp new file mode 100644 index 00000000..3ffa6873 --- /dev/null +++ b/zmq_src/RspMessage.cpp @@ -0,0 +1,112 @@ +/* + Copyright (C) 2018- SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file RspMessage.cpp + @author Oleh Nikolaiev + @date 2021 +*/ + +#include "SGXWalletServer.hpp" + +#include "RspMessage.h" + +Json::Value ECDSASignRspMessage::process() { + assert(false); +} + +string ECDSASignRspMessage::getSignature() { + string r = getStringRapid("signature_r"); + string v = getStringRapid("signature_v"); + string s = getStringRapid("signature_s"); + + auto ret = v + ":" + r.substr( 2 ) + ":" + s.substr( 2 ); + + return ret; +} + +Json::Value BLSSignRspMessage::process() { + assert(false); +} + +Json::Value importBLSRspMessage::process() { + assert(false); +} + +Json::Value importECDSARspMessage::process() { + assert(false); +} + +Json::Value generateECDSARspMessage::process() { + assert(false); +} + +Json::Value getPublicECDSARspMessage::process() { + assert(false); +} + +Json::Value generateDKGPolyRspMessage::process() { + assert(false); +} + +Json::Value getVerificationVectorRspMessage::process() { + assert(false); +} + +Json::Value getSecretShareRspMessage::process() { + assert(false); +} + +Json::Value dkgVerificationRspMessage::process() { + assert(false); +} + +Json::Value createBLSPrivateKeyRspMessage::process() { + assert(false); +} + +Json::Value getBLSPublicRspMessage::process() { + assert(false); +} + +Json::Value getAllBLSPublicKeysRspMessage::process() { + assert(false); +} + +Json::Value complaintResponseRspMessage::process() { + assert(false); +} + +Json::Value multG2RspMessage::process() { + assert(false); +} + +Json::Value isPolyExistsRspMessage::process() { + assert(false); +} + +Json::Value getServerStatusRspMessage::process() { + assert(false); +} + +Json::Value getServerVersionRspMessage::process() { + assert(false); +} + +Json::Value deleteBLSKeyRspMessage::process() { + assert(false); +} diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h new file mode 100644 index 00000000..d570e809 --- /dev/null +++ b/zmq_src/RspMessage.h @@ -0,0 +1,187 @@ +/* + Copyright (C) 2018- SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file RspMessage.h + @author Oleh Nikolaiev + @date 2021 +*/ + +#ifndef SGXWALLET_RSPMESSAGE_H +#define SGXWALLET_RSPMESSAGE_H + +#include "ZMQMessage.h" + +class ECDSASignRspMessage : public ZMQMessage { +public: + ECDSASignRspMessage(shared_ptr &_d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); + + string getSignature(); +}; + + +class BLSSignRspMessage : public ZMQMessage { +public: + BLSSignRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); + + string getSigShare() { + return getStringRapid("signatureShare"); + } +}; + + +class importBLSRspMessage : public ZMQMessage { +public: + importBLSRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class importECDSARspMessage : public ZMQMessage { +public: + importECDSARspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class generateECDSARspMessage : public ZMQMessage { +public: + generateECDSARspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getPublicECDSARspMessage : public ZMQMessage { +public: + getPublicECDSARspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class generateDKGPolyRspMessage : public ZMQMessage { +public: + generateDKGPolyRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getVerificationVectorRspMessage : public ZMQMessage { +public: + getVerificationVectorRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getSecretShareRspMessage : public ZMQMessage { +public: + getSecretShareRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class dkgVerificationRspMessage : public ZMQMessage { +public: + dkgVerificationRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class createBLSPrivateKeyRspMessage : public ZMQMessage { +public: + createBLSPrivateKeyRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getBLSPublicRspMessage : public ZMQMessage { +public: + getBLSPublicRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getAllBLSPublicKeysRspMessage : public ZMQMessage { +public: + getAllBLSPublicKeysRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class complaintResponseRspMessage : public ZMQMessage { +public: + complaintResponseRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class multG2RspMessage : public ZMQMessage { +public: + multG2RspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class isPolyExistsRspMessage : public ZMQMessage { +public: + isPolyExistsRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getServerStatusRspMessage : public ZMQMessage { +public: + getServerStatusRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class getServerVersionRspMessage : public ZMQMessage { +public: + getServerVersionRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +class deleteBLSKeyRspMessage : public ZMQMessage { +public: + deleteBLSKeyRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + + +#endif //SGXWALLET_RSPMESSAGE_H diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 61667543..5b8e23da 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -75,7 +75,7 @@ class ZMQClient { ZMQClient(const string &ip, uint16_t port, bool _sign, const string& _certPathName, const string& _certKeyName); - void reconnect() ; + void reconnect(); static pair readPublicKeyFromCertStr(const string& _cert); diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 84256016..d080a93a 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -30,8 +30,7 @@ #include "ZMQClient.h" #include "SGXWalletServer.hpp" #include "ReqMessage.h" -#include "BLSSignRspMessage.h" -#include "ECDSASignRspMessage.h" +#include "RspMessage.h" #include "ZMQMessage.h" @@ -250,55 +249,55 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr (_d); break; case ENUM_IMPORT_BLS_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_IMPORT_ECDSA_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GENERATE_ECDSA_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_PUBLIC_ECDSA_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GENERATE_DKG_POLY_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_VV_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_SECRET_SHARE_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_DKG_VERIFY_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_CREATE_BLS_PRIVATE_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_BLS_PUBLIC_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_ALL_BLS_PUBLIC_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_COMPLAINT_RESPONSE_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_MULT_G2_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_IS_POLY_EXISTS_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_SERVER_STATUS_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_GET_SERVER_VERSION_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; case ENUM_DELETE_BLS_KEY_RSP: - ret = make_shared(_d); + ret = make_shared(_d); break; default: break; From ddf62aa087094254eb48ddef6498e6b0ba722aa1 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 16:07:51 +0300 Subject: [PATCH 010/100] SKALE-3951 use v2 methods --- SGXWalletServer.hpp | 2 +- zmq_src/ReqMessage.cpp | 6 +++--- zmq_src/ServerWorker.cpp | 1 - zmq_src/ServerWorker.h | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index 69073a5a..6466dda0 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -172,7 +172,7 @@ class SGXWalletServer : public AbstractStubServer { static Json::Value dkgVerificationV2Impl(const string &_publicShares, const string &_ethKeyName, const string &_secretShare, int _t, int _n, int _index); - virtual Json::Value createBLSPrivateKeyV2Impl(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string & SecretShare, int t, int n); + static Json::Value createBLSPrivateKeyV2Impl(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string & SecretShare, int t, int n); static void printDB(); diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index b4fc4c9c..a38cb64c 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -97,7 +97,7 @@ Json::Value getSecretShareReqMessage::process() { auto t = getUint64Rapid("t"); auto n = getUint64Rapid("n"); auto pubKeys = getJsonValueRapid("publicKeys"); - auto result = SGXWalletServer::getSecretShareImpl(polyName, pubKeys, t, n); + auto result = SGXWalletServer::getSecretShareV2Impl(polyName, pubKeys, t, n); result["type"] = ZMQMessage::GET_SECRET_SHARE_RSP; return result; } @@ -109,7 +109,7 @@ Json::Value dkgVerificationReqMessage::process() { auto idx = getUint64Rapid("index"); auto pubShares = getStringRapid("publicShares"); auto secretShare = getStringRapid("secretShare"); - auto result = SGXWalletServer::dkgVerificationImpl(pubShares, ethKeyName, secretShare, t, n, idx); + auto result = SGXWalletServer::dkgVerificationV2Impl(pubShares, ethKeyName, secretShare, t, n, idx); result["type"] = ZMQMessage::DKG_VERIFY_RSP; return result; } @@ -121,7 +121,7 @@ Json::Value createBLSPrivateKeyReqMessage::process() { auto secretShare = getStringRapid("secretShare"); auto t = getUint64Rapid("t"); auto n = getUint64Rapid("n"); - auto result = SGXWalletServer::createBLSPrivateKeyImpl(blsKeyName, ethKeyName, polyName, secretShare, t, n); + auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; return result; } diff --git a/zmq_src/ServerWorker.cpp b/zmq_src/ServerWorker.cpp index 533b7c4f..8d15dcaa 100644 --- a/zmq_src/ServerWorker.cpp +++ b/zmq_src/ServerWorker.cpp @@ -139,7 +139,6 @@ void ServerWorker::doOneServerLoop() noexcept { void ServerWorker::work() { worker->connect("inproc://backend"); - while (!isExitRequested) { try { doOneServerLoop(); diff --git a/zmq_src/ServerWorker.h b/zmq_src/ServerWorker.h index 3f1a83f4..7c07388a 100644 --- a/zmq_src/ServerWorker.h +++ b/zmq_src/ServerWorker.h @@ -44,7 +44,6 @@ class ServerWorker { public: ServerWorker(zmq::context_t &ctx, int sock_type, bool _checkSignature, const string& _caCert ); - void work(); void requestExit(); From febe2c8b45fd11928b986e020ae25100e3b525b1 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 16:48:43 +0300 Subject: [PATCH 011/100] SKALE-3951 add rsp methods --- zmq_src/RspMessage.h | 60 ++++++++++++++++++++++++++++++++++++++++++ zmq_src/ZMQMessage.cpp | 8 ++++++ zmq_src/ZMQMessage.h | 2 ++ 3 files changed, 70 insertions(+) diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h index d570e809..328f52b0 100644 --- a/zmq_src/RspMessage.h +++ b/zmq_src/RspMessage.h @@ -61,6 +61,10 @@ class importECDSARspMessage : public ZMQMessage { importECDSARspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + string getECDSAPublicKey() { + return getStringRapid("publicKey"); + } }; @@ -69,6 +73,14 @@ class generateECDSARspMessage : public ZMQMessage { generateECDSARspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + string getECDSAPublicKey() { + return getStringRapid("publicKey"); + } + + string getKeyName() { + return getStringRapid("keyName"); + } }; @@ -77,6 +89,10 @@ class getPublicECDSARspMessage : public ZMQMessage { getPublicECDSARspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + string getECDSAPublicKey() { + return getStringRapid("publicKey"); + } }; @@ -93,6 +109,10 @@ class getVerificationVectorRspMessage : public ZMQMessage { getVerificationVectorRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + Json::Value getVerificationVector() { + return getJsonValueRapid("verificationVector"); + } }; @@ -101,6 +121,10 @@ class getSecretShareRspMessage : public ZMQMessage { getSecretShareRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + string getSecretShare() { + return getStringRapid("secretShare"); + } }; @@ -109,6 +133,10 @@ class dkgVerificationRspMessage : public ZMQMessage { dkgVerificationRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + bool isCorrect() { + return getBoolRapid("result"); + } }; @@ -125,6 +153,10 @@ class getBLSPublicRspMessage : public ZMQMessage { getBLSPublicRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + Json::Value getBLSPublicKey() { + return getJsonValueRapid("blsPublicKeyShare"); + } }; @@ -133,6 +165,10 @@ class getAllBLSPublicKeysRspMessage : public ZMQMessage { getAllBLSPublicKeysRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + Json::Value getPublicKeys() { + return getJsonValueRapid("publicKeys"); + } }; @@ -141,6 +177,14 @@ class complaintResponseRspMessage : public ZMQMessage { complaintResponseRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + string getDHKey() { + return getStringRapid("dhKey"); + } + + string getShare() { + return getStringRapid("share*G2"); + } }; @@ -149,6 +193,10 @@ class multG2RspMessage : public ZMQMessage { multG2RspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + Json::Value getResult() { + return getJsonValueRapid("x*G2"); + } }; @@ -157,6 +205,10 @@ class isPolyExistsRspMessage : public ZMQMessage { isPolyExistsRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + bool isExists() { + return getBoolRapid("IsExist"); + } }; @@ -173,6 +225,10 @@ class getServerVersionRspMessage : public ZMQMessage { getServerVersionRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + string getVesion() { + return getStringRapid("version"); + } }; @@ -181,6 +237,10 @@ class deleteBLSKeyRspMessage : public ZMQMessage { deleteBLSKeyRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; virtual Json::Value process(); + + bool isSuccessful() { + return getBoolRapid("deleted"); + } }; diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index d080a93a..dc1f2b56 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -60,6 +60,14 @@ Json::Value ZMQMessage::getJsonValueRapid(const char *_name) { return root; } +bool ZMQMessage::getBoolRapid(const char *_name) { + CHECK_STATE(_name); + CHECK_STATE(d->HasMember(_name)); + const rapidjson::Value &a = (*d)[_name]; + CHECK_STATE(a.IsBool()); + return a.GetBool(); +} + string ZMQMessage::getStringRapid(const char *_name) { CHECK_STATE(_name); CHECK_STATE(d->HasMember(_name)); diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index fcb62b46..454323f3 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -112,6 +112,8 @@ class ZMQMessage { Json::Value getJsonValueRapid(const char *_name); + bool getBoolRapid(const char *_name); + uint64_t getStatus() { return getUint64Rapid("status"); } From 2886b48d46a343d9e28bb3e055e14b7630376019 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 18:20:24 +0300 Subject: [PATCH 012/100] SKALE-3951 add rsp methods --- zmq_src/RspMessage.h | 2 +- zmq_src/ZMQClient.cpp | 188 +++++++++++++++++++++++++++++++++++++++++- zmq_src/ZMQClient.h | 35 ++++++++ 3 files changed, 222 insertions(+), 3 deletions(-) diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h index 328f52b0..090c0669 100644 --- a/zmq_src/RspMessage.h +++ b/zmq_src/RspMessage.h @@ -226,7 +226,7 @@ class getServerVersionRspMessage : public ZMQMessage { virtual Json::Value process(); - string getVesion() { + string getVersion() { return getStringRapid("version"); } }; diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 2c009cf3..4169b7bb 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -34,8 +34,7 @@ #include "common.h" #include "BLSCrypto.h" #include "ReqMessage.h" -#include "BLSSignRspMessage.h" -#include "ECDSASignRspMessage.h" +#include "RspMessage.h" #include "ZMQClient.h" @@ -314,6 +313,191 @@ string ZMQClient::ecdsaSignMessageHash(int base, const std::string &keyName, con return result->getSignature(); } +void ZMQClient::importBLSKeyShare(const std::string& keyShare, const std::string& keyName) { + Json::Value p; + p["type"] = ZMQMessage::IMPORT_BLS_REQ; + p["keyName"] = keyName; + p["keyShare"] = keyShare; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); +} + +string ZMQClient::importECDSAKey(const std::string& keyShare, const std::string& keyName) { + Json::Value p; + p["type"] = ZMQMessage::IMPORT_ECDSA_REQ; + p["keyName"] = keyName; + p["keyShare"] = keyShare; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getECDSAPublicKey(); +} + +pair ZMQClient::generateECDSAKey() { + Json::Value p; + p["type"] = ZMQMessage::GENERATE_ECDSA_REQ; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return {result->getECDSAPublicKey(), result->getKeyName()}; +} + +string ZMQClient::getECDSAPublicKey(const string& keyName) { + Json::Value p; + p["type"] = ZMQMessage::GET_PUBLIC_ECDSA_REQ; + p["keyName"] = keyName; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getECDSAPublicKey(); +} + +void ZMQClient::generateDKGPoly(const string& polyName) { + Json::Value p; + p["type"] = ZMQMessage::GENERATE_DKG_POLY_REQ; + p["polyName"] = polyName; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); +} + +Json::Value ZMQClient::getVerificationVector(const string& polyName, int t) { + Json::Value p; + p["type"] = ZMQMessage::GET_VV_REQ; + p["polyName"] = polyName; + p["t"] = t; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getVerificationVector(); +} + +string ZMQClient::getSecretShare(const string& polyName, const Json::Value& pubKeys, int t, int n) { + Json::Value p; + p["type"] = ZMQMessage::GET_SECRET_SHARE_REQ; + p["polyName"] = polyName; + p["publicKeys"] = pubKeys; + p["t"] = t; + p["n"] = n; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getSecretShare(); +} + +bool ZMQClient::dkgVerification(const string& publicShares, const string& ethKeyName, + const string& secretShare, int t, int n, int idx) { + Json::Value p; + p["type"] = ZMQMessage::DKG_VERIFY_REQ; + p["ethKeyName"] = ethKeyName; + p["publicShares"] = publicShares; + p["secretShare"] = secretShare; + p["t"] = t; + p["n"] = n; + p["index"] = idx; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->isCorrect(); +} + +void ZMQClient::createBLSPrivateKey(const string& blsKeyName, const string& ethKeyName, const string& polyName, + const string& secretShare, int t, int n) { + Json::Value p; + p["type"] = ZMQMessage::CREATE_BLS_PRIVATE_REQ; + p["ethKeyName"] = ethKeyName; + p["polyName"] = polyName; + p["blsKeyName"] = blsKeyName; + p["secretShare"] = secretShare; + p["t"] = t; + p["n"] = n; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); +} + +Json::Value ZMQClient::getBLSPublicKey(const string& blsKeyName) { + Json::Value p; + p["type"] = ZMQMessage::GET_BLS_PUBLIC_REQ; + p["blsKeyName"] = blsKeyName; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getBLSPublicKey(); +} + +Json::Value ZMQClient::getAllBlsPublicKeys(const Json::Value& publicShares, int n, int t) { + Json::Value p; + p["type"] = ZMQMessage::GET_ALL_BLS_PUBLIC_REQ; + p["publicShares"] = publicShares; + p["t"] = t; + p["n"] = n; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getPublicKeys(); +} + +pair ZMQClient::complaintResponse(const string& polyName, int t, int n, int idx) { + Json::Value p; + p["type"] = ZMQMessage::COMPLAINT_RESPONSE_REQ; + p["polyName"] = polyName; + p["t"] = t; + p["n"] = n; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return {result->getDHKey(), result->getShare()}; +} + +Json::Value ZMQClient::multG2(const string& x) { + Json::Value p; + p["type"] = ZMQMessage::MULT_G2_REQ; + p["x"] = x; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getResult(); +} + +bool ZMQClient::isPolyExists(const string& polyName) { + Json::Value p; + p["type"] = ZMQMessage::IS_POLY_EXISTS_REQ; + p["polyName"] = polyName; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->isExists(); +} + +void ZMQClient::getServerStatus() { + Json::Value p; + p["type"] = ZMQMessage::GET_SERVER_STATUS_REQ; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); +} + +string ZMQClient::getServerVersion() { + Json::Value p; + p["type"] = ZMQMessage::GET_SERVER_VERSION_REQ; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getVersion(); +} + +bool ZMQClient::deleteBLSKey(const string& blsKeyName) { + Json::Value p; + p["type"] = ZMQMessage::DELETE_BLS_KEY_REQ; + p["blsKeyName"] = blsKeyName; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->isSuccessful(); +} + uint64_t ZMQClient::getProcessID() { return syscall(__NR_gettid); } diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 5b8e23da..e4bf7821 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -87,6 +87,41 @@ class ZMQClient { string ecdsaSignMessageHash(int base, const std::string &keyName, const std::string &messageHash); + void importBLSKeyShare(const std::string& keyShare, const std::string& keyName); + + string importECDSAKey(const std::string& keyShare, const std::string& keyName); + + pair generateECDSAKey(); + + string getECDSAPublicKey(const string& keyName); + + void generateDKGPoly(const string& polyName); + + Json::Value getVerificationVector(const string& polyName, int t); + + string getSecretShare(const string& polyName, const Json::Value& pubKeys, int t, int n); + + bool dkgVerification(const string& publicShares, const string& ethKeyName, + const string& secretShare, int t, int n, int idx); + + void createBLSPrivateKey(const string& blsKeyName, const string& ethKeyName, const string& polyName, + const string& secretShare, int t, int n); + + Json::Value getBLSPublicKey(const string& blsKeyName); + + Json::Value getAllBlsPublicKeys(const Json::Value& publicShares, int n, int t); + + pair complaintResponse(const string& polyName, int t, int n, int idx); + + Json::Value multG2(const string& x); + + bool isPolyExists(const string& polyName); + + void getServerStatus(); + + string getServerVersion(); + + bool deleteBLSKey(const string& blsKeyName); }; From 3fb722fdb5a0add8eda17fe8b360ac53201f4160 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 7 Jun 2021 18:48:53 +0300 Subject: [PATCH 013/100] SKALE-3951 add tests --- testw.cpp | 22 +++++++++++++------- testw.py | 62 ++++++++++++++++++++++++++++++------------------------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/testw.cpp b/testw.cpp index 91bccae7..265db1da 100644 --- a/testw.cpp +++ b/testw.cpp @@ -236,11 +236,7 @@ TEST_CASE_METHOD("BLS key encrypt/decrypt", "[bls-key-encrypt-decrypt]") { printf("Decrypted key len %d\n", (int) strlen(plaintextKey)); printf("Decrypted key: %s\n", plaintextKey); free(plaintextKey); - - - } - */ string genECDSAKeyAPI(StubClient &_c) { @@ -432,8 +428,6 @@ TEST_CASE_METHOD(TestFixture, "DKG AES encrypted secret shares version 2 test", /* * ( "verification test", "[verify]" ) { - - char* pubshares = "0d72c21fc5a43452ad5f36699822309149ce6ce2cdce50dafa896e873f1b8ddd12f65a2e9c39c617a1f695f076b33b236b47ed773901fc2762f8b6f63277f5e30d7080be8e98c97f913d1920357f345dc0916c1fcb002b7beb060aa8b6b473a011bfafe9f8a5d8ea4c643ca4101e5119adbef5ae64f8dfb39cd10f1e69e31c591858d7eaca25b4c412fe909ca87ca7aadbf6d97d32d9b984e93d436f13d43ec31f40432cc750a64ac239cad6b8f78c1f1dd37427e4ff8c1cc4fe1c950fcbcec10ebfd79e0c19d0587adafe6db4f3c63ea9a329724a8804b63a9422e6898c0923209e828facf3a073254ec31af4231d999ba04eb5b7d1e0056d742a65b766f2f3"; char *sec_share = "11592366544581417165283270001305852351194685098958224535357729125789505948557"; mpz_t sshare; @@ -441,8 +435,6 @@ TEST_CASE_METHOD(TestFixture, "DKG AES encrypted secret shares version 2 test", mpz_set_str(sshare, "11592366544581417165283270001305852351194685098958224535357729125789505948557", 10); int result = Verification(pubshares, sshare, 2, 0); REQUIRE(result == 1); - - }*/ TEST_CASE_METHOD(TestFixture, "DKG_BLS test", "[dkg-bls]") { @@ -567,6 +559,13 @@ TEST_CASE_METHOD(TestFixture, "Get ServerStatus", "[get-server-status]") { sleep(3); } +TEST_CASE_METHOD(TestFixture, "Get ServerStatus", "[get-server-status-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + REQUIRE_NOTHROW(client->getServerStatus()); + sleep(3); +} + TEST_CASE_METHOD(TestFixture, "Get ServerVersion", "[get-server-version]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); @@ -574,6 +573,13 @@ TEST_CASE_METHOD(TestFixture, "Get ServerVersion", "[get-server-version]") { sleep(3); } +TEST_CASE_METHOD(TestFixture, "Get ServerVersion", "[get-server-version-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + REQUIRE(client->getServerVersion() == SGXWalletServer::getVersion()); + sleep(3); +} + TEST_CASE_METHOD(TestFixtureHTTPS, "Cert request sign", "[cert-sign]") { PRINT_SRC_LINE diff --git a/testw.py b/testw.py index bac5b254..4bd8be97 100755 --- a/testw.py +++ b/testw.py @@ -29,35 +29,41 @@ topDir = os.getcwd() + "/sgxwallet" print("Top directory is:" + topDir) testList = [ "[zmq-ecdsa]", - "[dkgzmqbls]", "[first-run]", - "[second-run]", - "[many-threads-crypto]", - "[many-threads-crypto-v2]", - "[backup-restore]", - "[cert-sign]", - "[get-server-status]", - "[get-server-version]", - "[backup-key]", - "[delete-bls-key]", - "[import-ecdsa-key]", - "[ecdsa-aes-key-gen]", - "[ecdsa-aes-key-sig-gen]", - "[ecdsa-aes-get-pub-key]", - "[ecdsa-key-gen-api]", - "[bls-key-encrypt]", - "[dkg-aes-gen]", - "[dkg-aes-encr-sshares]", - "[dkg-aes-encr-sshares-v2]", - "[dkg-api]", - "[dkg-api-v2]", - "[dkg-bls]", - "[dkg-bls-v2]", - "[dkg-poly-exists]", - "[dkg-aes-pub-shares]", - "[aes-encrypt-decrypt]", - "[aes-dkg]", - "[aes-dkg-v2]" + "[second-run]", + "[many-threads-crypto]", + "[many-threads-crypto-v2]", + # "[many-threads-crypto-v2-zmq]" + "[backup-restore]", + "[cert-sign]", + "[get-server-status]", + "[get-server-status-zmq]", + "[get-server-version]", + "[get-server-version-zmq]", + "[backup-key]", + "[delete-bls-key]", + # "[delete-bls-key-zmq]", + "[import-ecdsa-key]", + # [import-ecdsa-key-zmq]", + "[ecdsa-aes-key-gen]", + "[ecdsa-aes-key-sig-gen]", + "[ecdsa-aes-get-pub-key]", + "[ecdsa-key-gen-api]", + "[bls-key-encrypt]", + "[dkg-aes-gen]", + "[dkg-aes-encr-sshares]", + "[dkg-aes-encr-sshares-v2]", + "[dkg-api-v2]", + # "[dkg-api-v2-zmq]", + "[dkg-bls]", + "[dkgzmqbls]", + "[dkg-bls-v2]", + "[dkg-poly-exists]", + # "[dkg-poly-exists-zmq]", + "[dkg-aes-pub-shares]", + "[aes-encrypt-decrypt]", + "[aes-dkg]", + "[aes-dkg-v2]" ] From 6dfd5e5872eacd1551d9aa4357861bb5aff8fa2a Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 8 Jun 2021 11:57:05 +0300 Subject: [PATCH 014/100] SKALE0-3951 fix proceed requests, add tests --- testw.cpp | 22 +++++++++++++++++----- testw.py | 3 +-- zmq_src/ZMQClient.cpp | 3 ++- zmq_src/ZMQClient.h | 2 +- zmq_src/ZMQMessage.cpp | 8 ++++---- zmq_src/ZMQMessage.h | 4 ++-- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/testw.cpp b/testw.cpp index 265db1da..2d30ae9b 100644 --- a/testw.cpp +++ b/testw.cpp @@ -559,7 +559,7 @@ TEST_CASE_METHOD(TestFixture, "Get ServerStatus", "[get-server-status]") { sleep(3); } -TEST_CASE_METHOD(TestFixture, "Get ServerStatus", "[get-server-status-zmq]") { +TEST_CASE_METHOD(TestFixture, "Get ServerStatusZmq", "[get-server-status-zmq]") { auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", "./sgx_data/cert_data/rootCA.key"); REQUIRE_NOTHROW(client->getServerStatus()); @@ -573,7 +573,7 @@ TEST_CASE_METHOD(TestFixture, "Get ServerVersion", "[get-server-version]") { sleep(3); } -TEST_CASE_METHOD(TestFixture, "Get ServerVersion", "[get-server-version-zmq]") { +TEST_CASE_METHOD(TestFixture, "Get ServerVersionZmq", "[get-server-version-zmq]") { auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", "./sgx_data/cert_data/rootCA.key"); REQUIRE(client->getServerVersion() == SGXWalletServer::getVersion()); @@ -687,6 +687,20 @@ TEST_CASE_METHOD(TestFixture, "PolyExists test", "[dkg-poly-exists]") { REQUIRE(!polyDoesNotExist["IsExist"].asBool()); } +TEST_CASE_METHOD(TestFixture, "PolyExistsZmq test", "[dkg-poly-exists-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + + string polyName = SAMPLE_POLY_NAME; + REQUIRE_NOTHROW(client->generateDKGPoly(polyName, 2)); + + bool polyExists = client->isPolyExists(polyName); + REQUIRE(polyExists); + + bool polyDoesNotExist = client->isPolyExists("Vasya"); + REQUIRE(!polyDoesNotExist); +} + TEST_CASE_METHOD(TestFixture, "AES_DKG V2 test", "[aes-dkg-v2]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); @@ -936,9 +950,7 @@ TEST_CASE_METHOD(TestFixtureZMQSign, "ZMQ-ecdsa", "[zmq-ecdsa]") { HttpClient htp(RPC_ENDPOINT); StubClient c(htp, JSONRPC_CLIENT_V2); - string ip = ZMQ_IP; - - auto client = make_shared(ip, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", "./sgx_data/cert_data/rootCA.key"); string keyName = ""; diff --git a/testw.py b/testw.py index 4bd8be97..df3b34e1 100755 --- a/testw.py +++ b/testw.py @@ -59,10 +59,9 @@ "[dkgzmqbls]", "[dkg-bls-v2]", "[dkg-poly-exists]", - # "[dkg-poly-exists-zmq]", + "[dkg-poly-exists-zmq]", "[dkg-aes-pub-shares]", "[aes-encrypt-decrypt]", - "[aes-dkg]", "[aes-dkg-v2]" ] diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 4169b7bb..a00a8922 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -353,10 +353,11 @@ string ZMQClient::getECDSAPublicKey(const string& keyName) { return result->getECDSAPublicKey(); } -void ZMQClient::generateDKGPoly(const string& polyName) { +void ZMQClient::generateDKGPoly(const string& polyName, int t) { Json::Value p; p["type"] = ZMQMessage::GENERATE_DKG_POLY_REQ; p["polyName"] = polyName; + p["t"] = t; auto result = dynamic_pointer_cast(doRequestReply(p)); CHECK_STATE(result); CHECK_STATE(result->getStatus() == 0); diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index e4bf7821..be045eb0 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -95,7 +95,7 @@ class ZMQClient { string getECDSAPublicKey(const string& keyName); - void generateDKGPoly(const string& polyName); + void generateDKGPoly(const string& polyName, int t); Json::Value getVerificationVector(const string& polyName, int t); diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index dc1f2b56..5a91e6cc 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -163,7 +163,7 @@ shared_ptr ZMQMessage::parse(const char *_msg, shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr _d) { Requests r; try { - int t = requests.at( _type.c_str() ); + int t = requests.at( _type ); r = static_cast(t); } catch ( std::out_of_range& ) { BOOST_THROW_EXCEPTION(SGXException(-301, "Incorrect zmq message type: " + string(_type))); @@ -239,7 +239,7 @@ shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr _d) { Responses r; try { - int t = responses.at( _type.c_str() ); + int t = responses.at( _type ); r = static_cast(t); } catch ( std::out_of_range& ) { BOOST_THROW_EXCEPTION(InvalidStateException("Incorrect zmq message request type: " + string(_type), @@ -316,7 +316,7 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr > ZMQMessage::verifiedCerts(256); -const std::map ZMQMessage::responses { +const std::map ZMQMessage::requests{ {BLS_SIGN_REQ, 0}, {ECDSA_SIGN_REQ, 1}, {IMPORT_BLS_REQ, 2}, {IMPORT_ECDSA_REQ, 3}, {GENERATE_ECDSA_REQ, 4}, {GET_PUBLIC_ECDSA_REQ, 5}, {GENERATE_DKG_POLY_REQ, 6}, {GET_VV_REQ, 7}, {GET_SECRET_SHARE_REQ, 8}, {DKG_VERIFY_REQ, 9}, @@ -325,7 +325,7 @@ const std::map ZMQMessage::responses { {GET_SERVER_STATUS_REQ, 16}, {GET_SERVER_VERSION_REQ, 17}, {DELETE_BLS_KEY_REQ, 18} }; -const std::map ZMQMessage::requests { +const std::map ZMQMessage::responses { {BLS_SIGN_RSP, 0}, {ECDSA_SIGN_RSP, 1}, {IMPORT_BLS_RSP, 2}, {IMPORT_ECDSA_RSP, 3}, {GENERATE_ECDSA_RSP, 4}, {GET_PUBLIC_ECDSA_RSP, 5}, {GENERATE_DKG_POLY_RSP, 6}, {GET_VV_RSP, 7}, {GET_SECRET_SHARE_RSP, 8}, {DKG_VERIFY_RSP, 9}, diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index 454323f3..9a008101 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -92,8 +92,8 @@ class ZMQMessage { static constexpr const char *DELETE_BLS_KEY_REQ = "deleteBLSKeyReq"; static constexpr const char *DELETE_BLS_KEY_RSP = "deleteBLSKeyRsp"; - static const std::map requests; - static const std::map responses; + static const std::map requests; + static const std::map responses; enum Requests { ENUM_BLS_SIGN_REQ, ENUM_ECDSA_SIGN_REQ, ENUM_IMPORT_BLS_REQ, ENUM_IMPORT_ECDSA_REQ, ENUM_GENERATE_ECDSA_REQ, ENUM_GET_PUBLIC_ECDSA_REQ, ENUM_GENERATE_DKG_POLY_REQ, ENUM_GET_VV_REQ, ENUM_GET_SECRET_SHARE_REQ, ENUM_DKG_VERIFY_REQ, ENUM_CREATE_BLS_PRIVATE_REQ, From d635fca05f5efa436be4844a3a0913b2116b9856 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 8 Jun 2021 13:01:24 +0300 Subject: [PATCH 015/100] SKALE-3951 fix getStatus, add tests --- testw.cpp | 18 ++++++++++++++++++ zmq_src/ReqMessage.cpp | 36 ++++++++++++++++++------------------ zmq_src/ZMQClient.cpp | 6 +++--- zmq_src/ZMQClient.h | 2 +- zmq_src/ZMQMessage.cpp | 6 +++--- zmq_src/ZMQMessage.h | 12 ++++++++++-- 6 files changed, 53 insertions(+), 27 deletions(-) diff --git a/testw.cpp b/testw.cpp index 2d30ae9b..8b2b30fc 100644 --- a/testw.cpp +++ b/testw.cpp @@ -523,6 +523,24 @@ TEST_CASE_METHOD(TestFixture, "Delete Bls Key", "[delete-bls-key]") { REQUIRE(c.deleteBlsKey(name)["deleted"] == true); } +TEST_CASE_METHOD(TestFixture, "Delete Bls Key Zmq", "[delete-bls-key-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + + std::string name = "BLS_KEY:SCHAIN_ID:123456789:NODE_ID:0:DKG_ID:0"; + libff::alt_bn128_Fr key = libff::alt_bn128_Fr( + "6507625568967977077291849236396320012317305261598035438182864059942098934847"); + std::string key_str = TestUtils::stringFromFr(key); + REQUIRE(!client->importBLSKeyShare(key_str, name)); + + key_str = "0xe632f7fde2c90a073ec43eaa90dca7b82476bf28815450a11191484934b9c3f"; + REQUIRE(client->importBLSKeyShare(key_str, name)); + + REQUIRE_NOTHROW(client->blsSignMessageHash(name, SAMPLE_HASH, 1, 1)); + + REQUIRE(client->deleteBLSKey(name)); +} + TEST_CASE_METHOD(TestFixture, "Import ECDSA Key", "[import-ecdsa-key]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index a38cb64c..e3b74f9e 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -28,7 +28,7 @@ Json::Value ECDSASignReqMessage::process() { - auto base = getUint64Rapid("base"); + auto base = getInt64Rapid("base"); auto keyName = getStringRapid("keyName"); auto hash = getStringRapid("messageHash"); auto result = SGXWalletServer::ecdsaSignMessageHashImpl(base, keyName, hash); @@ -39,8 +39,8 @@ Json::Value ECDSASignReqMessage::process() { Json::Value BLSSignReqMessage::process() { auto keyName = getStringRapid("keyShareName"); auto hash = getStringRapid("messageHash"); - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); + auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); auto result = SGXWalletServer::blsSignMessageHashImpl(keyName, hash, t, n); result["type"] = ZMQMessage::BLS_SIGN_RSP; return result; @@ -77,7 +77,7 @@ Json::Value getPublicECDSAReqMessage::process() { Json::Value generateDKGPolyReqMessage::process() { auto polyName = getStringRapid("polyName"); - auto t = getUint64Rapid("t"); + auto t = getInt64Rapid("t"); auto result = SGXWalletServer::generateDKGPolyImpl(polyName, t); result["type"] = ZMQMessage::GENERATE_DKG_POLY_RSP; return result; @@ -85,8 +85,8 @@ Json::Value generateDKGPolyReqMessage::process() { Json::Value getVerificationVectorReqMessage::process() { auto polyName = getStringRapid("polyName"); - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); + auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); auto result = SGXWalletServer::getVerificationVectorImpl(polyName, t, n); result["type"] = ZMQMessage::GET_VV_RSP; return result; @@ -94,8 +94,8 @@ Json::Value getVerificationVectorReqMessage::process() { Json::Value getSecretShareReqMessage::process() { auto polyName = getStringRapid("polyName"); - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); + auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); auto pubKeys = getJsonValueRapid("publicKeys"); auto result = SGXWalletServer::getSecretShareV2Impl(polyName, pubKeys, t, n); result["type"] = ZMQMessage::GET_SECRET_SHARE_RSP; @@ -104,9 +104,9 @@ Json::Value getSecretShareReqMessage::process() { Json::Value dkgVerificationReqMessage::process() { auto ethKeyName = getStringRapid("ethKeyName"); - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); - auto idx = getUint64Rapid("index"); + auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); + auto idx = getInt64Rapid("index"); auto pubShares = getStringRapid("publicShares"); auto secretShare = getStringRapid("secretShare"); auto result = SGXWalletServer::dkgVerificationV2Impl(pubShares, ethKeyName, secretShare, t, n, idx); @@ -119,8 +119,8 @@ Json::Value createBLSPrivateKeyReqMessage::process() { auto ethKeyName = getStringRapid("ethKeyName"); auto polyName = getStringRapid("polyName"); auto secretShare = getStringRapid("secretShare"); - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); + auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; return result; @@ -134,8 +134,8 @@ Json::Value getBLSPublicReqMessage::process() { } Json::Value getAllBLSPublicKeysReqMessage::process() { - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); + auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); auto pubShares = getJsonValueRapid("publicShares"); auto result = SGXWalletServer::calculateAllBLSPublicKeysImpl(pubShares, t, n); result["type"] = ZMQMessage::GET_ALL_BLS_PUBLIC_RSP; @@ -144,9 +144,9 @@ Json::Value getAllBLSPublicKeysReqMessage::process() { Json::Value complaintResponseReqMessage::process() { auto polyName = getStringRapid("polyName"); - auto t = getUint64Rapid("t"); - auto n = getUint64Rapid("n"); - auto idx = getUint64Rapid("ind"); + auto t = getInt64Rapid("t"); + auto n = getInt64Rapid("n"); + auto idx = getInt64Rapid("ind"); auto result = SGXWalletServer::complaintResponseImpl(polyName, t, n, idx); result["type"] = ZMQMessage::COMPLAINT_RESPONSE_RSP; return result; diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index a00a8922..f8ac8643 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -313,14 +313,14 @@ string ZMQClient::ecdsaSignMessageHash(int base, const std::string &keyName, con return result->getSignature(); } -void ZMQClient::importBLSKeyShare(const std::string& keyShare, const std::string& keyName) { +bool ZMQClient::importBLSKeyShare(const std::string& keyShare, const std::string& keyName) { Json::Value p; p["type"] = ZMQMessage::IMPORT_BLS_REQ; - p["keyName"] = keyName; + p["keyShareName"] = keyName; p["keyShare"] = keyShare; auto result = dynamic_pointer_cast(doRequestReply(p)); CHECK_STATE(result); - CHECK_STATE(result->getStatus() == 0); + return result->getStatus() == 0; } string ZMQClient::importECDSAKey(const std::string& keyShare, const std::string& keyName) { diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index be045eb0..dc56918d 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -87,7 +87,7 @@ class ZMQClient { string ecdsaSignMessageHash(int base, const std::string &keyName, const std::string &messageHash); - void importBLSKeyShare(const std::string& keyShare, const std::string& keyName); + bool importBLSKeyShare(const std::string& keyShare, const std::string& keyName); string importECDSAKey(const std::string& keyShare, const std::string& keyName); diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 5a91e6cc..d97b4c61 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -34,12 +34,12 @@ #include "ZMQMessage.h" -uint64_t ZMQMessage::getUint64Rapid(const char *_name) { +uint64_t ZMQMessage::getInt64Rapid(const char *_name) { CHECK_STATE(_name); CHECK_STATE(d->HasMember(_name)); const rapidjson::Value &a = (*d)[_name]; - CHECK_STATE(a.IsUint64()); - return a.GetUint64(); + CHECK_STATE(a.IsInt64()); + return a.GetInt64(); }; Json::Value ZMQMessage::getJsonValueRapid(const char *_name) { diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index 9a008101..e4f057b2 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -108,14 +108,22 @@ class ZMQMessage { string getStringRapid(const char *_name); - uint64_t getUint64Rapid(const char *_name); + uint64_t getInt64Rapid(const char *_name); Json::Value getJsonValueRapid(const char *_name); bool getBoolRapid(const char *_name); uint64_t getStatus() { - return getUint64Rapid("status"); + return getInt64Rapid("status"); + } + + std::string rapidToString() { + rapidjson::StringBuffer buffer; + rapidjson::Writer< rapidjson::StringBuffer > writer( buffer ); + d->Accept( writer ); + std::string strRequest = buffer.GetString(); + return strRequest; } static shared_ptr parse(const char* _msg, size_t _size, bool _isRequest, From 370b6f33fc0cac3c6ddd85401cdbccf79cfb6099 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 8 Jun 2021 13:01:50 +0300 Subject: [PATCH 016/100] SKALE-3951 add test --- testw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testw.py b/testw.py index df3b34e1..b3619a7c 100755 --- a/testw.py +++ b/testw.py @@ -42,7 +42,7 @@ "[get-server-version-zmq]", "[backup-key]", "[delete-bls-key]", - # "[delete-bls-key-zmq]", + "[delete-bls-key-zmq]", "[import-ecdsa-key]", # [import-ecdsa-key-zmq]", "[ecdsa-aes-key-gen]", From 32063a16f992a117264696b3425e105e6f8f9ccc Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 8 Jun 2021 13:09:54 +0300 Subject: [PATCH 017/100] SKALE-3951 add test, fix importECDSAKey --- testw.cpp | 15 +++++++++++++++ testw.py | 2 +- zmq_src/ZMQClient.cpp | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/testw.cpp b/testw.cpp index 8b2b30fc..1dffbbe9 100644 --- a/testw.cpp +++ b/testw.cpp @@ -557,6 +557,21 @@ TEST_CASE_METHOD(TestFixture, "Import ECDSA Key", "[import-ecdsa-key]") { REQUIRE(c.ecdsaSignMessageHash(16, name, SAMPLE_HASH)["status"] == 0); } +TEST_CASE_METHOD(TestFixture, "Import ECDSA Key Zmq", "[import-ecdsa-key-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + + std::string name = "NEK:abcdef"; + REQUIRE_THROWS(client->importECDSAKey("6507625568967977077291849236396320012317305261598035438182864059942098934847", + name)); + + string key_str = "0xe632f7fde2c90a073ec43eaa90dca7b82476bf28815450a11191484934b9c3f"; + string response = client->importECDSAKey(key_str, name); + REQUIRE(response == client->getECDSAPublicKey(name)); + + REQUIRE_NOTHROW(client->ecdsaSignMessageHash(16, name, SAMPLE_HASH)); +} + TEST_CASE_METHOD(TestFixture, "Backup Key", "[backup-key]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); diff --git a/testw.py b/testw.py index b3619a7c..adafa8e0 100755 --- a/testw.py +++ b/testw.py @@ -44,7 +44,7 @@ "[delete-bls-key]", "[delete-bls-key-zmq]", "[import-ecdsa-key]", - # [import-ecdsa-key-zmq]", + "[import-ecdsa-key-zmq]", "[ecdsa-aes-key-gen]", "[ecdsa-aes-key-sig-gen]", "[ecdsa-aes-get-pub-key]", diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index f8ac8643..689ca4c0 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -327,7 +327,7 @@ string ZMQClient::importECDSAKey(const std::string& keyShare, const std::string& Json::Value p; p["type"] = ZMQMessage::IMPORT_ECDSA_REQ; p["keyName"] = keyName; - p["keyShare"] = keyShare; + p["key"] = keyShare; auto result = dynamic_pointer_cast(doRequestReply(p)); CHECK_STATE(result); CHECK_STATE(result->getStatus() == 0); From 1c5b7a5504ba777be1ec89340013b208de3697aa Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 8 Jun 2021 13:45:39 +0300 Subject: [PATCH 018/100] SKALE-3951 remove extra params --- DKGCrypto.cpp | 6 +++--- DKGCrypto.h | 2 +- SGXWalletServer.cpp | 12 ++++++------ SGXWalletServer.hpp | 4 ++-- TestUtils.cpp | 12 ++++++------ abstractstubserver.h | 6 +++--- secure_enclave/secure_enclave.c | 4 ++-- secure_enclave/secure_enclave.edl | 3 +-- stubclient.h | 3 +-- testw.cpp | 16 ++++++++-------- zmq_src/ReqMessage.cpp | 3 +-- 11 files changed, 34 insertions(+), 37 deletions(-) diff --git a/DKGCrypto.cpp b/DKGCrypto.cpp index 416f5cd5..9cb8b77e 100644 --- a/DKGCrypto.cpp +++ b/DKGCrypto.cpp @@ -150,7 +150,7 @@ string gen_dkg_poly(int _t) { return result; } -vector > get_verif_vect(const string &encryptedPolyHex, int t, int n) { +vector > get_verif_vect(const string &encryptedPolyHex, int t) { auto encryptedPolyHexPtr = encryptedPolyHex.c_str(); @@ -174,7 +174,7 @@ vector > get_verif_vect(const string &encryptedPolyHex, int t, in sgx_status_t status = SGX_SUCCESS; status = trustedGetPublicShares(eid, &errStatus, errMsg.data(), encrDKGPoly.data(), encLen, - pubShares.data(), t, n); + pubShares.data(), t); HANDLE_TRUSTED_FUNCTION_ERROR(status, errStatus, errMsg.data()); @@ -189,7 +189,7 @@ vector > get_verif_vect(const string &encryptedPolyHex, int t, in } vector > getVerificationVectorMult(const std::string &encryptedPolyHex, int t, int n, size_t ind) { - auto verificationVector = get_verif_vect(encryptedPolyHex, t, n); + auto verificationVector = get_verif_vect(encryptedPolyHex, t); vector > result(t); diff --git a/DKGCrypto.h b/DKGCrypto.h index f5312b07..c76b01e7 100644 --- a/DKGCrypto.h +++ b/DKGCrypto.h @@ -33,7 +33,7 @@ using namespace std; string gen_dkg_poly( int _t); -vector > get_verif_vect(const string& encryptedPolyHex, int t, int n); +vector > get_verif_vect(const string& encryptedPolyHex, int t); vector > getVerificationVectorMult(const std::string& encryptedPolyHex, int t, int n, size_t ind); diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index 820c8273..e4b452dc 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -501,7 +501,7 @@ Json::Value SGXWalletServer::generateDKGPolyImpl(const string &_polyName, int _t RETURN_SUCCESS(result) } -Json::Value SGXWalletServer::getVerificationVectorImpl(const string &_polyName, int _t, int _n) { +Json::Value SGXWalletServer::getVerificationVectorImpl(const string &_polyName, int _t) { COUNT_STATISTICS spdlog::info("Entering {}", __FUNCTION__); INIT_RESULT(result) @@ -511,13 +511,13 @@ Json::Value SGXWalletServer::getVerificationVectorImpl(const string &_polyName, if (!checkName(_polyName, "POLY")) { throw SGXException(INVALID_DKG_GETVV_POLY_NAME, string(__FUNCTION__) + ":Invalid polynomial name"); } - if (!check_n_t(_t, _n)) { - throw SGXException(INVALID_DKG_GETVV_PARAMS, string(__FUNCTION__) + ":Invalid parameters n or t "); + if (_t <= 0) { + throw SGXException(INVALID_DKG_GETVV_PARAMS, string(__FUNCTION__) + ":Invalid t "); } shared_ptr encrPoly = readFromDb(_polyName); - verifVector = get_verif_vect(*encrPoly, _t, _n); + verifVector = get_verif_vect(*encrPoly, _t); for (int i = 0; i < _t; i++) { vector currentCoef = verifVector.at(i); @@ -1000,8 +1000,8 @@ Json::Value SGXWalletServer::generateDKGPoly(const string &_polyName, int _t) { return generateDKGPolyImpl(_polyName, _t); } -Json::Value SGXWalletServer::getVerificationVector(const string &_polynomeName, int _t, int _n) { - return getVerificationVectorImpl(_polynomeName, _t, _n); +Json::Value SGXWalletServer::getVerificationVector(const string &_polynomeName, int _t) { + return getVerificationVectorImpl(_polynomeName, _t); } Json::Value SGXWalletServer::getSecretShare(const string &_polyName, const Json::Value &_publicKeys, int t, int n) { diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index 6466dda0..74d34396 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -82,7 +82,7 @@ class SGXWalletServer : public AbstractStubServer { virtual Json::Value generateDKGPoly(const string &_polyName, int _t); - virtual Json::Value getVerificationVector(const string &_polynomeName, int _t, int _n); + virtual Json::Value getVerificationVector(const string &_polynomeName, int _t); virtual Json::Value getSecretShare(const string &_polyName, const Json::Value &_publicKeys, int t, int n); @@ -140,7 +140,7 @@ class SGXWalletServer : public AbstractStubServer { static Json::Value generateDKGPolyImpl(const string &_polyName, int _t); - static Json::Value getVerificationVectorImpl(const string &_polyName, int _t, int _n); + static Json::Value getVerificationVectorImpl(const string &_polyName, int _t); static Json::Value getSecretShareImpl(const string &_polyName, const Json::Value &_pubKeys, int _t, int _n); diff --git a/TestUtils.cpp b/TestUtils.cpp index e43fffe1..f44f0dc7 100644 --- a/TestUtils.cpp +++ b/TestUtils.cpp @@ -197,7 +197,7 @@ void TestUtils::sendRPCRequest() { polyNames[i] = polyName; for (int i3 = 0; i3 <= testCount; i3++) { - verifVects[i] = c.getVerificationVector(polyName, t, n); + verifVects[i] = c.getVerificationVector(polyName, t); CHECK_STATE(verifVects[i]["status"] == 0); } @@ -317,7 +317,7 @@ void TestUtils::sendRPCRequestV2() { auto response = c.generateDKGPoly(polyName, t); CHECK_STATE(response["status"] == 0); polyNames[i] = polyName; - verifVects[i] = c.getVerificationVector(polyName, t, n); + verifVects[i] = c.getVerificationVector(polyName, t); CHECK_STATE(verifVects[i]["status"] == 0); pubEthKeys.append(ethKeys[i]["publicKey"]); @@ -421,7 +421,7 @@ void TestUtils::sendRPCRequestZMQ() { auto response = c.generateDKGPoly(polyName, t); CHECK_STATE(response["status"] == 0); polyNames[i] = polyName; - verifVects[i] = c.getVerificationVector(polyName, t, n); + verifVects[i] = c.getVerificationVector(polyName, t); CHECK_STATE(verifVects[i]["status"] == 0); pubEthKeys.append(ethKeys[i]["publicKey"]); @@ -519,7 +519,7 @@ void TestUtils::doDKG(StubClient &c, int n, int t, Json::Value response = c.generateDKGPoly(polyName, t); CHECK_STATE(response["status"] == 0); polyNames[i] = polyName; - verifVects[i] = c.getVerificationVector(polyName, t, n); + verifVects[i] = c.getVerificationVector(polyName, t); CHECK_STATE(verifVects[i]["status"] == 0); pubEthKeys.append(ethKeys[i]["publicKey"]); } @@ -660,7 +660,7 @@ void TestUtils::doDKGV2(StubClient &c, int n, int t, Json::Value response = c.generateDKGPoly(polyName, t); CHECK_STATE(response["status"] == 0); polyNames[i] = polyName; - verifVects[i] = c.getVerificationVector(polyName, t, n); + verifVects[i] = c.getVerificationVector(polyName, t); CHECK_STATE(verifVects[i]["status"] == 0); pubEthKeys.append(ethKeys[i]["publicKey"]); } @@ -802,7 +802,7 @@ void TestUtils::doZMQBLS(shared_ptr _zmqClient, StubClient &c, int n, Json::Value response = c.generateDKGPoly(polyName, t); CHECK_STATE(response["status"] == 0); polyNames[i] = polyName; - verifVects[i] = c.getVerificationVector(polyName, t, n); + verifVects[i] = c.getVerificationVector(polyName, t); CHECK_STATE(verifVects[i]["status"] == 0); pubEthKeys.append(ethKeys[i]["publicKey"]); } diff --git a/abstractstubserver.h b/abstractstubserver.h index a99fb0f0..bc34a51a 100644 --- a/abstractstubserver.h +++ b/abstractstubserver.h @@ -45,7 +45,7 @@ class AbstractStubServer : public jsonrpc::AbstractServer this->bindAndAddMethod(jsonrpc::Procedure("ecdsaSignMessageHash", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "base",jsonrpc::JSON_INTEGER,"keyName",jsonrpc::JSON_STRING,"messageHash",jsonrpc::JSON_STRING, NULL), &AbstractStubServer::ecdsaSignMessageHashI); this->bindAndAddMethod(jsonrpc::Procedure("generateDKGPoly", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "polyName",jsonrpc::JSON_STRING,"t",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::generateDKGPolyI); - this->bindAndAddMethod(jsonrpc::Procedure("getVerificationVector", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT,"polyName",jsonrpc::JSON_STRING, "t",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::getVerificationVectorI); + this->bindAndAddMethod(jsonrpc::Procedure("getVerificationVector", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "polyName", jsonrpc::JSON_STRING, "t", jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::getVerificationVectorI); this->bindAndAddMethod(jsonrpc::Procedure("getSecretShare", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "polyName",jsonrpc::JSON_STRING,"publicKeys",jsonrpc::JSON_ARRAY, "n",jsonrpc::JSON_INTEGER,"t",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::getSecretShareI); this->bindAndAddMethod(jsonrpc::Procedure("dkgVerification", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "publicShares",jsonrpc::JSON_STRING, "ethKeyName",jsonrpc::JSON_STRING, "secretShare",jsonrpc::JSON_STRING,"t",jsonrpc::JSON_INTEGER, "n",jsonrpc::JSON_INTEGER, "index",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::dkgVerificationI); this->bindAndAddMethod(jsonrpc::Procedure("createBLSPrivateKey", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "blsKeyName",jsonrpc::JSON_STRING, "ethKeyName",jsonrpc::JSON_STRING, "polyName", jsonrpc::JSON_STRING, "secretShare",jsonrpc::JSON_STRING,"t", jsonrpc::JSON_INTEGER,"n",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::createBLSPrivateKeyI); @@ -97,7 +97,7 @@ class AbstractStubServer : public jsonrpc::AbstractServer } inline virtual void getVerificationVectorI(const Json::Value &request, Json::Value &response) { - response = this->getVerificationVector(request["polyName"].asString(), request["t"].asInt(), request["n"].asInt()); + response = this->getVerificationVector(request["polyName"].asString(), request["t"].asInt()); } inline virtual void getSecretShareI(const Json::Value &request, Json::Value &response) { @@ -169,7 +169,7 @@ class AbstractStubServer : public jsonrpc::AbstractServer virtual Json::Value ecdsaSignMessageHash(int base, const std::string& keyName, const std::string& messageHash) = 0; virtual Json::Value generateDKGPoly(const std::string& polyName, int t) = 0; - virtual Json::Value getVerificationVector(const std::string& polyName, int t, int n) = 0; + virtual Json::Value getVerificationVector(const std::string& polyName, int t) = 0; virtual Json::Value getSecretShare(const std::string& polyName, const Json::Value& publicKeys, int t, int n) = 0; virtual Json::Value dkgVerification( const std::string& publicShares, const std::string& ethKeyName, const std::string& SecretShare, int t, int n, int index) = 0; virtual Json::Value createBLSPrivateKey(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string& SecretShare, int t, int n) = 0; diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 82031cb4..17855c9b 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -987,14 +987,14 @@ void trustedGetEncryptedSecretShareV2(int *errStatus, char *errString, void trustedGetPublicShares(int *errStatus, char *errString, uint8_t *encrypted_dkg_secret, uint64_t enc_len, char *public_shares, - unsigned _t, unsigned _n) { + unsigned _t) { LOG_INFO(__FUNCTION__); INIT_ERROR_STATE CHECK_STATE(encrypted_dkg_secret); CHECK_STATE(public_shares); - CHECK_STATE(_t <= _n && _n > 0) + CHECK_STATE(_t > 0) SAFE_CHAR_BUF(decrypted_dkg_secret, DKG_MAX_SEALED_LEN); diff --git a/secure_enclave/secure_enclave.edl b/secure_enclave/secure_enclave.edl index e671d82f..27a84f92 100644 --- a/secure_enclave/secure_enclave.edl +++ b/secure_enclave/secure_enclave.edl @@ -121,8 +121,7 @@ enclave { [in, count = 3050] uint8_t* encrypted_dkg_secret, uint64_t enc_len, [out, count = 10000] char* public_shares, - unsigned _t, - unsigned _n); + unsigned _t); public void trustedDkgVerify( [out] int *errStatus, diff --git a/stubclient.h b/stubclient.h index 2a379406..17d720c5 100644 --- a/stubclient.h +++ b/stubclient.h @@ -98,11 +98,10 @@ class StubClient : public jsonrpc::Client throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } - Json::Value getVerificationVector(const std::string& polyName, int t, int n) + Json::Value getVerificationVector(const std::string& polyName, int t) { Json::Value p; p["polyName"] = polyName; - p["n"] = n; p["t"] = t; Json::Value result = this->CallMethod("getVerificationVector",p); if (result.isObject()) diff --git a/testw.cpp b/testw.cpp index 1dffbbe9..90a7e1f8 100644 --- a/testw.cpp +++ b/testw.cpp @@ -336,7 +336,7 @@ TEST_CASE_METHOD(TestFixture, "DKG AES public shares test", "[dkg-aes-pub-shares vector pubShares(10000, 0); PRINT_SRC_LINE status = trustedGetPublicShares(eid, &errStatus, errMsg1.data(), - encryptedDKGSecret.data(), encLen, pubShares.data(), t, n); + encryptedDKGSecret.data(), encLen, pubShares.data(), t); REQUIRE(status == SGX_SUCCESS); REQUIRE(errStatus == SGX_SUCCESS); @@ -659,7 +659,7 @@ TEST_CASE_METHOD(TestFixture, "DKG API V2 test", "[dkg-api-v2]") { Json::Value genPolyWrongName = c.generateDKGPoly("poly", 2); REQUIRE(genPolyWrongName["status"].asInt() != 0); - Json::Value verifVectWrongName = c.getVerificationVector("poly", 2, 2); + Json::Value verifVectWrongName = c.getVerificationVector("poly", 2); REQUIRE(verifVectWrongName["status"].asInt() != 0); Json::Value secretSharesWrongName = c.getSecretShareV2("poly", publicKeys, 2, 2); @@ -669,14 +669,14 @@ TEST_CASE_METHOD(TestFixture, "DKG API V2 test", "[dkg-api-v2]") { Json::Value genPolyWrong_t = c.generateDKGPoly(polyName, 33); REQUIRE(genPolyWrong_t["status"].asInt() != 0); - Json::Value verifVectWrong_t = c.getVerificationVector(polyName, 1, 2); + Json::Value verifVectWrong_t = c.getVerificationVector(polyName, 1); REQUIRE(verifVectWrong_t["status"].asInt() != 0); Json::Value secretSharesWrong_t = c.getSecretShareV2(polyName, publicKeys, 3, 3); REQUIRE(secretSharesWrong_t["status"].asInt() != 0); // wrong_n - Json::Value verifVectWrong_n = c.getVerificationVector(polyName, 2, 1); + Json::Value verifVectWrong_n = c.getVerificationVector(polyName, 2); REQUIRE(verifVectWrong_n["status"].asInt() != 0); Json::Value publicKeys1; @@ -693,9 +693,9 @@ TEST_CASE_METHOD(TestFixture, "DKG API V2 test", "[dkg-api-v2]") { REQUIRE_NOTHROW(c.getSecretShare(polyName, publicKeys, 2, 2)); REQUIRE(Skeys == c.getSecretShare(polyName, publicKeys, 2, 2)); - Json::Value verifVect = c.getVerificationVector(polyName, 2, 2); - REQUIRE_NOTHROW(c.getVerificationVector(polyName, 2, 2)); - REQUIRE(verifVect == c.getVerificationVector(polyName, 2, 2)); + Json::Value verifVect = c.getVerificationVector(polyName, 2); + REQUIRE_NOTHROW(c.getVerificationVector(polyName, 2)); + REQUIRE(verifVect == c.getVerificationVector(polyName, 2)); Json::Value verificationWrongSkeys = c.dkgVerificationV2("", "", "", 2, 2, 1); REQUIRE(verificationWrongSkeys["status"].asInt() != 0); @@ -762,7 +762,7 @@ TEST_CASE_METHOD(TestFixture, "AES_DKG V2 test", "[aes-dkg-v2]") { polyNames[i] = polyName; PRINT_SRC_LINE - verifVects[i] = c.getVerificationVector(polyName, t, n); + verifVects[i] = c.getVerificationVector(polyName, t); REQUIRE(verifVects[i]["status"] == 0); pubEthKeys.append(ethKeys[i]["publicKey"]); diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index e3b74f9e..3bef6310 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -86,8 +86,7 @@ Json::Value generateDKGPolyReqMessage::process() { Json::Value getVerificationVectorReqMessage::process() { auto polyName = getStringRapid("polyName"); auto t = getInt64Rapid("t"); - auto n = getInt64Rapid("n"); - auto result = SGXWalletServer::getVerificationVectorImpl(polyName, t, n); + auto result = SGXWalletServer::getVerificationVectorImpl(polyName, t); result["type"] = ZMQMessage::GET_VV_RSP; return result; } From f423edbab2bde04931b875306ed834332ef9c004 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 8 Jun 2021 14:04:35 +0300 Subject: [PATCH 019/100] SKALE-3951 add tests --- testw.cpp | 50 +++++++++++++++++++++++++++++++++++++++---- testw.py | 2 +- zmq_src/ZMQClient.cpp | 4 ++-- zmq_src/ZMQClient.h | 2 +- 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/testw.cpp b/testw.cpp index 90a7e1f8..c5bbcb46 100644 --- a/testw.cpp +++ b/testw.cpp @@ -675,10 +675,6 @@ TEST_CASE_METHOD(TestFixture, "DKG API V2 test", "[dkg-api-v2]") { Json::Value secretSharesWrong_t = c.getSecretShareV2(polyName, publicKeys, 3, 3); REQUIRE(secretSharesWrong_t["status"].asInt() != 0); - // wrong_n - Json::Value verifVectWrong_n = c.getVerificationVector(polyName, 2); - REQUIRE(verifVectWrong_n["status"].asInt() != 0); - Json::Value publicKeys1; publicKeys1.append(SAMPLE_DKG_PUB_KEY_1); Json::Value secretSharesWrong_n = c.getSecretShareV2(polyName, publicKeys1, 2, 1); @@ -701,6 +697,52 @@ TEST_CASE_METHOD(TestFixture, "DKG API V2 test", "[dkg-api-v2]") { REQUIRE(verificationWrongSkeys["status"].asInt() != 0); } +TEST_CASE_METHOD(TestFixture, "DKG API V2 ZMQ test", "[dkg-api-v2-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + + string polyName = SAMPLE_POLY_NAME; + + PRINT_SRC_LINE + REQUIRE(client->generateDKGPoly(polyName, 2)); + + Json::Value publicKeys; + publicKeys.append(SAMPLE_DKG_PUB_KEY_1); + publicKeys.append(SAMPLE_DKG_PUB_KEY_2); + + // wrongName + REQUIRE(!client->generateDKGPoly("poly", 2)); + + REQUIRE_THROWS(client->getVerificationVector("poly", 2)); + + REQUIRE_THROWS(client->getSecretShare("poly", publicKeys, 2, 2)); + + // wrong_t + REQUIRE(!client->generateDKGPoly(polyName, 33)); + + REQUIRE_THROWS(client->getVerificationVector(polyName, 0)); + + REQUIRE_THROWS(client->getSecretShare(polyName, publicKeys, 3, 3)); + + Json::Value publicKeys1; + publicKeys1.append(SAMPLE_DKG_PUB_KEY_1); + REQUIRE_THROWS(client->getSecretShare(polyName, publicKeys1, 2, 1)); + + //wrong number of publicKeys + REQUIRE_THROWS(client->getSecretShare(polyName, publicKeys, 2, 3)); + + //wrong verif + string Skeys = client->getSecretShare(polyName, publicKeys, 2, 2); + REQUIRE_NOTHROW(client->getSecretShare(polyName, publicKeys, 2, 2)); + REQUIRE(Skeys == client->getSecretShare(polyName, publicKeys, 2, 2)); + + Json::Value verifVect = client->getVerificationVector(polyName, 2); + REQUIRE_NOTHROW(client->getVerificationVector(polyName, 2)); + REQUIRE(verifVect == client->getVerificationVector(polyName, 2)); + + REQUIRE_THROWS(client->dkgVerification("", "", "", 2, 2, 1)); +} + TEST_CASE_METHOD(TestFixture, "PolyExists test", "[dkg-poly-exists]") { HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); diff --git a/testw.py b/testw.py index adafa8e0..9e197477 100755 --- a/testw.py +++ b/testw.py @@ -54,7 +54,7 @@ "[dkg-aes-encr-sshares]", "[dkg-aes-encr-sshares-v2]", "[dkg-api-v2]", - # "[dkg-api-v2-zmq]", + "[dkg-api-v2-zmq]", "[dkg-bls]", "[dkgzmqbls]", "[dkg-bls-v2]", diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 689ca4c0..67f5b64b 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -353,14 +353,14 @@ string ZMQClient::getECDSAPublicKey(const string& keyName) { return result->getECDSAPublicKey(); } -void ZMQClient::generateDKGPoly(const string& polyName, int t) { +bool ZMQClient::generateDKGPoly(const string& polyName, int t) { Json::Value p; p["type"] = ZMQMessage::GENERATE_DKG_POLY_REQ; p["polyName"] = polyName; p["t"] = t; auto result = dynamic_pointer_cast(doRequestReply(p)); CHECK_STATE(result); - CHECK_STATE(result->getStatus() == 0); + return result->getStatus() == 0; } Json::Value ZMQClient::getVerificationVector(const string& polyName, int t) { diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index dc56918d..e782a9ca 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -95,7 +95,7 @@ class ZMQClient { string getECDSAPublicKey(const string& keyName); - void generateDKGPoly(const string& polyName, int t); + bool generateDKGPoly(const string& polyName, int t); Json::Value getVerificationVector(const string& polyName, int t); From ab08ff9d7512dd491c3e413593d224126442fb1a Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 8 Jun 2021 15:59:31 +0300 Subject: [PATCH 020/100] SKALE-3951 add tests, update VERSION --- TestUtils.cpp | 65 ++++++++++++++++++++++++------------------ TestUtils.h | 1 - VERSION | 2 +- testw.cpp | 12 ++++++++ testw.py | 2 +- zmq_src/ZMQClient.cpp | 6 ++-- zmq_src/ZMQClient.h | 2 +- zmq_src/ZMQMessage.cpp | 1 - 8 files changed, 56 insertions(+), 35 deletions(-) diff --git a/TestUtils.cpp b/TestUtils.cpp index f44f0dc7..4a168a73 100644 --- a/TestUtils.cpp +++ b/TestUtils.cpp @@ -42,7 +42,6 @@ #include "SGXWalletServer.hpp" #include "catch.hpp" -#include "zmq_src/ZMQClient.h" #include "BLSSigShare.h" #include "BLSSigShareSet.h" #include "BLSPublicKeyShare.h" @@ -396,42 +395,40 @@ void TestUtils::sendRPCRequestV2() { } void TestUtils::sendRPCRequestZMQ() { - HttpClient client(RPC_ENDPOINT); - StubClient c(client, JSONRPC_CLIENT_V2); + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); int n = 16, t = 16; - Json::Value ethKeys[n]; + vector ethKeys(n); Json::Value verifVects[n]; Json::Value pubEthKeys; - Json::Value secretShares[n]; + vector secretShares(n); Json::Value pubBLSKeys[n]; - Json::Value blsSigShares[n]; - vector pubShares(n); - vector polyNames(n); + vector blsSigShares(n); + vector pubShares(n); + vector polyNames(n); static atomic counter(1); int schainID = counter.fetch_add(1); int dkgID = counter.fetch_add(1); for (uint8_t i = 0; i < n; i++) { - ethKeys[i] = c.generateECDSAKey(); - CHECK_STATE(ethKeys[i]["status"] == 0); + auto generatedKey = client->generateECDSAKey(); + ethKeys[i] = generatedKey.second; string polyName = "POLY:SCHAIN_ID:" + to_string(schainID) + ":NODE_ID:" + to_string(i) + ":DKG_ID:" + to_string(dkgID); - auto response = c.generateDKGPoly(polyName, t); - CHECK_STATE(response["status"] == 0); + CHECK_STATE(client->generateDKGPoly(polyName, t)); polyNames[i] = polyName; - verifVects[i] = c.getVerificationVector(polyName, t); - CHECK_STATE(verifVects[i]["status"] == 0); + verifVects[i] = client->getVerificationVector(polyName, t); - pubEthKeys.append(ethKeys[i]["publicKey"]); + pubEthKeys.append(generatedKey.first); } for (uint8_t i = 0; i < n; i++) { - secretShares[i] = c.getSecretShareV2(polyNames[i], pubEthKeys, t, n); + secretShares[i] = client->getSecretShare(polyNames[i], pubEthKeys, t, n); for (uint8_t k = 0; k < t; k++) { for (uint8_t j = 0; j < 4; j++) { - string pubShare = verifVects[i]["verificationVector"][k][j].asString(); + string pubShare = verifVects[i][k][j].asString(); pubShares[i] += convertDecToHex(pubShare); } } @@ -441,10 +438,10 @@ void TestUtils::sendRPCRequestZMQ() { for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { - string secretShare = secretShares[i]["secretShare"].asString().substr(192 * j, 192); - secShares[i] += secretShares[j]["secretShare"].asString().substr(192 * i, 192); - Json::Value verif = c.dkgVerificationV2(pubShares[i], ethKeys[j]["keyName"].asString(), secretShare, t, n, j); - CHECK_STATE(verif["status"] == 0); + string secretShare = secretShares[i].substr(192 * j, 192); + secShares[i] += secretShares[j].substr(192 * i, 192); + bool verif = client->dkgVerification(pubShares[i], ethKeys[j], secretShare, t, n, j); + CHECK_STATE(verif); } BLSSigShareSet sigShareSet(t, n); @@ -463,17 +460,31 @@ void TestUtils::sendRPCRequestZMQ() { for (int i = 0; i < n; ++i) { publicShares["publicShares"][i] = pubShares[i]; } - - Json::Value blsPublicKeys = c.calculateAllBLSPublicKeys(publicShares, t, n); - CHECK_STATE(blsPublicKeys["status"] == 0); + + Json::Value blsPublicKeys = client->getAllBlsPublicKeys(publicShares, t, n); for (int i = 0; i < t; i++) { string blsName = "BLS_KEY" + polyNames[i].substr(4); + string secretShare = secretShares[i]; + + CHECK_STATE(client->createBLSPrivateKey(blsName, ethKeys[i], polyNames[i], secShares[i], t, n)); + pubBLSKeys[i] = client->getBLSPublicKey(blsName); + + libff::alt_bn128_G2 publicKey(libff::alt_bn128_Fq2(libff::alt_bn128_Fq(pubBLSKeys[i][0].asCString()), + libff::alt_bn128_Fq(pubBLSKeys[i][1].asCString())), + libff::alt_bn128_Fq2(libff::alt_bn128_Fq(pubBLSKeys[i][2].asCString()), + libff::alt_bn128_Fq(pubBLSKeys[i][3].asCString())), + libff::alt_bn128_Fq2::one()); + + string public_key_str = convertG2ToString(publicKey); + + CHECK_STATE(public_key_str == blsPublicKeys[i].asString()); + string hash = SAMPLE_HASH; - blsSigShares[i] = c.blsSignMessageHash(blsName, hash, t, n); - CHECK_STATE(blsSigShares[i]["status"] == 0); + blsSigShares[i] = client->blsSignMessageHash(blsName, hash, t, n); + CHECK_STATE(blsSigShares[i].length() > 0); - shared_ptr sig_share_ptr = make_shared(blsSigShares[i]["signatureShare"].asString()); + shared_ptr sig_share_ptr = make_shared(blsSigShares[i]); BLSSigShare sig(sig_share_ptr, i + 1, t, n); sigShareSet.addSigShare(make_shared(sig)); } diff --git a/TestUtils.h b/TestUtils.h index 169f3687..9c004d63 100644 --- a/TestUtils.h +++ b/TestUtils.h @@ -25,7 +25,6 @@ #define SGXWALLET_TESTUTILS_H #include -#include #include #include #include "sgxwallet_common.h" diff --git a/VERSION b/VERSION index 1f3171c7..ef8b0923 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.76.0 \ No newline at end of file +1.77.0 \ No newline at end of file diff --git a/testw.cpp b/testw.cpp index c5bbcb46..e0101995 100644 --- a/testw.cpp +++ b/testw.cpp @@ -981,6 +981,18 @@ TEST_CASE_METHOD(TestFixture, "Many threads ecdsa dkg v2 bls", "[many-threads-cr } } +TEST_CASE_METHOD(TestFixture, "Many threads ecdsa dkg v2 bls zmq", "[many-threads-crypto-v2-zmq]") { + vector threads; + int num_threads = 4; + for (int i = 0; i < num_threads; i++) { + threads.push_back(thread(TestUtils::sendRPCRequestZMQ)); + } + + for (auto &thread : threads) { + thread.join(); + } +} + TEST_CASE_METHOD(TestFixture, "First run", "[first-run]") { HttpClient client(RPC_ENDPOINT); diff --git a/testw.py b/testw.py index 9e197477..15d49891 100755 --- a/testw.py +++ b/testw.py @@ -33,7 +33,7 @@ "[second-run]", "[many-threads-crypto]", "[many-threads-crypto-v2]", - # "[many-threads-crypto-v2-zmq]" + "[many-threads-crypto-v2-zmq]" "[backup-restore]", "[cert-sign]", "[get-server-status]", diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 67f5b64b..6497ca04 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -403,7 +403,7 @@ bool ZMQClient::dkgVerification(const string& publicShares, const string& ethKey return result->isCorrect(); } -void ZMQClient::createBLSPrivateKey(const string& blsKeyName, const string& ethKeyName, const string& polyName, +bool ZMQClient::createBLSPrivateKey(const string& blsKeyName, const string& ethKeyName, const string& polyName, const string& secretShare, int t, int n) { Json::Value p; p["type"] = ZMQMessage::CREATE_BLS_PRIVATE_REQ; @@ -415,7 +415,7 @@ void ZMQClient::createBLSPrivateKey(const string& blsKeyName, const string& ethK p["n"] = n; auto result = dynamic_pointer_cast(doRequestReply(p)); CHECK_STATE(result); - CHECK_STATE(result->getStatus() == 0); + return result->getStatus() == 0; } Json::Value ZMQClient::getBLSPublicKey(const string& blsKeyName) { @@ -431,7 +431,7 @@ Json::Value ZMQClient::getBLSPublicKey(const string& blsKeyName) { Json::Value ZMQClient::getAllBlsPublicKeys(const Json::Value& publicShares, int n, int t) { Json::Value p; p["type"] = ZMQMessage::GET_ALL_BLS_PUBLIC_REQ; - p["publicShares"] = publicShares; + p["publicShares"] = publicShares["publicShares"]; p["t"] = t; p["n"] = n; auto result = dynamic_pointer_cast(doRequestReply(p)); diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index e782a9ca..46c946f5 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -104,7 +104,7 @@ class ZMQClient { bool dkgVerification(const string& publicShares, const string& ethKeyName, const string& secretShare, int t, int n, int idx); - void createBLSPrivateKey(const string& blsKeyName, const string& ethKeyName, const string& polyName, + bool createBLSPrivateKey(const string& blsKeyName, const string& ethKeyName, const string& polyName, const string& secretShare, int t, int n); Json::Value getBLSPublicKey(const string& blsKeyName); diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index d97b4c61..def2cb48 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -46,7 +46,6 @@ Json::Value ZMQMessage::getJsonValueRapid(const char *_name) { CHECK_STATE(_name); CHECK_STATE(d->HasMember(_name)); const rapidjson::Value &a = (*d)[_name]; - CHECK_STATE(a.IsArray()); rapidjson::StringBuffer buffer; rapidjson::Writer< rapidjson::StringBuffer > writer(buffer); From 6f45b5060ee143eb27788871d443932b651717e3 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 10 Jun 2021 12:44:40 +0300 Subject: [PATCH 021/100] SKALE-3951 add more tests --- testw.cpp | 150 +++++++++++++++++++++++++++++++++++++++ testw.py | 3 +- zmq_src/RspMessage.h | 4 ++ zmq_src/ServerWorker.cpp | 3 +- zmq_src/ZMQClient.cpp | 5 +- zmq_src/ZMQClient.h | 2 +- zmq_src/ZMQServer.cpp | 7 -- 7 files changed, 161 insertions(+), 13 deletions(-) diff --git a/testw.cpp b/testw.cpp index e0101995..3875cb9e 100644 --- a/testw.cpp +++ b/testw.cpp @@ -946,6 +946,156 @@ TEST_CASE_METHOD(TestFixture, "AES_DKG V2 test", "[aes-dkg-v2]") { REQUIRE(common_public.VerifySigWithHelper(hash_arr, commonSig, t, n)); } +TEST_CASE_METHOD(TestFixture, "AES_DKG V2 ZMQ test", "[aes-dkg-v2-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + + int n = 2, t = 2; + vector ethKeys(n); + Json::Value verifVects[n]; + Json::Value pubEthKeys; + vector secretShares(n); + Json::Value pubBLSKeys[n]; + vector blsSigShares(n); + vector pubShares(n); + vector polyNames(n); + + int schainID = TestUtils::randGen(); + int dkgID = TestUtils::randGen(); + for (uint8_t i = 0; i < n; i++) { + auto generatedKey = client->generateECDSAKey(); + ethKeys[i] = generatedKey.second; + string polyName = + "POLY:SCHAIN_ID:" + to_string(schainID) + ":NODE_ID:" + to_string(i) + ":DKG_ID:" + to_string(dkgID); + CHECK_STATE(client->generateDKGPoly(polyName, t)); + polyNames[i] = polyName; + verifVects[i] = client->getVerificationVector(polyName, t); + + pubEthKeys.append(generatedKey.first); + } + + for (uint8_t i = 0; i < n; i++) { + secretShares[i] = client->getSecretShare(polyNames[i], pubEthKeys, t, n); + for (uint8_t k = 0; k < t; k++) { + for (uint8_t j = 0; j < 4; j++) { + string pubShare = verifVects[i][k][j].asString(); + pubShares[i] += TestUtils::convertDecToHex(pubShare); + } + } + } + + int k = 0; + vector secShares(n); + + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) { + string secretShare = secretShares[i].substr(192 * j, 192); + secShares[i] += secretShares[j].substr(192 * i, 192); + REQUIRE(client->dkgVerification(pubShares[i], ethKeys[j], secretShare, t, n, j)); + k++; + } + + auto complaintResponse = client->complaintResponse(polyNames[1], t, n, 0); + + string dhKey = std::get<0>(complaintResponse); + string shareG2 = std::get<1>(complaintResponse); + string secretShare = secretShares[1].substr(0, 192); + + vector message(65, 0); + + SAFE_CHAR_BUF(encr_sshare, BUF_LEN) + strncpy(encr_sshare, pubEthKeys[0].asString().c_str(), 128); + + SAFE_CHAR_BUF(common_key, BUF_LEN); + REQUIRE(sessionKeyRecoverDH(dhKey.c_str(), encr_sshare, common_key) == 0); + + uint8_t key_to_hash[33]; + uint64_t len; + REQUIRE( hex2carray(common_key, &len, key_to_hash, 64) ); + + auto hashed_key = cryptlite::sha256::hash_hex(string((char*)key_to_hash, 32)); + + SAFE_CHAR_BUF(derived_key, 33) + + uint64_t key_length; + REQUIRE(hex2carray(&hashed_key[0], &key_length, (uint8_t *) derived_key, 33)); + + SAFE_CHAR_BUF(encr_sshare_check, BUF_LEN) + strncpy(encr_sshare_check, secretShare.c_str(), ECDSA_SKEY_LEN - 1); + + REQUIRE(xorDecryptDHV2(derived_key, encr_sshare_check, message) == 0); + + mpz_t hex_share; + mpz_init(hex_share); + mpz_set_str(hex_share, message.data(), 16); + + libff::alt_bn128_Fr share(hex_share); + libff::alt_bn128_G2 decrypted_share_G2 = share * libff::alt_bn128_G2::one(); + decrypted_share_G2.to_affine_coordinates(); + + mpz_clear(hex_share); + + REQUIRE(convertG2ToString(decrypted_share_G2) == shareG2); + + Json::Value verificationVectorMult = std::get<2>(complaintResponse); + + libff::alt_bn128_G2 verificationValue = libff::alt_bn128_G2::zero(); + for (int i = 0; i < t; ++i) { + libff::alt_bn128_G2 value; + value.Z = libff::alt_bn128_Fq2::one(); + value.X.c0 = libff::alt_bn128_Fq(verificationVectorMult[i][0].asCString()); + value.X.c1 = libff::alt_bn128_Fq(verificationVectorMult[i][1].asCString()); + value.Y.c0 = libff::alt_bn128_Fq(verificationVectorMult[i][2].asCString()); + value.Y.c1 = libff::alt_bn128_Fq(verificationVectorMult[i][3].asCString()); + verificationValue = verificationValue + value; + } + verificationValue.to_affine_coordinates(); + REQUIRE(verificationValue == decrypted_share_G2); + + BLSSigShareSet sigShareSet(t, n); + + string hash = SAMPLE_HASH; + + auto hash_arr = make_shared < array < uint8_t, 32 > > (); + + uint64_t binLen; + + if (!hex2carray(hash.c_str(), &binLen, hash_arr->data(), 32)) { + throw SGXException(TEST_INVALID_HEX, "Invalid hash"); + } + + map > coeffs_pkeys_map; + + for (int i = 0; i < t; i++) { + string blsName = "BLS_KEY" + polyNames[i].substr(4); + REQUIRE(client->createBLSPrivateKey(blsName, ethKeys[i], polyNames[i], secShares[i], t, n)); + + pubBLSKeys[i] = client->getBLSPublicKey(blsName); + + string hash = SAMPLE_HASH; + blsSigShares[i] = client->blsSignMessageHash(blsName, hash, t, n); + REQUIRE(blsSigShares[i].length() > 0); + + shared_ptr sig_share_ptr = make_shared(blsSigShares[i]); + BLSSigShare sig(sig_share_ptr, i + 1, t, n); + sigShareSet.addSigShare(make_shared(sig)); + + vector pubKey_vect; + for (uint8_t j = 0; j < 4; j++) { + pubKey_vect.push_back(pubBLSKeys[i][j].asString()); + } + BLSPublicKeyShare pubKey(make_shared < vector < string >> (pubKey_vect), t, n); + REQUIRE(pubKey.VerifySigWithHelper(hash_arr, make_shared(sig), t, n)); + + coeffs_pkeys_map[i + 1] = make_shared(pubKey); + } + + shared_ptr commonSig = sigShareSet.merge(); + BLSPublicKey + common_public(make_shared < map < size_t, shared_ptr < BLSPublicKeyShare >>>(coeffs_pkeys_map), t, n); + REQUIRE(common_public.VerifySigWithHelper(hash_arr, commonSig, t, n)); +} + TEST_CASE_METHOD(TestFixture, "AES encrypt/decrypt", "[aes-encrypt-decrypt]") { int errStatus = 0; vector errMsg(BUF_LEN, 0); diff --git a/testw.py b/testw.py index 15d49891..3c9b4d11 100755 --- a/testw.py +++ b/testw.py @@ -62,7 +62,8 @@ "[dkg-poly-exists-zmq]", "[dkg-aes-pub-shares]", "[aes-encrypt-decrypt]", - "[aes-dkg-v2]" + "[aes-dkg-v2]", + "[aes-dkg-v2-zmq]" ] diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h index 090c0669..95ee95e8 100644 --- a/zmq_src/RspMessage.h +++ b/zmq_src/RspMessage.h @@ -185,6 +185,10 @@ class complaintResponseRspMessage : public ZMQMessage { string getShare() { return getStringRapid("share*G2"); } + + Json::Value getVerificationVectorMult() { + return getJsonValueRapid("verificationVectorMult"); + } }; diff --git a/zmq_src/ServerWorker.cpp b/zmq_src/ServerWorker.cpp index 8d15dcaa..ba47b01b 100644 --- a/zmq_src/ServerWorker.cpp +++ b/zmq_src/ServerWorker.cpp @@ -39,8 +39,7 @@ std::atomic ServerWorker::workerCount(1); ServerWorker::ServerWorker(zmq::context_t &_ctx, int sock_type, bool _checkSignature, const string& _caCert ) : checkSignature(_checkSignature), - caCert(_caCert), - isExitRequested(false) { + caCert(_caCert), isExitRequested(false) { worker = make_shared(_ctx, sock_type); diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 6497ca04..112ad744 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -440,16 +440,17 @@ Json::Value ZMQClient::getAllBlsPublicKeys(const Json::Value& publicShares, int return result->getPublicKeys(); } -pair ZMQClient::complaintResponse(const string& polyName, int t, int n, int idx) { +tuple ZMQClient::complaintResponse(const string& polyName, int t, int n, int idx) { Json::Value p; p["type"] = ZMQMessage::COMPLAINT_RESPONSE_REQ; p["polyName"] = polyName; p["t"] = t; p["n"] = n; + p["ind"] = idx; auto result = dynamic_pointer_cast(doRequestReply(p)); CHECK_STATE(result); CHECK_STATE(result->getStatus() == 0); - return {result->getDHKey(), result->getShare()}; + return {result->getDHKey(), result->getShare(), result->getVerificationVectorMult()}; } Json::Value ZMQClient::multG2(const string& x) { diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 46c946f5..8901be10 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -111,7 +111,7 @@ class ZMQClient { Json::Value getAllBlsPublicKeys(const Json::Value& publicShares, int n, int t); - pair complaintResponse(const string& polyName, int t, int n, int idx); + tuple complaintResponse(const string& polyName, int t, int n, int idx); Json::Value multG2(const string& x); diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 543acc53..088f1d0f 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -41,14 +41,11 @@ ZMQServer::ZMQServer(bool _checkSignature, const string &_caCertFile) : checkSignature(_checkSignature), caCertFile(_caCertFile), ctx_(make_shared(1)) { - frontend = make_shared(*ctx_, ZMQ_ROUTER); backend = make_shared(*ctx_, ZMQ_DEALER); //workerThreads = 2 * thread::hardware_concurrency(); - - workerThreads = 1; // do one thread for now if (_checkSignature) { @@ -61,13 +58,11 @@ ZMQServer::ZMQServer(bool _checkSignature, const string &_caCertFile) int linger = 0; - zmq_setsockopt(*frontend, ZMQ_LINGER, &linger, sizeof(linger)); zmq_setsockopt(*backend, ZMQ_LINGER, &linger, sizeof(linger)); } - void ZMQServer::run() { auto port = BASE_PORT + 5; @@ -92,7 +87,6 @@ void ZMQServer::run() { throw SGXException(ZMQ_COULD_NOT_BIND_BACK_END, "Could not bind to zmq backend."); } - spdlog::info("Creating {} zmq server workers ...", workerThreads); try { @@ -131,7 +125,6 @@ void ZMQServer::run() { } } - void ZMQServer::exitAll() { spdlog::info("Exiting zmq server workers ..."); From b128bca42d16b7ab1d8a1ae487579d275a3b68e1 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 16 Jun 2021 15:30:59 +0300 Subject: [PATCH 022/100] SKALE-3951 cleanup --- sgxwall.cpp | 4 ---- testw.py | 1 - zmq_src/ZMQServer.cpp | 4 ---- 3 files changed, 9 deletions(-) diff --git a/sgxwall.cpp b/sgxwall.cpp index 798da45b..a02f6e0e 100644 --- a/sgxwall.cpp +++ b/sgxwall.cpp @@ -182,7 +182,6 @@ int main(int argc, char *argv[]) { initAll(enclaveLogLevel, checkClientCertOption, checkClientCertOption, autoSignClientCertOption, generateTestKeys); cerr << "Completed initAll." << endl; - //check if test keys already exist string TEST_KEYS_4_NODE = "sgx_data/4node.json"; @@ -194,7 +193,6 @@ int main(int argc, char *argv[]) { cerr << "Found test keys." << endl; } - if (generateTestKeys && !keysExist && !ExitHandler::shouldExit()) { cerr << "Generating test keys ..." << endl; @@ -221,8 +219,6 @@ int main(int argc, char *argv[]) { cerr << "Successfully completed generating test keys into sgx_data" << endl; } - - while ( !ExitHandler::shouldExit() ) { sleep(10); } diff --git a/testw.py b/testw.py index 3c9b4d11..e8bce540 100755 --- a/testw.py +++ b/testw.py @@ -31,7 +31,6 @@ testList = [ "[zmq-ecdsa]", "[first-run]", "[second-run]", - "[many-threads-crypto]", "[many-threads-crypto-v2]", "[many-threads-crypto-v2-zmq]" "[backup-restore]", diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 0735de32..7767b1fd 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -84,7 +84,6 @@ void ZMQServer::run() { spdlog::info("Exited zmq server loop"); } - std::atomic ZMQServer::isExitRequested(false); void ZMQServer::exitZMQServer() { @@ -95,7 +94,6 @@ void ZMQServer::exitZMQServer() { spdlog::info("Exited zmq server."); } - void ZMQServer::initZMQServer(bool _checkSignature) { static bool initedServer = false; CHECK_STATE(!initedServer) @@ -134,10 +132,8 @@ void ZMQServer::doOneServerLoop() { result["errorMessage"] = ""; zmq::message_t identity; - zmq::message_t identit2; zmq::message_t copied_id; - string stringToParse = ""; try { From f2d4b3c0eb2750445d75b19d985a23f3f29abe95 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 16 Jun 2021 15:44:50 +0300 Subject: [PATCH 023/100] SKALE-3951 cleanup --- SGXWalletServer.cpp | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index e4b452dc..7a0a28f9 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -49,6 +49,12 @@ #include "Log.h" +#ifdef SGX_HW_SIM +#define NUM_THREADS 16 +#else +#define NUM_THREADS 200 +#endif + using namespace std; std::shared_timed_mutex sgxInitMutex; @@ -111,20 +117,12 @@ void SGXWalletServer::printDB() { LevelDB::getLevelDb()->visitKeys(&v, 100000000); } - -#ifdef SGX_HW_SIM -#define NUM_THREADS 16 -#else -#define NUM_THREADS 200 -#endif - bool SGXWalletServer::verifyCert(string &_certFileName) { string rootCAPath = string(SGXDATA_FOLDER) + "cert_data/rootCA.pem"; string verifyCert = "cert/verify_client_cert " + rootCAPath + " " + _certFileName; return system(verifyCert.c_str()) == 0; } - void SGXWalletServer::createCertsIfNeeded() { string rootCAPath = string(SGXDATA_FOLDER) + "cert_data/rootCA.pem"; @@ -170,7 +168,6 @@ void SGXWalletServer::createCertsIfNeeded() { } } - void SGXWalletServer::initHttpsServer(bool _checkCerts) { COUNT_STATISTICS spdlog::info("Entering {}", __FUNCTION__); @@ -214,15 +211,15 @@ void SGXWalletServer::initHttpServer() { //without ssl } int SGXWalletServer::exitServer() { - spdlog::info("Stoping sgx server"); + spdlog::info("Stoping sgx server"); - if (server && !server->StopListening()) { - spdlog::error("Sgx server could not be stopped. Will forcefully terminate the app"); - } else { - spdlog::info("Sgx server stopped"); - } + if (server && !server->StopListening()) { + spdlog::error("Sgx server could not be stopped. Will forcefully terminate the app"); + } else { + spdlog::info("Sgx server stopped"); + } - return 0; + return 0; } Json::Value @@ -269,7 +266,6 @@ SGXWalletServer::importBLSKeyShareImpl(const string &_keyShare, const string &_k RETURN_SUCCESS(result); } - map SGXWalletServer::blsRequests; recursive_mutex SGXWalletServer::blsRequestsLock; @@ -288,7 +284,6 @@ void SGXWalletServer::checkForDuplicate(map &_map, recursive_mu _map[_key] = _value; } - Json::Value SGXWalletServer::blsSignMessageHashImpl(const string &_keyShareName, const string &_messageHash, int t, int n) { spdlog::trace("Entering {}", __FUNCTION__); @@ -305,10 +300,8 @@ SGXWalletServer::blsSignMessageHashImpl(const string &_keyShareName, const strin shared_ptr value = nullptr; - checkForDuplicate(blsRequests, blsRequestsLock, _keyShareName, _messageHash); - try { if (!checkName(_keyShareName, "BLS_KEY")) { throw SGXException(BLS_SIGN_INVALID_KS_NAME, string(__FUNCTION__) + ":Invalid BLSKey name"); @@ -342,9 +335,7 @@ SGXWalletServer::blsSignMessageHashImpl(const string &_keyShareName, const strin result["signatureShare"] = string(signature.data()); - RETURN_SUCCESS(result); - } Json::Value SGXWalletServer::importECDSAKeyImpl(const string &_keyShare, @@ -648,7 +639,6 @@ SGXWalletServer::createBLSPrivateKeyImpl(const string &_blsKeyName, const string string(__FUNCTION__) + ":Error while creating BLS key share"); } - for (int i = 0; i < _n; i++) { string name = _polyName + "_" + to_string(i) + ":"; LevelDB::getLevelDb()->deleteDHDKGKey(name); @@ -978,7 +968,6 @@ SGXWalletServer::createBLSPrivateKeyV2Impl(const string &_blsKeyName, const stri string(__FUNCTION__) + ":Error while creating BLS key share"); } - for (int i = 0; i < _n; i++) { string name = _polyName + "_" + to_string(i) + ":"; LevelDB::getLevelDb()->deleteDHDKGKey(name); @@ -987,7 +976,6 @@ SGXWalletServer::createBLSPrivateKeyV2Impl(const string &_blsKeyName, const stri } LevelDB::getLevelDb()->deleteKey(_polyName); - string encryptedSecretShareName = "encryptedSecretShare:" + _polyName; LevelDB::getLevelDb()->deleteKey(encryptedSecretShareName); From 34f8be85fd9ae7a2f722bafb6128753e7291f372 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 17 Jun 2021 15:45:10 +0300 Subject: [PATCH 024/100] SKALE-3951 debug --- zmq_src/ZMQClient.cpp | 11 +++++------ zmq_src/ZMQMessage.cpp | 2 +- zmq_src/ZMQServer.cpp | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 112ad744..93ae3093 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -134,6 +134,9 @@ void ZMQClient::verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _ auto msgToSign = std::regex_replace(_str, r, ""); vector binSig(256,0); + std::cout << "VERIFYING SIG: " << _str << '\n' << msgToSign << std::endl; + + // std::cout << signString(pkey, _str) << std::endl; uint64_t binLen = 0; @@ -153,9 +156,6 @@ void ZMQClient::verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _ CHECK_STATE(EVP_DigestVerifyUpdate(mdctx, msgToSign.c_str(), msgToSign.size()) == 1); -/* First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the - * signature. Length is returned in slen */ - CHECK_STATE2(EVP_DigestVerifyFinal(mdctx, binSig.data(), binLen) == 1, ZMQ_COULD_NOT_VERIFY_SIG); @@ -260,7 +260,6 @@ ZMQClient::ZMQClient(const string &ip, uint16_t port, bool _sign, const string & certFileName = _certFileName; certKeyName = _certKeyName; - url = "tcp://" + ip + ":" + to_string(port); } @@ -273,13 +272,13 @@ void ZMQClient::reconnect() { clientSockets.erase( pid ); } - uint64_t randNumber; + uint64_t randNumber; CHECK_STATE(getrandom( &randNumber, sizeof(uint64_t), 0 ) == sizeof(uint64_t)); string identity = to_string(135) + ":" + to_string(randNumber); auto clientSocket = make_shared< zmq::socket_t >( ctx, ZMQ_DEALER ); - clientSocket->setsockopt( ZMQ_IDENTITY, identity.c_str(), identity.size() + 1); + clientSocket->setsockopt( ZMQ_IDENTITY, identity.c_str(), identity.size() + 1); // Configure socket to not wait at close time int linger = 0; clientSocket->setsockopt( ZMQ_LINGER, &linger, sizeof( linger ) ); diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index def2cb48..436e54a7 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -145,7 +145,7 @@ shared_ptr ZMQMessage::parse(const char *_msg, auto msgToVerify = buffer.GetString(); - ZMQClient::verifySig(publicKey,msgToVerify, *msgSig ); + ZMQClient::verifySig(publicKey, msgToVerify, *msgSig ); } } diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 7767b1fd..dd62edfd 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -174,6 +174,7 @@ void ZMQServer::doOneServerLoop() { } stringToParse = string((char *) reqMsg.data(), reqMsg.size()); + std::cout << "RECEIVED MSG: " << stringToParse << std::endl; CHECK_STATE(stringToParse.front() == '{') CHECK_STATE(stringToParse.back() == '}') From 454e457219a16d192c73b2bce3058cee023e1a6f Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 22 Jun 2021 15:22:03 +0300 Subject: [PATCH 025/100] SKALE-3951 remove debug --- zmq_src/ZMQClient.cpp | 3 --- zmq_src/ZMQMessage.cpp | 1 - zmq_src/ZMQServer.cpp | 1 - 3 files changed, 5 deletions(-) diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 93ae3093..172f9964 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -134,9 +134,6 @@ void ZMQClient::verifySig(EVP_PKEY* _pubkey, const string& _str, const string& _ auto msgToSign = std::regex_replace(_str, r, ""); vector binSig(256,0); - std::cout << "VERIFYING SIG: " << _str << '\n' << msgToSign << std::endl; - - // std::cout << signString(pkey, _str) << std::endl; uint64_t binLen = 0; diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 436e54a7..70eb1d99 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -33,7 +33,6 @@ #include "RspMessage.h" #include "ZMQMessage.h" - uint64_t ZMQMessage::getInt64Rapid(const char *_name) { CHECK_STATE(_name); CHECK_STATE(d->HasMember(_name)); diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index dd62edfd..7767b1fd 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -174,7 +174,6 @@ void ZMQServer::doOneServerLoop() { } stringToParse = string((char *) reqMsg.data(), reqMsg.size()); - std::cout << "RECEIVED MSG: " << stringToParse << std::endl; CHECK_STATE(stringToParse.front() == '{') CHECK_STATE(stringToParse.back() == '}') From 1d331742c10c848126bd7f6998f2deeaf53dbd72 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 22 Jun 2021 18:13:45 +0300 Subject: [PATCH 026/100] SKALE-3951 update VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ef8b0923..3c4c7c2d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.77.0 \ No newline at end of file +1.78.0 \ No newline at end of file From b8801ae80df08def6a0819cac714401b2066ade5 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 22 Jun 2021 18:14:08 +0300 Subject: [PATCH 027/100] SKALE-3951 cleanup --- SGXRegistrationServer.cpp | 3 --- SGXRegistrationServer.h | 4 ---- ServerInit.cpp | 1 - 3 files changed, 8 deletions(-) diff --git a/SGXRegistrationServer.cpp b/SGXRegistrationServer.cpp index a040db89..47287f76 100644 --- a/SGXRegistrationServer.cpp +++ b/SGXRegistrationServer.cpp @@ -150,7 +150,6 @@ Json::Value getCertificateImpl(const string &hash) { RETURN_SUCCESS(result) } - Json::Value SGXRegistrationServer::SignCertificate(const string &csr) { spdlog::info(__FUNCTION__); return signCertificateImpl(csr, autoSign); @@ -161,7 +160,6 @@ Json::Value SGXRegistrationServer::GetCertificate(const string &hash) { return getCertificateImpl(hash); } - void SGXRegistrationServer::initRegistrationServer(bool _autoSign) { httpServer = make_shared(BASE_PORT + 1); server = make_shared(*httpServer, @@ -190,7 +188,6 @@ int SGXRegistrationServer::exitServer() { return 0; } - shared_ptr SGXRegistrationServer::getServer() { CHECK_STATE(server); return server; diff --git a/SGXRegistrationServer.h b/SGXRegistrationServer.h index f9d9594c..aa199470 100644 --- a/SGXRegistrationServer.h +++ b/SGXRegistrationServer.h @@ -42,20 +42,16 @@ class SGXRegistrationServer : public AbstractRegServer { recursive_mutex m; bool autoSign; - static shared_ptr httpServer; static shared_ptr server; - public: static shared_ptr getServer(); - SGXRegistrationServer(AbstractServerConnector &connector, serverVersion_t type, bool _autoSign = false); - virtual Json::Value SignCertificate(const string &csr); virtual Json::Value GetCertificate(const string &hash); diff --git a/ServerInit.cpp b/ServerInit.cpp index 3892fde7..c118a099 100644 --- a/ServerInit.cpp +++ b/ServerInit.cpp @@ -162,7 +162,6 @@ uint64_t initEnclave() { void initAll(uint32_t _logLevel, bool _checkCert, bool _checkZMQSig, bool _autoSign, bool _generateTestKeys) { - static atomic sgxServerInited(false); static mutex initMutex; enclaveLogLevel = _logLevel; From eec2b6a5a3fa1bf600c9b5fda58f0906a4dd4e2d Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 22 Jun 2021 18:58:50 +0300 Subject: [PATCH 028/100] SKALE-2941 -y flag --- SEKManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SEKManager.cpp b/SEKManager.cpp index ee7e8bff..c0a20aab 100644 --- a/SEKManager.cpp +++ b/SEKManager.cpp @@ -181,12 +181,12 @@ void gen_SEK() { if (!autoconfirm) { - sleep(10); string confirm_str = "I confirm"; string buffer; do { cout << " DO YOU CONFIRM THAT YOU COPIED THE KEY? (if you confirm type - I confirm)" << endl; + sleep(10); getline(cin, buffer); } while (case_insensitive_match(confirm_str, buffer)); } From a1796198dd2f39f0a7b025aaee1f34a32fb7c5d4 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 23 Jun 2021 14:17:19 +0300 Subject: [PATCH 029/100] SKALE-2941 -s flag --- SEKManager.cpp | 10 ---------- SGXRegistrationServer.cpp | 12 ++++-------- SGXRegistrationServer.h | 4 ---- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/SEKManager.cpp b/SEKManager.cpp index c0a20aab..6ca6d46d 100644 --- a/SEKManager.cpp +++ b/SEKManager.cpp @@ -28,7 +28,6 @@ #include "third_party/spdlog/spdlog.h" - #include "sgxwallet_common.h" #include "common.h" #include "sgxwallet.h" @@ -76,7 +75,6 @@ void create_test_key() { LevelDB::getLevelDb()->writeDataUnique("TEST_KEY", hexEncrKey.data()); } - void validate_SEK() { shared_ptr test_key_ptr = LevelDB::getLevelDb()->readString("TEST_KEY"); @@ -112,7 +110,6 @@ void validate_SEK() { } } - shared_ptr > check_and_set_SEK(const string &SEK) { vector decr_key(BUF_LEN, 0); @@ -131,7 +128,6 @@ shared_ptr > check_and_set_SEK(const string &SEK) { SEK.c_str()); } - HANDLE_TRUSTED_FUNCTION_ERROR(status, err_status, errMsg.data()); encrypted_SEK->resize(l); @@ -151,7 +147,6 @@ void gen_SEK() { spdlog::info("Generating backup key. Will be stored in backup_key.txt ... "); - sgx_status_t status = SGX_SUCCESS; { @@ -160,7 +155,6 @@ void gen_SEK() { HANDLE_TRUSTED_FUNCTION_ERROR(status, err_status, errMsg.data()); - if (strnlen(SEK, 33) != 32) { throw SGXException(-1, "strnlen(SEK,33) != 32"); } @@ -174,12 +168,10 @@ void gen_SEK() { sek_file << SEK; - cout << "ATTENTION! YOUR BACKUP KEY HAS BEEN WRITTEN INTO sgx_data/backup_key.txt \n" << "PLEASE COPY IT TO THE SAFE PLACE AND THEN DELETE THE FILE MANUALLY BY RUNNING THE FOLLOWING COMMAND:\n" << "apt-get install secure-delete && srm -vz sgx_data/backup_key.txt" << endl; - if (!autoconfirm) { string confirm_str = "I confirm"; string buffer; @@ -191,7 +183,6 @@ void gen_SEK() { } while (case_insensitive_match(confirm_str, buffer)); } - LevelDB::getLevelDb()->writeDataUnique("SEK", hexEncrKey.data()); create_test_key(); @@ -242,7 +233,6 @@ void enter_SEK() { throw SGXException(CORRUPT_DATABASE, "Could not find TEST_KEY in database."); } - if (!experimental::filesystem::is_regular_file(BACKUP_PATH)) { spdlog::error("File does not exist: " BACKUP_PATH); throw SGXException(FILE_NOT_FOUND, "File does not exist: " BACKUP_PATH); diff --git a/SGXRegistrationServer.cpp b/SGXRegistrationServer.cpp index a040db89..af6d2bea 100644 --- a/SGXRegistrationServer.cpp +++ b/SGXRegistrationServer.cpp @@ -90,22 +90,18 @@ Json::Value signCertificateImpl(const string &_csr, bool _autoSign = false) { } if (_autoSign) { - string genCert = string("cd ") + CERT_DIR + "&& ./" - + CERT_CREATE_COMMAND + " " + hash ; + string genCert = string("cd ") + CERT_DIR + "&& ./" + CERT_CREATE_COMMAND + " " + hash ; if (system(genCert.c_str()) == 0) { spdlog::info("Client cert " + hash + " generated"); - string db_key = "CSR:HASH:" + hash + "STATUS:"; - string status = "0"; - LevelDB::getCsrStatusDb()->writeDataUnique(db_key, status); } else { spdlog::error("Client cert generation failed: {} ", genCert); throw SGXException(FAIL_TO_CREATE_CERTIFICATE, "CLIENT CERTIFICATE GENERATION FAILED"); } - } else { - string db_key = "CSR:HASH:" + hash; - LevelDB::getCsrStatusDb()->writeDataUnique(db_key, _csr); } + string db_key = "CSR:HASH:" + hash + "STATUS:"; + string status = "0"; + LevelDB::getCsrStatusDb()->writeDataUnique(db_key, status); result["result"] = true; result["hash"] = hash; diff --git a/SGXRegistrationServer.h b/SGXRegistrationServer.h index f9d9594c..aa199470 100644 --- a/SGXRegistrationServer.h +++ b/SGXRegistrationServer.h @@ -42,20 +42,16 @@ class SGXRegistrationServer : public AbstractRegServer { recursive_mutex m; bool autoSign; - static shared_ptr httpServer; static shared_ptr server; - public: static shared_ptr getServer(); - SGXRegistrationServer(AbstractServerConnector &connector, serverVersion_t type, bool _autoSign = false); - virtual Json::Value SignCertificate(const string &csr); virtual Json::Value GetCertificate(const string &hash); From b034e149a57a09c959a5087904bf92837f4e170c Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 24 Jun 2021 17:37:32 +0300 Subject: [PATCH 030/100] cleanup --- scripts/build_deps.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/scripts/build_deps.py b/scripts/build_deps.py index 2df8b310..2e204583 100755 --- a/scripts/build_deps.py +++ b/scripts/build_deps.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with sgxwallet. If not, see . # -# @file build_deps.py +# @file build_deps.py # @author Stan Kladko # @date 2018 # @@ -36,8 +36,6 @@ SGX_SDK_DIR_SSL = topDir + "/sgx-sdk-build/sgxsdk" ZMQ_DIR = topDir + "/libzmq" ZMQ_BUILD_DIR = ZMQ_DIR + "/build" -CZMQ_DIR = topDir + "/cppzmq" -CZMQ_BUILD_DIR = CZMQ_DIR + "/build" LEVELDB_DIR = topDir + "/leveldb" LEVELDB_BUILD_DIR = LEVELDB_DIR + "/build" @@ -52,27 +50,20 @@ print("Cleaning") -subprocess.call(["rm", "-f", "install-sh"]) -subprocess.call(["rm", "-f", "compile"]) -subprocess.call(["rm", "-f", "missing"]) -subprocess.call(["rm", "-f", "depcomp"]) -subprocess.call(["rm", "-rf", GMP_BUILD_DIR]) +subprocess.call(["rm", "-f", "install-sh"]) +subprocess.call(["rm", "-f", "compile"]) +subprocess.call(["rm", "-f", "missing"]) +subprocess.call(["rm", "-f", "depcomp"]) +subprocess.call(["rm", "-rf", GMP_BUILD_DIR]) subprocess.call(["rm", "-rf", TGMP_BUILD_DIR]) subprocess.call(["rm", "-rf", SDK_DIR]) -subprocess.call(["rm", "-rf", GMP_BUILD_DIR]) +subprocess.call(["rm", "-rf", GMP_BUILD_DIR]) subprocess.call(["rm", "-rf", TGMP_BUILD_DIR]) subprocess.call(["rm", "-rf", SDK_DIR]) - - - - - - assert subprocess.call(["cp", "configure.gmp", GMP_DIR + "/configure"]) == 0 - print("Build LibBLS"); os.chdir(BLS_DIR + "/deps") assert subprocess.call(["bash", "-c", "./build.sh"]) == 0 @@ -81,7 +72,6 @@ os.chdir(BLS_DIR + "/build") assert subprocess.call(["bash", "-c", "make"]) == 0 - print("Build ZMQ"); os.chdir(ZMQ_DIR) @@ -89,7 +79,6 @@ os.chdir(ZMQ_BUILD_DIR) assert subprocess.call(["bash", "-c", "cmake -DDZMQ_EXPERIMENTAL=1 -DCMAKE_BUILD_TYPE=Release .. && cmake --build ."]) == 0 - print("Build LevelDB"); os.chdir(LEVELDB_DIR) From e8c852a654c37551d010f1d772a898273f67b90e Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 7 Jul 2021 13:06:49 +0300 Subject: [PATCH 031/100] SKALE-4091 fix multithreading issues for generating certs --- SGXRegistrationServer.cpp | 30 +++++++++++------------------- SGXRegistrationServer.h | 6 +----- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/SGXRegistrationServer.cpp b/SGXRegistrationServer.cpp index a040db89..1ecee81c 100644 --- a/SGXRegistrationServer.cpp +++ b/SGXRegistrationServer.cpp @@ -59,14 +59,16 @@ SGXRegistrationServer::SGXRegistrationServer(AbstractServerConnector &connector, : AbstractRegServer(connector, type), autoSign(_autoSign) {} -Json::Value signCertificateImpl(const string &_csr, bool _autoSign = false) { +Json::Value SGXRegistrationServer::SignCertificate(const string &csr) { spdlog::info(__FUNCTION__); INIT_RESULT(result) result["result"] = false; try { - string hash = cryptlite::sha256::hash_hex(_csr); + std::lock_guard lock(m); + + string hash = cryptlite::sha256::hash_hex(csr); if (system("ls " CERT_DIR "/" CERT_CREATE_COMMAND) != 0) { spdlog::error("cert/create_client_cert does not exist"); @@ -76,7 +78,7 @@ Json::Value signCertificateImpl(const string &_csr, bool _autoSign = false) { string csr_name = string(CERT_DIR) + "/" + hash + ".csr"; ofstream outfile(csr_name); outfile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - outfile << _csr << endl; + outfile << csr << endl; outfile.close(); if (system(("ls " + csr_name).c_str()) != 0) { @@ -85,11 +87,11 @@ Json::Value signCertificateImpl(const string &_csr, bool _autoSign = false) { } if (system(("openssl req -in " + csr_name).c_str()) != 0) { - spdlog::error("Incorrect CSR format: {}", _csr); + spdlog::error("Incorrect CSR format: {}", csr); throw SGXException(FAIL_TO_CREATE_CERTIFICATE, "Incorrect CSR format "); } - if (_autoSign) { + if (autoSign) { string genCert = string("cd ") + CERT_DIR + "&& ./" + CERT_CREATE_COMMAND + " " + hash ; @@ -104,7 +106,7 @@ Json::Value signCertificateImpl(const string &_csr, bool _autoSign = false) { } } else { string db_key = "CSR:HASH:" + hash; - LevelDB::getCsrStatusDb()->writeDataUnique(db_key, _csr); + LevelDB::getCsrStatusDb()->writeDataUnique(db_key, csr); } result["result"] = true; @@ -115,7 +117,9 @@ Json::Value signCertificateImpl(const string &_csr, bool _autoSign = false) { RETURN_SUCCESS(result) } -Json::Value getCertificateImpl(const string &hash) { +Json::Value SGXRegistrationServer::GetCertificate(const string &hash) { + spdlog::info(__FUNCTION__); + Json::Value result; string cert; @@ -151,17 +155,6 @@ Json::Value getCertificateImpl(const string &hash) { } -Json::Value SGXRegistrationServer::SignCertificate(const string &csr) { - spdlog::info(__FUNCTION__); - return signCertificateImpl(csr, autoSign); -} - -Json::Value SGXRegistrationServer::GetCertificate(const string &hash) { - spdlog::info(__FUNCTION__); - return getCertificateImpl(hash); -} - - void SGXRegistrationServer::initRegistrationServer(bool _autoSign) { httpServer = make_shared(BASE_PORT + 1); server = make_shared(*httpServer, @@ -190,7 +183,6 @@ int SGXRegistrationServer::exitServer() { return 0; } - shared_ptr SGXRegistrationServer::getServer() { CHECK_STATE(server); return server; diff --git a/SGXRegistrationServer.h b/SGXRegistrationServer.h index f9d9594c..e39fa9d3 100644 --- a/SGXRegistrationServer.h +++ b/SGXRegistrationServer.h @@ -39,23 +39,19 @@ using namespace jsonrpc; using namespace std; class SGXRegistrationServer : public AbstractRegServer { - recursive_mutex m; + mutex m; bool autoSign; - static shared_ptr httpServer; static shared_ptr server; - public: static shared_ptr getServer(); - SGXRegistrationServer(AbstractServerConnector &connector, serverVersion_t type, bool _autoSign = false); - virtual Json::Value SignCertificate(const string &csr); virtual Json::Value GetCertificate(const string &hash); From fe335d3457f66c191a3ce3718f8c0c06438ebe83 Mon Sep 17 00:00:00 2001 From: Dima Litvinov Date: Thu, 8 Jul 2021 12:07:08 +0300 Subject: [PATCH 032/100] Update install_packages.sh --- scripts/install_packages.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/install_packages.sh b/scripts/install_packages.sh index d7ebef70..7e1e4093 100755 --- a/scripts/install_packages.sh +++ b/scripts/install_packages.sh @@ -1,4 +1,5 @@ #!/bin/bash +sudo apt update sudo apt install -y build-essential make gcc g++ yasm python libprotobuf10 flex bison automake sudo apt install -y ccache cmake ccache autoconf texinfo libgcrypt20-dev libgnutls28-dev libtool pkg-config -sudo apt install -y ocaml ocaml-build \ No newline at end of file +sudo apt install -y ocaml From e46adcf414d9fcb603a13cd13eb2b75dd5bf1e0c Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 8 Jul 2021 16:19:41 +0300 Subject: [PATCH 033/100] SKALE-4262 cleanup --- DockerfileBase | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/DockerfileBase b/DockerfileBase index 942d4005..044a4d58 100644 --- a/DockerfileBase +++ b/DockerfileBase @@ -1,6 +1,6 @@ FROM ubuntu:18.04 -RUN apt-get update && apt-get install -y \ +RUN apt-get update && apt-get install -y \ autoconf \ automake \ build-essential \ @@ -48,19 +48,19 @@ COPY . /usr/src/sdk RUN ls /usr/src/sdk/autoconf.bash WORKDIR /usr/src/sdk -RUN apt update && \ - apt install -yq apt-utils && \ - apt install -yq --no-install-recommends python-yaml vim \ - telnet git ca-certificates perl \ - reprepro libboost-all-dev alien uuid-dev libxml2-dev ccache \ - yasm flex bison libprocps-dev ccache texinfo \ - libjsonrpccpp-dev curl libjsonrpccpp-tools && \ - ln -s /usr/bin/ccache /usr/local/bin/clang && \ - ln -s /usr/bin/ccache /usr/local/bin/clang++ && \ - ln -s /usr/bin/ccache /usr/local/bin/gcc && \ - ln -s /usr/bin/ccache /usr/local/bin/g++ && \ - ln -s /usr/bin/ccache /usr/local/bin/cc && \ - ln -s /usr/bin/ccache /usr/local/bin/c++ +RUN apt update && \ + apt install -yq apt-utils && \ + apt install -yq --no-install-recommends python-yaml vim \ + telnet git ca-certificates perl \ + reprepro libboost-all-dev alien uuid-dev libxml2-dev ccache \ + yasm flex bison libprocps-dev ccache texinfo \ + libjsonrpccpp-dev curl libjsonrpccpp-tools && \ + ln -s /usr/bin/ccache /usr/local/bin/clang && \ + ln -s /usr/bin/ccache /usr/local/bin/clang++ && \ + ln -s /usr/bin/ccache /usr/local/bin/gcc && \ + ln -s /usr/bin/ccache /usr/local/bin/g++ && \ + ln -s /usr/bin/ccache /usr/local/bin/cc && \ + ln -s /usr/bin/ccache /usr/local/bin/c++ RUN cd scripts && ./build_deps.py && \ wget --progress=dot:mega -O - https://github.com/intel/dynamic-application-loader-host-interface/archive/072d233296c15d0dcd1fb4570694d0244729f87b.tar.gz | tar -xz && \ From 348cf00047c7bde8dc50fb8e788991021c541e8b Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 8 Jul 2021 16:48:37 +0300 Subject: [PATCH 034/100] SKALE-4262 cleanup --- secure_enclave/EnclaveCommon.cpp | 1 - secure_enclave/secure_enclave.c | 5 ----- 2 files changed, 6 deletions(-) diff --git a/secure_enclave/EnclaveCommon.cpp b/secure_enclave/EnclaveCommon.cpp index cd3c755b..251d5d11 100644 --- a/secure_enclave/EnclaveCommon.cpp +++ b/secure_enclave/EnclaveCommon.cpp @@ -171,7 +171,6 @@ void enclave_init() { return; inited = 1; - LOG_INFO("Initing libff"); try { diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 17855c9b..3ed32782 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -66,7 +66,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INIT_ERROR_STATE *errString = 0; *errStatus = UNKNOWN_ERROR; #define SET_SUCCESS *errStatus = 0; - #define CHECK_STATE(_EXPRESSION_) \ if (!(_EXPRESSION_)) { \ LOG_ERROR("State check failed::");LOG_ERROR(#_EXPRESSION_); \ @@ -91,7 +90,6 @@ LOG_ERROR(errString); \ goto clean; \ }; - #define CHECK_STATUS2(__ERRMESSAGE__) if (status != SGX_SUCCESS) { \ snprintf(errString, BUF_LEN, __ERRMESSAGE__, status); \ LOG_ERROR(errString); \ @@ -138,10 +136,8 @@ void trustedEnclaveInit(uint64_t _logLevel) { LOG_INFO("Calling enclave init"); - enclave_init(); - LOG_INFO("Reading random"); globalRandom = calloc(32,1); @@ -228,7 +224,6 @@ void get_global_random(unsigned char *_randBuff, uint64_t _size) { memcpy(_randBuff, globalRandom, _size); } - void sealHexSEK(int *errStatus, char *errString, uint8_t *encrypted_sek, uint64_t *enc_len, char *sek_hex) { CALL_ONCE From a8ae2db02cff1fe490ac01821d3ed37c80aced6e Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 8 Jul 2021 19:35:42 +0300 Subject: [PATCH 035/100] SKALE-4262 cleanup --- secure_enclave/AESUtils.c | 8 +------- secure_enclave/AESUtils.h | 3 --- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/secure_enclave/AESUtils.c b/secure_enclave/AESUtils.c index 146ae9ea..b99790be 100644 --- a/secure_enclave/AESUtils.c +++ b/secure_enclave/AESUtils.c @@ -21,25 +21,19 @@ @date 2020 */ - #include "sgx_trts.h" #include "sgx_tcrypto.h" #include "stdlib.h" #include - #include "AESUtils.h" sgx_aes_gcm_128bit_key_t AES_key[1024]; - #define SAFE_CHAR_BUF(__X__, __Y__) ;char __X__ [ __Y__ ]; memset(__X__, 0, __Y__); -int AES_encrypt(char *message, uint8_t *encr_message, uint64_t encrBufLen, unsigned char type, +int AES_encrypt(char *message, uint8_t *encr_message, uint64_t encrBufLen, unsigned char type, unsigned char exportable, uint64_t* resultLen) { - - - if (!type) { LOG_ERROR("Null type in AES_encrypt"); return -1; diff --git a/secure_enclave/AESUtils.h b/secure_enclave/AESUtils.h index d57e5a95..337f2ec1 100644 --- a/secure_enclave/AESUtils.h +++ b/secure_enclave/AESUtils.h @@ -32,7 +32,6 @@ int AES_decrypt(uint8_t *encr_message, uint64_t length, char *message, uint64_t uint8_t *type, uint8_t* exportable) ; - #define ECDSA '1' #define BLS '2' #define DKG '3' @@ -40,6 +39,4 @@ int AES_decrypt(uint8_t *encr_message, uint64_t length, char *message, uint64_t #define EXPORTABLE '1' #define NON_EXPORTABLE '2' - - #endif //SGXD_AESUTILS_H From 5fb22f0ef72a31bcf04cc364a183719441782daa Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 20 Jul 2021 13:07:31 +0300 Subject: [PATCH 036/100] fix build scripts --- scripts/install_packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_packages.sh b/scripts/install_packages.sh index 7e1e4093..1afb7671 100755 --- a/scripts/install_packages.sh +++ b/scripts/install_packages.sh @@ -2,4 +2,4 @@ sudo apt update sudo apt install -y build-essential make gcc g++ yasm python libprotobuf10 flex bison automake sudo apt install -y ccache cmake ccache autoconf texinfo libgcrypt20-dev libgnutls28-dev libtool pkg-config -sudo apt install -y ocaml +sudo apt install -y ocaml ocamlbuid From 131a5ef9184f627199ffd96cfd84e98cc2de5287 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 20 Jul 2021 13:55:35 +0300 Subject: [PATCH 037/100] cleanup --- scripts/install_packages.sh | 2 +- zmq_src/BLSSignRspMessage.cpp | 30 --------------------- zmq_src/BLSSignRspMessage.h | 43 ------------------------------ zmq_src/ECDSASignRspMessage.cpp | 46 --------------------------------- zmq_src/ECDSASignRspMessage.h | 42 ------------------------------ 5 files changed, 1 insertion(+), 162 deletions(-) delete mode 100644 zmq_src/BLSSignRspMessage.cpp delete mode 100644 zmq_src/BLSSignRspMessage.h delete mode 100644 zmq_src/ECDSASignRspMessage.cpp delete mode 100644 zmq_src/ECDSASignRspMessage.h diff --git a/scripts/install_packages.sh b/scripts/install_packages.sh index 1afb7671..3354434e 100755 --- a/scripts/install_packages.sh +++ b/scripts/install_packages.sh @@ -2,4 +2,4 @@ sudo apt update sudo apt install -y build-essential make gcc g++ yasm python libprotobuf10 flex bison automake sudo apt install -y ccache cmake ccache autoconf texinfo libgcrypt20-dev libgnutls28-dev libtool pkg-config -sudo apt install -y ocaml ocamlbuid +sudo apt install -y ocaml ocamlbuild diff --git a/zmq_src/BLSSignRspMessage.cpp b/zmq_src/BLSSignRspMessage.cpp deleted file mode 100644 index e30f0eda..00000000 --- a/zmq_src/BLSSignRspMessage.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - Copyright (C) 2018-2019 SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file BLSRspSignMessage.cpp - @author Stan Kladko - @date 2020 -*/ - -#include "BLSSignRspMessage.h" -#include "SGXWalletServer.hpp" - - -Json::Value BLSSignRspMessage::process() { - assert(false); -} \ No newline at end of file diff --git a/zmq_src/BLSSignRspMessage.h b/zmq_src/BLSSignRspMessage.h deleted file mode 100644 index fc733b60..00000000 --- a/zmq_src/BLSSignRspMessage.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (C) 2018-2019 SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file BLSRspSignMessage.h - @author Stan Kladko - @date 2020 -*/ - -#ifndef SGXWALLET_BLSSIGNRSPMSG_H -#define SGXWALLET_BLSSIGNRSPMSG_H - -#include "ZMQMessage.h" - -class BLSSignRspMessage : public ZMQMessage { -public: - - BLSSignRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; - - virtual Json::Value process(); - - string getSigShare() { - return getStringRapid("signatureShare"); - } - -}; - - -#endif //SGXWALLET_BLSSIGNRSPMSG_H diff --git a/zmq_src/ECDSASignRspMessage.cpp b/zmq_src/ECDSASignRspMessage.cpp deleted file mode 100644 index eeb7202b..00000000 --- a/zmq_src/ECDSASignRspMessage.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (C) 2018- SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file ECDSARspSignMessage.cpp - @author Stan Kladko - @date 2020 -*/ - -#include "SGXWalletServer.hpp" - -#include "ECDSASignRspMessage.h" - - - -Json::Value ECDSASignRspMessage::process() { - // never called - assert(false); -} - -string ECDSASignRspMessage::getSignature() { - - - - string r = getStringRapid( "signature_r" ); - string v = getStringRapid( "signature_v" ); - string s = getStringRapid("signature_s" ); - - auto ret = v + ":" + r.substr( 2 ) + ":" + s.substr( 2 ); - - return ret; -} diff --git a/zmq_src/ECDSASignRspMessage.h b/zmq_src/ECDSASignRspMessage.h deleted file mode 100644 index 3c8f7989..00000000 --- a/zmq_src/ECDSASignRspMessage.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2018- SKALE Labs - - This file is part of sgxwallet. - - sgxwallet is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - sgxwallet is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . - - @file ECDSARspSignMessage.h - @author Stan Kladko - @date 2020 -*/ - -#ifndef SGXWALLET_ECDSASIGNRSPMESSAGE_H -#define SGXWALLET_ECDSASIGNRSPMESSAGE_H - -#include "ZMQMessage.h" - -class ECDSASignRspMessage : public ZMQMessage { -public: - - ECDSASignRspMessage(shared_ptr &_d) : ZMQMessage(_d) {}; - - virtual Json::Value process(); - - string getSignature(); - - -}; - - -#endif //SGXWALLET_ECDSASIGNRSPMESSAGE_H From 331cc90ab11a0b656124efd47d54a4d23d37ac70 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 20 Jul 2021 15:15:10 +0300 Subject: [PATCH 038/100] SKALE-4262 only owner access the key --- zmq_src/ReqMessage.cpp | 62 +++++++++++++++++++++++++++++++++++++++++- zmq_src/ZMQMessage.cpp | 6 ++++ zmq_src/ZMQMessage.h | 6 ++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index 3bef6310..9252ea4a 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -10,7 +10,7 @@ sgxwallet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License @@ -31,6 +31,10 @@ Json::Value ECDSASignReqMessage::process() { auto base = getInt64Rapid("base"); auto keyName = getStringRapid("keyName"); auto hash = getStringRapid("messageHash"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(keyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::ecdsaSignMessageHashImpl(base, keyName, hash); result["type"] = ZMQMessage::ECDSA_SIGN_RSP; return result; @@ -41,6 +45,10 @@ Json::Value BLSSignReqMessage::process() { auto hash = getStringRapid("messageHash"); auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(keyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::blsSignMessageHashImpl(keyName, hash, t, n); result["type"] = ZMQMessage::BLS_SIGN_RSP; return result; @@ -50,6 +58,10 @@ Json::Value importBLSReqMessage::process() { auto keyName = getStringRapid("keyShareName"); auto keyShare = getStringRapid("keyShare"); auto result = SGXWalletServer::importBLSKeyShareImpl(keyShare, keyName); + if (result["status"] == 0) { + auto cert = getStringRapid("cert"); + keysByOwners[keyName] = cert; + } result["type"] = ZMQMessage::IMPORT_BLS_RSP; return result; } @@ -58,18 +70,31 @@ Json::Value importECDSAReqMessage::process() { auto keyName = getStringRapid("keyName"); auto key = getStringRapid("key"); auto result = SGXWalletServer::importECDSAKeyImpl(key, keyName); + if (result["status"] == 0) { + auto cert = getStringRapid("cert"); + keysByOwners[keyName] = cert; + } result["type"] = ZMQMessage::IMPORT_ECDSA_RSP; return result; } Json::Value generateECDSAReqMessage::process() { auto result = SGXWalletServer::generateECDSAKeyImpl(); + string keyName = result["keyName"].asString(); + if (result["status"] == 0) { + auto cert = getStringRapid("cert"); + keysByOwners[keyName] = cert; + } result["type"] = ZMQMessage::GENERATE_ECDSA_RSP; return result; } Json::Value getPublicECDSAReqMessage::process() { auto keyName = getStringRapid("keyName"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(keyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::getPublicECDSAKeyImpl(keyName); result["type"] = ZMQMessage::GET_PUBLIC_ECDSA_RSP; return result; @@ -79,12 +104,20 @@ Json::Value generateDKGPolyReqMessage::process() { auto polyName = getStringRapid("polyName"); auto t = getInt64Rapid("t"); auto result = SGXWalletServer::generateDKGPolyImpl(polyName, t); + if (result["status"] == 0) { + auto cert = getStringRapid("cert"); + keysByOwners[polyName] = cert; + } result["type"] = ZMQMessage::GENERATE_DKG_POLY_RSP; return result; } Json::Value getVerificationVectorReqMessage::process() { auto polyName = getStringRapid("polyName"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(polyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto t = getInt64Rapid("t"); auto result = SGXWalletServer::getVerificationVectorImpl(polyName, t); result["type"] = ZMQMessage::GET_VV_RSP; @@ -96,6 +129,10 @@ Json::Value getSecretShareReqMessage::process() { auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); auto pubKeys = getJsonValueRapid("publicKeys"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(polyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::getSecretShareV2Impl(polyName, pubKeys, t, n); result["type"] = ZMQMessage::GET_SECRET_SHARE_RSP; return result; @@ -108,6 +145,10 @@ Json::Value dkgVerificationReqMessage::process() { auto idx = getInt64Rapid("index"); auto pubShares = getStringRapid("publicShares"); auto secretShare = getStringRapid("secretShare"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(ethKeyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::dkgVerificationV2Impl(pubShares, ethKeyName, secretShare, t, n, idx); result["type"] = ZMQMessage::DKG_VERIFY_RSP; return result; @@ -120,13 +161,24 @@ Json::Value createBLSPrivateKeyReqMessage::process() { auto secretShare = getStringRapid("secretShare"); auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(ethKeyName, cert) || !isKeyByOwner(polyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); + if (result["status"] == 0) { + keysByOwners[blsKeyName] = cert; + } result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; return result; } Json::Value getBLSPublicReqMessage::process() { auto blsKeyName = getStringRapid("blsKeyName"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(blsKeyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::getBLSPublicKeyShareImpl(blsKeyName); result["type"] = ZMQMessage::GET_BLS_PUBLIC_RSP; return result; @@ -146,6 +198,10 @@ Json::Value complaintResponseReqMessage::process() { auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); auto idx = getInt64Rapid("ind"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(polyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::complaintResponseImpl(polyName, t, n, idx); result["type"] = ZMQMessage::COMPLAINT_RESPONSE_RSP; return result; @@ -179,6 +235,10 @@ Json::Value getServerVersionReqMessage::process() { Json::Value deleteBLSKeyReqMessage::process() { auto blsKeyName = getStringRapid("blsKeyName"); + auto cert = getStringRapid("cert"); + if (!isKeyByOwner(blsKeyName, cert)) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::deleteBlsKeyImpl(blsKeyName); result["type"] = ZMQMessage::DELETE_BLS_KEY_RSP; return result; diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 70eb1d99..b400fc01 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -312,6 +312,12 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr ZMQMessage::keysByOwners; + +bool ZMQMessage::isKeyByOwner(const string& keyName, const string& cert) { + return keysByOwners.count(keyName) && keysByOwners[keyName] == cert; +} + cache::lru_cache> ZMQMessage::verifiedCerts(256); const std::map ZMQMessage::requests{ diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index e4f057b2..9ec24dd5 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -51,6 +51,12 @@ class ZMQMessage { static cache::lru_cache> verifiedCerts; +protected: + + static std::map keysByOwners; + + static bool isKeyByOwner(const string& keyName, const string& cert); + public: static constexpr const char *BLS_SIGN_REQ = "BLSSignReq"; From 63a08c859c648d777a333cb974eaa4f16f48f8da Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 20 Jul 2021 15:39:40 +0300 Subject: [PATCH 039/100] SKALE-4262 write key owners to db --- LevelDB.h | 5 +---- SGXWalletServer.hpp | 5 +---- zmq_src/ReqMessage.cpp | 10 +++++----- zmq_src/ZMQMessage.cpp | 8 +++++++- zmq_src/ZMQMessage.h | 2 ++ 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/LevelDB.h b/LevelDB.h index 4afd6a35..53df6b3b 100644 --- a/LevelDB.h +++ b/LevelDB.h @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file LevelDB.h @author Stan Kladko @@ -54,7 +54,6 @@ class LevelDB { static string sgx_data_folder; - public: static void initDataFolderAndDBs(); @@ -104,8 +103,6 @@ class LevelDB { virtual ~LevelDB(); static const string &getSgxDataFolder(); - - }; diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index 74d34396..d9928983 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file SGXWalletServer.hpp @author Stan Kladko @@ -48,12 +48,9 @@ class SGXWalletServer : public AbstractStubServer { static map ecdsaRequests; static recursive_mutex ecdsaRequestsLock; - - static void checkForDuplicate(map &_map, recursive_mutex &_m, const string &_key, const string &_value); - public: static bool verifyCert(string& _certFileName); diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index 9252ea4a..cc26e758 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -60,7 +60,7 @@ Json::Value importBLSReqMessage::process() { auto result = SGXWalletServer::importBLSKeyShareImpl(keyShare, keyName); if (result["status"] == 0) { auto cert = getStringRapid("cert"); - keysByOwners[keyName] = cert; + addKeyByOwner(keyName, cert); } result["type"] = ZMQMessage::IMPORT_BLS_RSP; return result; @@ -72,7 +72,7 @@ Json::Value importECDSAReqMessage::process() { auto result = SGXWalletServer::importECDSAKeyImpl(key, keyName); if (result["status"] == 0) { auto cert = getStringRapid("cert"); - keysByOwners[keyName] = cert; + addKeyByOwner(keyName, cert); } result["type"] = ZMQMessage::IMPORT_ECDSA_RSP; return result; @@ -83,7 +83,7 @@ Json::Value generateECDSAReqMessage::process() { string keyName = result["keyName"].asString(); if (result["status"] == 0) { auto cert = getStringRapid("cert"); - keysByOwners[keyName] = cert; + addKeyByOwner(keyName, cert); } result["type"] = ZMQMessage::GENERATE_ECDSA_RSP; return result; @@ -106,7 +106,7 @@ Json::Value generateDKGPolyReqMessage::process() { auto result = SGXWalletServer::generateDKGPolyImpl(polyName, t); if (result["status"] == 0) { auto cert = getStringRapid("cert"); - keysByOwners[polyName] = cert; + addKeyByOwner(polyName, cert); } result["type"] = ZMQMessage::GENERATE_DKG_POLY_RSP; return result; @@ -167,7 +167,7 @@ Json::Value createBLSPrivateKeyReqMessage::process() { } auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); if (result["status"] == 0) { - keysByOwners[blsKeyName] = cert; + addKeyByOwner(blsKeyName, cert); } result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; return result; diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index b400fc01..f497dc16 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -28,6 +28,7 @@ #include #include "ZMQClient.h" +#include "LevelDB.h" #include "SGXWalletServer.hpp" #include "ReqMessage.h" #include "RspMessage.h" @@ -315,7 +316,12 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr ZMQMessage::keysByOwners; bool ZMQMessage::isKeyByOwner(const string& keyName, const string& cert) { - return keysByOwners.count(keyName) && keysByOwners[keyName] == cert; + auto value = LevelDB::getLevelDb()->readString(keyName); + return value && *value == cert; +} + +void ZMQMessage::addKeyByOwner(const string& keyName, const string& cert) { + SGXWalletServer::writeDataToDB(keyName, cert); } cache::lru_cache> ZMQMessage::verifiedCerts(256); diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index 9ec24dd5..0f3cd52c 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -57,6 +57,8 @@ class ZMQMessage { static bool isKeyByOwner(const string& keyName, const string& cert); + static void addKeyByOwner(const string& keyName, const string& cert); + public: static constexpr const char *BLS_SIGN_REQ = "BLSSignReq"; From 72bb58de8b262287fd1e892fcea83a2e307431b4 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 20 Jul 2021 16:42:12 +0300 Subject: [PATCH 040/100] SKALE-4262 set checkKeyOwnership on start --- ServerInit.cpp | 4 ++-- zmq_src/ReqMessage.cpp | 42 ++++++++++++++++-------------------------- zmq_src/ZMQClient.cpp | 2 +- zmq_src/ZMQMessage.cpp | 18 +++++++++++------- zmq_src/ZMQMessage.h | 11 ++++++++--- zmq_src/ZMQServer.cpp | 13 +++++++------ zmq_src/ZMQServer.h | 8 +++++--- 7 files changed, 50 insertions(+), 48 deletions(-) diff --git a/ServerInit.cpp b/ServerInit.cpp index c118a099..baf21edf 100644 --- a/ServerInit.cpp +++ b/ServerInit.cpp @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file ServerInit.cpp @author Stan Kladko @@ -209,7 +209,7 @@ void initAll(uint32_t _logLevel, bool _checkCert, SGXRegistrationServer::initRegistrationServer(_autoSign); CSRManagerServer::initCSRManagerServer(); SGXInfoServer::initInfoServer(_logLevel, _checkCert, _autoSign, _generateTestKeys); - ZMQServer::initZMQServer(_checkZMQSig); + ZMQServer::initZMQServer(_checkZMQSig, useHTTPS); sgxServerInited = true; } catch (SGXException &_e) { diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index cc26e758..b3b63f35 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -31,8 +31,7 @@ Json::Value ECDSASignReqMessage::process() { auto base = getInt64Rapid("base"); auto keyName = getStringRapid("keyName"); auto hash = getStringRapid("messageHash"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(keyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::ecdsaSignMessageHashImpl(base, keyName, hash); @@ -45,8 +44,7 @@ Json::Value BLSSignReqMessage::process() { auto hash = getStringRapid("messageHash"); auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(keyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::blsSignMessageHashImpl(keyName, hash, t, n); @@ -58,7 +56,7 @@ Json::Value importBLSReqMessage::process() { auto keyName = getStringRapid("keyShareName"); auto keyShare = getStringRapid("keyShare"); auto result = SGXWalletServer::importBLSKeyShareImpl(keyShare, keyName); - if (result["status"] == 0) { + if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); addKeyByOwner(keyName, cert); } @@ -70,7 +68,7 @@ Json::Value importECDSAReqMessage::process() { auto keyName = getStringRapid("keyName"); auto key = getStringRapid("key"); auto result = SGXWalletServer::importECDSAKeyImpl(key, keyName); - if (result["status"] == 0) { + if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); addKeyByOwner(keyName, cert); } @@ -81,7 +79,7 @@ Json::Value importECDSAReqMessage::process() { Json::Value generateECDSAReqMessage::process() { auto result = SGXWalletServer::generateECDSAKeyImpl(); string keyName = result["keyName"].asString(); - if (result["status"] == 0) { + if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); addKeyByOwner(keyName, cert); } @@ -91,8 +89,7 @@ Json::Value generateECDSAReqMessage::process() { Json::Value getPublicECDSAReqMessage::process() { auto keyName = getStringRapid("keyName"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(keyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::getPublicECDSAKeyImpl(keyName); @@ -104,7 +101,7 @@ Json::Value generateDKGPolyReqMessage::process() { auto polyName = getStringRapid("polyName"); auto t = getInt64Rapid("t"); auto result = SGXWalletServer::generateDKGPolyImpl(polyName, t); - if (result["status"] == 0) { + if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); addKeyByOwner(polyName, cert); } @@ -114,8 +111,7 @@ Json::Value generateDKGPolyReqMessage::process() { Json::Value getVerificationVectorReqMessage::process() { auto polyName = getStringRapid("polyName"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(polyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(polyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto t = getInt64Rapid("t"); @@ -129,8 +125,7 @@ Json::Value getSecretShareReqMessage::process() { auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); auto pubKeys = getJsonValueRapid("publicKeys"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(polyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(polyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::getSecretShareV2Impl(polyName, pubKeys, t, n); @@ -145,8 +140,7 @@ Json::Value dkgVerificationReqMessage::process() { auto idx = getInt64Rapid("index"); auto pubShares = getStringRapid("publicShares"); auto secretShare = getStringRapid("secretShare"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(ethKeyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(ethKeyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::dkgVerificationV2Impl(pubShares, ethKeyName, secretShare, t, n, idx); @@ -161,13 +155,12 @@ Json::Value createBLSPrivateKeyReqMessage::process() { auto secretShare = getStringRapid("secretShare"); auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(ethKeyName, cert) || !isKeyByOwner(polyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(ethKeyName, getStringRapid("cert")) || !isKeyByOwner(polyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); - if (result["status"] == 0) { - addKeyByOwner(blsKeyName, cert); + if (checkKeyOwnership && result["status"] == 0) { + addKeyByOwner(blsKeyName, getStringRapid("cert")); } result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; return result; @@ -175,8 +168,7 @@ Json::Value createBLSPrivateKeyReqMessage::process() { Json::Value getBLSPublicReqMessage::process() { auto blsKeyName = getStringRapid("blsKeyName"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(blsKeyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(blsKeyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::getBLSPublicKeyShareImpl(blsKeyName); @@ -198,8 +190,7 @@ Json::Value complaintResponseReqMessage::process() { auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); auto idx = getInt64Rapid("ind"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(polyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(polyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::complaintResponseImpl(polyName, t, n, idx); @@ -235,8 +226,7 @@ Json::Value getServerVersionReqMessage::process() { Json::Value deleteBLSKeyReqMessage::process() { auto blsKeyName = getStringRapid("blsKeyName"); - auto cert = getStringRapid("cert"); - if (!isKeyByOwner(blsKeyName, cert)) { + if (checkKeyOwnership && !isKeyByOwner(blsKeyName, getStringRapid("cert"))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::deleteBlsKeyImpl(blsKeyName); diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 172f9964..7d578076 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -67,7 +67,7 @@ shared_ptr ZMQClient::doRequestReply(Json::Value &_req) { CHECK_STATE(resultStr.front() == '{') CHECK_STATE(resultStr.back() == '}') - return ZMQMessage::parse(resultStr.c_str(), resultStr.size(), false, false); + return ZMQMessage::parse(resultStr.c_str(), resultStr.size(), false, false, false); } catch (std::exception &e) { spdlog::error(string("Error in doRequestReply:") + e.what()); throw; diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index f497dc16..2684635c 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -76,7 +76,7 @@ string ZMQMessage::getStringRapid(const char *_name) { shared_ptr ZMQMessage::parse(const char *_msg, size_t _size, bool _isRequest, - bool _verifySig) { + bool _verifySig, bool _checkKeyOwnership) { CHECK_STATE(_msg); CHECK_STATE2(_size > 5, ZMQ_INVALID_MESSAGE_SIZE); @@ -150,16 +150,15 @@ shared_ptr ZMQMessage::parse(const char *_msg, } } - shared_ptr result; - if (_isRequest) { - return buildRequest(type, d); + return buildRequest(type, d, _checkKeyOwnership); } else { - return buildResponse(type, d); + return buildResponse(type, d, _checkKeyOwnership); } } -shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr _d) { +shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr _d, + bool _checkKeyOwnership) { Requests r; try { int t = requests.at( _type ); @@ -232,10 +231,13 @@ shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr setCheckKeyOwnership(_checkKeyOwnership); + return ret; } -shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr _d) { +shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr _d, + bool _checkKeyOwnership) { Responses r; try { int t = responses.at( _type ); @@ -310,6 +312,8 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr setCheckKeyOwnership(_checkKeyOwnership); + return ret; } diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index 0f3cd52c..b53e1b3a 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -52,6 +52,7 @@ class ZMQMessage { static cache::lru_cache> verifiedCerts; protected: + bool checkKeyOwnership = true; static std::map keysByOwners; @@ -135,11 +136,15 @@ class ZMQMessage { } static shared_ptr parse(const char* _msg, size_t _size, bool _isRequest, - bool _verifySig); + bool _verifySig, bool _checkKeyOwnership); - static shared_ptr buildRequest(string& type, shared_ptr _d); - static shared_ptr buildResponse(string& type, shared_ptr _d); + static shared_ptr buildRequest(string& type, shared_ptr _d, + bool _checkKeyOwnership); + static shared_ptr buildResponse(string& type, shared_ptr _d, + bool _checkKeyOwnership); virtual Json::Value process() = 0; + void setCheckKeyOwnership(bool _check) { checkKeyOwnership = _check; } + }; diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 7767b1fd..f74fccfe 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -38,8 +38,8 @@ using namespace std; shared_ptr ZMQServer::zmqServer = nullptr; -ZMQServer::ZMQServer(bool _checkSignature, const string &_caCertFile) - : checkSignature(_checkSignature), +ZMQServer::ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string &_caCertFile) + : checkSignature(_checkSignature), checkKeyOwnership(_checkKeyOwnership), caCertFile(_caCertFile), ctx(make_shared(1)) { socket = make_shared(*ctx, ZMQ_ROUTER); @@ -94,12 +94,13 @@ void ZMQServer::exitZMQServer() { spdlog::info("Exited zmq server."); } -void ZMQServer::initZMQServer(bool _checkSignature) { +void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { static bool initedServer = false; CHECK_STATE(!initedServer) initedServer = true; - spdlog::info("Initing zmq server. checkSignature is set to {}", _checkSignature); + spdlog::info("Initing zmq server.\n checkSignature is set to {}.\n checkKeyOwnership is set to {}", + _checkSignature, _checkKeyOwnership); string rootCAPath = ""; @@ -109,7 +110,7 @@ void ZMQServer::initZMQServer(bool _checkSignature) { CHECK_STATE(access(rootCAPath.c_str(), F_OK) == 0); }; - zmqServer = make_shared(_checkSignature, rootCAPath); + zmqServer = make_shared(_checkSignature, _checkKeyOwnership, rootCAPath); CHECK_STATE(zmqServer) @@ -179,7 +180,7 @@ void ZMQServer::doOneServerLoop() { CHECK_STATE(stringToParse.back() == '}') auto parsedMsg = ZMQMessage::parse( - stringToParse.c_str(), stringToParse.size(), true, checkSignature); + stringToParse.c_str(), stringToParse.size(), true, checkSignature, checkKeyOwnership); CHECK_STATE2(parsedMsg, ZMQ_COULD_NOT_PARSE); diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 9f28265f..068b2767 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file ZMQServer.h @author Stan Kladko @@ -52,16 +52,18 @@ class ZMQServer { static shared_ptr serverThread; - ZMQServer(bool _checkSignature, const string& _caCertFile); + ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string& _caCertFile); ~ZMQServer(); void run(); - static void initZMQServer(bool _checkSignature); + static void initZMQServer(bool _checkSignature, bool _checkKeyOwnership); static void exitZMQServer(); private: + bool checkKeyOwnership = true; + shared_ptr ctx; shared_ptr socket; From 8a11d1f9a29091d3c2c5b2d02809ad462b2407f5 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 20 Jul 2021 17:40:28 +0300 Subject: [PATCH 041/100] SKALE-4262 add -e flag --- ServerInit.cpp | 4 ++-- ServerInit.h | 2 +- docker/start.sh | 2 +- run_sgx/docker-compose.yml | 2 +- run_sgx_sim/docker-compose.yml | 2 +- sgxwall.cpp | 12 +++++++++--- testw.cpp | 10 +++++----- zmq_src/ReqMessage.cpp | 2 +- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ServerInit.cpp b/ServerInit.cpp index baf21edf..1167a6e1 100644 --- a/ServerInit.cpp +++ b/ServerInit.cpp @@ -160,7 +160,7 @@ uint64_t initEnclave() { } void initAll(uint32_t _logLevel, bool _checkCert, - bool _checkZMQSig, bool _autoSign, bool _generateTestKeys) { + bool _checkZMQSig, bool _autoSign, bool _generateTestKeys, bool _checkKeyOwnership) { static atomic sgxServerInited(false); static mutex initMutex; @@ -209,7 +209,7 @@ void initAll(uint32_t _logLevel, bool _checkCert, SGXRegistrationServer::initRegistrationServer(_autoSign); CSRManagerServer::initCSRManagerServer(); SGXInfoServer::initInfoServer(_logLevel, _checkCert, _autoSign, _generateTestKeys); - ZMQServer::initZMQServer(_checkZMQSig, useHTTPS); + ZMQServer::initZMQServer(_checkZMQSig, _checkKeyOwnership); sgxServerInited = true; } catch (SGXException &_e) { diff --git a/ServerInit.h b/ServerInit.h index 83241483..16b94eb5 100644 --- a/ServerInit.h +++ b/ServerInit.h @@ -32,7 +32,7 @@ #define EXTERNC #endif -EXTERNC void initAll(uint32_t _logLevel, bool _checkCert, bool _checkZMQSig, bool _autoSign, bool _generateTestKeys); +EXTERNC void initAll(uint32_t _logLevel, bool _checkCert, bool _checkZMQSig, bool _autoSign, bool _generateTestKeys, bool _checkKeyOwnership); void exitAll(); diff --git a/docker/start.sh b/docker/start.sh index 20d735d0..cd8eeffe 100755 --- a/docker/start.sh +++ b/docker/start.sh @@ -47,6 +47,6 @@ sleep 5 ./testw.py else sleep 3 -./sgxwallet $1 $2 $3 $4 $5 +./sgxwallet $1 $2 $3 $4 $5 $6 fi diff --git a/run_sgx/docker-compose.yml b/run_sgx/docker-compose.yml index 43691a96..a8430f8d 100644 --- a/run_sgx/docker-compose.yml +++ b/run_sgx/docker-compose.yml @@ -21,7 +21,7 @@ services: options: max-size: "10m" max-file: "4" - command: -s -y -d + command: -s -y -d -e healthcheck: test: ["CMD", "ls", "/dev/isgx", "/dev/mei0"] diff --git a/run_sgx_sim/docker-compose.yml b/run_sgx_sim/docker-compose.yml index dd4ef5bc..aaeddba0 100644 --- a/run_sgx_sim/docker-compose.yml +++ b/run_sgx_sim/docker-compose.yml @@ -18,4 +18,4 @@ services: options: max-size: "10m" max-file: "4" - command: -s -y + command: -s -y -e diff --git a/sgxwall.cpp b/sgxwall.cpp index a02f6e0e..8f71fa70 100644 --- a/sgxwall.cpp +++ b/sgxwall.cpp @@ -55,6 +55,7 @@ void SGXWallet::printUsage() { cerr << " -n Use http instead of https. Default is to use https with a selg-signed server cert. Insecure! \n"; cerr << " -c Disable client authentication using certificates. Insecure!\n"; cerr << " -s Sign client certificates without human confirmation. Insecure! \n"; + cerr << " -e Only owner of the key can access it.\n"; } @@ -100,6 +101,7 @@ int main(int argc, char *argv[]) { bool checkClientCertOption = true; bool autoSignClientCertOption = false; bool generateTestKeys = false; + bool checkKeyOwnership = true; std::signal(SIGABRT, SGXWallet::signalHandler); @@ -110,7 +112,7 @@ int main(int argc, char *argv[]) { exit(-21); } - while ((opt = getopt(argc, argv, "cshd0abyvVnT")) != -1) { + while ((opt = getopt(argc, argv, "cshd0abyvVneT")) != -1) { switch (opt) { case 'h': SGXWallet::printUsage(); @@ -136,7 +138,11 @@ int main(int argc, char *argv[]) { break; case 'n': useHTTPSOption = false; - break; + checkKeyOwnership = false; + break; + case 'e': + checkKeyOwnership = true; + break; case 'a': enterBackupKeyOption = false; break; @@ -179,7 +185,7 @@ int main(int argc, char *argv[]) { } cerr << "Calling initAll ..." << endl; - initAll(enclaveLogLevel, checkClientCertOption, checkClientCertOption, autoSignClientCertOption, generateTestKeys); + initAll(enclaveLogLevel, checkClientCertOption, checkClientCertOption, autoSignClientCertOption, generateTestKeys, checkKeyOwnership); cerr << "Completed initAll." << endl; //check if test keys already exist diff --git a/testw.cpp b/testw.cpp index 3875cb9e..e9262081 100644 --- a/testw.cpp +++ b/testw.cpp @@ -73,7 +73,7 @@ class TestFixture { TestFixture() { TestUtils::resetDB(); setOptions(L_INFO, false, true); - initAll(L_INFO, false, false, true, false); + initAll(L_INFO, false, false, true, false, false); } ~TestFixture() { @@ -87,7 +87,7 @@ class TestFixtureHTTPS { TestFixtureHTTPS() { TestUtils::resetDB(); setOptions(L_INFO, true, true); - initAll(L_INFO, false, true, true, false); + initAll(L_INFO, false, true, true, false, true); } ~TestFixtureHTTPS() { @@ -101,7 +101,7 @@ class TestFixtureZMQSign { TestFixtureZMQSign() { TestUtils::resetDB(); setOptions(L_INFO, false, true); - initAll(L_INFO, false, true, true, false); + initAll(L_INFO, false, true, true, false, false); } ~TestFixtureZMQSign() { @@ -114,7 +114,7 @@ class TestFixtureNoResetFromBackup { public: TestFixtureNoResetFromBackup() { setFullOptions(L_INFO, false, true, true); - initAll(L_INFO, false, false, true, false); + initAll(L_INFO, false, false, true, false, false); } ~TestFixtureNoResetFromBackup() { @@ -128,7 +128,7 @@ class TestFixtureNoReset { public: TestFixtureNoReset() { setOptions(L_INFO, false, true); - initAll(L_INFO, false, false, true, false); + initAll(L_INFO, false, false, true, false, false); } ~TestFixtureNoReset() { diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index b3b63f35..2ca107e1 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -155,7 +155,7 @@ Json::Value createBLSPrivateKeyReqMessage::process() { auto secretShare = getStringRapid("secretShare"); auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); - if (checkKeyOwnership && !isKeyByOwner(ethKeyName, getStringRapid("cert")) || !isKeyByOwner(polyName, getStringRapid("cert"))) { + if (checkKeyOwnership && (!isKeyByOwner(ethKeyName, getStringRapid("cert")) || !isKeyByOwner(polyName, getStringRapid("cert")))) { throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); From 29ed18853e89f0cdc544e22227596af69da0d903 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 26 Jul 2021 11:17:38 +0300 Subject: [PATCH 042/100] SKALE-4411 initialize te crypto --- BLSCrypto.cpp | 68 +-------------------------------------- BLSCrypto.h | 9 +----- BLSPrivateKeyShareSGX.cpp | 4 +-- DKGCrypto.cpp | 7 ++-- ECDSACrypto.cpp | 2 +- Makefile.am | 4 +-- SEKManager.cpp | 4 +-- SGXWalletServer.cpp | 27 +++++++++++++++- SGXWalletServer.h | 2 +- SGXWalletServer.hpp | 4 +++ TestUtils.cpp | 3 +- abstractstubserver.h | 11 ++++++- common.h | 5 --- secure_enclave/DKGUtils.h | 6 ++-- testw.cpp | 3 +- zmq_src/ZMQClient.cpp | 2 +- 16 files changed, 61 insertions(+), 100 deletions(-) diff --git a/BLSCrypto.cpp b/BLSCrypto.cpp index dbfbc59f..9a31ca9b 100644 --- a/BLSCrypto.cpp +++ b/BLSCrypto.cpp @@ -46,6 +46,7 @@ #include "LevelDB.h" #include "ServerInit.h" #include "BLSCrypto.h" +#include "CryptoTools.h" string *FqToString(libff::alt_bn128_Fq *_fq) { @@ -65,71 +66,6 @@ string *FqToString(libff::alt_bn128_Fq *_fq) { return new string(arr); } -int char2int(char _input) { - if (_input >= '0' && _input <= '9') - return _input - '0'; - if (_input >= 'A' && _input <= 'F') - return _input - 'A' + 10; - if (_input >= 'a' && _input <= 'f') - return _input - 'a' + 10; - return -1; -} - -vector carray2Hex(const unsigned char *d, uint64_t _len) { - - CHECK_STATE(d); - - vector _hexArray( 2 * _len + 1); - - char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - for (uint64_t j = 0; j < _len; j++) { - _hexArray[j * 2] = hexval[((d[j] >> 4) & 0xF)]; - _hexArray[j * 2 + 1] = hexval[(d[j]) & 0x0F]; - } - - _hexArray[_len * 2] = 0; - - return _hexArray; -} - - -bool hex2carray(const char *_hex, uint64_t *_bin_len, - uint8_t *_bin, uint64_t _max_length) { - - - CHECK_STATE(_hex); - CHECK_STATE(_bin); - CHECK_STATE(_bin_len) - - - uint64_t len = strnlen(_hex, 2 * _max_length + 1); - - CHECK_STATE(len != 2 * _max_length + 1); - - CHECK_STATE(len <= 2 * _max_length); - - - if (len == 0 && len % 2 == 1) - return false; - - *_bin_len = len / 2; - - for (uint64_t i = 0; i < len / 2; i++) { - int high = char2int((char) _hex[i * 2]); - int low = char2int((char) _hex[i * 2 + 1]); - - if (high < 0 || low < 0) { - return false; - } - - _bin[i] = (unsigned char) (high * 16 + low); - } - - return true; -} - bool sign_aes(const char *_encryptedKeyHex, const char *_hashHex, size_t _t, size_t _n, char *_sig) { CHECK_STATE(_encryptedKeyHex); @@ -182,13 +118,11 @@ bool sign_aes(const char *_encryptedKeyHex, const char *_hashHex, size_t _t, siz int errStatus = 0; - sgx_status_t status = SGX_SUCCESS; status = trustedBlsSignMessage(eid, &errStatus, errMsg.data(), encryptedKey, sz, xStrArg, yStrArg, signature); - HANDLE_TRUSTED_FUNCTION_ERROR(status, errStatus, errMsg.data()); string hint = BLSutils::ConvertToString(hash_with_hint.first.Y) + ":" + hash_with_hint.second; diff --git a/BLSCrypto.h b/BLSCrypto.h index 7c2d6fd9..4cfd3007 100644 --- a/BLSCrypto.h +++ b/BLSCrypto.h @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file BLSCrypto.h @author Stan Kladko @@ -37,13 +37,6 @@ EXTERNC bool bls_sign(const char* encryptedKeyHex, const char* hashHex, size_t t, size_t n, char* _sig); -EXTERNC int char2int(char _input); - -EXTERNC std::vector carray2Hex(const unsigned char *d, uint64_t _len); - -EXTERNC bool hex2carray(const char * _hex, uint64_t *_bin_len, - uint8_t* _bin, uint64_t _max_length ); - std::string encryptBLSKeyShare2Hex(int *errStatus, char *err_string, const char *_key); #endif //SGXWALLET_BLSCRYPTO_H diff --git a/BLSPrivateKeyShareSGX.cpp b/BLSPrivateKeyShareSGX.cpp index 5f718d3a..c22127f1 100644 --- a/BLSPrivateKeyShareSGX.cpp +++ b/BLSPrivateKeyShareSGX.cpp @@ -32,6 +32,7 @@ #include "sgxwallet.h" #include "BLSCrypto.h" +#include "CryptoTools.h" #include "ServerInit.h" #include "SEKManager.h" #include "BLSPrivateKeyShareSGX.h" @@ -122,13 +123,10 @@ string BLSPrivateKeyShareSGX::signWithHelperSGXstr( BOOST_THROW_EXCEPTION(runtime_error("Null yStr")); } - vector errMsg(BUF_LEN, 0); - SAFE_CHAR_BUF(xStrArg, BUF_LEN)SAFE_CHAR_BUF(yStrArg, BUF_LEN)SAFE_CHAR_BUF(signature, BUF_LEN); - strncpy(xStrArg, xStr->c_str(), BUF_LEN); strncpy(yStrArg, yStr->c_str(), BUF_LEN); diff --git a/DKGCrypto.cpp b/DKGCrypto.cpp index 9cb8b77e..72bcb1f4 100644 --- a/DKGCrypto.cpp +++ b/DKGCrypto.cpp @@ -32,7 +32,7 @@ #include "SGXException.h" #include "SGXWalletServer.hpp" -#include "BLSCrypto.h" +#include "CryptoTools.h" #include "SEKManager.h" #include "DKGCrypto.h" @@ -448,7 +448,10 @@ bool createBLSShareV2(const string &blsKeyName, const char *s_shares, const char vector errMsg(BUF_LEN, 0); int errStatus = 0; - uint64_t decKeyLen;SAFE_UINT8_BUF(encr_bls_key, BUF_LEN);SAFE_UINT8_BUF(encr_key, BUF_LEN); + uint64_t decKeyLen; + SAFE_UINT8_BUF(encr_bls_key, BUF_LEN) + SAFE_UINT8_BUF(encr_key, BUF_LEN) + if (!hex2carray(encryptedKeyHex, &decKeyLen, encr_key, BUF_LEN)) { throw SGXException(CREATE_BLS_SHARE_INVALID_KEY_HEX, string(__FUNCTION__) + ":Invalid encryptedKeyHex"); } diff --git a/ECDSACrypto.cpp b/ECDSACrypto.cpp index f22ccbab..cdc31f2e 100644 --- a/ECDSACrypto.cpp +++ b/ECDSACrypto.cpp @@ -36,7 +36,7 @@ #include "secure_enclave/Verify.h" -#include "BLSCrypto.h" +#include "CryptoTools.h" #include "SEKManager.h" #include "ECDSACrypto.h" diff --git a/Makefile.am b/Makefile.am index 23842c51..d66ec781 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,8 +72,8 @@ bin_PROGRAMS = sgxwallet testw sgx_util COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/RspMessage.cpp zmq_src/ReqMessage.cpp \ zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp \ - InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp \ - SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp \ + InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp TECrypto.cpp \ + SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp CryptoTools.cpp \ DKGCrypto.cpp ServerInit.cpp BLSPrivateKeyShareSGX.cpp LevelDB.cpp ServerDataChecker.cpp SEKManager.cpp \ third_party/intel/sgx_stub.c third_party/intel/sgx_detect_linux.c third_party/intel/create_enclave.c \ third_party/intel/oc_alloc.c ECDSAImpl.c TestUtils.cpp sgxwallet.c SGXInfoServer.cpp ECDSACrypto.cpp diff --git a/SEKManager.cpp b/SEKManager.cpp index 6ca6d46d..4206aa26 100644 --- a/SEKManager.cpp +++ b/SEKManager.cpp @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file SEKManager.cpp @author Stan Kladko @@ -33,7 +33,7 @@ #include "sgxwallet.h" #include "SGXException.h" -#include "BLSCrypto.h" +#include "CryptoTools.h" #include "LevelDB.h" #include "ServerDataChecker.h" diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index 7a0a28f9..0db75333 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -38,8 +38,9 @@ #include "SGXException.h" #include "LevelDB.h" #include "BLSCrypto.h" -#include "ECDSACrypto.h" #include "DKGCrypto.h" +#include "ECDSACrypto.h" +#include "TECrypto.h" #include "SGXWalletServer.h" #include "SGXWalletServer.hpp" @@ -984,6 +985,26 @@ SGXWalletServer::createBLSPrivateKeyV2Impl(const string &_blsKeyName, const stri RETURN_SUCCESS(result); } +Json::Value SGXWalletServer::getDecryptionShareImpl(const std::string& teKeyName, const std::string& publicDecryptionValue) { + spdlog::info("Entering {}", __FUNCTION__); + INIT_RESULT(result) + + try { + if (!checkName(teKeyName, "BLS_KEY")) { + throw SGXException(BLS_SIGN_INVALID_KS_NAME, string(__FUNCTION__) + ":Invalid BLSKey name"); + } + + shared_ptr encryptedKeyHex_ptr = readFromDb(teKeyName); + + vector decryptionValueVector = calculateDecryptionShare(encryptedKeyHex_ptr->c_str(), publicDecryptionValue); + for (uint8_t i = 0; i < 4; ++i) { + result["decryptionValue"][i] = decryptionValueVector.at(i); + } + } HANDLE_SGX_EXCEPTION(result) + + RETURN_SUCCESS(result) +} + Json::Value SGXWalletServer::generateDKGPoly(const string &_polyName, int _t) { return generateDKGPolyImpl(_polyName, _t); } @@ -1084,6 +1105,10 @@ SGXWalletServer::createBLSPrivateKeyV2(const string &blsKeyName, const string &e return createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, SecretShare, t, n); } +Json::Value SGXWalletServer::getDecryptionShare(const std::string& teKeyName, const std::string& publicDecryptionValue) { + return getDecryptionShareImpl(teKeyName, publicDecryptionValue); +} + shared_ptr SGXWalletServer::readFromDb(const string &name, const string &prefix) { auto dataStr = checkDataFromDb(prefix + name); diff --git a/SGXWalletServer.h b/SGXWalletServer.h index 29a6970a..ac1fe47b 100644 --- a/SGXWalletServer.h +++ b/SGXWalletServer.h @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file SGXWalletServer.h @author Stan Kladko diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index 74d34396..ac021ae9 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -116,6 +116,8 @@ class SGXWalletServer : public AbstractStubServer { virtual Json::Value createBLSPrivateKeyV2(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string & SecretShare, int t, int n); + virtual Json::Value getDecryptionShare(const std::string& blsKeyName, const std::string& publicDecryptionValue); + static shared_ptr readFromDb(const string &name, const string &prefix = ""); static shared_ptr checkDataFromDb(const string &name, const string &prefix = ""); @@ -174,6 +176,8 @@ class SGXWalletServer : public AbstractStubServer { static Json::Value createBLSPrivateKeyV2Impl(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string & SecretShare, int t, int n); + static Json::Value getDecryptionShareImpl(const std::string& teKeyName, const std::string& publicDecryptionValue); + static void printDB(); static void initHttpServer(); diff --git a/TestUtils.cpp b/TestUtils.cpp index 4a168a73..86b9ccb3 100644 --- a/TestUtils.cpp +++ b/TestUtils.cpp @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file TestUtils.cpp @author Stan Kladko @@ -37,6 +37,7 @@ #include "BLSCrypto.h" #include "ServerInit.h" #include "DKGCrypto.h" +#include "CryptoTools.h" #include "SGXException.h" #include "LevelDB.h" #include "SGXWalletServer.hpp" diff --git a/abstractstubserver.h b/abstractstubserver.h index bc34a51a..5a19e2f6 100644 --- a/abstractstubserver.h +++ b/abstractstubserver.h @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file BLSEnclave.cpp @author Stan Kladko @@ -62,6 +62,8 @@ class AbstractStubServer : public jsonrpc::AbstractServer this->bindAndAddMethod(jsonrpc::Procedure("getSecretShareV2", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "polyName",jsonrpc::JSON_STRING,"publicKeys",jsonrpc::JSON_ARRAY, "n",jsonrpc::JSON_INTEGER,"t",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::getSecretShareV2I); this->bindAndAddMethod(jsonrpc::Procedure("dkgVerificationV2", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "publicShares",jsonrpc::JSON_STRING, "ethKeyName",jsonrpc::JSON_STRING, "secretShare",jsonrpc::JSON_STRING,"t",jsonrpc::JSON_INTEGER, "n",jsonrpc::JSON_INTEGER, "index",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::dkgVerificationV2I); this->bindAndAddMethod(jsonrpc::Procedure("createBLSPrivateKeyV2", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "blsKeyName",jsonrpc::JSON_STRING, "ethKeyName",jsonrpc::JSON_STRING, "polyName", jsonrpc::JSON_STRING, "secretShare",jsonrpc::JSON_STRING,"t", jsonrpc::JSON_INTEGER,"n",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::createBLSPrivateKeyV2I); + + this->bindAndAddMethod(jsonrpc::Procedure("getDecryptionShare", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "teKeyName",jsonrpc::JSON_STRING,"publicDecryptionValue",jsonrpc::JSON_STRING, NULL), &AbstractStubServer::getDecryptionShareI); } inline virtual void importBLSKeyShareI(const Json::Value &request, Json::Value &response) @@ -161,6 +163,11 @@ class AbstractStubServer : public jsonrpc::AbstractServer response = this->createBLSPrivateKeyV2(request["blsKeyName"].asString(), request["ethKeyName"].asString(), request["polyName"].asString(),request["secretShare"].asString(),request["t"].asInt(), request["n"].asInt()); } + inline virtual void getDecryptionShareI(const Json::Value &request, Json::Value &response) + { + response = this->getDecryptionShare(request["teKeyName"].asString(), request["publicDecryptionValue"].asString()); + } + virtual Json::Value importBLSKeyShare(const std::string& keyShare, const std::string& keyShareName) = 0; virtual Json::Value blsSignMessageHash(const std::string& keyShareName, const std::string& messageHash, int t, int n ) = 0; virtual Json::Value importECDSAKey(const std::string& keyShare, const std::string& keyShareName) = 0; @@ -186,6 +193,8 @@ class AbstractStubServer : public jsonrpc::AbstractServer virtual Json::Value getSecretShareV2(const std::string& polyName, const Json::Value& publicKeys, int t, int n) = 0; virtual Json::Value dkgVerificationV2( const std::string& publicShares, const std::string& ethKeyName, const std::string& SecretShare, int t, int n, int index) = 0; virtual Json::Value createBLSPrivateKeyV2(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string & SecretShare, int t, int n) = 0; + + virtual Json::Value getDecryptionShare(const std::string& teKeyName, const std::string& publicDecryptionValue) = 0; }; #endif //JSONRPC_CPP_STUB_ABSTRACTSTUBSERVER_H_ diff --git a/common.h b/common.h index 0b744ef6..94169413 100644 --- a/common.h +++ b/common.h @@ -98,8 +98,6 @@ inline int getValue() { //Note: this value is in KB! return result; } - - #define CHECK_STATE(_EXPRESSION_) \ if (!(_EXPRESSION_)) { \ auto __msg__ = std::string("State check failed::") + #_EXPRESSION_ + " " + std::string(__FILE__) + ":" + std::to_string(__LINE__); \ @@ -136,8 +134,6 @@ BOOST_THROW_EXCEPTION(runtime_error(__ERR_STRING__)); \ // Copy from libconsensus - - inline string exec( const char* cmd ) { CHECK_STATE( cmd ); std::array< char, 128 > buffer; @@ -162,5 +158,4 @@ extern uint64_t initTime; #define WRITE_LOCK(__X__) std::unique_lock __LOCK__(__X__); - #endif //SGXWALLET_COMMON_H diff --git a/secure_enclave/DKGUtils.h b/secure_enclave/DKGUtils.h index d5d57bad..95565d86 100644 --- a/secure_enclave/DKGUtils.h +++ b/secure_enclave/DKGUtils.h @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file DKGUtils.h @author Stan Kladko @@ -45,14 +45,12 @@ EXTERNC void calc_secret_shares(const char* decrypted_coeffs, char * secret_shar EXTERNC int calc_secret_share(const char* decrypted_coeffs, char * s_share, unsigned _t, unsigned _n, unsigned ind); -EXTERNC int calc_public_shares(const char* decrypted_coeffs, char * public_shares, - unsigned _t); +EXTERNC int calc_public_shares(const char* decrypted_coeffs, char * public_shares, unsigned _t); EXTERNC int Verification ( char * public_shares, mpz_t decr_secret_share, int _t, int ind); EXTERNC int calc_bls_public_key(char* skey, char* pub_key); - EXTERNC int calc_secret_shareG2(const char* s_share, char * s_shareG2); #endif diff --git a/testw.cpp b/testw.cpp index 3875cb9e..9d68bbd1 100644 --- a/testw.cpp +++ b/testw.cpp @@ -14,7 +14,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License - along with sgxwallet. If not, see . + along with sgxwallet. If not, see . @file testw.cpp @author Stan Kladko @@ -37,6 +37,7 @@ #include #include "BLSCrypto.h" +#include "CryptoTools.h" #include "ServerInit.h" #include "DKGCrypto.h" #include "SGXException.h" diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 172f9964..0a4c3d21 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -32,7 +32,7 @@ #include "sgxwallet_common.h" #include "common.h" -#include "BLSCrypto.h" +#include "CryptoTools.h" #include "ReqMessage.h" #include "RspMessage.h" #include "ZMQClient.h" From ca720ecb1c4b6dc03dcfe8ac575837d64b3f3c2b Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 26 Jul 2021 11:18:19 +0300 Subject: [PATCH 043/100] SKALE-4411 initialize te crypto --- CryptoTools.cpp | 89 ++++++++++++++++++++++++++++++++++++++ CryptoTools.h | 45 +++++++++++++++++++ TECrypto.cpp | 54 +++++++++++++++++++++++ TECrypto.h | 41 ++++++++++++++++++ secure_enclave/TEUtils.cpp | 42 ++++++++++++++++++ secure_enclave/TEUtils.h | 42 ++++++++++++++++++ 6 files changed, 313 insertions(+) create mode 100644 CryptoTools.cpp create mode 100644 CryptoTools.h create mode 100644 TECrypto.cpp create mode 100644 TECrypto.h create mode 100644 secure_enclave/TEUtils.cpp create mode 100644 secure_enclave/TEUtils.h diff --git a/CryptoTools.cpp b/CryptoTools.cpp new file mode 100644 index 00000000..725b86b6 --- /dev/null +++ b/CryptoTools.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2021-Present SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file CryptoTools.cpp + @author Oleh Nikolaiev + @date 2021 +*/ + +#include + +#include "common.h" +#include "CryptoTools.h" + +using std::vector; + +int char2int(char _input) { + if (_input >= '0' && _input <= '9') + return _input - '0'; + if (_input >= 'A' && _input <= 'F') + return _input - 'A' + 10; + if (_input >= 'a' && _input <= 'f') + return _input - 'a' + 10; + return -1; +} + +vector carray2Hex(const unsigned char *d, uint64_t _len) { + + CHECK_STATE(d); + + vector _hexArray( 2 * _len + 1); + + char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + for (uint64_t j = 0; j < _len; j++) { + _hexArray[j * 2] = hexval[((d[j] >> 4) & 0xF)]; + _hexArray[j * 2 + 1] = hexval[(d[j]) & 0x0F]; + } + + _hexArray[_len * 2] = 0; + + return _hexArray; +} + +bool hex2carray(const char *_hex, uint64_t *_bin_len, + uint8_t *_bin, uint64_t _max_length) { + CHECK_STATE(_hex); + CHECK_STATE(_bin); + CHECK_STATE(_bin_len) + + uint64_t len = strnlen(_hex, 2 * _max_length + 1); + + CHECK_STATE(len != 2 * _max_length + 1); + + CHECK_STATE(len <= 2 * _max_length); + + if (len == 0 && len % 2 == 1) + return false; + + *_bin_len = len / 2; + + for (uint64_t i = 0; i < len / 2; i++) { + int high = char2int((char) _hex[i * 2]); + int low = char2int((char) _hex[i * 2 + 1]); + + if (high < 0 || low < 0) { + return false; + } + + _bin[i] = (unsigned char) (high * 16 + low); + } + + return true; +} \ No newline at end of file diff --git a/CryptoTools.h b/CryptoTools.h new file mode 100644 index 00000000..9eed91ac --- /dev/null +++ b/CryptoTools.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2021-Present SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file CryptoTools.h + @author Oleh Nikolaiev + @date 2021 +*/ + +#ifndef SGXWALLET_CRYPTOTOOLS_H +#define SGXWALLET_CRYPTOTOOLS_H + +#ifdef __cplusplus +#define EXTERNC extern "C" +#else +#define EXTERNC +#endif + +#include "stddef.h" +#include "stdint.h" +#include +#include + +EXTERNC int char2int(char _input); + +EXTERNC std::vector carray2Hex(const unsigned char *d, uint64_t _len); + +EXTERNC bool hex2carray(const char * _hex, uint64_t *_bin_len, + uint8_t* _bin, uint64_t _max_length ); + +#endif // SGXWALLET_CRYPTOTOOLS_H diff --git a/TECrypto.cpp b/TECrypto.cpp new file mode 100644 index 00000000..a943385b --- /dev/null +++ b/TECrypto.cpp @@ -0,0 +1,54 @@ +/* + Copyright (C) 2021-Present SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file TECrypto.cpp + @author Oleh Nikolaiev + @date 2021 +*/ + +#include +#include "leveldb/db.h" +#include + +#include "threshold_encryption/threshold_encryption.h" + +#include "sgxwallet_common.h" +#include "sgxwallet.h" +#include "SGXException.h" +#include "third_party/spdlog/spdlog.h" +#include "common.h" +#include "SGXWalletServer.h" + +#include "SEKManager.h" +#include "LevelDB.h" +#include "ServerInit.h" +#include "TECrypto.h" +#include "CryptoTools.h" + +vector calculateDecryptionShare(const string& encryptedKeyShare, + const string& publicDecryptionValue) { + size_t sz = 0; + + SAFE_UINT8_BUF(encryptedKey, BUF_LEN); + + bool result = hex2carray(encryptedKeyShare.data(), &sz, encryptedKey, BUF_LEN); + + if (!result) { + BOOST_THROW_EXCEPTION(invalid_argument("Invalid hex encrypted key")); + } +} \ No newline at end of file diff --git a/TECrypto.h b/TECrypto.h new file mode 100644 index 00000000..4c03dd57 --- /dev/null +++ b/TECrypto.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2021-Present SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file TECrypto.h + @author Oleh Nikolaiev + @date 2021 +*/ + +#ifndef SGXWALLET_TECRYPTO_H +#define SGXWALLET_TECRYPTO_H + +#ifdef __cplusplus +#define EXTERNC extern "C" +#else +#define EXTERNC +#endif + +#include "stddef.h" +#include "stdint.h" +#include +#include + +std::vector calculateDecryptionShare(const std::string& encryptedKeyShare, + const std::string& publicDecryptionValue); + +#endif // SGXWALLET_TECRYPTO_H diff --git a/secure_enclave/TEUtils.cpp b/secure_enclave/TEUtils.cpp new file mode 100644 index 00000000..16411b49 --- /dev/null +++ b/secure_enclave/TEUtils.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2021-Present SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file TEUtils.cpp + @author Oleh Nikolaiev + @date 2021 +*/ + +#ifndef SGXWALLET_DKGUTILS_H +#define SGXWALLET_DKGUTILS_H + +#ifdef __cplusplus +#define EXTERNC extern "C" +#else +#define EXTERNC +#endif + +#ifdef USER_SPACE + +#include +#else +#include <../tgmp-build/include/sgx_tgmp.h> +#endif + +EXTERNC int getDecryptionShare( char* secret, unsigned _t); + +#endif diff --git a/secure_enclave/TEUtils.h b/secure_enclave/TEUtils.h new file mode 100644 index 00000000..1262d1a8 --- /dev/null +++ b/secure_enclave/TEUtils.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2021-Present SKALE Labs + + This file is part of sgxwallet. + + sgxwallet is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + sgxwallet is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with sgxwallet. If not, see . + + @file TEUtils.h + @author Oleh Nikolaiev + @date 2021 +*/ + +#ifndef SGXWALLET_DKGUTILS_H +#define SGXWALLET_DKGUTILS_H + +#ifdef __cplusplus +#define EXTERNC extern "C" +#else +#define EXTERNC +#endif + +#ifdef USER_SPACE + +#include +#else +#include <../tgmp-build/include/sgx_tgmp.h> +#endif + +EXTERNC int getDecryptionShare(char* secret, unsigned _t); + +#endif From 63f349878e42980633863357fdd36da17664ad4f Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Sun, 1 Aug 2021 19:57:27 +0300 Subject: [PATCH 044/100] SKALE-4411 add new function to http server --- SGXWalletServer.cpp | 12 +-- SGXWalletServer.hpp | 2 +- TECrypto.cpp | 24 ++++- abstractstubserver.h | 6 +- secure_enclave/Makefile.am | 4 +- secure_enclave/Makefile.in | 137 ++++++++++++++++-------- secure_enclave/TEUtils.cpp | 171 +++++++++++++++++++++++++++++- secure_enclave/TEUtils.h | 2 +- secure_enclave/secure_enclave.c | 33 ++++++ secure_enclave/secure_enclave.edl | 10 +- stubclient.h | 13 +++ testw.cpp | 32 ++++++ testw.py | 3 +- 13 files changed, 387 insertions(+), 62 deletions(-) diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index 0db75333..1d113918 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -985,20 +985,20 @@ SGXWalletServer::createBLSPrivateKeyV2Impl(const string &_blsKeyName, const stri RETURN_SUCCESS(result); } -Json::Value SGXWalletServer::getDecryptionShareImpl(const std::string& teKeyName, const std::string& publicDecryptionValue) { +Json::Value SGXWalletServer::getDecryptionShareImpl(const std::string& blsKeyName, const std::string& publicDecryptionValue) { spdlog::info("Entering {}", __FUNCTION__); INIT_RESULT(result) try { - if (!checkName(teKeyName, "BLS_KEY")) { + if (!checkName(blsKeyName, "BLS_KEY")) { throw SGXException(BLS_SIGN_INVALID_KS_NAME, string(__FUNCTION__) + ":Invalid BLSKey name"); } - shared_ptr encryptedKeyHex_ptr = readFromDb(teKeyName); + shared_ptr encryptedKeyHex_ptr = readFromDb(blsKeyName); vector decryptionValueVector = calculateDecryptionShare(encryptedKeyHex_ptr->c_str(), publicDecryptionValue); for (uint8_t i = 0; i < 4; ++i) { - result["decryptionValue"][i] = decryptionValueVector.at(i); + result["decryptionShare"][i] = decryptionValueVector.at(i); } } HANDLE_SGX_EXCEPTION(result) @@ -1105,8 +1105,8 @@ SGXWalletServer::createBLSPrivateKeyV2(const string &blsKeyName, const string &e return createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, SecretShare, t, n); } -Json::Value SGXWalletServer::getDecryptionShare(const std::string& teKeyName, const std::string& publicDecryptionValue) { - return getDecryptionShareImpl(teKeyName, publicDecryptionValue); +Json::Value SGXWalletServer::getDecryptionShare(const std::string& blsKeyName, const std::string& publicDecryptionValue) { + return getDecryptionShareImpl(blsKeyName, publicDecryptionValue); } shared_ptr SGXWalletServer::readFromDb(const string &name, const string &prefix) { diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index ac021ae9..d054de4f 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -176,7 +176,7 @@ class SGXWalletServer : public AbstractStubServer { static Json::Value createBLSPrivateKeyV2Impl(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string & SecretShare, int t, int n); - static Json::Value getDecryptionShareImpl(const std::string& teKeyName, const std::string& publicDecryptionValue); + static Json::Value getDecryptionShareImpl(const std::string& KeyName, const std::string& publicDecryptionValue); static void printDB(); diff --git a/TECrypto.cpp b/TECrypto.cpp index a943385b..2dc0bb34 100644 --- a/TECrypto.cpp +++ b/TECrypto.cpp @@ -34,12 +34,11 @@ #include "common.h" #include "SGXWalletServer.h" -#include "SEKManager.h" -#include "LevelDB.h" -#include "ServerInit.h" #include "TECrypto.h" #include "CryptoTools.h" +#include + vector calculateDecryptionShare(const string& encryptedKeyShare, const string& publicDecryptionValue) { size_t sz = 0; @@ -50,5 +49,22 @@ vector calculateDecryptionShare(const string& encryptedKeyShare, if (!result) { BOOST_THROW_EXCEPTION(invalid_argument("Invalid hex encrypted key")); - } + } + + SAFE_CHAR_BUF(decryptionShare, BUF_LEN) + + vector errMsg(BUF_LEN, 0); + + int errStatus = 0; + + sgx_status_t status = SGX_SUCCESS; + + status = trustedGetDecryptionShare(eid, &errStatus, errMsg.data(), encryptedKey, + publicDecryptionValue.data(), sz, decryptionShare); + + HANDLE_TRUSTED_FUNCTION_ERROR(status, errStatus, errMsg.data()); + + auto splitted_share = BLSutils::SplitString(std::make_shared(decryptionShare), ":"); + + return *splitted_share; } \ No newline at end of file diff --git a/abstractstubserver.h b/abstractstubserver.h index 5a19e2f6..e6d3d0cf 100644 --- a/abstractstubserver.h +++ b/abstractstubserver.h @@ -63,7 +63,7 @@ class AbstractStubServer : public jsonrpc::AbstractServer this->bindAndAddMethod(jsonrpc::Procedure("dkgVerificationV2", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "publicShares",jsonrpc::JSON_STRING, "ethKeyName",jsonrpc::JSON_STRING, "secretShare",jsonrpc::JSON_STRING,"t",jsonrpc::JSON_INTEGER, "n",jsonrpc::JSON_INTEGER, "index",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::dkgVerificationV2I); this->bindAndAddMethod(jsonrpc::Procedure("createBLSPrivateKeyV2", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "blsKeyName",jsonrpc::JSON_STRING, "ethKeyName",jsonrpc::JSON_STRING, "polyName", jsonrpc::JSON_STRING, "secretShare",jsonrpc::JSON_STRING,"t", jsonrpc::JSON_INTEGER,"n",jsonrpc::JSON_INTEGER, NULL), &AbstractStubServer::createBLSPrivateKeyV2I); - this->bindAndAddMethod(jsonrpc::Procedure("getDecryptionShare", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "teKeyName",jsonrpc::JSON_STRING,"publicDecryptionValue",jsonrpc::JSON_STRING, NULL), &AbstractStubServer::getDecryptionShareI); + this->bindAndAddMethod(jsonrpc::Procedure("getDecryptionShare", jsonrpc::PARAMS_BY_NAME, jsonrpc::JSON_OBJECT, "blsKeyName",jsonrpc::JSON_STRING,"publicDecryptionValue",jsonrpc::JSON_STRING, NULL), &AbstractStubServer::getDecryptionShareI); } inline virtual void importBLSKeyShareI(const Json::Value &request, Json::Value &response) @@ -165,7 +165,7 @@ class AbstractStubServer : public jsonrpc::AbstractServer inline virtual void getDecryptionShareI(const Json::Value &request, Json::Value &response) { - response = this->getDecryptionShare(request["teKeyName"].asString(), request["publicDecryptionValue"].asString()); + response = this->getDecryptionShare(request["blsKeyName"].asString(), request["publicDecryptionValue"].asString()); } virtual Json::Value importBLSKeyShare(const std::string& keyShare, const std::string& keyShareName) = 0; @@ -194,7 +194,7 @@ class AbstractStubServer : public jsonrpc::AbstractServer virtual Json::Value dkgVerificationV2( const std::string& publicShares, const std::string& ethKeyName, const std::string& SecretShare, int t, int n, int index) = 0; virtual Json::Value createBLSPrivateKeyV2(const std::string& blsKeyName, const std::string& ethKeyName, const std::string& polyName, const std::string & SecretShare, int t, int n) = 0; - virtual Json::Value getDecryptionShare(const std::string& teKeyName, const std::string& publicDecryptionValue) = 0; + virtual Json::Value getDecryptionShare(const std::string& KeyName, const std::string& publicDecryptionValue) = 0; }; #endif //JSONRPC_CPP_STUB_ABSTRACTSTUBSERVER_H_ diff --git a/secure_enclave/Makefile.am b/secure_enclave/Makefile.am index a69d48bb..d81e848b 100644 --- a/secure_enclave/Makefile.am +++ b/secure_enclave/Makefile.am @@ -84,7 +84,7 @@ CLEANFILES+= secure_enclave_t.c secure_enclave_t.h secure_enclave_SOURCES = secure_enclave_t.c secure_enclave_t.h \ secure_enclave.c \ Curves.c NumberTheory.c Point.c Signature.c DHDkg.c AESUtils.c \ - DKGUtils.cpp EnclaveCommon.cpp DomainParameters.cpp ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_init.cpp \ + DKGUtils.cpp TEUtils.cpp EnclaveCommon.cpp DomainParameters.cpp ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_init.cpp \ ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_g2.cpp \ ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp $(ENCLAVE_KEY) $(ENCLAVE_CONFIG) @@ -113,7 +113,7 @@ secure_enclave_LDADD = @SGX_ENCLAVE_LDADD@ ## --startgroup and --endgroup flags. (This would be where you'd add ## SGXSSL libraries, and your trusted c++ library -SGX_EXTRA_TLIBS=-lsgx_tgmp -lsgx_tservice -lsgx_urts -lsgx_tcxx +SGX_EXTRA_TLIBS=-lsgx_tgmp -lsgx_tservice -lsgx_urts -lsgx_tcxx diff --git a/secure_enclave/Makefile.in b/secure_enclave/Makefile.in index 4031e17f..af926874 100644 --- a/secure_enclave/Makefile.in +++ b/secure_enclave/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -110,9 +110,10 @@ am_secure_enclave_OBJECTS = secure_enclave_t.$(OBJEXT) \ secure_enclave.$(OBJEXT) Curves.$(OBJEXT) \ NumberTheory.$(OBJEXT) Point.$(OBJEXT) Signature.$(OBJEXT) \ DHDkg.$(OBJEXT) AESUtils.$(OBJEXT) DKGUtils.$(OBJEXT) \ - EnclaveCommon.$(OBJEXT) DomainParameters.$(OBJEXT) \ - alt_bn128_init.$(OBJEXT) alt_bn128_g2.$(OBJEXT) \ - alt_bn128_g1.$(OBJEXT) $(am__objects_1) $(am__objects_1) + TEUtils.$(OBJEXT) EnclaveCommon.$(OBJEXT) \ + DomainParameters.$(OBJEXT) alt_bn128_init.$(OBJEXT) \ + alt_bn128_g2.$(OBJEXT) alt_bn128_g1.$(OBJEXT) $(am__objects_1) \ + $(am__objects_1) secure_enclave_OBJECTS = $(am_secure_enclave_OBJECTS) secure_enclave_DEPENDENCIES = @ENCLAVE_RELEASE_SIGN_FALSE@nodist_signed_enclave_debug_OBJECTS = \ @@ -137,7 +138,17 @@ am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/AESUtils.Po ./$(DEPDIR)/Curves.Po \ + ./$(DEPDIR)/DHDkg.Po ./$(DEPDIR)/DKGUtils.Po \ + ./$(DEPDIR)/DomainParameters.Po ./$(DEPDIR)/EnclaveCommon.Po \ + ./$(DEPDIR)/NumberTheory.Po ./$(DEPDIR)/Point.Po \ + ./$(DEPDIR)/Signature.Po ./$(DEPDIR)/TEUtils.Po \ + ./$(DEPDIR)/alt_bn128_g1.Po ./$(DEPDIR)/alt_bn128_g2.Po \ + ./$(DEPDIR)/alt_bn128_init.Po ./$(DEPDIR)/secure_enclave.Po \ + ./$(DEPDIR)/secure_enclave_t.Po \ + ./$(DEPDIR)/signed_enclave_debug.Po \ + ./$(DEPDIR)/signed_enclave_rel.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -338,12 +349,12 @@ ENCLAVE_KEY = test_insecure_private_key.pem #$(ENCLAVE)_private.pem secure_enclave_SOURCES = secure_enclave_t.c secure_enclave_t.h \ secure_enclave.c \ Curves.c NumberTheory.c Point.c Signature.c DHDkg.c AESUtils.c \ - DKGUtils.cpp EnclaveCommon.cpp DomainParameters.cpp ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_init.cpp \ + DKGUtils.cpp TEUtils.cpp EnclaveCommon.cpp DomainParameters.cpp ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_init.cpp \ ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_g2.cpp \ ../third_party/SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp $(ENCLAVE_KEY) $(ENCLAVE_CONFIG) secure_enclave_LDADD = @SGX_ENCLAVE_LDADD@ -SGX_EXTRA_TLIBS = -lsgx_tgmp -lsgx_tservice -lsgx_urts -lsgx_tcxx +SGX_EXTRA_TLIBS = -lsgx_tgmp -lsgx_tservice -lsgx_urts -lsgx_tcxx all: all-am .SUFFIXES: @@ -365,8 +376,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_srcdir)/build-aux/sgx_enclave.am $(am__empty): @@ -431,22 +442,29 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AESUtils.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Curves.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHDkg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DKGUtils.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DomainParameters.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EnclaveCommon.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NumberTheory.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Point.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Signature.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alt_bn128_g1.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alt_bn128_g2.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alt_bn128_init.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/secure_enclave.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/secure_enclave_t.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signed_enclave_debug.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signed_enclave_rel.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AESUtils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Curves.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHDkg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DKGUtils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DomainParameters.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EnclaveCommon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NumberTheory.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Point.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Signature.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TEUtils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alt_bn128_g1.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alt_bn128_g2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alt_bn128_init.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/secure_enclave.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/secure_enclave_t.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signed_enclave_debug.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signed_enclave_rel.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -570,7 +588,10 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -643,7 +664,23 @@ clean: clean-am clean-am: clean-generic clean-libexecPROGRAMS mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/AESUtils.Po + -rm -f ./$(DEPDIR)/Curves.Po + -rm -f ./$(DEPDIR)/DHDkg.Po + -rm -f ./$(DEPDIR)/DKGUtils.Po + -rm -f ./$(DEPDIR)/DomainParameters.Po + -rm -f ./$(DEPDIR)/EnclaveCommon.Po + -rm -f ./$(DEPDIR)/NumberTheory.Po + -rm -f ./$(DEPDIR)/Point.Po + -rm -f ./$(DEPDIR)/Signature.Po + -rm -f ./$(DEPDIR)/TEUtils.Po + -rm -f ./$(DEPDIR)/alt_bn128_g1.Po + -rm -f ./$(DEPDIR)/alt_bn128_g2.Po + -rm -f ./$(DEPDIR)/alt_bn128_init.Po + -rm -f ./$(DEPDIR)/secure_enclave.Po + -rm -f ./$(DEPDIR)/secure_enclave_t.Po + -rm -f ./$(DEPDIR)/signed_enclave_debug.Po + -rm -f ./$(DEPDIR)/signed_enclave_rel.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -689,7 +726,23 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/AESUtils.Po + -rm -f ./$(DEPDIR)/Curves.Po + -rm -f ./$(DEPDIR)/DHDkg.Po + -rm -f ./$(DEPDIR)/DKGUtils.Po + -rm -f ./$(DEPDIR)/DomainParameters.Po + -rm -f ./$(DEPDIR)/EnclaveCommon.Po + -rm -f ./$(DEPDIR)/NumberTheory.Po + -rm -f ./$(DEPDIR)/Point.Po + -rm -f ./$(DEPDIR)/Signature.Po + -rm -f ./$(DEPDIR)/TEUtils.Po + -rm -f ./$(DEPDIR)/alt_bn128_g1.Po + -rm -f ./$(DEPDIR)/alt_bn128_g2.Po + -rm -f ./$(DEPDIR)/alt_bn128_init.Po + -rm -f ./$(DEPDIR)/secure_enclave.Po + -rm -f ./$(DEPDIR)/secure_enclave_t.Po + -rm -f ./$(DEPDIR)/signed_enclave_debug.Po + -rm -f ./$(DEPDIR)/signed_enclave_rel.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -709,19 +762,19 @@ uninstall-am: uninstall-libexecPROGRAMS .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libexecPROGRAMS cscopelist-am ctags ctags-am distclean \ - distclean-compile distclean-generic distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-libexecPROGRAMS \ - install-man install-pdf install-pdf-am install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ - ps ps-am tags tags-am uninstall uninstall-am \ - uninstall-libexecPROGRAMS +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libexecPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libexecPROGRAMS install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-libexecPROGRAMS .PRECIOUS: Makefile diff --git a/secure_enclave/TEUtils.cpp b/secure_enclave/TEUtils.cpp index 16411b49..3ffa7f14 100644 --- a/secure_enclave/TEUtils.cpp +++ b/secure_enclave/TEUtils.cpp @@ -37,6 +37,175 @@ #include <../tgmp-build/include/sgx_tgmp.h> #endif -EXTERNC int getDecryptionShare( char* secret, unsigned _t); +#include +#include +#include +#include + +#include <../SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_pp.hpp> +#include <../SCIPR/libff/algebra/fields/fp.hpp> + +#include <../SCIPR/libff/algebra/curves/alt_bn128/alt_bn128_g2.hpp> + +#include "EnclaveConstants.h" +#include "EnclaveCommon.h" +#include "TEUtils.h" + +template +std::string fieldElementToString(const T &field_elem, int base = 10) { + + std::string ret; + + mpz_t t; + mpz_init(t); + + try { + + field_elem.as_bigint().to_mpz(t); + + SAFE_CHAR_BUF(arr, BUF_LEN); + + char *tmp = mpz_get_str(arr, base, t); + + ret = std::string(tmp); + + goto clean; + + } catch (std::exception &e) { + LOG_ERROR(e.what()); + goto clean; + } catch (...) { + LOG_ERROR("Unknown throwable"); + goto clean; + } + + clean: + mpz_clear(t); + return ret; +} + +std::string ConvertG2ElementToString(const libff::alt_bn128_G2 &elem, int base = 10, const std::string &delim = ":") { + + std::string result = ""; + + try { + + result += fieldElementToString(elem.X.c0); + result += delim; + result += fieldElementToString(elem.X.c1); + result += delim; + result += fieldElementToString(elem.Y.c0); + result += delim; + result += fieldElementToString(elem.Y.c1); + + return result; + + } catch (std::exception &e) { + LOG_ERROR(e.what()); + return result; + } catch (...) { + LOG_ERROR("Unknown throwable"); + return result; + } + + return result; +} + +std::vector SplitStringToFq(const char *coeffs, const char symbol) { + std::vector result; + std::string str(coeffs); + std::string delim; + + CHECK_ARG_CLEAN(coeffs); + + try { + + delim.push_back(symbol); + + size_t prev = 0, pos = 0; + do { + pos = str.find(delim, prev); + if (pos == std::string::npos) pos = str.length(); + std::string token = str.substr(prev, pos - prev); + if (!token.empty()) { + libff::alt_bn128_Fq coeff(token.c_str()); + result.push_back(coeff); + } + prev = pos + delim.length(); + } while (pos < str.length() && prev < str.length()); + + return result; + + } catch (std::exception &e) { + LOG_ERROR(e.what()); + return result; + } catch (...) { + LOG_ERROR("Unknown throwable"); + return result; + } + + clean: + return result; +} + +EXTERNC int getDecryptionShare(char* skey_hex, char* decryptionValue, char* decryption_share) { + mpz_t skey; + mpz_init(skey); + + int ret = 1; + + CHECK_ARG_CLEAN(skey_hex); + CHECK_ARG_CLEAN(decryptionValue); + CHECK_ARG_CLEAN(decryption_share); + + try { + if (mpz_set_str(skey, skey_hex, 16) == -1) { + mpz_clear(skey); + return 1; + } + + char skey_dec[mpz_sizeinbase(skey, 10) + 2]; + mpz_get_str(skey_dec, 10, skey); + + libff::alt_bn128_Fr bls_skey(skey_dec); + + auto splitted_decryption_value = SplitStringToFq(decryptionValue, ':'); + + libff::alt_bn128_G2 decryption_value; + decryption_value.Z = libff::alt_bn128_Fq2::one(); + + decryption_value.X.c0 = splitted_decryption_value[0]; + decryption_value.X.c1 = splitted_decryption_value[1]; + decryption_value.Y.c0 = splitted_decryption_value[2]; + decryption_value.Y.c1 = splitted_decryption_value[3]; + + if ( !decryption_value.is_well_formed() ) { + mpz_clear(skey); + return 1; + } + + libff::alt_bn128_G2 decryption_share_point = bls_skey * decryption_value; + decryption_share_point.to_affine_coordinates(); + + std::string result = ConvertG2ElementToString(decryption_share_point); + + strncpy(decryption_share, result.c_str(), result.length()); + + mpz_clear(skey); + + return 0; + + } catch (std::exception &e) { + LOG_ERROR(e.what()); + return 1; + } catch (...) { + LOG_ERROR("Unknown throwable"); + return 1; + } + + clean: + mpz_clear(skey); + return ret; +} #endif diff --git a/secure_enclave/TEUtils.h b/secure_enclave/TEUtils.h index 1262d1a8..0e5c5442 100644 --- a/secure_enclave/TEUtils.h +++ b/secure_enclave/TEUtils.h @@ -37,6 +37,6 @@ #include <../tgmp-build/include/sgx_tgmp.h> #endif -EXTERNC int getDecryptionShare(char* secret, unsigned _t); +EXTERNC int getDecryptionShare(char* secret, char* decryptionValue, char* decryption_share); #endif diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 17855c9b..c67bfbac 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -54,6 +54,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Curves.h" #include "DHDkg.h" #include "AESUtils.h" +#include "TEUtils.h" #include "EnclaveConstants.h" #include "EnclaveCommon.h" @@ -1368,3 +1369,35 @@ trustedGetBlsPubKey(int *errStatus, char *errString, uint8_t *encryptedPrivateKe clean: ; } + +void trustedGetDecryptionShare( int *errStatus, char* errString, uint8_t* encryptedPrivateKey, + const char* public_decryption_value, uint64_t key_len, + char* decryption_share ) { + LOG_DEBUG(__FUNCTION__); + + INIT_ERROR_STATE + + CHECK_STATE(decryption_share); + CHECK_STATE(encryptedPrivateKey); + + SAFE_CHAR_BUF(skey_hex, BUF_LEN); + + uint8_t type = 0; + uint8_t exportable = 0; + + int status = AES_decrypt(encryptedPrivateKey, key_len, skey_hex, BUF_LEN, + &type, &exportable); + + CHECK_STATUS2("AES decrypt failed %d"); + + skey_hex[ECDSA_SKEY_LEN - 1] = 0; + + status = getDecryptionShare(skey_hex, public_decryption_value, decryption_share); + + CHECK_STATUS("could not calculate decryption share"); + + SET_SUCCESS + + clean: + ; +} diff --git a/secure_enclave/secure_enclave.edl b/secure_enclave/secure_enclave.edl index 27a84f92..1eaee973 100644 --- a/secure_enclave/secure_enclave.edl +++ b/secure_enclave/secure_enclave.edl @@ -174,10 +174,18 @@ enclave { public void trustedGetBlsPubKey( [out]int *errStatus, - [out, count = SMALL_BUF_SIZE] char* err_string, + [out, count = SMALL_BUF_SIZE] char* err_string, [in, count = SMALL_BUF_SIZE] uint8_t* encrypted_key, uint64_t key_len, [out, count = 320] char* bls_pub_key); + + public void trustedGetDecryptionShare( + [out]int *errStatus, + [out, count = SMALL_BUF_SIZE] char* err_string, + [in, count = SMALL_BUF_SIZE] uint8_t* encrypted_key, + [in, count = 320] const char* public_decryption_value, + uint64_t key_len, + [out, count = 320] char* decrption_share); }; untrusted { diff --git a/stubclient.h b/stubclient.h index 17d720c5..b2334ed5 100644 --- a/stubclient.h +++ b/stubclient.h @@ -214,6 +214,19 @@ class StubClient : public jsonrpc::Client throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); } + Json::Value getDecryptionShare(const std::string& blsKeyName, const std::string& publicDecryptionValue) + { + Json::Value p; + p["blsKeyName"] = blsKeyName; + p["publicDecryptionValue"] = publicDecryptionValue; + + Json::Value result = this->CallMethod("getDecryptionShare",p); + if (result.isObject()) + return result; + else + throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString()); + } + Json::Value calculateAllBLSPublicKeys(const Json::Value& publicShares, int t, int n) { Json::Value p; diff --git a/testw.cpp b/testw.cpp index 9d68bbd1..1f3d585c 100644 --- a/testw.cpp +++ b/testw.cpp @@ -1183,6 +1183,38 @@ TEST_CASE_METHOD(TestFixtureNoReset, "Second run", "[second-run]") { } } +TEST_CASE_METHOD(TestFixture, "Test decryption share for threshold encryption", "[te-decryption-share]") { + // auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + // "./sgx_data/cert_data/rootCA.key"); + + HttpClient client(RPC_ENDPOINT); + StubClient c(client, JSONRPC_CLIENT_V2); + + std::string key_str = "0xe632f7fde2c90a073ec43eaa90dca7b82476bf28815450a11191484934b9c3f"; + std::string name = "BLS_KEY:SCHAIN_ID:123456789:NODE_ID:0:DKG_ID:0"; + auto response = c.importBLSKeyShare(key_str, name); + + // the same key writtn in decimal + libff::alt_bn128_Fr key = libff::alt_bn128_Fr( + "6507625568967977077291849236396320012317305261598035438182864059942098934847"); + + libff::alt_bn128_G2 decryption_value = libff::alt_bn128_G2::random_element(); + decryption_value.to_affine_coordinates(); + + auto decrytion_value_str = convertG2ToString( decryption_value, ':' ); + auto decryption_share = c.getDecryptionShare( name, decrytion_value_str )["decryptionShare"]; + + libff::alt_bn128_G2 share; + share.Z = libff::alt_bn128_Fq2::one(); + + share.X.c0 = libff::alt_bn128_Fq( decryption_share[0].asCString() ); + share.X.c1 = libff::alt_bn128_Fq( decryption_share[1].asCString() ); + share.Y.c0 = libff::alt_bn128_Fq( decryption_share[2].asCString() ); + share.Y.c1 = libff::alt_bn128_Fq( decryption_share[3].asCString() ); + + REQUIRE( share == key * decryption_value ); +} + TEST_CASE_METHOD(TestFixtureZMQSign, "ZMQ-ecdsa", "[zmq-ecdsa]") { HttpClient htp(RPC_ENDPOINT); diff --git a/testw.py b/testw.py index e8bce540..e3d6053e 100755 --- a/testw.py +++ b/testw.py @@ -62,7 +62,8 @@ "[dkg-aes-pub-shares]", "[aes-encrypt-decrypt]", "[aes-dkg-v2]", - "[aes-dkg-v2-zmq]" + "[aes-dkg-v2-zmq]", + "[te-decryption-share]" ] From 9b090a3a4c33e21760b21d4de196d042c319dbf0 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Sun, 1 Aug 2021 21:06:38 +0300 Subject: [PATCH 045/100] SKALE-4411 add new function to zmq server --- testw.cpp | 35 ++++++++++++++++++++++++++++++----- testw.py | 3 ++- zmq_src/ReqMessage.cpp | 8 ++++++++ zmq_src/ReqMessage.h | 7 +++++++ zmq_src/RspMessage.cpp | 4 ++++ zmq_src/RspMessage.h | 12 ++++++++++++ zmq_src/ZMQClient.cpp | 11 +++++++++++ zmq_src/ZMQClient.h | 2 ++ zmq_src/ZMQMessage.cpp | 12 ++++++++++-- zmq_src/ZMQMessage.h | 6 ++++-- 10 files changed, 90 insertions(+), 10 deletions(-) diff --git a/testw.cpp b/testw.cpp index 1f3d585c..ea0cf346 100644 --- a/testw.cpp +++ b/testw.cpp @@ -1184,15 +1184,12 @@ TEST_CASE_METHOD(TestFixtureNoReset, "Second run", "[second-run]") { } TEST_CASE_METHOD(TestFixture, "Test decryption share for threshold encryption", "[te-decryption-share]") { - // auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", - // "./sgx_data/cert_data/rootCA.key"); - HttpClient client(RPC_ENDPOINT); StubClient c(client, JSONRPC_CLIENT_V2); std::string key_str = "0xe632f7fde2c90a073ec43eaa90dca7b82476bf28815450a11191484934b9c3f"; std::string name = "BLS_KEY:SCHAIN_ID:123456789:NODE_ID:0:DKG_ID:0"; - auto response = c.importBLSKeyShare(key_str, name); + c.importBLSKeyShare(key_str, name); // the same key writtn in decimal libff::alt_bn128_Fr key = libff::alt_bn128_Fr( @@ -1215,8 +1212,36 @@ TEST_CASE_METHOD(TestFixture, "Test decryption share for threshold encryption", REQUIRE( share == key * decryption_value ); } -TEST_CASE_METHOD(TestFixtureZMQSign, "ZMQ-ecdsa", "[zmq-ecdsa]") { +TEST_CASE_METHOD(TestFixture, "Test decryption share for threshold encryption via zmq", "[te-decryption-share-zmq]") { + auto client = make_shared(ZMQ_IP, ZMQ_PORT, true, "./sgx_data/cert_data/rootCA.pem", + "./sgx_data/cert_data/rootCA.key"); + + std::string key_str = "0xe632f7fde2c90a073ec43eaa90dca7b82476bf28815450a11191484934b9c3f"; + std::string name = "BLS_KEY:SCHAIN_ID:123456789:NODE_ID:0:DKG_ID:0"; + client->importBLSKeyShare(key_str, name); + + // the same key writtn in decimal + libff::alt_bn128_Fr key = libff::alt_bn128_Fr( + "6507625568967977077291849236396320012317305261598035438182864059942098934847"); + + libff::alt_bn128_G2 decryption_value = libff::alt_bn128_G2::random_element(); + decryption_value.to_affine_coordinates(); + auto decrytion_value_str = convertG2ToString( decryption_value, ':' ); + auto decryption_share = client->getDecryptionShare( name, decrytion_value_str ); + + libff::alt_bn128_G2 share; + share.Z = libff::alt_bn128_Fq2::one(); + + share.X.c0 = libff::alt_bn128_Fq( decryption_share[0].asCString() ); + share.X.c1 = libff::alt_bn128_Fq( decryption_share[1].asCString() ); + share.Y.c0 = libff::alt_bn128_Fq( decryption_share[2].asCString() ); + share.Y.c1 = libff::alt_bn128_Fq( decryption_share[3].asCString() ); + + REQUIRE( share == key * decryption_value ); +} + +TEST_CASE_METHOD(TestFixtureZMQSign, "ZMQ-ecdsa", "[zmq-ecdsa]") { HttpClient htp(RPC_ENDPOINT); StubClient c(htp, JSONRPC_CLIENT_V2); diff --git a/testw.py b/testw.py index e3d6053e..8334391e 100755 --- a/testw.py +++ b/testw.py @@ -63,7 +63,8 @@ "[aes-encrypt-decrypt]", "[aes-dkg-v2]", "[aes-dkg-v2-zmq]", - "[te-decryption-share]" + "[te-decryption-share]", + "[te-decryption-share-zmq]" ] diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index 3bef6310..4cf3c1f0 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -183,3 +183,11 @@ Json::Value deleteBLSKeyReqMessage::process() { result["type"] = ZMQMessage::DELETE_BLS_KEY_RSP; return result; } + +Json::Value GetDecryptionShareReqMessage::process() { + auto blsKeyName = getStringRapid("blsKeyName"); + auto publicDecryptionValue = getStringRapid("publicDecryptionValue"); + auto result = SGXWalletServer::getDecryptionShareImpl(blsKeyName, publicDecryptionValue); + result["type"] = ZMQMessage::GET_DECRYPTION_SHARE_RSP; + return result; +} diff --git a/zmq_src/ReqMessage.h b/zmq_src/ReqMessage.h index 252313aa..cb1815fc 100644 --- a/zmq_src/ReqMessage.h +++ b/zmq_src/ReqMessage.h @@ -178,4 +178,11 @@ class deleteBLSKeyReqMessage : public ZMQMessage { virtual Json::Value process(); }; +class GetDecryptionShareReqMessage : public ZMQMessage { +public: + GetDecryptionShareReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + #endif //SGXWALLET_REQMESSAGE_H diff --git a/zmq_src/RspMessage.cpp b/zmq_src/RspMessage.cpp index 3ffa6873..61840678 100644 --- a/zmq_src/RspMessage.cpp +++ b/zmq_src/RspMessage.cpp @@ -110,3 +110,7 @@ Json::Value getServerVersionRspMessage::process() { Json::Value deleteBLSKeyRspMessage::process() { assert(false); } + +Json::Value GetDecryptionShareRspMessage::process() { + assert(false); +} diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h index 95ee95e8..09c026aa 100644 --- a/zmq_src/RspMessage.h +++ b/zmq_src/RspMessage.h @@ -248,4 +248,16 @@ class deleteBLSKeyRspMessage : public ZMQMessage { }; +class GetDecryptionShareRspMessage : public ZMQMessage { +public: + GetDecryptionShareRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); + + Json::Value getShare() { + return getJsonValueRapid("decryptionShare"); + } +}; + + #endif //SGXWALLET_RSPMESSAGE_H diff --git a/zmq_src/ZMQClient.cpp b/zmq_src/ZMQClient.cpp index 0a4c3d21..b3f47ca3 100644 --- a/zmq_src/ZMQClient.cpp +++ b/zmq_src/ZMQClient.cpp @@ -496,6 +496,17 @@ bool ZMQClient::deleteBLSKey(const string& blsKeyName) { return result->isSuccessful(); } +Json::Value ZMQClient::getDecryptionShare(const string& blsKeyName, const string& publicDecryptionValue) { + Json::Value p; + p["type"] = ZMQMessage::GET_DECRYPTION_SHARE_REQ; + p["blsKeyName"] = blsKeyName; + p["publicDecryptionValue"] = publicDecryptionValue; + auto result = dynamic_pointer_cast(doRequestReply(p)); + CHECK_STATE(result); + CHECK_STATE(result->getStatus() == 0); + return result->getShare(); +} + uint64_t ZMQClient::getProcessID() { return syscall(__NR_gettid); } diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 8901be10..5386953e 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -122,6 +122,8 @@ class ZMQClient { string getServerVersion(); bool deleteBLSKey(const string& blsKeyName); + + Json::Value getDecryptionShare(const string& blsKeyName, const string& publicDecryptionValue); }; diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 70eb1d99..5049a569 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -227,6 +227,9 @@ shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr (_d); break; + case ENUM_GET_DECRYPTION_SHARE_REQ: + ret = make_shared(_d); + break; default: break; } @@ -305,6 +308,9 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr (_d); break; + case ENUM_GET_DECRYPTION_SHARE_RSP: + ret = make_shared(_d); + break; default: break; } @@ -320,7 +326,8 @@ const std::map ZMQMessage::requests{ {GET_VV_REQ, 7}, {GET_SECRET_SHARE_REQ, 8}, {DKG_VERIFY_REQ, 9}, {CREATE_BLS_PRIVATE_REQ, 10}, {GET_BLS_PUBLIC_REQ, 11}, {GET_ALL_BLS_PUBLIC_REQ, 12}, {COMPLAINT_RESPONSE_REQ, 13}, {MULT_G2_REQ, 14}, {IS_POLY_EXISTS_REQ, 15}, - {GET_SERVER_STATUS_REQ, 16}, {GET_SERVER_VERSION_REQ, 17}, {DELETE_BLS_KEY_REQ, 18} + {GET_SERVER_STATUS_REQ, 16}, {GET_SERVER_VERSION_REQ, 17}, {DELETE_BLS_KEY_REQ, 18}, + {GET_DECRYPTION_SHARE_REQ, 19} }; const std::map ZMQMessage::responses { @@ -329,5 +336,6 @@ const std::map ZMQMessage::responses { {GET_VV_RSP, 7}, {GET_SECRET_SHARE_RSP, 8}, {DKG_VERIFY_RSP, 9}, {CREATE_BLS_PRIVATE_RSP, 10}, {GET_BLS_PUBLIC_RSP, 11}, {GET_ALL_BLS_PUBLIC_RSP, 12}, {COMPLAINT_RESPONSE_RSP, 13}, {MULT_G2_RSP, 14}, {IS_POLY_EXISTS_RSP, 15}, - {GET_SERVER_STATUS_RSP, 16}, {GET_SERVER_VERSION_RSP, 17}, {DELETE_BLS_KEY_RSP, 18} + {GET_SERVER_STATUS_RSP, 16}, {GET_SERVER_VERSION_RSP, 17}, {DELETE_BLS_KEY_RSP, 18}, + {GET_DECRYPTION_SHARE_RSP, 19} }; diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index e4f057b2..da0f41b6 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -91,6 +91,8 @@ class ZMQMessage { static constexpr const char *GET_SERVER_VERSION_RSP = "getServerVersionRsp"; static constexpr const char *DELETE_BLS_KEY_REQ = "deleteBLSKeyReq"; static constexpr const char *DELETE_BLS_KEY_RSP = "deleteBLSKeyRsp"; + static constexpr const char *GET_DECRYPTION_SHARE_REQ = "getDecryptionShareReq"; + static constexpr const char *GET_DECRYPTION_SHARE_RSP = "getDecryptionShareRsp"; static const std::map requests; static const std::map responses; @@ -98,11 +100,11 @@ class ZMQMessage { enum Requests { ENUM_BLS_SIGN_REQ, ENUM_ECDSA_SIGN_REQ, ENUM_IMPORT_BLS_REQ, ENUM_IMPORT_ECDSA_REQ, ENUM_GENERATE_ECDSA_REQ, ENUM_GET_PUBLIC_ECDSA_REQ, ENUM_GENERATE_DKG_POLY_REQ, ENUM_GET_VV_REQ, ENUM_GET_SECRET_SHARE_REQ, ENUM_DKG_VERIFY_REQ, ENUM_CREATE_BLS_PRIVATE_REQ, ENUM_GET_BLS_PUBLIC_REQ, ENUM_GET_ALL_BLS_PUBLIC_REQ, ENUM_COMPLAINT_RESPONSE_REQ, ENUM_MULT_G2_REQ, ENUM_IS_POLY_EXISTS_REQ, - ENUM_GET_SERVER_STATUS_REQ, ENUM_GET_SERVER_VERSION_REQ, ENUM_DELETE_BLS_KEY_REQ }; + ENUM_GET_SERVER_STATUS_REQ, ENUM_GET_SERVER_VERSION_REQ, ENUM_DELETE_BLS_KEY_REQ, ENUM_GET_DECRYPTION_SHARE_REQ }; enum Responses { ENUM_BLS_SIGN_RSP, ENUM_ECDSA_SIGN_RSP, ENUM_IMPORT_BLS_RSP, ENUM_IMPORT_ECDSA_RSP, ENUM_GENERATE_ECDSA_RSP, ENUM_GET_PUBLIC_ECDSA_RSP, ENUM_GENERATE_DKG_POLY_RSP, ENUM_GET_VV_RSP, ENUM_GET_SECRET_SHARE_RSP, ENUM_DKG_VERIFY_RSP, ENUM_CREATE_BLS_PRIVATE_RSP, ENUM_GET_BLS_PUBLIC_RSP, ENUM_GET_ALL_BLS_PUBLIC_RSP, ENUM_COMPLAINT_RESPONSE_RSP, ENUM_MULT_G2_RSP, ENUM_IS_POLY_EXISTS_RSP, - ENUM_GET_SERVER_STATUS_RSP, ENUM_GET_SERVER_VERSION_RSP, ENUM_DELETE_BLS_KEY_RSP }; + ENUM_GET_SERVER_STATUS_RSP, ENUM_GET_SERVER_VERSION_RSP, ENUM_DELETE_BLS_KEY_RSP, ENUM_GET_DECRYPTION_SHARE_RSP }; explicit ZMQMessage(shared_ptr &_d) : d(_d) {}; From baac2f5f7b77ff492801537d20ffcf50a1a5a7ca Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 2 Aug 2021 11:04:59 +0300 Subject: [PATCH 046/100] SKALE-4411 check key ownership for getDecryptionShare --- zmq_src/ReqMessage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index 2a3dc263..2942680b 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -237,6 +237,9 @@ Json::Value deleteBLSKeyReqMessage::process() { Json::Value GetDecryptionShareReqMessage::process() { auto blsKeyName = getStringRapid("blsKeyName"); auto publicDecryptionValue = getStringRapid("publicDecryptionValue"); + if (checkKeyOwnership && !isKeyByOwner(blsKeyName, getStringRapid("cert"))) { + throw std::invalid_argument("Only owner of the key can access it"); + } auto result = SGXWalletServer::getDecryptionShareImpl(blsKeyName, publicDecryptionValue); result["type"] = ZMQMessage::GET_DECRYPTION_SHARE_RSP; return result; From 9f4d903bf6ce7d2987f490da889831c4b23a11fe Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 2 Aug 2021 12:21:31 +0300 Subject: [PATCH 047/100] SKALE-4411 add format check --- SGXWalletServer.cpp | 4 ++++ secure_enclave/TEUtils.cpp | 6 +++--- sgxwallet_common.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index 1d113918..d41c5e2f 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -994,6 +994,10 @@ Json::Value SGXWalletServer::getDecryptionShareImpl(const std::string& blsKeyNam throw SGXException(BLS_SIGN_INVALID_KS_NAME, string(__FUNCTION__) + ":Invalid BLSKey name"); } + if ( publicDecryptionValue.length() < 7 || publicDecryptionValue.length() > 78 * 4 ) { + throw SGXException(INVALID_DECRYPTION_VALUE_FORMAT, string(__FUNCTION__) + ":Invalid publicDecryptionValue format"); + } + shared_ptr encryptedKeyHex_ptr = readFromDb(blsKeyName); vector decryptionValueVector = calculateDecryptionShare(encryptedKeyHex_ptr->c_str(), publicDecryptionValue); diff --git a/secure_enclave/TEUtils.cpp b/secure_enclave/TEUtils.cpp index 3ffa7f14..6947f3c9 100644 --- a/secure_enclave/TEUtils.cpp +++ b/secure_enclave/TEUtils.cpp @@ -111,12 +111,12 @@ std::string ConvertG2ElementToString(const libff::alt_bn128_G2 &elem, int base = return result; } -std::vector SplitStringToFq(const char *coeffs, const char symbol) { +std::vector SplitStringToFq(const char *coords, const char symbol) { std::vector result; - std::string str(coeffs); + std::string str(coords); std::string delim; - CHECK_ARG_CLEAN(coeffs); + CHECK_ARG_CLEAN(coords); try { diff --git a/sgxwallet_common.h b/sgxwallet_common.h index bc7c475d..dd80cda2 100644 --- a/sgxwallet_common.h +++ b/sgxwallet_common.h @@ -184,6 +184,7 @@ extern bool autoconfirm; #define SGX_SERVER_FAILED_TO_START -111 #define CORRUPT_DATABASE -112 #define INVALID_SEK -113 +#define INVALID_DECRYPTION_VALUE_FORMAT -114 #define SGX_ENCLAVE_ERROR -666 From da16094e1629114d33e700836eb5cbd38401ba67 Mon Sep 17 00:00:00 2001 From: Oleh Date: Mon, 2 Aug 2021 12:39:18 +0300 Subject: [PATCH 048/100] SKALE-4411 fix typo --- CryptoTools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoTools.cpp b/CryptoTools.cpp index 725b86b6..9b05ad2a 100644 --- a/CryptoTools.cpp +++ b/CryptoTools.cpp @@ -69,7 +69,7 @@ bool hex2carray(const char *_hex, uint64_t *_bin_len, CHECK_STATE(len <= 2 * _max_length); - if (len == 0 && len % 2 == 1) + if (len % 2 == 1) return false; *_bin_len = len / 2; From a9769d906f8a45fa076f056416992fe978b92d30 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 2 Aug 2021 13:03:45 +0300 Subject: [PATCH 049/100] SKALE-4262 check key ownership false by default --- sgxwall.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sgxwall.cpp b/sgxwall.cpp index 8f71fa70..42740c90 100644 --- a/sgxwall.cpp +++ b/sgxwall.cpp @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) { bool checkClientCertOption = true; bool autoSignClientCertOption = false; bool generateTestKeys = false; - bool checkKeyOwnership = true; + bool checkKeyOwnership = false; std::signal(SIGABRT, SGXWallet::signalHandler); From 119170448fe6ebece3782ed7e17bdd563ce9b6a5 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 2 Aug 2021 13:20:47 +0300 Subject: [PATCH 050/100] SKALE-4262 write key owner to db --- testw.cpp | 6 +++--- testw.py | 1 - zmq_src/ZMQMessage.cpp | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/testw.cpp b/testw.cpp index e9262081..3ee4e1a3 100644 --- a/testw.cpp +++ b/testw.cpp @@ -73,7 +73,7 @@ class TestFixture { TestFixture() { TestUtils::resetDB(); setOptions(L_INFO, false, true); - initAll(L_INFO, false, false, true, false, false); + initAll(L_INFO, false, false, true, false, true); } ~TestFixture() { @@ -114,7 +114,7 @@ class TestFixtureNoResetFromBackup { public: TestFixtureNoResetFromBackup() { setFullOptions(L_INFO, false, true, true); - initAll(L_INFO, false, false, true, false, false); + initAll(L_INFO, false, false, true, false, true); } ~TestFixtureNoResetFromBackup() { @@ -128,7 +128,7 @@ class TestFixtureNoReset { public: TestFixtureNoReset() { setOptions(L_INFO, false, true); - initAll(L_INFO, false, false, true, false, false); + initAll(L_INFO, false, false, true, false, true); } ~TestFixtureNoReset() { diff --git a/testw.py b/testw.py index e8bce540..ec513a2e 100755 --- a/testw.py +++ b/testw.py @@ -55,7 +55,6 @@ "[dkg-api-v2]", "[dkg-api-v2-zmq]", "[dkg-bls]", - "[dkgzmqbls]", "[dkg-bls-v2]", "[dkg-poly-exists]", "[dkg-poly-exists-zmq]", diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 2684635c..6ff65ddc 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -320,12 +320,12 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr ZMQMessage::keysByOwners; bool ZMQMessage::isKeyByOwner(const string& keyName, const string& cert) { - auto value = LevelDB::getLevelDb()->readString(keyName); + auto value = LevelDB::getLevelDb()->readString(keyName + ":OWNER"); return value && *value == cert; } void ZMQMessage::addKeyByOwner(const string& keyName, const string& cert) { - SGXWalletServer::writeDataToDB(keyName, cert); + SGXWalletServer::writeDataToDB(keyName + ":OWNER", cert); } cache::lru_cache> ZMQMessage::verifiedCerts(256); From 23fdca39071d0cf7d7702166b36d73e88365b062 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 2 Aug 2021 16:30:00 +0300 Subject: [PATCH 051/100] SKALE-4262 add more logs --- run_sgx_sim/docker-compose.yml | 2 +- zmq_src/ReqMessage.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/run_sgx_sim/docker-compose.yml b/run_sgx_sim/docker-compose.yml index aaeddba0..2972c918 100644 --- a/run_sgx_sim/docker-compose.yml +++ b/run_sgx_sim/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: sgxwallet: - image: skalenetwork/sgxwallet_sim:develop-latest + image: skalenetwork/sgxwallet_sim:119170448fe6ebece3782ed7e17bdd563ce9b6a5 restart: unless-stopped ports: - "1026:1026" diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index 2ca107e1..bb5d808e 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -26,12 +26,14 @@ #include "ReqMessage.h" +#include "third_party/spdlog/spdlog.h" Json::Value ECDSASignReqMessage::process() { auto base = getInt64Rapid("base"); auto keyName = getStringRapid("keyName"); auto hash = getStringRapid("messageHash"); if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), keyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::ecdsaSignMessageHashImpl(base, keyName, hash); @@ -45,6 +47,7 @@ Json::Value BLSSignReqMessage::process() { auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), keyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::blsSignMessageHashImpl(keyName, hash, t, n); @@ -57,6 +60,7 @@ Json::Value importBLSReqMessage::process() { auto keyShare = getStringRapid("keyShare"); auto result = SGXWalletServer::importBLSKeyShareImpl(keyShare, keyName); if (checkKeyOwnership && result["status"] == 0) { + spdlog::info("Cert {} creates key {}", getStringRapid("cert"), keyName); auto cert = getStringRapid("cert"); addKeyByOwner(keyName, cert); } @@ -90,6 +94,7 @@ Json::Value generateECDSAReqMessage::process() { Json::Value getPublicECDSAReqMessage::process() { auto keyName = getStringRapid("keyName"); if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), keyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::getPublicECDSAKeyImpl(keyName); @@ -103,6 +108,7 @@ Json::Value generateDKGPolyReqMessage::process() { auto result = SGXWalletServer::generateDKGPolyImpl(polyName, t); if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); + spdlog::info("Cert {} creates key {}", cert, polyName); addKeyByOwner(polyName, cert); } result["type"] = ZMQMessage::GENERATE_DKG_POLY_RSP; @@ -112,6 +118,7 @@ Json::Value generateDKGPolyReqMessage::process() { Json::Value getVerificationVectorReqMessage::process() { auto polyName = getStringRapid("polyName"); if (checkKeyOwnership && !isKeyByOwner(polyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), polyName); throw std::invalid_argument("Only owner of the key can access it"); } auto t = getInt64Rapid("t"); @@ -126,6 +133,7 @@ Json::Value getSecretShareReqMessage::process() { auto n = getInt64Rapid("n"); auto pubKeys = getJsonValueRapid("publicKeys"); if (checkKeyOwnership && !isKeyByOwner(polyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), polyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::getSecretShareV2Impl(polyName, pubKeys, t, n); @@ -141,6 +149,7 @@ Json::Value dkgVerificationReqMessage::process() { auto pubShares = getStringRapid("publicShares"); auto secretShare = getStringRapid("secretShare"); if (checkKeyOwnership && !isKeyByOwner(ethKeyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), ethKeyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::dkgVerificationV2Impl(pubShares, ethKeyName, secretShare, t, n, idx); @@ -156,10 +165,12 @@ Json::Value createBLSPrivateKeyReqMessage::process() { auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); if (checkKeyOwnership && (!isKeyByOwner(ethKeyName, getStringRapid("cert")) || !isKeyByOwner(polyName, getStringRapid("cert")))) { + spdlog::error("Cert {} try to access keys {} {} which do not belong to it", getStringRapid("cert"), ethKeyName ,polyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); if (checkKeyOwnership && result["status"] == 0) { + spdlog::info("Cert {} creates key {}", getStringRapid("cert"), blsKeyName); addKeyByOwner(blsKeyName, getStringRapid("cert")); } result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; @@ -169,6 +180,7 @@ Json::Value createBLSPrivateKeyReqMessage::process() { Json::Value getBLSPublicReqMessage::process() { auto blsKeyName = getStringRapid("blsKeyName"); if (checkKeyOwnership && !isKeyByOwner(blsKeyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), blsKeyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::getBLSPublicKeyShareImpl(blsKeyName); @@ -191,6 +203,7 @@ Json::Value complaintResponseReqMessage::process() { auto n = getInt64Rapid("n"); auto idx = getInt64Rapid("ind"); if (checkKeyOwnership && !isKeyByOwner(polyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), polyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::complaintResponseImpl(polyName, t, n, idx); @@ -227,6 +240,7 @@ Json::Value getServerVersionReqMessage::process() { Json::Value deleteBLSKeyReqMessage::process() { auto blsKeyName = getStringRapid("blsKeyName"); if (checkKeyOwnership && !isKeyByOwner(blsKeyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), blsKeyName); throw std::invalid_argument("Only owner of the key can access it"); } auto result = SGXWalletServer::deleteBlsKeyImpl(blsKeyName); From a904a3c298f892cb6c47ee9f33de47a6aa88ba1a Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 2 Aug 2021 16:31:08 +0300 Subject: [PATCH 052/100] SKALE-4262 edit docker image --- run_sgx_sim/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_sgx_sim/docker-compose.yml b/run_sgx_sim/docker-compose.yml index 2972c918..aaeddba0 100644 --- a/run_sgx_sim/docker-compose.yml +++ b/run_sgx_sim/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: sgxwallet: - image: skalenetwork/sgxwallet_sim:119170448fe6ebece3782ed7e17bdd563ce9b6a5 + image: skalenetwork/sgxwallet_sim:develop-latest restart: unless-stopped ports: - "1026:1026" From 303331faba89605ab25a36f9517e6f8c017f1ae8 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Mon, 2 Aug 2021 18:02:01 +0300 Subject: [PATCH 053/100] SKALE-4262 add docs --- docs/run-in-hardware-mode.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/run-in-hardware-mode.md b/docs/run-in-hardware-mode.md index 0f7c4e68..ca985804 100644 --- a/docs/run-in-hardware-mode.md +++ b/docs/run-in-hardware-mode.md @@ -66,6 +66,7 @@ If operating with a firewall, please make sure these ports are open so clients a - \-n Launch SGXWalletServer using http (not https) - \-b Restore from back up (you will need to enter backup key) - \-y Do not ask user to acknowledge receipt of backup key +- \-e Check whether one who is trying to access the key is the same user who created it (Ownership is checked via SSL certificate for now. Deleting old SSL certificates and trying to access the keys created before will cause the error!) - \-T Generate test keys ### Healthcheck From 5e4870ba36001623764e733e21fe33346f7c8a71 Mon Sep 17 00:00:00 2001 From: Chadwick Strange Date: Thu, 5 Aug 2021 15:27:07 -0400 Subject: [PATCH 054/100] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ae32ef4d..66739c72 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ -[![Discord](https://img.shields.io/discord/534485763354787851.svg)](https://discord.gg/vvUtWJB) +[![Discord](https://img.shields.io/discord/534485763354787851.svg)](https://discord.gg/vvUtWJB) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3581/badge)](https://bestpractices.coreinfrastructure.org/projects/3581) ## Intro @@ -80,7 +80,7 @@ guide [docs/developer-guide.md](docs/developer-guide.md). ## Contributing -See [contributing](CONTRIBUTING.md) for information on how to contribute. +See [contributing](.github/CONTRIBUTING.md) for information on how to contribute. ## Libraries used by this project From ad43a06e039f493bf1ba712e8df909639e06431b Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Tue, 10 Aug 2021 16:43:20 +0300 Subject: [PATCH 055/100] SKALE-4411 add more logs --- zmq_src/ReqMessage.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index 027ef6e7..38948845 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -74,6 +74,7 @@ Json::Value importECDSAReqMessage::process() { auto result = SGXWalletServer::importECDSAKeyImpl(key, keyName); if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); + spdlog::info("Cert {} creates key {}", cert, keyName); addKeyByOwner(keyName, cert); } result["type"] = ZMQMessage::IMPORT_ECDSA_RSP; @@ -85,6 +86,7 @@ Json::Value generateECDSAReqMessage::process() { string keyName = result["keyName"].asString(); if (checkKeyOwnership && result["status"] == 0) { auto cert = getStringRapid("cert"); + spdlog::info("Cert {} creates key {}", cert, keyName); addKeyByOwner(keyName, cert); } result["type"] = ZMQMessage::GENERATE_ECDSA_RSP; @@ -170,8 +172,9 @@ Json::Value createBLSPrivateKeyReqMessage::process() { } auto result = SGXWalletServer::createBLSPrivateKeyV2Impl(blsKeyName, ethKeyName, polyName, secretShare, t, n); if (checkKeyOwnership && result["status"] == 0) { - spdlog::info("Cert {} creates key {}", getStringRapid("cert"), blsKeyName); - addKeyByOwner(blsKeyName, getStringRapid("cert")); + auto cert = getStringRapid("cert"); + spdlog::info("Cert {} creates key {}", cert, blsKeyName); + addKeyByOwner(blsKeyName, cert); } result["type"] = ZMQMessage::CREATE_BLS_PRIVATE_RSP; return result; From ab21f0ed29d9dcb142747475bafd2981b3d610fb Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 11 Aug 2021 09:34:42 +0300 Subject: [PATCH 056/100] SKALE-4411 format --- SGXWalletServer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index d41c5e2f..e47dfbcf 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -1117,7 +1117,6 @@ shared_ptr SGXWalletServer::readFromDb(const string &name, const string auto dataStr = checkDataFromDb(prefix + name); if (dataStr == nullptr) { - throw SGXException(KEY_SHARE_DOES_NOT_EXIST, string(__FUNCTION__) + ":Data with this name does not exist: " + prefix + name); } @@ -1141,9 +1140,9 @@ void SGXWalletServer::writeKeyShare(const string &_keyShareName, const string &_ } void SGXWalletServer::writeDataToDB(const string &name, const string &value) { - if (LevelDB::getLevelDb()->readString(name) != nullptr) { throw SGXException(KEY_NAME_ALREADY_EXISTS, string(__FUNCTION__) + ":Name already exists" + name); } + LevelDB::getLevelDb()->writeString(name, value); } From 52ed4a2250444383dcac7a71d689db83a1fab040 Mon Sep 17 00:00:00 2001 From: Oleh Date: Sat, 28 Aug 2021 15:12:22 +0300 Subject: [PATCH 057/100] SKALE-4520 remove extra debug output --- secure_enclave/secure_enclave.c | 1 - 1 file changed, 1 deletion(-) diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 57d1c407..2bd34701 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -454,7 +454,6 @@ void trustedGetPublicEcdsaKey(int *errStatus, char *errString, CHECK_STATUS2("AES_decrypt failed with status %d"); skey[enc_len - SGX_AESGCM_MAC_SIZE - SGX_AESGCM_IV_SIZE] = '\0'; - strncpy(errString, skey, 1024); status = mpz_set_str(privateKeyMpz, skey, ECDSA_SKEY_BASE); From 28de93d114eb541da08f56bc739675fb2884e768 Mon Sep 17 00:00:00 2001 From: Oleh Date: Mon, 30 Aug 2021 13:51:58 +0300 Subject: [PATCH 058/100] SKALE-4522 fix exportable keys, add tests --- ECDSACrypto.cpp | 8 +++-- secure_enclave/secure_enclave.c | 21 ++++++++--- secure_enclave/secure_enclave.edl | 1 + testw.cpp | 59 +++++++++++++++++++++++++++++-- testw.py | 1 + 5 files changed, 80 insertions(+), 10 deletions(-) diff --git a/ECDSACrypto.cpp b/ECDSACrypto.cpp index cdc31f2e..e413b83f 100644 --- a/ECDSACrypto.cpp +++ b/ECDSACrypto.cpp @@ -59,9 +59,11 @@ vector genECDSAKey() { sgx_status_t status = SGX_SUCCESS; - status = trustedGenerateEcdsaKey(eid, &errStatus, - errMsg.data(), encr_pr_key.data(), &enc_len, - pub_key_x.data(), pub_key_y.data()); + int exportable = 0; + + status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), + &exportable, encr_pr_key.data(), &enc_len, + pub_key_x.data(), pub_key_y.data()); HANDLE_TRUSTED_FUNCTION_ERROR(status, errStatus,errMsg.data()); diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 2bd34701..636c0ad9 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -358,7 +358,7 @@ void trustedSetSEKBackup(int *errStatus, char *errString, LOG_INFO("SGX call completed"); } -void trustedGenerateEcdsaKey(int *errStatus, char *errString, +void trustedGenerateEcdsaKey(int *errStatus, char *errString, int *is_exportable, uint8_t *encryptedPrivateKey, uint64_t *enc_len, char *pub_key_x, char *pub_key_y) { LOG_INFO(__FUNCTION__); INIT_ERROR_STATE @@ -409,8 +409,15 @@ void trustedGenerateEcdsaKey(int *errStatus, char *errString, strncpy(skey_str + n_zeroes, arr_skey_str, 65 - n_zeroes); snprintf(errString, BUF_LEN, "skey len is %d\n", (int) strlen(skey_str)); - int status = AES_encrypt((char *) skey_str, encryptedPrivateKey, BUF_LEN, + int status = -1; + + if ( *is_exportable ) { + status = AES_encrypt((char *) skey_str, encryptedPrivateKey, BUF_LEN, + ECDSA, EXPORTABLE, enc_len); + } else { + status = AES_encrypt((char *) skey_str, encryptedPrivateKey, BUF_LEN, ECDSA, NON_EXPORTABLE, enc_len); + } CHECK_STATUS("ecdsa private key encryption failed"); uint8_t type = 0; @@ -611,6 +618,8 @@ void trustedDecryptKey(int *errStatus, char *errString, uint8_t *encryptedPrivat if (exportable != EXPORTABLE) { *errStatus = -11; snprintf(errString, BUF_LEN, "Key is not exportable"); + LOG_ERROR(errString); + goto clean; } if (status != 0) { @@ -855,7 +864,9 @@ void trustedGetEncryptedSecretShare(int *errStatus, char *errString, SAFE_CHAR_BUF(pub_key_x, BUF_LEN);SAFE_CHAR_BUF(pub_key_y, BUF_LEN); - trustedGenerateEcdsaKey(&status, errString, encrypted_skey, &enc_len, pub_key_x, pub_key_y); + int is_exportable = 1; + + trustedGenerateEcdsaKey(&status, errString, &is_exportable, encrypted_skey, &enc_len, pub_key_x, pub_key_y); CHECK_STATUS("trustedGenerateEcdsaKey failed"); @@ -929,7 +940,9 @@ void trustedGetEncryptedSecretShareV2(int *errStatus, char *errString, SAFE_CHAR_BUF(pub_key_x, BUF_LEN); SAFE_CHAR_BUF(pub_key_y, BUF_LEN); - trustedGenerateEcdsaKey(&status, errString, encrypted_skey, &enc_len, pub_key_x, pub_key_y); + int is_exportable = 1; + + trustedGenerateEcdsaKey(&status, errString, &is_exportable, encrypted_skey, &enc_len, pub_key_x, pub_key_y); CHECK_STATUS("trustedGenerateEcdsaKey failed"); diff --git a/secure_enclave/secure_enclave.edl b/secure_enclave/secure_enclave.edl index 1eaee973..00c91bf3 100644 --- a/secure_enclave/secure_enclave.edl +++ b/secure_enclave/secure_enclave.edl @@ -35,6 +35,7 @@ enclave { public void trustedGenerateEcdsaKey ( [out] int *errStatus, [out, count = SMALL_BUF_SIZE] char* err_string, + [out] int *is_exportable, [out, count = SMALL_BUF_SIZE] uint8_t* encrypted_key, [out] uint64_t *enc_len, [out, count = SMALL_BUF_SIZE] char * pub_key_x, diff --git a/testw.cpp b/testw.cpp index e14db45f..005791bb 100644 --- a/testw.cpp +++ b/testw.cpp @@ -146,8 +146,9 @@ TEST_CASE_METHOD(TestFixture, "ECDSA AES keygen and signature test", "[ecdsa-aes vector pubKeyY(BUF_LEN, 0); uint64_t encLen = 0; + int exportable = 0; PRINT_SRC_LINE - auto status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), encrPrivKey.data(), &encLen, + auto status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), &exportable, encrPrivKey.data(), &encLen, pubKeyX.data(), pubKeyY.data()); REQUIRE(status == SGX_SUCCESS); @@ -177,8 +178,9 @@ TEST_CASE_METHOD(TestFixture, "ECDSA AES key gen", "[ecdsa-aes-key-gen]") { vector pubKeyX(BUF_LEN, 0); vector pubKeyY(BUF_LEN, 0); uint64_t encLen = 0; + int exportable = 0; PRINT_SRC_LINE - auto status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), encrPrivKey.data(), &encLen, + auto status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), &exportable, encrPrivKey.data(), &encLen, pubKeyX.data(), pubKeyY.data()); @@ -194,9 +196,10 @@ TEST_CASE_METHOD(TestFixture, "ECDSA AES get public key", "[ecdsa-aes-get-pub-ke vector pubKeyY(BUF_LEN, 0); uint64_t encLen = 0; + int exportable = 0; PRINT_SRC_LINE - auto status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), encPrivKey.data(), &encLen, pubKeyX.data(), + auto status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), &exportable, encPrivKey.data(), &encLen, pubKeyX.data(), pubKeyY.data()); REQUIRE(status == SGX_SUCCESS); @@ -1120,6 +1123,56 @@ TEST_CASE_METHOD(TestFixture, "AES encrypt/decrypt", "[aes-encrypt-decrypt]") { sleep(3); } +TEST_CASE_METHOD(TestFixture, "Exportable / non-exportable keys", "[exportable-nonexportable-keys]") { + int errStatus = 0; + vector errMsg(BUF_LEN, 0); + vector encPrivKey(BUF_LEN, 0); + vector pubKeyX(BUF_LEN, 0); + vector pubKeyY(BUF_LEN, 0); + + uint64_t encLen = 0; + int exportable = 0; + + auto status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), &exportable, encPrivKey.data(), &encLen, pubKeyX.data(), + pubKeyY.data()); + + vector decrypted_key(BUF_LEN, 0); + status = trustedDecryptKey(eid, &errStatus, errMsg.data(), encPrivKey.data(), encLen, decrypted_key.data()); + REQUIRE( errStatus == -11 ); + + exportable = 1; + + encPrivKey.clear(); + errMsg.clear(); + pubKeyX.clear(); + pubKeyY.clear(); + + status = trustedGenerateEcdsaKey(eid, &errStatus, errMsg.data(), &exportable, encPrivKey.data(), &encLen, pubKeyX.data(), + pubKeyY.data()); + + decrypted_key.clear(); + status = trustedDecryptKey(eid, &errStatus, errMsg.data(), encPrivKey.data(), encLen, decrypted_key.data()); + REQUIRE( errStatus == 0 ); + REQUIRE( status == SGX_SUCCESS ); + + string key = SAMPLE_AES_KEY; + vector encrypted_key(BUF_LEN, 0); + + status = trustedEncryptKey(eid, &errStatus, errMsg.data(), key.c_str(), encrypted_key.data(), &encLen); + + REQUIRE(status == 0); + REQUIRE(errStatus == 0); + + vector decr_key(BUF_LEN, 0); + PRINT_SRC_LINE + status = trustedDecryptKey(eid, &errStatus, errMsg.data(), encrypted_key.data(), encLen, decr_key.data()); + + REQUIRE(status == 0); + REQUIRE(key.compare(decr_key.data()) == 0); + REQUIRE(errStatus == 0); + sleep(3); +} + TEST_CASE_METHOD(TestFixture, "Many threads ecdsa dkg v2 bls", "[many-threads-crypto-v2]") { vector threads; int num_threads = 4; diff --git a/testw.py b/testw.py index 988ef6c2..ed4e8c62 100755 --- a/testw.py +++ b/testw.py @@ -60,6 +60,7 @@ "[dkg-poly-exists-zmq]", "[dkg-aes-pub-shares]", "[aes-encrypt-decrypt]", + "[exportable-nonexportable-keys]", "[aes-dkg-v2]", "[aes-dkg-v2-zmq]", "[te-decryption-share]", From bdb15e6dcdf6c1cb32baf3d89c6bf3008b0ef969 Mon Sep 17 00:00:00 2001 From: Oleh Date: Tue, 31 Aug 2021 12:33:12 +0300 Subject: [PATCH 059/100] SKALE-4522 cleanup decrypted key on error --- secure_enclave/secure_enclave.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 636c0ad9..88423b45 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -616,6 +616,9 @@ void trustedDecryptKey(int *errStatus, char *errString, uint8_t *encryptedPrivat &type, &exportable); if (exportable != EXPORTABLE) { + while (*key != '\0') { + *key++ = '0'; + } *errStatus = -11; snprintf(errString, BUF_LEN, "Key is not exportable"); LOG_ERROR(errString); From 1703eca26457930941aceae4b64440d1223d11ca Mon Sep 17 00:00:00 2001 From: Stan Kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 16:42:51 +0300 Subject: [PATCH 060/100] Update building.md --- docs/building.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/building.md b/docs/building.md index ba4fa312..58692ff5 100644 --- a/docs/building.md +++ b/docs/building.md @@ -40,6 +40,7 @@ make Note: to run in simulation mode, add --enable-sgx-simulation flag when you run configure. ```bash +./autoconf.bash ./configure --enable-sgx-simulation make ``` From 5fdaee3abf2c7b7f803e07cb86d529deb034360d Mon Sep 17 00:00:00 2001 From: Stan Kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 16:44:22 +0300 Subject: [PATCH 061/100] Update building.md --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 58692ff5..7e94e5aa 100644 --- a/docs/building.md +++ b/docs/building.md @@ -23,7 +23,7 @@ cd scripts; ./build_deps.py; cd .. ## Set SGX environment variables ```bash -source sgx-sdk-build/sgxsdk/environment +source /opt/intel/sgxsdk/environment ``` ## Configure and build sgxwallet From 4ceaa72763aebb046b5dd730f816a0bd5d9d3f44 Mon Sep 17 00:00:00 2001 From: Stan Kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 16:50:10 +0300 Subject: [PATCH 062/100] Update building.md --- docs/building.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/building.md b/docs/building.md index 7e94e5aa..57808a7c 100644 --- a/docs/building.md +++ b/docs/building.md @@ -2,6 +2,20 @@ # Building SGX wallet from source +## Build and install Intel SGX SDK + +`git clone -b sgx_2.13 --depth 1 https://github.com/intel/linux-sgx` + +`cd linux-sgx` +`make preparation` +`sudo make sdk_install_pkg_no_mitigation` +` cd /opt/intel` +`sudo sh -c 'echo yes | /linux-sgx/linux/installer/bin/sgx_linux_x64_sdk_*.bin'` +`sudo make psw_install_pkg +`sudo cp /linux-sgx/linux/installer/bin/sgx_linux_x64_psw*.bin . +`sudo ./sgx_linux_x64_psw*.bin --no-start-aesm` + + ## Clone this repository and its submodules `git clone --recurse-submodules https://github.com/skalenetwork/sgxwallet.git` From a7b10da74de9e3a4dc32c913968499d838f8661a Mon Sep 17 00:00:00 2001 From: Stan Kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 16:51:02 +0300 Subject: [PATCH 063/100] Update building.md --- docs/building.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/building.md b/docs/building.md index 57808a7c..34bc228a 100644 --- a/docs/building.md +++ b/docs/building.md @@ -4,16 +4,16 @@ ## Build and install Intel SGX SDK -`git clone -b sgx_2.13 --depth 1 https://github.com/intel/linux-sgx` - -`cd linux-sgx` -`make preparation` -`sudo make sdk_install_pkg_no_mitigation` -` cd /opt/intel` -`sudo sh -c 'echo yes | /linux-sgx/linux/installer/bin/sgx_linux_x64_sdk_*.bin'` -`sudo make psw_install_pkg -`sudo cp /linux-sgx/linux/installer/bin/sgx_linux_x64_psw*.bin . -`sudo ./sgx_linux_x64_psw*.bin --no-start-aesm` +```bash +git clone -b sgx_2.13 --depth 1 https://github.com/intel/linux-sgx +cd linux-sgx +make preparation +sudo make sdk_install_pkg_no_mitigation +cd /opt/intel +sudo sh -c 'echo yes | /linux-sgx/linux/installer/bin/sgx_linux_x64_sdk_*.bin' +sudo make psw_install_pkg +sudo cp /linux-sgx/linux/installer/bin/sgx_linux_x64_psw*.bin . +sudo ./sgx_linux_x64_psw*.bin --no-start-aesm ## Clone this repository and its submodules From c1e3846d390234d520ce6a7d926e89bc9736cfd9 Mon Sep 17 00:00:00 2001 From: Stan Kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 16:51:25 +0300 Subject: [PATCH 064/100] Update building.md --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 34bc228a..42cba77d 100644 --- a/docs/building.md +++ b/docs/building.md @@ -14,7 +14,7 @@ sudo sh -c 'echo yes | /linux-sgx/linux/installer/bin/sgx_linux_x64_sdk_*.bin' sudo make psw_install_pkg sudo cp /linux-sgx/linux/installer/bin/sgx_linux_x64_psw*.bin . sudo ./sgx_linux_x64_psw*.bin --no-start-aesm - +``` ## Clone this repository and its submodules From 5b4091f2bd7ad21cca7b2fec1f0cc1ceacf315bd Mon Sep 17 00:00:00 2001 From: Stan Kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 16:53:24 +0300 Subject: [PATCH 065/100] Update building.md --- docs/building.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 42cba77d..e2549ed0 100644 --- a/docs/building.md +++ b/docs/building.md @@ -4,13 +4,18 @@ ## Build and install Intel SGX SDK +We are currently using SGX SDK version 2.13. + +Below is a sequence of commands that builds SDK and installs it into /opt/intel directory. + + ```bash git clone -b sgx_2.13 --depth 1 https://github.com/intel/linux-sgx cd linux-sgx make preparation sudo make sdk_install_pkg_no_mitigation cd /opt/intel -sudo sh -c 'echo yes | /linux-sgx/linux/installer/bin/sgx_linux_x64_sdk_*.bin' +sudo sh -c 'echo yes | /linux-sgx/linux/installer/bin/sgx_linux_x64_sdk_*.bin sudo make psw_install_pkg sudo cp /linux-sgx/linux/installer/bin/sgx_linux_x64_psw*.bin . sudo ./sgx_linux_x64_psw*.bin --no-start-aesm From 9730ceeccda14fd7640fd36a686eff0ffec8024c Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 17:59:43 +0300 Subject: [PATCH 066/100] bug/SKALE-4549-fix-exportable --- secure_enclave/secure_enclave.edl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/secure_enclave/secure_enclave.edl b/secure_enclave/secure_enclave.edl index 00c91bf3..82cf6e64 100644 --- a/secure_enclave/secure_enclave.edl +++ b/secure_enclave/secure_enclave.edl @@ -35,7 +35,7 @@ enclave { public void trustedGenerateEcdsaKey ( [out] int *errStatus, [out, count = SMALL_BUF_SIZE] char* err_string, - [out] int *is_exportable, + [in, count = 1] int *is_exportable, [out, count = SMALL_BUF_SIZE] uint8_t* encrypted_key, [out] uint64_t *enc_len, [out, count = SMALL_BUF_SIZE] char * pub_key_x, From 644abe9fc11b0fe7bc50ebc6154469203d6f814e Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 18:48:18 +0300 Subject: [PATCH 067/100] SKALE-4586 Add exit requested exception --- ExitRequestedException.cpp | 32 ++++++++++++++++++++++++++++++++ ExitRequestedException.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 ExitRequestedException.cpp create mode 100644 ExitRequestedException.h diff --git a/ExitRequestedException.cpp b/ExitRequestedException.cpp new file mode 100644 index 00000000..5aca7ad5 --- /dev/null +++ b/ExitRequestedException.cpp @@ -0,0 +1,32 @@ +/* + Copyright (C) 2019-Present SKALE Labs + + This file is part of sgxwallet. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file ExitRequestedException.cpp + @author Stan Kladko + @date 2018 +*/ + +#include "common.h" +#include "Log.h" +#include "ExitRequestedException.h" + + +ExitRequestedException::ExitRequestedException(const std::string &_message, const string& _className) : + Exception(_message, _className) { + fatal = false; +} diff --git a/ExitRequestedException.h b/ExitRequestedException.h new file mode 100644 index 00000000..2c0e8b7e --- /dev/null +++ b/ExitRequestedException.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2019-Present SKALE Labs + + This file is part of sgxwallet. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file ExitRequestedException.h + @author Stan Kladko + @date 2018 +*/ + +#pragma once + +#include "Exception.h" + + +class ExitRequestedException : public Exception { +public: + ExitRequestedException( const std::string& _message, const std::string& _className ); +}; From c45204e189b8c891f54c24343a94b0c3b2e2aa60 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Mon, 6 Sep 2021 19:00:54 +0300 Subject: [PATCH 068/100] SKALE-4586 --- Makefile.am | 2 +- zmq_src/ZMQServer.cpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index d66ec781..44b53c4f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,7 +71,7 @@ bin_PROGRAMS = sgxwallet testw sgx_util COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/RspMessage.cpp zmq_src/ReqMessage.cpp \ - zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp \ + zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp ExitRequestedException.cpp \ InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp TECrypto.cpp \ SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp CryptoTools.cpp \ DKGCrypto.cpp ServerInit.cpp BLSPrivateKeyShareSGX.cpp LevelDB.cpp ServerDataChecker.cpp SEKManager.cpp \ diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index f74fccfe..33ee79da 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -133,12 +133,12 @@ void ZMQServer::doOneServerLoop() { result["errorMessage"] = ""; zmq::message_t identity; - zmq::message_t copied_id; string stringToParse = ""; try { + zmq_pollitem_t items[1]; items[0].socket = *socket; items[0].events = ZMQ_POLLIN; @@ -164,8 +164,6 @@ void ZMQServer::doOneServerLoop() { exit(-12); } - copied_id.copy(&identity); - zmq::message_t reqMsg; if (!socket->recv(&reqMsg, 0)) { @@ -215,7 +213,7 @@ void ZMQServer::doOneServerLoop() { CHECK_STATE(replyStr.front() == '{'); CHECK_STATE(replyStr.back() == '}'); - if (!socket->send(copied_id, ZMQ_SNDMORE)) { + if (!socket->send(identity, ZMQ_SNDMORE)) { if (isExitRequested) { return; } From 60ac3b2681e749904ce6d2ac73c6ca99477d86fd Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Tue, 7 Sep 2021 17:02:25 +0300 Subject: [PATCH 069/100] SKALE-4586 Add Agent class --- Makefile.am | 4 +-- zmq_src/Agent.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++ zmq_src/Agent.h | 60 +++++++++++++++++++++++++++++++++++++++++++++ zmq_src/ZMQServer.h | 4 ++- 4 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 zmq_src/Agent.cpp create mode 100644 zmq_src/Agent.h diff --git a/Makefile.am b/Makefile.am index 44b53c4f..d4d5159b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,9 +71,9 @@ bin_PROGRAMS = sgxwallet testw sgx_util COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/RspMessage.cpp zmq_src/ReqMessage.cpp \ - zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp ExitRequestedException.cpp \ + zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp zmq_src/Agent.cpp ExitRequestedException.cpp \ InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp TECrypto.cpp \ - SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp CryptoTools.cpp \ + SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp CryptoTools.cpp \ DKGCrypto.cpp ServerInit.cpp BLSPrivateKeyShareSGX.cpp LevelDB.cpp ServerDataChecker.cpp SEKManager.cpp \ third_party/intel/sgx_stub.c third_party/intel/sgx_detect_linux.c third_party/intel/create_enclave.c \ third_party/intel/oc_alloc.c ECDSAImpl.c TestUtils.cpp sgxwallet.c SGXInfoServer.cpp ECDSACrypto.cpp diff --git a/zmq_src/Agent.cpp b/zmq_src/Agent.cpp new file mode 100644 index 00000000..ae0e1d99 --- /dev/null +++ b/zmq_src/Agent.cpp @@ -0,0 +1,56 @@ +/* + Copyright (C) 2021 SKALE Labs + + This file is part of skale-consensus. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file Agent.cpp + @author Stan Kladko + @date 2021 +*/ + + +#include "Agent.h" + +void Agent::notifyAllConditionVariables() { + messageCond.notify_all(); + queueCond.notify_all(); +} + + + +Agent::Agent() : startedRun(false) {}; + +void Agent::waitOnGlobalStartBarrier() { + unique_lock mlock(queueMutex); + while (!startedRun) { + queueCond.wait(mlock); + } +} + +Agent::~Agent() { +} + + +void Agent::releaseGlobalStartBarrier() { + + if (startedRun.exchange(true)) { + return; + } + + lock_guard lock(queueMutex); + + queueCond.notify_all(); +} diff --git a/zmq_src/Agent.h b/zmq_src/Agent.h new file mode 100644 index 00000000..83593b74 --- /dev/null +++ b/zmq_src/Agent.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2021 SKALE Labs + + This file is part of skale-consensus. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file Agent.h + @author Stan Kladko + @date 2021 +*/ + +#pragma once + +#include +#include +#include + +using namespace std; + +class Agent { + +protected: + + atomic_bool startedRun; + + mutex messageMutex; + condition_variable messageCond; + + condition_variable queueCond; + mutex queueMutex; + + recursive_mutex m; + + +public: + + Agent(); + + void notifyAllConditionVariables(); + + virtual ~Agent(); + + void releaseGlobalStartBarrier(); + + void waitOnGlobalStartBarrier(); + + recursive_mutex& getMainMutex() { return m; } +}; diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 068b2767..78f3fc6d 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -35,10 +35,12 @@ #include #include "zhelpers.hpp" +#include "Agent.h" + using namespace std; -class ZMQServer { +class ZMQServer : Agent{ uint64_t workerThreads; From bc2f6e449cc0357bbad3ef94624985644cf0a377 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Tue, 7 Sep 2021 17:09:31 +0300 Subject: [PATCH 070/100] SKALE-4586 Added agent start --- zmq_src/ZMQServer.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 33ee79da..718ffc33 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -73,6 +73,10 @@ void ZMQServer::run() { spdlog::info("Bound port ..."); + waitOnGlobalStartBarrier(); + + spdlog::info("Started zmq read loop ..."); + while (!isExitRequested) { try { zmqServer->doOneServerLoop(); @@ -108,16 +112,26 @@ void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { rootCAPath = string(SGXDATA_FOLDER) + "cert_data/rootCA.pem"; spdlog::info("Reading root CA from {}", rootCAPath); CHECK_STATE(access(rootCAPath.c_str(), F_OK) == 0); + spdlog::info("Read CA.", rootCAPath); }; + spdlog::info("Initing zmq server."); + zmqServer = make_shared(_checkSignature, _checkKeyOwnership, rootCAPath); CHECK_STATE(zmqServer) - serverThread = make_shared(std::bind(&ZMQServer::run, ZMQServer::zmqServer)); serverThread->detach(); - spdlog::info("Inited zmq server ..."); + spdlog::info("Inited zmq server."); + + spdlog::info("Starting zmq server ..."); + + zmqServer->releaseGlobalStartBarrier(); + + spdlog::info("Started zmq server."); + + } shared_ptr ZMQServer::serverThread = nullptr; From ca858196bac5bfdaad3eb8c56bd9f58a0a5c8ab5 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Tue, 7 Sep 2021 18:44:31 +0300 Subject: [PATCH 071/100] SKALE-4586 Added Thread Pool --- Makefile.am | 2 +- zmq_src/WorkerThreadPool.cpp | 71 ++++++++++++++++++++++++++++++++++++ zmq_src/WorkerThreadPool.h | 63 ++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 zmq_src/WorkerThreadPool.cpp create mode 100644 zmq_src/WorkerThreadPool.h diff --git a/Makefile.am b/Makefile.am index d4d5159b..ac4ac491 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,7 +71,7 @@ bin_PROGRAMS = sgxwallet testw sgx_util COMMON_SRC = SGXException.cpp ExitHandler.cpp zmq_src/ZMQClient.cpp zmq_src/RspMessage.cpp zmq_src/ReqMessage.cpp \ - zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp zmq_src/Agent.cpp ExitRequestedException.cpp \ + zmq_src/ZMQMessage.cpp zmq_src/ZMQServer.cpp zmq_src/Agent.cpp zmq_src/WorkerThreadPool.cpp ExitRequestedException.cpp \ InvalidStateException.cpp Exception.cpp InvalidArgumentException.cpp Log.cpp TECrypto.cpp \ SGXWalletServer.cpp SGXRegistrationServer.cpp CSRManagerServer.cpp BLSCrypto.cpp CryptoTools.cpp \ DKGCrypto.cpp ServerInit.cpp BLSPrivateKeyShareSGX.cpp LevelDB.cpp ServerDataChecker.cpp SEKManager.cpp \ diff --git a/zmq_src/WorkerThreadPool.cpp b/zmq_src/WorkerThreadPool.cpp new file mode 100644 index 00000000..948df7c5 --- /dev/null +++ b/zmq_src/WorkerThreadPool.cpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2021 SKALE Labs + + This file is part of skale-consensus. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file WorkerThreadPool.cpp + @author Stan Kladko + @date 2021 +*/ + +#include "common.h" +#include "third_party/spdlog/spdlog.h" +#include "WorkerThreadPool.h" + + +void WorkerThreadPool::startService() { + + lock_guard lock(threadPoolMutex); + + CHECK_STATE(!started.exchange(true)) + + for (uint64_t i = 0; i < (uint64_t) numThreads; i++) { + createThread(i); + } + +} + + +WorkerThreadPool::WorkerThreadPool(uint64_t _numThreads, Agent *_agent) : started(false), joined(false) { + CHECK_STATE(_numThreads > 0); + CHECK_STATE(_agent); + spdlog::info("Started thread pool. Threads count:" + to_string(_numThreads)); + this->agent = _agent; + this->numThreads = _numThreads;; +} + + +void WorkerThreadPool::joinAll() { + if (joined) + return; + + lock_guard lock(threadPoolMutex); + + joined = true; + + for (auto &&thread : threadpool) { + if (thread->joinable()) + thread->join(); + CHECK_STATE(!thread->joinable()); + } +} + +bool WorkerThreadPool::isJoined() const { + return joined; +} + +WorkerThreadPool::~WorkerThreadPool(){ +} diff --git a/zmq_src/WorkerThreadPool.h b/zmq_src/WorkerThreadPool.h new file mode 100644 index 00000000..9aabbb33 --- /dev/null +++ b/zmq_src/WorkerThreadPool.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2018-2019 SKALE Labs + + This file is part of skale-consensus. + + skale-consensus is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + skale-consensus is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with skale-consensus. If not, see . + + @file WorkerThreadPool.h + @author Stan Kladko + @date 2018 +*/ + +#pragma once + +#include +#include +#include + +#include "Agent.h" + +class WorkerThreadPool { + + atomic_bool started; + + virtual void createThread( uint64_t threadNumber ) = 0; + +protected: + + atomic_bool joined; + + vector> threadpool; + + recursive_mutex threadPoolMutex; + uint64_t numThreads = 0; + Agent* agent = nullptr; + +protected: + + WorkerThreadPool(uint64_t _numThreads, Agent *_agent); + +public: + + virtual ~WorkerThreadPool(); + + virtual void startService(); + + void joinAll(); + + bool isJoined() const; + + +}; From 4066708dfd9d697d0e6203c59eed97fec484accf Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Tue, 7 Sep 2021 19:16:13 +0300 Subject: [PATCH 072/100] SKALE-4586 Added Thread Pool --- zmq_src/Agent.cpp | 11 +++++------ zmq_src/Agent.h | 6 +++--- zmq_src/WorkerThreadPool.cpp | 19 +++++++++++++++---- zmq_src/WorkerThreadPool.h | 19 +++++++++---------- zmq_src/ZMQServer.cpp | 18 ++++++++++++++++-- zmq_src/ZMQServer.h | 30 ++++++++++++++++++------------ 6 files changed, 66 insertions(+), 37 deletions(-) diff --git a/zmq_src/Agent.cpp b/zmq_src/Agent.cpp index ae0e1d99..d380a712 100644 --- a/zmq_src/Agent.cpp +++ b/zmq_src/Agent.cpp @@ -29,13 +29,11 @@ void Agent::notifyAllConditionVariables() { queueCond.notify_all(); } - - -Agent::Agent() : startedRun(false) {}; +Agent::Agent() : startedWorkers(false) {}; void Agent::waitOnGlobalStartBarrier() { unique_lock mlock(queueMutex); - while (!startedRun) { + while (!startedWorkers) { queueCond.wait(mlock); } } @@ -44,9 +42,10 @@ Agent::~Agent() { } -void Agent::releaseGlobalStartBarrier() { +void Agent::releaseWorkers() { - if (startedRun.exchange(true)) { + if (startedWorkers.exchange(true)) { + // already started return; } diff --git a/zmq_src/Agent.h b/zmq_src/Agent.h index 83593b74..c6d715ed 100644 --- a/zmq_src/Agent.h +++ b/zmq_src/Agent.h @@ -33,7 +33,7 @@ class Agent { protected: - atomic_bool startedRun; + atomic_bool startedWorkers; mutex messageMutex; condition_variable messageCond; @@ -52,9 +52,9 @@ class Agent { virtual ~Agent(); - void releaseGlobalStartBarrier(); + void releaseWorkers(); void waitOnGlobalStartBarrier(); - + recursive_mutex& getMainMutex() { return m; } }; diff --git a/zmq_src/WorkerThreadPool.cpp b/zmq_src/WorkerThreadPool.cpp index 948df7c5..6650dcac 100644 --- a/zmq_src/WorkerThreadPool.cpp +++ b/zmq_src/WorkerThreadPool.cpp @@ -23,15 +23,16 @@ #include "common.h" #include "third_party/spdlog/spdlog.h" +#include "ZMQServer.h" #include "WorkerThreadPool.h" void WorkerThreadPool::startService() { - lock_guard lock(threadPoolMutex); - CHECK_STATE(!started.exchange(true)) + LOCK(m) + for (uint64_t i = 0; i < (uint64_t) numThreads; i++) { createThread(i); } @@ -39,7 +40,7 @@ void WorkerThreadPool::startService() { } -WorkerThreadPool::WorkerThreadPool(uint64_t _numThreads, Agent *_agent) : started(false), joined(false) { +WorkerThreadPool::WorkerThreadPool(uint64_t _numThreads, ZMQServer *_agent) : started(false), joined(false) { CHECK_STATE(_numThreads > 0); CHECK_STATE(_agent); spdlog::info("Started thread pool. Threads count:" + to_string(_numThreads)); @@ -52,7 +53,7 @@ void WorkerThreadPool::joinAll() { if (joined) return; - lock_guard lock(threadPoolMutex); + LOCK(m); joined = true; @@ -69,3 +70,13 @@ bool WorkerThreadPool::isJoined() const { WorkerThreadPool::~WorkerThreadPool(){ } + +void WorkerThreadPool::createThread(uint64_t _threadNumber) { + + spdlog::info("Starting ZMQ worker thread " + to_string(_threadNumber) ); + + this->threadpool.push_back( + make_shared< thread >( ZMQServer::workerThreadMessageProcessLoop, agent ) ); + + spdlog::info("Started ZMQ worker thread " + to_string(_threadNumber) ); +} diff --git a/zmq_src/WorkerThreadPool.h b/zmq_src/WorkerThreadPool.h index 9aabbb33..2c17605a 100644 --- a/zmq_src/WorkerThreadPool.h +++ b/zmq_src/WorkerThreadPool.h @@ -27,30 +27,30 @@ #include #include -#include "Agent.h" + +class Agent; +class ZMQServer; class WorkerThreadPool { atomic_bool started; - virtual void createThread( uint64_t threadNumber ) = 0; + void createThread( uint64_t threadNumber ); + + recursive_mutex m; protected: atomic_bool joined; - vector> threadpool; - recursive_mutex threadPoolMutex; uint64_t numThreads = 0; - Agent* agent = nullptr; - -protected: - - WorkerThreadPool(uint64_t _numThreads, Agent *_agent); + ZMQServer* agent = nullptr; public: + WorkerThreadPool(uint64_t _numThreads, ZMQServer *_agent); + virtual ~WorkerThreadPool(); virtual void startService(); @@ -59,5 +59,4 @@ class WorkerThreadPool { bool isJoined() const; - }; diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 718ffc33..323f3aad 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -55,6 +55,9 @@ ZMQServer::ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string int linger = 0; zmq_setsockopt(*socket, ZMQ_LINGER, &linger, sizeof(linger)); + + threadPool = make_shared(1, this); + } void ZMQServer::run() { @@ -73,7 +76,7 @@ void ZMQServer::run() { spdlog::info("Bound port ..."); - waitOnGlobalStartBarrier(); + spdlog::info("Started zmq read loop ..."); @@ -123,11 +126,13 @@ void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { serverThread = make_shared(std::bind(&ZMQServer::run, ZMQServer::zmqServer)); serverThread->detach(); + zmqServer->releaseWorkers(); + spdlog::info("Inited zmq server."); spdlog::info("Starting zmq server ..."); - zmqServer->releaseGlobalStartBarrier(); + zmqServer->releaseWorkers(); spdlog::info("Started zmq server."); @@ -254,3 +259,12 @@ void ZMQServer::doOneServerLoop() { exit(-18); } } + + +void ZMQServer::workerThreadMessageProcessLoop(ZMQServer* _agent ) { + CHECK_STATE(_agent); + _agent->waitOnGlobalStartBarrier(); + while (!isExitRequested) { + sleep(100); + } +} \ No newline at end of file diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 78f3fc6d..30245cc5 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -36,22 +36,36 @@ #include "zhelpers.hpp" #include "Agent.h" +#include "WorkerThreadPool.h" using namespace std; -class ZMQServer : Agent{ +class ZMQServer : public Agent{ uint64_t workerThreads; + string caCertFile; + string caCert; + + bool checkKeyOwnership = true; + + shared_ptr ctx; + shared_ptr socket; + + static std::atomic isExitRequested; + + void doOneServerLoop(); + public: bool checkSignature = false; - string caCertFile = ""; - string caCert = ""; static shared_ptr zmqServer; + shared_ptr threadPool = nullptr; + + static shared_ptr serverThread; ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string& _caCertFile); @@ -63,15 +77,7 @@ class ZMQServer : Agent{ static void initZMQServer(bool _checkSignature, bool _checkKeyOwnership); static void exitZMQServer(); -private: - bool checkKeyOwnership = true; - - shared_ptr ctx; - shared_ptr socket; - - static std::atomic isExitRequested; - - void doOneServerLoop(); + static void workerThreadMessageProcessLoop(ZMQServer* agent ); }; From d4e26311653b3b9d68049eb5d5c70d2c7f1e4c2b Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Tue, 7 Sep 2021 19:23:06 +0300 Subject: [PATCH 073/100] SKALE-4586 Added Thread Pool --- VERSION | 2 +- zmq_src/Agent.cpp | 13 +++++-------- zmq_src/Agent.h | 6 ++---- zmq_src/ZMQServer.cpp | 5 +++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/VERSION b/VERSION index 3c4c7c2d..b1fd0f76 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.78.0 \ No newline at end of file +1.83.0 \ No newline at end of file diff --git a/zmq_src/Agent.cpp b/zmq_src/Agent.cpp index d380a712..43b16fb8 100644 --- a/zmq_src/Agent.cpp +++ b/zmq_src/Agent.cpp @@ -24,17 +24,14 @@ #include "Agent.h" -void Agent::notifyAllConditionVariables() { - messageCond.notify_all(); - queueCond.notify_all(); -} Agent::Agent() : startedWorkers(false) {}; void Agent::waitOnGlobalStartBarrier() { - unique_lock mlock(queueMutex); + unique_lock lock(startMutex); while (!startedWorkers) { - queueCond.wait(mlock); + // wait until notified to start running + startCond.wait(lock); } } @@ -49,7 +46,7 @@ void Agent::releaseWorkers() { return; } - lock_guard lock(queueMutex); + lock_guard lock(startMutex); - queueCond.notify_all(); + startCond.notify_all(); } diff --git a/zmq_src/Agent.h b/zmq_src/Agent.h index c6d715ed..82304d3c 100644 --- a/zmq_src/Agent.h +++ b/zmq_src/Agent.h @@ -38,8 +38,8 @@ class Agent { mutex messageMutex; condition_variable messageCond; - condition_variable queueCond; - mutex queueMutex; + condition_variable startCond; + mutex startMutex; recursive_mutex m; @@ -48,8 +48,6 @@ class Agent { Agent(); - void notifyAllConditionVariables(); - virtual ~Agent(); void releaseWorkers(); diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 323f3aad..4d0b04f8 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -260,11 +260,12 @@ void ZMQServer::doOneServerLoop() { } } - void ZMQServer::workerThreadMessageProcessLoop(ZMQServer* _agent ) { CHECK_STATE(_agent); _agent->waitOnGlobalStartBarrier(); + // do work forever until told to exit while (!isExitRequested) { - sleep(100); + sleep(1000); + cerr << "WORKER LOOP" << endl; } } \ No newline at end of file From 9b1b6981a5221082a6c943122e9c73f645c24541 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Tue, 7 Sep 2021 20:20:49 +0300 Subject: [PATCH 074/100] SKALE-4586 Added Thread Pool --- zmq_src/WorkerThreadPool.cpp | 28 +++++++++++++--------------- zmq_src/WorkerThreadPool.h | 4 ---- zmq_src/ZMQServer.cpp | 36 +++++++++++++++++++++++++----------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/zmq_src/WorkerThreadPool.cpp b/zmq_src/WorkerThreadPool.cpp index 6650dcac..e8927913 100644 --- a/zmq_src/WorkerThreadPool.cpp +++ b/zmq_src/WorkerThreadPool.cpp @@ -27,41 +27,39 @@ #include "WorkerThreadPool.h" -void WorkerThreadPool::startService() { +WorkerThreadPool::WorkerThreadPool(uint64_t _numThreads, ZMQServer *_agent) : joined(false) { + CHECK_STATE(_numThreads > 0); + CHECK_STATE(_agent); - CHECK_STATE(!started.exchange(true)) + spdlog::info("Creating thread pool. Threads count:" + to_string(_numThreads)); + + this->agent = _agent; + this->numThreads = _numThreads;; - LOCK(m) for (uint64_t i = 0; i < (uint64_t) numThreads; i++) { createThread(i); } -} - + spdlog::info("Created thread pool"); -WorkerThreadPool::WorkerThreadPool(uint64_t _numThreads, ZMQServer *_agent) : started(false), joined(false) { - CHECK_STATE(_numThreads > 0); - CHECK_STATE(_agent); - spdlog::info("Started thread pool. Threads count:" + to_string(_numThreads)); - this->agent = _agent; - this->numThreads = _numThreads;; } void WorkerThreadPool::joinAll() { - if (joined) - return; - LOCK(m); + spdlog::info("Joining worker threads ..."); - joined = true; + if (joined.exchange(true)) + return; for (auto &&thread : threadpool) { if (thread->joinable()) thread->join(); CHECK_STATE(!thread->joinable()); } + + spdlog::info("Joined worker threads."); } bool WorkerThreadPool::isJoined() const { diff --git a/zmq_src/WorkerThreadPool.h b/zmq_src/WorkerThreadPool.h index 2c17605a..85ead506 100644 --- a/zmq_src/WorkerThreadPool.h +++ b/zmq_src/WorkerThreadPool.h @@ -33,8 +33,6 @@ class ZMQServer; class WorkerThreadPool { - atomic_bool started; - void createThread( uint64_t threadNumber ); recursive_mutex m; @@ -53,8 +51,6 @@ class WorkerThreadPool { virtual ~WorkerThreadPool(); - virtual void startService(); - void joinAll(); bool isJoined() const; diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 4d0b04f8..d6a05ab4 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -94,10 +94,26 @@ void ZMQServer::run() { std::atomic ZMQServer::isExitRequested(false); void ZMQServer::exitZMQServer() { - isExitRequested.exchange(true); + // if already exited do not exit + spdlog::info("exitZMQServer called"); + if (isExitRequested.exchange(true)) { + spdlog::info("Exit is already under way"); + return; + } + + spdlog::info("Exiting ZMQServer"); + spdlog::info("Joining worker thread pool threads ..."); + zmqServer->threadPool->joinAll(); + spdlog::info("Joined worker thread pool threads"); + spdlog::info("Shutting down ZMQ contect"); zmqServer->ctx->shutdown(); + spdlog::info("Shut down ZMQ contect"); + spdlog::info("Closing ZMQ server socket ..."); zmqServer->socket->close(); + spdlog::info("Closed ZMQ server socket"); + spdlog::info("Closing ZMQ context ..."); zmqServer->ctx->close(); + spdlog::info("Closed ZMQ context."); spdlog::info("Exited zmq server."); } @@ -118,7 +134,7 @@ void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { spdlog::info("Read CA.", rootCAPath); }; - spdlog::info("Initing zmq server."); + spdlog::info("Initing zmq server ..."); zmqServer = make_shared(_checkSignature, _checkKeyOwnership, rootCAPath); @@ -126,22 +142,20 @@ void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { serverThread = make_shared(std::bind(&ZMQServer::run, ZMQServer::zmqServer)); serverThread->detach(); - zmqServer->releaseWorkers(); - - spdlog::info("Inited zmq server."); - - spdlog::info("Starting zmq server ..."); + spdlog::info("Releasing SGX worker threads ..."); zmqServer->releaseWorkers(); - spdlog::info("Started zmq server."); - + spdlog::info("Released SGX worker threads."); + spdlog::info("Inited zmq server."); } shared_ptr ZMQServer::serverThread = nullptr; -ZMQServer::~ZMQServer() {} +ZMQServer::~ZMQServer() { + exitZMQServer(); +} void ZMQServer::doOneServerLoop() { @@ -265,7 +279,7 @@ void ZMQServer::workerThreadMessageProcessLoop(ZMQServer* _agent ) { _agent->waitOnGlobalStartBarrier(); // do work forever until told to exit while (!isExitRequested) { - sleep(1000); + usleep(1000000); cerr << "WORKER LOOP" << endl; } } \ No newline at end of file From a36f87e3fc911cdb5cc7933d121f291cf87ea681 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Tue, 7 Sep 2021 20:31:26 +0300 Subject: [PATCH 075/100] SKALE-4586 Added Thread Pool --- zmq_src/ZMQServer.cpp | 17 +++++++++++++++-- zmq_src/ZMQServer.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index d6a05ab4..669ca9b3 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -30,6 +30,7 @@ #include "common.h" #include "SGXException.h" +#include "ExitRequestedException.h" #include "ZMQMessage.h" #include "ZMQServer.h" #include "sgxwallet_common.h" @@ -274,12 +275,24 @@ void ZMQServer::doOneServerLoop() { } } +void ZMQServer::workerThreadProcessNextMessage() { + usleep(1000000); + cerr << "WORKER LOOP" << endl; +} + void ZMQServer::workerThreadMessageProcessLoop(ZMQServer* _agent ) { CHECK_STATE(_agent); _agent->waitOnGlobalStartBarrier(); // do work forever until told to exit while (!isExitRequested) { - usleep(1000000); - cerr << "WORKER LOOP" << endl; + try { + _agent->workerThreadProcessNextMessage(); + } catch (ExitRequestedException& e) { + break; + } catch (Exception& e) { + spdlog::error(string("Caught exception in worker thread loop:") + e.what()); + } } + + spdlog::info("Exit requested. Exiting worker thread."); } \ No newline at end of file diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 30245cc5..e6080edd 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -79,6 +79,8 @@ class ZMQServer : public Agent{ static void workerThreadMessageProcessLoop(ZMQServer* agent ); + void workerThreadProcessNextMessage(); + }; From 9e9f36bca6b1514b3a27b9002b2027ffde3f3823 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Wed, 8 Sep 2021 00:57:58 +0300 Subject: [PATCH 076/100] SKALE-4586 Added Thread Pool --- ExitRequestedException.cpp | 7 ++--- ExitRequestedException.h | 7 ++--- zmq_src/ZMQServer.cpp | 57 +++++++++++++++++++++----------------- zmq_src/ZMQServer.h | 4 +++ 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/ExitRequestedException.cpp b/ExitRequestedException.cpp index 5aca7ad5..b6db2e61 100644 --- a/ExitRequestedException.cpp +++ b/ExitRequestedException.cpp @@ -21,12 +21,9 @@ @date 2018 */ -#include "common.h" -#include "Log.h" #include "ExitRequestedException.h" -ExitRequestedException::ExitRequestedException(const std::string &_message, const string& _className) : - Exception(_message, _className) { - fatal = false; +ExitRequestedException::ExitRequestedException() { + } diff --git a/ExitRequestedException.h b/ExitRequestedException.h index 2c0e8b7e..9850ee82 100644 --- a/ExitRequestedException.h +++ b/ExitRequestedException.h @@ -23,10 +23,9 @@ #pragma once -#include "Exception.h" +#include - -class ExitRequestedException : public Exception { +class ExitRequestedException : public std::exception { public: - ExitRequestedException( const std::string& _message, const std::string& _className ); + ExitRequestedException(); }; diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 669ca9b3..bb0d4b12 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -61,7 +61,7 @@ ZMQServer::ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string } -void ZMQServer::run() { +void ZMQServer::initListenSocket() { auto port = BASE_PORT + 5; @@ -71,20 +71,29 @@ void ZMQServer::run() { CHECK_STATE(socket); socket->bind("tcp://*:" + to_string(port)); } catch (...) { - spdlog::error("Server task could not bind to port:{}", port); + spdlog::error("Zmq server task could not bind to port:{}", port); throw SGXException(ZMQ_COULD_NOT_BIND_FRONT_END, "Server task could not bind."); } - spdlog::info("Bound port ..."); + spdlog::info("ZMQ server socket created and bound."); + +} + +void ZMQServer::run() { + zmqServer->initListenSocket(); - spdlog::info("Started zmq read loop ..."); + spdlog::info("Started zmq read loop."); while (!isExitRequested) { try { zmqServer->doOneServerLoop(); - } catch (...) { + } catch (ExitRequestedException& e) { + spdlog::info("Exit requested. Exiting server loop"); + break; + } + catch (...) { spdlog::error("doOneServerLoop threw exception. This should never happen!"); } } @@ -124,7 +133,7 @@ void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { initedServer = true; spdlog::info("Initing zmq server.\n checkSignature is set to {}.\n checkKeyOwnership is set to {}", - _checkSignature, _checkKeyOwnership); + _checkSignature, _checkKeyOwnership); string rootCAPath = ""; @@ -158,6 +167,12 @@ ZMQServer::~ZMQServer() { exitZMQServer(); } +void ZMQServer::checkForExit() { + if (isExitRequested) { + throw ExitRequestedException(); + } +} + void ZMQServer::doOneServerLoop() { string replyStr; @@ -172,7 +187,6 @@ void ZMQServer::doOneServerLoop() { try { - zmq_pollitem_t items[1]; items[0].socket = *socket; items[0].events = ZMQ_POLLIN; @@ -180,21 +194,19 @@ void ZMQServer::doOneServerLoop() { int pollResult = 0; do { + checkForExit(); pollResult = zmq_poll(items, 1, 1000); - if (isExitRequested) { - return; - } } while (pollResult == 0); if (!socket->recv(&identity)) { // something terrible happened - spdlog::error("Fatal error: socket->recv(&identity) returned false"); + spdlog::error("Fatal error: socket->recv(&identity) returned false. Exiting."); exit(-11); } if (!identity.more()) { // something terrible happened - spdlog::error("Fatal error: zmq_msg_more(identity) returned false"); + spdlog::error("Fatal error: zmq_msg_more(identity) returned false. Existing."); exit(-12); } @@ -202,7 +214,7 @@ void ZMQServer::doOneServerLoop() { if (!socket->recv(&reqMsg, 0)) { // something terrible happened - spdlog::error("Fatal error: socket.recv(&reqMsg, 0) returned false"); + spdlog::error("Fatal error: socket.recv(&reqMsg, 0) returned false. Exiting"); exit(-13); } @@ -216,23 +228,18 @@ void ZMQServer::doOneServerLoop() { CHECK_STATE2(parsedMsg, ZMQ_COULD_NOT_PARSE); + result = parsedMsg->process(); } catch (std::exception &e) { - if (isExitRequested) { - return; - } result["errorMessage"] = string(e.what()); spdlog::error("Exception in zmq server :{}", e.what()); - spdlog::error("ID:" + string((char*) identity.data(), identity.size())); + spdlog::error("ID:" + string((char *) identity.data(), identity.size())); spdlog::error("Client request :" + stringToParse); } catch (...) { - if (isExitRequested) { - return; - } spdlog::error("Error in zmq server "); result["errorMessage"] = "Error in zmq server "; - spdlog::error("ID:" + string((char*) identity.data(), identity.size())); + spdlog::error("ID:" + string((char *) identity.data(), identity.size())); spdlog::error("Client request :" + stringToParse); } @@ -260,7 +267,7 @@ void ZMQServer::doOneServerLoop() { exit(-16); } - } catch ( std::exception &e ) { + } catch (std::exception &e) { if (isExitRequested) { return; } @@ -280,16 +287,16 @@ void ZMQServer::workerThreadProcessNextMessage() { cerr << "WORKER LOOP" << endl; } -void ZMQServer::workerThreadMessageProcessLoop(ZMQServer* _agent ) { +void ZMQServer::workerThreadMessageProcessLoop(ZMQServer *_agent) { CHECK_STATE(_agent); _agent->waitOnGlobalStartBarrier(); // do work forever until told to exit while (!isExitRequested) { try { _agent->workerThreadProcessNextMessage(); - } catch (ExitRequestedException& e) { + } catch (ExitRequestedException &e) { break; - } catch (Exception& e) { + } catch (Exception &e) { spdlog::error(string("Caught exception in worker thread loop:") + e.what()); } } diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index e6080edd..dbae9f73 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -74,6 +74,8 @@ class ZMQServer : public Agent{ void run(); + void initListenSocket(); + static void initZMQServer(bool _checkSignature, bool _checkKeyOwnership); static void exitZMQServer(); @@ -81,6 +83,8 @@ class ZMQServer : public Agent{ void workerThreadProcessNextMessage(); + void checkForExit(); + }; From 150b2cdaf48f9613053805b0b472aa6ec96610df Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Wed, 8 Sep 2021 01:02:57 +0300 Subject: [PATCH 077/100] SKALE-4586 Added Thread Pool --- zmq_src/ZMQServer.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index bb0d4b12..3aee5ae6 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -230,12 +230,13 @@ void ZMQServer::doOneServerLoop() { result = parsedMsg->process(); + } catch (ExitRequestedException) { + throw; } catch (std::exception &e) { result["errorMessage"] = string(e.what()); spdlog::error("Exception in zmq server :{}", e.what()); spdlog::error("ID:" + string((char *) identity.data(), identity.size())); spdlog::error("Client request :" + stringToParse); - } catch (...) { spdlog::error("Error in zmq server "); result["errorMessage"] = "Error in zmq server "; @@ -244,7 +245,6 @@ void ZMQServer::doOneServerLoop() { } try { - Json::FastWriter fastWriter; fastWriter.omitEndingLineFeed(); @@ -266,17 +266,12 @@ void ZMQServer::doOneServerLoop() { } exit(-16); } - + } catch (ExitRequestedException) { + throw; } catch (std::exception &e) { - if (isExitRequested) { - return; - } spdlog::error("Exception in zmq server worker send :{}", e.what()); exit(-17); } catch (...) { - if (isExitRequested) { - return; - } spdlog::error("Unklnown exception in zmq server worker send"); exit(-18); } From f575911f0822338c50a2df6e7172b8093c0d1cf0 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Wed, 8 Sep 2021 01:18:59 +0300 Subject: [PATCH 078/100] SKALE-4586 Added Thread Pool --- zmq_src/ZMQServer.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 3aee5ae6..11da3cee 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -31,6 +31,7 @@ #include "SGXException.h" #include "ExitRequestedException.h" +#include "ReqMessage.h" #include "ZMQMessage.h" #include "ZMQServer.h" #include "sgxwallet_common.h" @@ -199,6 +200,7 @@ void ZMQServer::doOneServerLoop() { } while (pollResult == 0); if (!socket->recv(&identity)) { + checkForExit(); // something terrible happened spdlog::error("Fatal error: socket->recv(&identity) returned false. Exiting."); exit(-11); @@ -213,6 +215,7 @@ void ZMQServer::doOneServerLoop() { zmq::message_t reqMsg; if (!socket->recv(&reqMsg, 0)) { + checkForExit(); // something terrible happened spdlog::error("Fatal error: socket.recv(&reqMsg, 0) returned false. Exiting"); exit(-13); @@ -226,6 +229,16 @@ void ZMQServer::doOneServerLoop() { auto parsedMsg = ZMQMessage::parse( stringToParse.c_str(), stringToParse.size(), true, checkSignature, checkKeyOwnership); + + if ((dynamic_pointer_cast(parsedMsg)!= nullptr) || + dynamic_pointer_cast(parsedMsg)) { + spdlog::info("FUFUFUFUF"); + } else { + spdlog::info("HAHAHA"); + } + + + CHECK_STATE2(parsedMsg, ZMQ_COULD_NOT_PARSE); @@ -233,11 +246,13 @@ void ZMQServer::doOneServerLoop() { } catch (ExitRequestedException) { throw; } catch (std::exception &e) { + checkForExit(); result["errorMessage"] = string(e.what()); spdlog::error("Exception in zmq server :{}", e.what()); spdlog::error("ID:" + string((char *) identity.data(), identity.size())); spdlog::error("Client request :" + stringToParse); } catch (...) { + checkForExit(); spdlog::error("Error in zmq server "); result["errorMessage"] = "Error in zmq server "; spdlog::error("ID:" + string((char *) identity.data(), identity.size())); @@ -255,23 +270,20 @@ void ZMQServer::doOneServerLoop() { CHECK_STATE(replyStr.back() == '}'); if (!socket->send(identity, ZMQ_SNDMORE)) { - if (isExitRequested) { - return; - } exit(-15); } if (!s_send(*socket, replyStr)) { - if (isExitRequested) { - return; - } exit(-16); } } catch (ExitRequestedException) { throw; } catch (std::exception &e) { + + checkForExit(); spdlog::error("Exception in zmq server worker send :{}", e.what()); exit(-17); } catch (...) { + checkForExit(); spdlog::error("Unklnown exception in zmq server worker send"); exit(-18); } From fbdd91ac639b7831af6016607d0a20d012d514a9 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Wed, 8 Sep 2021 16:28:34 +0300 Subject: [PATCH 079/100] SKALE-4586 Added Thread Pool --- common.h | 12 ++- zmq_src/Agent.cpp | 3 +- zmq_src/Agent.h | 6 -- zmq_src/WorkerThreadPool.cpp | 7 ++ zmq_src/WorkerThreadPool.h | 5 -- zmq_src/ZMQMessage.h | 3 - zmq_src/ZMQServer.cpp | 161 ++++++++++++++++++----------------- zmq_src/ZMQServer.h | 16 ++-- 8 files changed, 107 insertions(+), 106 deletions(-) diff --git a/common.h b/common.h index 94169413..f5e51a94 100644 --- a/common.h +++ b/common.h @@ -31,16 +31,20 @@ using namespace std; #include #include #include - #include #include #include - #include - +#include #include - #include +#include +#include +#include +#include +#include + + #include "secure_enclave/Verify.h" #include "InvalidStateException.h" #include "SGXException.h" diff --git a/zmq_src/Agent.cpp b/zmq_src/Agent.cpp index 43b16fb8..4b27aa01 100644 --- a/zmq_src/Agent.cpp +++ b/zmq_src/Agent.cpp @@ -21,10 +21,9 @@ @date 2021 */ - +#include "common.h" #include "Agent.h" - Agent::Agent() : startedWorkers(false) {}; void Agent::waitOnGlobalStartBarrier() { diff --git a/zmq_src/Agent.h b/zmq_src/Agent.h index 82304d3c..803c2d5a 100644 --- a/zmq_src/Agent.h +++ b/zmq_src/Agent.h @@ -23,12 +23,6 @@ #pragma once -#include -#include -#include - -using namespace std; - class Agent { protected: diff --git a/zmq_src/WorkerThreadPool.cpp b/zmq_src/WorkerThreadPool.cpp index e8927913..2d602d8d 100644 --- a/zmq_src/WorkerThreadPool.cpp +++ b/zmq_src/WorkerThreadPool.cpp @@ -21,7 +21,14 @@ @date 2021 */ +#include "document.h" +#include "stringbuffer.h" +#include "writer.h" + + + #include "common.h" +#include "sgxwallet_common.h" #include "third_party/spdlog/spdlog.h" #include "ZMQServer.h" #include "WorkerThreadPool.h" diff --git a/zmq_src/WorkerThreadPool.h b/zmq_src/WorkerThreadPool.h index 85ead506..d23ffdc2 100644 --- a/zmq_src/WorkerThreadPool.h +++ b/zmq_src/WorkerThreadPool.h @@ -23,11 +23,6 @@ #pragma once -#include -#include -#include - - class Agent; class ZMQServer; diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index b68de588..e4908047 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -24,9 +24,6 @@ #pragma once -#include -#include - #include #include #include diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 11da3cee..fbea99f7 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -28,13 +28,14 @@ #include "third_party/spdlog/spdlog.h" #include "common.h" +#include "sgxwallet_common.h" #include "SGXException.h" #include "ExitRequestedException.h" #include "ReqMessage.h" #include "ZMQMessage.h" #include "ZMQServer.h" -#include "sgxwallet_common.h" + using namespace std; @@ -174,75 +175,109 @@ void ZMQServer::checkForExit() { } } -void ZMQServer::doOneServerLoop() { +void ZMQServer::poll() { + zmq_pollitem_t items[1]; + items[0].socket = *socket; + items[0].events = ZMQ_POLLIN; - string replyStr; + int pollResult = 0; - Json::Value result; - result["status"] = ZMQ_SERVER_ERROR; - result["errorMessage"] = ""; + do { + checkForExit(); + pollResult = zmq_poll(items, 1, 1000); + } while (pollResult == 0); +} - zmq::message_t identity; +string ZMQServer::receiveMessage(zmq::message_t& _identity) { + if (!socket->recv(&_identity)) { + checkForExit(); + // something terrible happened + spdlog::error("Fatal error: socket->recv(&identity) returned false. Exiting."); + exit(-11); + } - string stringToParse = ""; + if (!_identity.more()) { + checkForExit(); + // something terrible happened + spdlog::error("Fatal error: zmq_msg_more(identity) returned false. Existing."); + exit(-12); + } - try { + zmq::message_t reqMsg; - zmq_pollitem_t items[1]; - items[0].socket = *socket; - items[0].events = ZMQ_POLLIN; + if (!socket->recv(&reqMsg, 0)) { + checkForExit(); + // something terrible happened + spdlog::error("Fatal error: socket.recv(&reqMsg, 0) returned false. Exiting"); + exit(-13); + } - int pollResult = 0; + auto result = string((char *) reqMsg.data(), reqMsg.size()); - do { - checkForExit(); - pollResult = zmq_poll(items, 1, 1000); - } while (pollResult == 0); + CHECK_STATE(result.front() == '{') + CHECK_STATE(result.back() == '}') + return result; +} - if (!socket->recv(&identity)) { - checkForExit(); - // something terrible happened - spdlog::error("Fatal error: socket->recv(&identity) returned false. Exiting."); - exit(-11); - } +void ZMQServer::sendToClient(Json::Value& _result, zmq::message_t& _identity ) { + string replyStr; + try { + Json::FastWriter fastWriter; + fastWriter.omitEndingLineFeed(); - if (!identity.more()) { - // something terrible happened - spdlog::error("Fatal error: zmq_msg_more(identity) returned false. Existing."); - exit(-12); - } + replyStr = fastWriter.write(_result); - zmq::message_t reqMsg; + CHECK_STATE(replyStr.size() > 2); + CHECK_STATE(replyStr.front() == '{'); + CHECK_STATE(replyStr.back() == '}'); - if (!socket->recv(&reqMsg, 0)) { - checkForExit(); - // something terrible happened - spdlog::error("Fatal error: socket.recv(&reqMsg, 0) returned false. Exiting"); - exit(-13); + if (!socket->send(_identity, ZMQ_SNDMORE)) { + exit(-15); + } + if (!s_send(*socket, replyStr)) { + exit(-16); } + } catch (ExitRequestedException) { + throw; + } catch (std::exception &e) { + checkForExit(); + spdlog::error("Exception in zmq server worker send :{}", e.what()); + exit(-17); + } catch (...) { + checkForExit(); + spdlog::error("Unklnown exception in zmq server worker send"); + exit(-18); + } - stringToParse = string((char *) reqMsg.data(), reqMsg.size()); +} - CHECK_STATE(stringToParse.front() == '{') - CHECK_STATE(stringToParse.back() == '}') +void ZMQServer::doOneServerLoop() { - auto parsedMsg = ZMQMessage::parse( - stringToParse.c_str(), stringToParse.size(), true, checkSignature, checkKeyOwnership); + Json::Value result; + result["status"] = ZMQ_SERVER_ERROR; + zmq::message_t identity; + string msgStr; - if ((dynamic_pointer_cast(parsedMsg)!= nullptr) || - dynamic_pointer_cast(parsedMsg)) { - spdlog::info("FUFUFUFUF"); - } else { - spdlog::info("HAHAHA"); - } + try { + poll(); + msgStr = receiveMessage(identity); - CHECK_STATE2(parsedMsg, ZMQ_COULD_NOT_PARSE); + auto msg = ZMQMessage::parse( + msgStr.c_str(), msgStr.size(), true, checkSignature, checkKeyOwnership); + CHECK_STATE2(msg, ZMQ_COULD_NOT_PARSE); - result = parsedMsg->process(); + if ((dynamic_pointer_cast(msg)!= nullptr) || + dynamic_pointer_cast(msg)) { + spdlog::info("FUFUFUFUF"); + } else { + spdlog::info("HAHAHA"); + } + + result = msg->process(); } catch (ExitRequestedException) { throw; } catch (std::exception &e) { @@ -250,43 +285,17 @@ void ZMQServer::doOneServerLoop() { result["errorMessage"] = string(e.what()); spdlog::error("Exception in zmq server :{}", e.what()); spdlog::error("ID:" + string((char *) identity.data(), identity.size())); - spdlog::error("Client request :" + stringToParse); + spdlog::error("Client request :" + msgStr); } catch (...) { checkForExit(); spdlog::error("Error in zmq server "); result["errorMessage"] = "Error in zmq server "; spdlog::error("ID:" + string((char *) identity.data(), identity.size())); - spdlog::error("Client request :" + stringToParse); + spdlog::error("Client request :" + msgStr); } - try { - Json::FastWriter fastWriter; - fastWriter.omitEndingLineFeed(); + sendToClient(result, identity); - replyStr = fastWriter.write(result); - - CHECK_STATE(replyStr.size() > 2); - CHECK_STATE(replyStr.front() == '{'); - CHECK_STATE(replyStr.back() == '}'); - - if (!socket->send(identity, ZMQ_SNDMORE)) { - exit(-15); - } - if (!s_send(*socket, replyStr)) { - exit(-16); - } - } catch (ExitRequestedException) { - throw; - } catch (std::exception &e) { - - checkForExit(); - spdlog::error("Exception in zmq server worker send :{}", e.what()); - exit(-17); - } catch (...) { - checkForExit(); - spdlog::error("Unklnown exception in zmq server worker send"); - exit(-18); - } } void ZMQServer::workerThreadProcessNextMessage() { diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index dbae9f73..5cbddef4 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -25,22 +25,12 @@ #ifndef SGXWALLET_ZMQServer_H #define SGXWALLET_ZMQServer_H - -#include -#include -#include -#include -#include - #include #include "zhelpers.hpp" #include "Agent.h" #include "WorkerThreadPool.h" -using namespace std; - - class ZMQServer : public Agent{ uint64_t workerThreads; @@ -85,6 +75,12 @@ class ZMQServer : public Agent{ void checkForExit(); + void poll(); + + string receiveMessage(zmq::message_t& _identity); + + void sendToClient(Json::Value& _result, zmq::message_t& _identity); + }; From 832ff75a1e780fab46ef9a40315dfcb31cce2c92 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 14:44:33 +0300 Subject: [PATCH 080/100] SKALE-4586 Added Thread Pool --- zmq_src/ZMQServer.cpp | 8 ++++++-- zmq_src/ZMQServer.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index fbea99f7..25f5664f 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -175,7 +175,9 @@ void ZMQServer::checkForExit() { } } -void ZMQServer::poll() { + + +PollResult ZMQServer::poll() { zmq_pollitem_t items[1]; items[0].socket = *socket; items[0].events = ZMQ_POLLIN; @@ -184,8 +186,10 @@ void ZMQServer::poll() { do { checkForExit(); - pollResult = zmq_poll(items, 1, 1000); + pollResult = zmq_poll(items, 1, 1); } while (pollResult == 0); + + return GOT_INCOMING_MSG; } string ZMQServer::receiveMessage(zmq::message_t& _identity) { diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 5cbddef4..ae7582fe 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -31,6 +31,8 @@ #include "Agent.h" #include "WorkerThreadPool.h" +typedef enum {GOT_INCOMING_MSG = 0, GOT_OUTFOING_MSG = 1} PollResult; + class ZMQServer : public Agent{ uint64_t workerThreads; @@ -75,7 +77,7 @@ class ZMQServer : public Agent{ void checkForExit(); - void poll(); + PollResult poll(); string receiveMessage(zmq::message_t& _identity); From ecde0240c43c3d414228a9ac3c43a1b033ca8bec Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 16:19:11 +0300 Subject: [PATCH 081/100] SKALE-4586 Added Queue --- third_party/atomicops.h | 761 +++++++++++++++++++++++++ third_party/readerwriterqueue.h | 979 ++++++++++++++++++++++++++++++++ zmq_src/ZMQClient.h | 1 - zmq_src/ZMQServer.cpp | 44 +- zmq_src/ZMQServer.h | 19 +- 5 files changed, 1778 insertions(+), 26 deletions(-) create mode 100644 third_party/atomicops.h create mode 100644 third_party/readerwriterqueue.h diff --git a/third_party/atomicops.h b/third_party/atomicops.h new file mode 100644 index 00000000..696888b4 --- /dev/null +++ b/third_party/atomicops.h @@ -0,0 +1,761 @@ +// ©2013-2016 Cameron Desrochers. +// Distributed under the simplified BSD license (see the license file that +// should have come with this header). +// Uses Jeff Preshing's semaphore implementation (under the terms of its +// separate zlib license, embedded below). + +#pragma once + +// Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation +// of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment). +// Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees). +// Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols. + +#include +#include +#include +#include +#include +#include + +// Platform detection +#if defined(__INTEL_COMPILER) +#define AE_ICC +#elif defined(_MSC_VER) +#define AE_VCPP +#elif defined(__GNUC__) +#define AE_GCC +#endif + +#if defined(_M_IA64) || defined(__ia64__) +#define AE_ARCH_IA64 +#elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__) +#define AE_ARCH_X64 +#elif defined(_M_IX86) || defined(__i386__) +#define AE_ARCH_X86 +#elif defined(_M_PPC) || defined(__powerpc__) +#define AE_ARCH_PPC +#else +#define AE_ARCH_UNKNOWN +#endif + + +// AE_UNUSED +#define AE_UNUSED(x) ((void)x) + +// AE_NO_TSAN/AE_TSAN_ANNOTATE_* +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#if __cplusplus >= 201703L // inline variables require C++17 +namespace moodycamel { inline int ae_tsan_global; } +#define AE_TSAN_ANNOTATE_RELEASE() AnnotateHappensBefore(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global)) +#define AE_TSAN_ANNOTATE_ACQUIRE() AnnotateHappensAfter(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global)) +extern "C" void AnnotateHappensBefore(const char*, int, void*); +extern "C" void AnnotateHappensAfter(const char*, int, void*); +#else // when we can't work with tsan, attempt to disable its warnings +#define AE_NO_TSAN __attribute__((no_sanitize("thread"))) +#endif +#endif +#endif +#ifndef AE_NO_TSAN +#define AE_NO_TSAN +#endif +#ifndef AE_TSAN_ANNOTATE_RELEASE +#define AE_TSAN_ANNOTATE_RELEASE() +#define AE_TSAN_ANNOTATE_ACQUIRE() +#endif + + +// AE_FORCEINLINE +#if defined(AE_VCPP) || defined(AE_ICC) +#define AE_FORCEINLINE __forceinline +#elif defined(AE_GCC) +//#define AE_FORCEINLINE __attribute__((always_inline)) +#define AE_FORCEINLINE inline +#else +#define AE_FORCEINLINE inline +#endif + + +// AE_ALIGN +#if defined(AE_VCPP) || defined(AE_ICC) +#define AE_ALIGN(x) __declspec(align(x)) +#elif defined(AE_GCC) +#define AE_ALIGN(x) __attribute__((aligned(x))) +#else +// Assume GCC compliant syntax... +#define AE_ALIGN(x) __attribute__((aligned(x))) +#endif + + +// Portable atomic fences implemented below: + +namespace moodycamel { + + enum memory_order { + memory_order_relaxed, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, + + // memory_order_sync: Forces a full sync: + // #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad + memory_order_sync = memory_order_seq_cst + }; + +} // end namespace moodycamel + +#if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || (defined(AE_ICC) && __INTEL_COMPILER < 1600) +// VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences + +#include + +#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) +#define AeFullSync _mm_mfence +#define AeLiteSync _mm_mfence +#elif defined(AE_ARCH_IA64) +#define AeFullSync __mf +#define AeLiteSync __mf +#elif defined(AE_ARCH_PPC) +#include +#define AeFullSync __sync +#define AeLiteSync __lwsync +#endif + + +#ifdef AE_VCPP +#pragma warning(push) +#pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert` +#ifdef __cplusplus_cli +#pragma managed(push, off) +#endif +#endif + +namespace moodycamel { + +AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN +{ + switch (order) { + case memory_order_relaxed: break; + case memory_order_acquire: _ReadBarrier(); break; + case memory_order_release: _WriteBarrier(); break; + case memory_order_acq_rel: _ReadWriteBarrier(); break; + case memory_order_seq_cst: _ReadWriteBarrier(); break; + default: assert(false); + } +} + +// x86/x64 have a strong memory model -- all loads and stores have +// acquire and release semantics automatically (so only need compiler +// barriers for those). +#if defined(AE_ARCH_X86) || defined(AE_ARCH_X64) +AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN +{ + switch (order) { + case memory_order_relaxed: break; + case memory_order_acquire: _ReadBarrier(); break; + case memory_order_release: _WriteBarrier(); break; + case memory_order_acq_rel: _ReadWriteBarrier(); break; + case memory_order_seq_cst: + _ReadWriteBarrier(); + AeFullSync(); + _ReadWriteBarrier(); + break; + default: assert(false); + } +} +#else +AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN +{ + // Non-specialized arch, use heavier memory barriers everywhere just in case :-( + switch (order) { + case memory_order_relaxed: + break; + case memory_order_acquire: + _ReadBarrier(); + AeLiteSync(); + _ReadBarrier(); + break; + case memory_order_release: + _WriteBarrier(); + AeLiteSync(); + _WriteBarrier(); + break; + case memory_order_acq_rel: + _ReadWriteBarrier(); + AeLiteSync(); + _ReadWriteBarrier(); + break; + case memory_order_seq_cst: + _ReadWriteBarrier(); + AeFullSync(); + _ReadWriteBarrier(); + break; + default: assert(false); + } +} +#endif +} // end namespace moodycamel +#else +// Use standard library of atomics +#include + +namespace moodycamel { + + AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN + { + switch (order) { + case memory_order_relaxed: break; + case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break; + case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break; + case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break; + case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break; + default: assert(false); + } + } + + AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN + { + switch (order) { + case memory_order_relaxed: break; + case memory_order_acquire: AE_TSAN_ANNOTATE_ACQUIRE(); std::atomic_thread_fence(std::memory_order_acquire); break; + case memory_order_release: AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_release); break; + case memory_order_acq_rel: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_acq_rel); break; + case memory_order_seq_cst: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_seq_cst); break; + default: assert(false); + } + } + +} // end namespace moodycamel + +#endif + + +#if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli)) +#define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC +#endif + +#ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC +#include +#endif +#include + +// WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY: +// Provides basic support for atomic variables -- no memory ordering guarantees are provided. +// The guarantee of atomicity is only made for types that already have atomic load and store guarantees +// at the hardware level -- on most platforms this generally means aligned pointers and integers (only). +namespace moodycamel { + template + class weak_atomic + { + public: + AE_NO_TSAN weak_atomic() : value() { } +#ifdef AE_VCPP + #pragma warning(push) +#pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning +#endif + template AE_NO_TSAN weak_atomic(U&& x) : value(std::forward(x)) { } +#ifdef __cplusplus_cli + // Work around bug with universal reference/nullptr combination that only appears when /clr is on + AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) { } +#endif + AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) { } + AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) { } +#ifdef AE_VCPP +#pragma warning(pop) +#endif + + AE_FORCEINLINE operator T() const AE_NO_TSAN { return load(); } + + +#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC + template AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { value = std::forward(x); return *this; } + AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { value = other.value; return *this; } + + AE_FORCEINLINE T load() const AE_NO_TSAN { return value; } + + AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN + { +#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) + if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); +#if defined(_M_AMD64) + else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); +#endif +#else +#error Unsupported platform +#endif + assert(false && "T must be either a 32 or 64 bit type"); + return value; + } + + AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN + { +#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) + if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); +#if defined(_M_AMD64) + else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); +#endif +#else +#error Unsupported platform +#endif + assert(false && "T must be either a 32 or 64 bit type"); + return value; + } +#else + template + AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN + { + value.store(std::forward(x), std::memory_order_relaxed); + return *this; + } + + AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN + { + value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); + return *this; + } + + AE_FORCEINLINE T load() const AE_NO_TSAN { return value.load(std::memory_order_relaxed); } + + AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN + { + return value.fetch_add(increment, std::memory_order_acquire); + } + + AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN + { + return value.fetch_add(increment, std::memory_order_release); + } +#endif + + + private: +#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC + // No std::atomic support, but still need to circumvent compiler optimizations. + // `volatile` will make memory access slow, but is guaranteed to be reliable. + volatile T value; +#else + std::atomic value; +#endif + }; + +} // end namespace moodycamel + + + +// Portable single-producer, single-consumer semaphore below: + +#if defined(_WIN32) +// Avoid including windows.h in a header; we only need a handful of +// items, so we'll redeclare them here (this is relatively safe since +// the API generally has to remain stable between Windows versions). +// I know this is an ugly hack but it still beats polluting the global +// namespace with thousands of generic names or adding a .cpp for nothing. +extern "C" { + struct _SECURITY_ATTRIBUTES; + __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName); + __declspec(dllimport) int __stdcall CloseHandle(void* hObject); + __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds); + __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount); +} +#elif defined(__MACH__) +#include +#elif defined(__unix__) +#include +#elif defined(FREERTOS) +#include +#include +#include +#endif + +namespace moodycamel +{ + // Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's + // portable + lightweight semaphore implementations, originally from + // https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h + // LICENSE: + // Copyright (c) 2015 Jeff Preshing + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgement in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + namespace spsc_sema + { +#if defined(_WIN32) + class Semaphore + { + private: + void* m_hSema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema() + { + assert(initialCount >= 0); + const long maxLong = 0x7fffffff; + m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); + assert(m_hSema); + } + + AE_NO_TSAN ~Semaphore() + { + CloseHandle(m_hSema); + } + + bool wait() AE_NO_TSAN + { + const unsigned long infinite = 0xffffffff; + return WaitForSingleObject(m_hSema, infinite) == 0; + } + + bool try_wait() AE_NO_TSAN + { + return WaitForSingleObject(m_hSema, 0) == 0; + } + + bool timed_wait(std::uint64_t usecs) AE_NO_TSAN + { + return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0; + } + + void signal(int count = 1) AE_NO_TSAN + { + while (!ReleaseSemaphore(m_hSema, count, nullptr)); + } + }; +#elif defined(__MACH__) + //--------------------------------------------------------- + // Semaphore (Apple iOS and OSX) + // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html + //--------------------------------------------------------- + class Semaphore + { + private: + semaphore_t m_sema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() + { + assert(initialCount >= 0); + kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount); + assert(rc == KERN_SUCCESS); + AE_UNUSED(rc); + } + + AE_NO_TSAN ~Semaphore() + { + semaphore_destroy(mach_task_self(), m_sema); + } + + bool wait() AE_NO_TSAN + { + return semaphore_wait(m_sema) == KERN_SUCCESS; + } + + bool try_wait() AE_NO_TSAN + { + return timed_wait(0); + } + + bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN + { + mach_timespec_t ts; + ts.tv_sec = static_cast(timeout_usecs / 1000000); + ts.tv_nsec = static_cast((timeout_usecs % 1000000) * 1000); + + // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html + kern_return_t rc = semaphore_timedwait(m_sema, ts); + return rc == KERN_SUCCESS; + } + + void signal() AE_NO_TSAN + { + while (semaphore_signal(m_sema) != KERN_SUCCESS); + } + + void signal(int count) AE_NO_TSAN + { + while (count-- > 0) + { + while (semaphore_signal(m_sema) != KERN_SUCCESS); + } + } + }; +#elif defined(__unix__) + //--------------------------------------------------------- + // Semaphore (POSIX, Linux) + //--------------------------------------------------------- + class Semaphore + { + private: + sem_t m_sema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() + { + assert(initialCount >= 0); + int rc = sem_init(&m_sema, 0, static_cast(initialCount)); + assert(rc == 0); + AE_UNUSED(rc); + } + + AE_NO_TSAN ~Semaphore() + { + sem_destroy(&m_sema); + } + + bool wait() AE_NO_TSAN + { + // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error + int rc; + do + { + rc = sem_wait(&m_sema); + } + while (rc == -1 && errno == EINTR); + return rc == 0; + } + + bool try_wait() AE_NO_TSAN + { + int rc; + do { + rc = sem_trywait(&m_sema); + } while (rc == -1 && errno == EINTR); + return rc == 0; + } + + bool timed_wait(std::uint64_t usecs) AE_NO_TSAN + { + struct timespec ts; + const int usecs_in_1_sec = 1000000; + const int nsecs_in_1_sec = 1000000000; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += static_cast(usecs / usecs_in_1_sec); + ts.tv_nsec += static_cast(usecs % usecs_in_1_sec) * 1000; + // sem_timedwait bombs if you have more than 1e9 in tv_nsec + // so we have to clean things up before passing it in + if (ts.tv_nsec >= nsecs_in_1_sec) { + ts.tv_nsec -= nsecs_in_1_sec; + ++ts.tv_sec; + } + + int rc; + do { + rc = sem_timedwait(&m_sema, &ts); + } while (rc == -1 && errno == EINTR); + return rc == 0; + } + + void signal() AE_NO_TSAN + { + while (sem_post(&m_sema) == -1); + } + + void signal(int count) AE_NO_TSAN + { + while (count-- > 0) + { + while (sem_post(&m_sema) == -1); + } + } + }; +#elif defined(FREERTOS) + //--------------------------------------------------------- + // Semaphore (FreeRTOS) + //--------------------------------------------------------- + class Semaphore + { + private: + SemaphoreHandle_t m_sema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() + { + assert(initialCount >= 0); + m_sema = xSemaphoreCreateCounting(static_cast(~0ull), static_cast(initialCount)); + assert(m_sema); + } + + AE_NO_TSAN ~Semaphore() + { + vSemaphoreDelete(m_sema); + } + + bool wait() AE_NO_TSAN + { + return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE; + } + + bool try_wait() AE_NO_TSAN + { + // Note: In an ISR context, if this causes a task to unblock, + // the caller won't know about it + if (xPortIsInsideInterrupt()) + return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE; + return xSemaphoreTake(m_sema, 0) == pdTRUE; + } + + bool timed_wait(std::uint64_t usecs) AE_NO_TSAN + { + std::uint64_t msecs = usecs / 1000; + TickType_t ticks = static_cast(msecs / portTICK_PERIOD_MS); + if (ticks == 0) + return try_wait(); + return xSemaphoreTake(m_sema, ticks) == pdTRUE; + } + + void signal() AE_NO_TSAN + { + // Note: In an ISR context, if this causes a task to unblock, + // the caller won't know about it + BaseType_t rc; + if (xPortIsInsideInterrupt()) + rc = xSemaphoreGiveFromISR(m_sema, NULL); + else + rc = xSemaphoreGive(m_sema); + assert(rc == pdTRUE); + AE_UNUSED(rc); + } + + void signal(int count) AE_NO_TSAN + { + while (count-- > 0) + signal(); + } + }; +#else +#error Unsupported platform! (No semaphore wrapper available) +#endif + + //--------------------------------------------------------- + // LightweightSemaphore + //--------------------------------------------------------- + class LightweightSemaphore + { + public: + typedef std::make_signed::type ssize_t; + + private: + weak_atomic m_count; + Semaphore m_sema; + + bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) AE_NO_TSAN + { + ssize_t oldCount; + // Is there a better way to set the initial spin count? + // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC, + // as threads start hitting the kernel semaphore. + int spin = 1024; + while (--spin >= 0) + { + if (m_count.load() > 0) + { + m_count.fetch_add_acquire(-1); + return true; + } + compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop. + } + oldCount = m_count.fetch_add_acquire(-1); + if (oldCount > 0) + return true; + if (timeout_usecs < 0) + { + if (m_sema.wait()) + return true; + } + if (timeout_usecs > 0 && m_sema.timed_wait(static_cast(timeout_usecs))) + return true; + // At this point, we've timed out waiting for the semaphore, but the + // count is still decremented indicating we may still be waiting on + // it. So we have to re-adjust the count, but only if the semaphore + // wasn't signaled enough times for us too since then. If it was, we + // need to release the semaphore too. + while (true) + { + oldCount = m_count.fetch_add_release(1); + if (oldCount < 0) + return false; // successfully restored things to the way they were + // Oh, the producer thread just signaled the semaphore after all. Try again: + oldCount = m_count.fetch_add_acquire(-1); + if (oldCount > 0 && m_sema.try_wait()) + return true; + } + } + + public: + AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema() + { + assert(initialCount >= 0); + } + + bool tryWait() AE_NO_TSAN + { + if (m_count.load() > 0) + { + m_count.fetch_add_acquire(-1); + return true; + } + return false; + } + + bool wait() AE_NO_TSAN + { + return tryWait() || waitWithPartialSpinning(); + } + + bool wait(std::int64_t timeout_usecs) AE_NO_TSAN + { + return tryWait() || waitWithPartialSpinning(timeout_usecs); + } + + void signal(ssize_t count = 1) AE_NO_TSAN + { + assert(count >= 0); + ssize_t oldCount = m_count.fetch_add_release(count); + assert(oldCount >= -1); + if (oldCount < 0) + { + m_sema.signal(1); + } + } + + std::size_t availableApprox() const AE_NO_TSAN + { + ssize_t count = m_count.load(); + return count > 0 ? static_cast(count) : 0; + } + }; + } // end namespace spsc_sema +} // end namespace moodycamel + +#if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli)) +#pragma warning(pop) +#ifdef __cplusplus_cli +#pragma managed(pop) +#endif +#endif diff --git a/third_party/readerwriterqueue.h b/third_party/readerwriterqueue.h new file mode 100644 index 00000000..8b26556c --- /dev/null +++ b/third_party/readerwriterqueue.h @@ -0,0 +1,979 @@ +// ©2013-2020 Cameron Desrochers. +// Distributed under the simplified BSD license (see the license file that +// should have come with this header). + +#pragma once + +#include "atomicops.h" +#include +#include +#include +#include +#include +#include +#include +#include // For malloc/free/abort & size_t +#include +#if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012 +#include +#endif + + +// A lock-free queue for a single-consumer, single-producer architecture. +// The queue is also wait-free in the common path (except if more memory +// needs to be allocated, in which case malloc is called). +// Allocates memory sparingly, and only once if the original maximum size +// estimate is never exceeded. +// Tested on x86/x64 processors, but semantics should be correct for all +// architectures (given the right implementations in atomicops.h), provided +// that aligned integer and pointer accesses are naturally atomic. +// Note that there should only be one consumer thread and producer thread; +// Switching roles of the threads, or using multiple consecutive threads for +// one role, is not safe unless properly synchronized. +// Using the queue exclusively from one thread is fine, though a bit silly. + +#ifndef MOODYCAMEL_CACHE_LINE_SIZE +#define MOODYCAMEL_CACHE_LINE_SIZE 64 +#endif + +#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED +#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) +#define MOODYCAMEL_EXCEPTIONS_ENABLED +#endif +#endif + +#ifndef MOODYCAMEL_HAS_EMPLACE +#if !defined(_MSC_VER) || _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013 +#define MOODYCAMEL_HAS_EMPLACE 1 +#endif +#endif + +#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE +#if defined (__APPLE__) && defined (__MACH__) && __cplusplus >= 201703L +// This is required to find out what deployment target we are using +#include +#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14 +// C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we can't support over-alignment in this case +#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE +#endif +#endif +#endif + +#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE +#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE) +#endif + +#ifdef AE_VCPP +#pragma warning(push) +#pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +#pragma warning(disable: 4820) // padding was added +#pragma warning(disable: 4127) // conditional expression is constant +#endif + +namespace moodycamel { + + template + class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue +{ + // Design: Based on a queue-of-queues. The low-level queues are just + // circular buffers with front and tail indices indicating where the + // next element to dequeue is and where the next element can be enqueued, + // respectively. Each low-level queue is called a "block". Each block + // wastes exactly one element's worth of space to keep the design simple + // (if front == tail then the queue is empty, and can't be full). + // The high-level queue is a circular linked list of blocks; again there + // is a front and tail, but this time they are pointers to the blocks. + // The front block is where the next element to be dequeued is, provided + // the block is not empty. The back block is where elements are to be + // enqueued, provided the block is not full. + // The producer thread owns all the tail indices/pointers. The consumer + // thread owns all the front indices/pointers. Both threads read each + // other's variables, but only the owning thread updates them. E.g. After + // the consumer reads the producer's tail, the tail may change before the + // consumer is done dequeuing an object, but the consumer knows the tail + // will never go backwards, only forwards. + // If there is no room to enqueue an object, an additional block (of + // equal size to the last block) is added. Blocks are never removed. + + public: + typedef T value_type; + + // Constructs a queue that can hold at least `size` elements without further + // allocations. If more than MAX_BLOCK_SIZE elements are requested, + // then several blocks of MAX_BLOCK_SIZE each are reserved (including + // at least one extra buffer block). + AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15) +#ifndef NDEBUG + : enqueuing(false) + ,dequeuing(false) +#endif +{ + assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2"); + assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); + + Block* firstBlock = nullptr; + + largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block + if (largestBlockSize > MAX_BLOCK_SIZE * 2) { + // We need a spare block in case the producer is writing to a different block the consumer is reading from, and + // wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity + // between front == tail meaning "empty" and "full". + // So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the + // number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying): + size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); + largestBlockSize = MAX_BLOCK_SIZE; + Block* lastBlock = nullptr; + for (size_t i = 0; i != initialBlockCount; ++i) { + auto block = make_block(largestBlockSize); + if (block == nullptr) { +#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED + throw std::bad_alloc(); +#else + abort(); +#endif +} +if (firstBlock == nullptr) { +firstBlock = block; +} +else { +lastBlock->next = block; +} +lastBlock = block; +block->next = firstBlock; +} +} +else { +firstBlock = make_block(largestBlockSize); +if (firstBlock == nullptr) { +#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED +throw std::bad_alloc(); +#else +abort(); +#endif +} +firstBlock->next = firstBlock; +} +frontBlock = firstBlock; +tailBlock = firstBlock; + +// Make sure the reader/writer threads will have the initialized memory setup above: +fence(memory_order_sync); +} + +// Note: The queue should not be accessed concurrently while it's +// being moved. It's up to the user to synchronize this. +AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other) +: frontBlock(other.frontBlock.load()), +tailBlock(other.tailBlock.load()), +largestBlockSize(other.largestBlockSize) +#ifndef NDEBUG +,enqueuing(false) +,dequeuing(false) +#endif +{ +other.largestBlockSize = 32; +Block* b = other.make_block(other.largestBlockSize); +if (b == nullptr) { +#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED +throw std::bad_alloc(); +#else +abort(); +#endif +} +b->next = b; +other.frontBlock = b; +other.tailBlock = b; +} + +// Note: The queue should not be accessed concurrently while it's +// being moved. It's up to the user to synchronize this. +ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN +{ +Block* b = frontBlock.load(); +frontBlock = other.frontBlock.load(); +other.frontBlock = b; +b = tailBlock.load(); +tailBlock = other.tailBlock.load(); +other.tailBlock = b; +std::swap(largestBlockSize, other.largestBlockSize); +return *this; +} + +// Note: The queue should not be accessed concurrently while it's +// being deleted. It's up to the user to synchronize this. +AE_NO_TSAN ~ReaderWriterQueue() +{ + // Make sure we get the latest version of all variables from other CPUs: + fence(memory_order_sync); + + // Destroy any remaining objects in queue and free memory + Block* frontBlock_ = frontBlock; + Block* block = frontBlock_; + do { + Block* nextBlock = block->next; + size_t blockFront = block->front; + size_t blockTail = block->tail; + + for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { + auto element = reinterpret_cast(block->data + i * sizeof(T)); + element->~T(); + (void)element; + } + + auto rawBlock = block->rawThis; + block->~Block(); + std::free(rawBlock); + block = nextBlock; + } while (block != frontBlock_); +} + + +// Enqueues a copy of element if there is room in the queue. +// Returns true if the element was enqueued, false otherwise. +// Does not allocate memory. +AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN +{ +return inner_enqueue(element); +} + +// Enqueues a moved copy of element if there is room in the queue. +// Returns true if the element was enqueued, false otherwise. +// Does not allocate memory. +AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN +{ +return inner_enqueue(std::forward(element)); +} + +#if MOODYCAMEL_HAS_EMPLACE +// Like try_enqueue() but with emplace semantics (i.e. construct-in-place). +template +AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN +{ +return inner_enqueue(std::forward(args)...); +} +#endif + +// Enqueues a copy of element on the queue. +// Allocates an additional block of memory if needed. +// Only fails (returns false) if memory allocation fails. +AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN +{ +return inner_enqueue(element); +} + +// Enqueues a moved copy of element on the queue. +// Allocates an additional block of memory if needed. +// Only fails (returns false) if memory allocation fails. +AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN +{ +return inner_enqueue(std::forward(element)); +} + +#if MOODYCAMEL_HAS_EMPLACE +// Like enqueue() but with emplace semantics (i.e. construct-in-place). +template +AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN +{ +return inner_enqueue(std::forward(args)...); +} +#endif + +// Attempts to dequeue an element; if the queue is empty, +// returns false instead. If the queue has at least one element, +// moves front to result using operator=, then returns true. +template +bool try_dequeue(U& result) AE_NO_TSAN +{ +#ifndef NDEBUG +ReentrantGuard guard(this->dequeuing); +#endif + +// High-level pseudocode: +// Remember where the tail block is +// If the front block has an element in it, dequeue it +// Else +// If front block was the tail block when we entered the function, return false +// Else advance to next block and dequeue the item there + +// Note that we have to use the value of the tail block from before we check if the front +// block is full or not, in case the front block is empty and then, before we check if the +// tail block is at the front block or not, the producer fills up the front block *and +// moves on*, which would make us skip a filled block. Seems unlikely, but was consistently +// reproducible in practice. +// In order to avoid overhead in the common case, though, we do a double-checked pattern +// where we have the fast path if the front block is not empty, then read the tail block, +// then re-read the front block and check if it's not empty again, then check if the tail +// block has advanced. + +Block* frontBlock_ = frontBlock.load(); +size_t blockTail = frontBlock_->localTail; +size_t blockFront = frontBlock_->front.load(); + +if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { +fence(memory_order_acquire); + +non_empty_front_block: +// Front block not empty, dequeue from here +auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); +result = std::move(*element); +element->~T(); + +blockFront = (blockFront + 1) & frontBlock_->sizeMask; + +fence(memory_order_release); +frontBlock_->front = blockFront; +} +else if (frontBlock_ != tailBlock.load()) { +fence(memory_order_acquire); + +frontBlock_ = frontBlock.load(); +blockTail = frontBlock_->localTail = frontBlock_->tail.load(); +blockFront = frontBlock_->front.load(); +fence(memory_order_acquire); + +if (blockFront != blockTail) { +// Oh look, the front block isn't empty after all +goto non_empty_front_block; +} + +// Front block is empty but there's another block ahead, advance to it +Block* nextBlock = frontBlock_->next; +// Don't need an acquire fence here since next can only ever be set on the tailBlock, +// and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which +// ensures next is up-to-date on this CPU in case we recently were at tailBlock. + +size_t nextBlockFront = nextBlock->front.load(); +size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); +fence(memory_order_acquire); + +// Since the tailBlock is only ever advanced after being written to, +// we know there's for sure an element to dequeue on it +assert(nextBlockFront != nextBlockTail); +AE_UNUSED(nextBlockTail); + +// We're done with this block, let the producer use it if it needs +fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue +frontBlock = frontBlock_ = nextBlock; + +compiler_fence(memory_order_release); // Not strictly needed + +auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); + +result = std::move(*element); +element->~T(); + +nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; + +fence(memory_order_release); +frontBlock_->front = nextBlockFront; +} +else { +// No elements in current block and no other block to advance to +return false; +} + +return true; +} + + +// Returns a pointer to the front element in the queue (the one that +// would be removed next by a call to `try_dequeue` or `pop`). If the +// queue appears empty at the time the method is called, nullptr is +// returned instead. +// Must be called only from the consumer thread. +T* peek() const AE_NO_TSAN +{ +#ifndef NDEBUG +ReentrantGuard guard(this->dequeuing); +#endif +// See try_dequeue() for reasoning + +Block* frontBlock_ = frontBlock.load(); +size_t blockTail = frontBlock_->localTail; +size_t blockFront = frontBlock_->front.load(); + +if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { +fence(memory_order_acquire); +non_empty_front_block: +return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); +} +else if (frontBlock_ != tailBlock.load()) { +fence(memory_order_acquire); +frontBlock_ = frontBlock.load(); +blockTail = frontBlock_->localTail = frontBlock_->tail.load(); +blockFront = frontBlock_->front.load(); +fence(memory_order_acquire); + +if (blockFront != blockTail) { +goto non_empty_front_block; +} + +Block* nextBlock = frontBlock_->next; + +size_t nextBlockFront = nextBlock->front.load(); +fence(memory_order_acquire); + +assert(nextBlockFront != nextBlock->tail.load()); +return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); +} + +return nullptr; +} + +// Removes the front element from the queue, if any, without returning it. +// Returns true on success, or false if the queue appeared empty at the time +// `pop` was called. +bool pop() AE_NO_TSAN +{ +#ifndef NDEBUG +ReentrantGuard guard(this->dequeuing); +#endif +// See try_dequeue() for reasoning + +Block* frontBlock_ = frontBlock.load(); +size_t blockTail = frontBlock_->localTail; +size_t blockFront = frontBlock_->front.load(); + +if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { +fence(memory_order_acquire); + +non_empty_front_block: +auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); +element->~T(); + +blockFront = (blockFront + 1) & frontBlock_->sizeMask; + +fence(memory_order_release); +frontBlock_->front = blockFront; +} +else if (frontBlock_ != tailBlock.load()) { +fence(memory_order_acquire); +frontBlock_ = frontBlock.load(); +blockTail = frontBlock_->localTail = frontBlock_->tail.load(); +blockFront = frontBlock_->front.load(); +fence(memory_order_acquire); + +if (blockFront != blockTail) { +goto non_empty_front_block; +} + +// Front block is empty but there's another block ahead, advance to it +Block* nextBlock = frontBlock_->next; + +size_t nextBlockFront = nextBlock->front.load(); +size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); +fence(memory_order_acquire); + +assert(nextBlockFront != nextBlockTail); +AE_UNUSED(nextBlockTail); + +fence(memory_order_release); +frontBlock = frontBlock_ = nextBlock; + +compiler_fence(memory_order_release); + +auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); +element->~T(); + +nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; + +fence(memory_order_release); +frontBlock_->front = nextBlockFront; +} +else { +// No elements in current block and no other block to advance to +return false; +} + +return true; +} + +// Returns the approximate number of items currently in the queue. +// Safe to call from both the producer and consumer threads. +inline size_t size_approx() const AE_NO_TSAN +{ +size_t result = 0; +Block* frontBlock_ = frontBlock.load(); +Block* block = frontBlock_; +do { +fence(memory_order_acquire); +size_t blockFront = block->front.load(); +size_t blockTail = block->tail.load(); +result += (blockTail - blockFront) & block->sizeMask; +block = block->next.load(); +} while (block != frontBlock_); +return result; +} + +// Returns the total number of items that could be enqueued without incurring +// an allocation when this queue is empty. +// Safe to call from both the producer and consumer threads. +// +// NOTE: The actual capacity during usage may be different depending on the consumer. +// If the consumer is removing elements concurrently, the producer cannot add to +// the block the consumer is removing from until it's completely empty, except in +// the case where the producer was writing to the same block the consumer was +// reading from the whole time. +inline size_t max_capacity() const { + size_t result = 0; + Block* frontBlock_ = frontBlock.load(); + Block* block = frontBlock_; + do { + fence(memory_order_acquire); + result += block->sizeMask; + block = block->next.load(); + } while (block != frontBlock_); + return result; +} + + +private: +enum AllocationMode { CanAlloc, CannotAlloc }; + +#if MOODYCAMEL_HAS_EMPLACE +template +bool inner_enqueue(Args&&... args) AE_NO_TSAN +#else +template + bool inner_enqueue(U&& element) AE_NO_TSAN +#endif +{ +#ifndef NDEBUG +ReentrantGuard guard(this->enqueuing); +#endif + +// High-level pseudocode (assuming we're allowed to alloc a new block): +// If room in tail block, add to tail +// Else check next block +// If next block is not the head block, enqueue on next block +// Else create a new block and enqueue there +// Advance tail to the block we just enqueued to + +Block* tailBlock_ = tailBlock.load(); +size_t blockFront = tailBlock_->localFront; +size_t blockTail = tailBlock_->tail.load(); + +size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; +if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { +fence(memory_order_acquire); +// This block has room for at least one more element +char* location = tailBlock_->data + blockTail * sizeof(T); +#if MOODYCAMEL_HAS_EMPLACE +new (location) T(std::forward(args)...); +#else +new (location) T(std::forward(element)); +#endif + +fence(memory_order_release); +tailBlock_->tail = nextBlockTail; +} +else { +fence(memory_order_acquire); +if (tailBlock_->next.load() != frontBlock) { +// Note that the reason we can't advance to the frontBlock and start adding new entries there +// is because if we did, then dequeue would stay in that block, eventually reading the new values, +// instead of advancing to the next full block (whose values were enqueued first and so should be +// consumed first). + +fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock + +// tailBlock is full, but there's a free block ahead, use it +Block* tailBlockNext = tailBlock_->next.load(); +size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); +nextBlockTail = tailBlockNext->tail.load(); +fence(memory_order_acquire); + +// This block must be empty since it's not the head block and we +// go through the blocks in a circle +assert(nextBlockFront == nextBlockTail); +tailBlockNext->localFront = nextBlockFront; + +char* location = tailBlockNext->data + nextBlockTail * sizeof(T); +#if MOODYCAMEL_HAS_EMPLACE +new (location) T(std::forward(args)...); +#else +new (location) T(std::forward(element)); +#endif + +tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; + +fence(memory_order_release); +tailBlock = tailBlockNext; +} +else if (canAlloc == CanAlloc) { +// tailBlock is full and there's no free block ahead; create a new block +auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; +auto newBlock = make_block(newBlockSize); +if (newBlock == nullptr) { +// Could not allocate a block! +return false; +} +largestBlockSize = newBlockSize; + +#if MOODYCAMEL_HAS_EMPLACE +new (newBlock->data) T(std::forward(args)...); +#else +new (newBlock->data) T(std::forward(element)); +#endif +assert(newBlock->front == 0); +newBlock->tail = newBlock->localTail = 1; + +newBlock->next = tailBlock_->next.load(); +tailBlock_->next = newBlock; + +// Might be possible for the dequeue thread to see the new tailBlock->next +// *without* seeing the new tailBlock value, but this is OK since it can't +// advance to the next block until tailBlock is set anyway (because the only +// case where it could try to read the next is if it's already at the tailBlock, +// and it won't advance past tailBlock in any circumstance). + +fence(memory_order_release); +tailBlock = newBlock; +} +else if (canAlloc == CannotAlloc) { +// Would have had to allocate a new block to enqueue, but not allowed +return false; +} +else { +assert(false && "Should be unreachable code"); +return false; +} +} + +return true; +} + + +// Disable copying +ReaderWriterQueue(ReaderWriterQueue const&) { } + +// Disable assignment +ReaderWriterQueue& operator=(ReaderWriterQueue const&) { } + + +AE_FORCEINLINE static size_t ceilToPow2(size_t x) +{ +// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +--x; +x |= x >> 1; +x |= x >> 2; +x |= x >> 4; +for (size_t i = 1; i < sizeof(size_t); i <<= 1) { +x |= x >> (i << 3); +} +++x; +return x; +} + +template +static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN +{ +const std::size_t alignment = std::alignment_of::value; +return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; +} +private: +#ifndef NDEBUG +struct ReentrantGuard +{ + AE_NO_TSAN ReentrantGuard(weak_atomic& _inSection) + : inSection(_inSection) + { + assert(!inSection && "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)"); + inSection = true; + } + + AE_NO_TSAN ~ReentrantGuard() { inSection = false; } + +private: + ReentrantGuard& operator=(ReentrantGuard const&); + +private: + weak_atomic& inSection; +}; +#endif + +struct Block +{ + // Avoid false-sharing by putting highly contended variables on their own cache lines + weak_atomic front; // (Atomic) Elements are read from here + size_t localTail; // An uncontended shadow copy of tail, owned by the consumer + + char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; + weak_atomic tail; // (Atomic) Elements are enqueued here + size_t localFront; + + char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is) + weak_atomic next; // (Atomic) + + char* data; // Contents (on heap) are aligned to T's alignment + + const size_t sizeMask; + + + // size must be a power of two (and greater than 0) + AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data) + : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis) + { + } + +private: + // C4512 - Assignment operator could not be generated + Block& operator=(Block const&); + +public: + char* rawThis; +}; + + +static Block* make_block(size_t capacity) AE_NO_TSAN +{ +// Allocate enough memory for the block itself, as well as all the elements it will contain +auto size = sizeof(Block) + std::alignment_of::value - 1; +size += sizeof(T) * capacity + std::alignment_of::value - 1; +auto newBlockRaw = static_cast(std::malloc(size)); +if (newBlockRaw == nullptr) { +return nullptr; +} + +auto newBlockAligned = align_for(newBlockRaw); +auto newBlockData = align_for(newBlockAligned + sizeof(Block)); +return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData); +} + +private: +weak_atomic frontBlock; // (Atomic) Elements are dequeued from this block + +char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic)]; +weak_atomic tailBlock; // (Atomic) Elements are enqueued to this block + +size_t largestBlockSize; + +#ifndef NDEBUG +weak_atomic enqueuing; +mutable weak_atomic dequeuing; +#endif +}; + +// Like ReaderWriterQueue, but also providees blocking operations +template +class BlockingReaderWriterQueue +{ +private: + typedef ::moodycamel::ReaderWriterQueue ReaderWriterQueue; + +public: + explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN + : inner(size), sema(new spsc_sema::LightweightSemaphore()) + { } + + BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN + : inner(std::move(other.inner)), sema(std::move(other.sema)) + { } + + BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN + { + std::swap(sema, other.sema); + std::swap(inner, other.inner); + return *this; + } + + + // Enqueues a copy of element if there is room in the queue. + // Returns true if the element was enqueued, false otherwise. + // Does not allocate memory. + AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN + { + if (inner.try_enqueue(element)) { + sema->signal(); + return true; + } + return false; + } + + // Enqueues a moved copy of element if there is room in the queue. + // Returns true if the element was enqueued, false otherwise. + // Does not allocate memory. + AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN + { + if (inner.try_enqueue(std::forward(element))) { + sema->signal(); + return true; + } + return false; + } + +#if MOODYCAMEL_HAS_EMPLACE + // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). + template + AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN + { + if (inner.try_emplace(std::forward(args)...)) { + sema->signal(); + return true; + } + return false; + } +#endif + + + // Enqueues a copy of element on the queue. + // Allocates an additional block of memory if needed. + // Only fails (returns false) if memory allocation fails. + AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN + { + if (inner.enqueue(element)) { + sema->signal(); + return true; + } + return false; + } + + // Enqueues a moved copy of element on the queue. + // Allocates an additional block of memory if needed. + // Only fails (returns false) if memory allocation fails. + AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN + { + if (inner.enqueue(std::forward(element))) { + sema->signal(); + return true; + } + return false; + } + +#if MOODYCAMEL_HAS_EMPLACE + // Like enqueue() but with emplace semantics (i.e. construct-in-place). + template + AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN + { + if (inner.emplace(std::forward(args)...)) { + sema->signal(); + return true; + } + return false; + } +#endif + + + // Attempts to dequeue an element; if the queue is empty, + // returns false instead. If the queue has at least one element, + // moves front to result using operator=, then returns true. + template + bool try_dequeue(U& result) AE_NO_TSAN + { + if (sema->tryWait()) { + bool success = inner.try_dequeue(result); + assert(success); + AE_UNUSED(success); + return true; + } + return false; + } + + + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available, then dequeues it. + template + void wait_dequeue(U& result) AE_NO_TSAN + { + while (!sema->wait()); + bool success = inner.try_dequeue(result); + AE_UNUSED(result); + assert(success); + AE_UNUSED(success); + } + + + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available up to the specified timeout, + // then dequeues it and returns true, or returns false if the timeout + // expires before an element can be dequeued. + // Using a negative timeout indicates an indefinite timeout, + // and is thus functionally equivalent to calling wait_dequeue. + template + bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN + { + if (!sema->wait(timeout_usecs)) { + return false; + } + bool success = inner.try_dequeue(result); + AE_UNUSED(result); + assert(success); + AE_UNUSED(success); + return true; + } + + +#if __cplusplus > 199711L || _MSC_VER >= 1700 + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available up to the specified timeout, + // then dequeues it and returns true, or returns false if the timeout + // expires before an element can be dequeued. + // Using a negative timeout indicates an indefinite timeout, + // and is thus functionally equivalent to calling wait_dequeue. + template + inline bool wait_dequeue_timed(U& result, std::chrono::duration const& timeout) AE_NO_TSAN + { + return wait_dequeue_timed(result, std::chrono::duration_cast(timeout).count()); + } +#endif + + + // Returns a pointer to the front element in the queue (the one that + // would be removed next by a call to `try_dequeue` or `pop`). If the + // queue appears empty at the time the method is called, nullptr is + // returned instead. + // Must be called only from the consumer thread. + AE_FORCEINLINE T* peek() const AE_NO_TSAN + { + return inner.peek(); + } + + // Removes the front element from the queue, if any, without returning it. + // Returns true on success, or false if the queue appeared empty at the time + // `pop` was called. + AE_FORCEINLINE bool pop() AE_NO_TSAN + { + if (sema->tryWait()) { + bool result = inner.pop(); + assert(result); + AE_UNUSED(result); + return true; + } + return false; + } + + // Returns the approximate number of items currently in the queue. + // Safe to call from both the producer and consumer threads. + AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN + { + return sema->availableApprox(); + } + + // Returns the total number of items that could be enqueued without incurring + // an allocation when this queue is empty. + // Safe to call from both the producer and consumer threads. + // + // NOTE: The actual capacity during usage may be different depending on the consumer. + // If the consumer is removing elements concurrently, the producer cannot add to + // the block the consumer is removing from until it's completely empty, except in + // the case where the producer was writing to the same block the consumer was + // reading from the whole time. + AE_FORCEINLINE size_t max_capacity() const { + return inner.max_capacity(); + } + +private: + // Disable copying & assignment + BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) { } + BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) { } + +private: + ReaderWriterQueue inner; + std::unique_ptr sema; +}; + +} // end namespace moodycamel + +#ifdef AE_VCPP +#pragma warning(pop) +#endif \ No newline at end of file diff --git a/zmq_src/ZMQClient.h b/zmq_src/ZMQClient.h index 5386953e..0e10f2dd 100644 --- a/zmq_src/ZMQClient.h +++ b/zmq_src/ZMQClient.h @@ -31,7 +31,6 @@ #include #include "third_party/spdlog/spdlog.h" - #include #include "zhelpers.hpp" #include diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 25f5664f..d0a513df 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -42,7 +42,7 @@ using namespace std; shared_ptr ZMQServer::zmqServer = nullptr; ZMQServer::ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string &_caCertFile) - : checkSignature(_checkSignature), checkKeyOwnership(_checkKeyOwnership), + : outgoingQueue(NUM_ZMQ_WORKER_THREADS), checkSignature(_checkSignature), checkKeyOwnership(_checkKeyOwnership), caCertFile(_caCertFile), ctx(make_shared(1)) { socket = make_shared(*ctx, ZMQ_ROUTER); @@ -103,7 +103,7 @@ void ZMQServer::run() { spdlog::info("Exited zmq server loop"); } -std::atomic ZMQServer::isExitRequested(false); +atomic ZMQServer::isExitRequested(false); void ZMQServer::exitZMQServer() { // if already exited do not exit @@ -151,7 +151,7 @@ void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { zmqServer = make_shared(_checkSignature, _checkKeyOwnership, rootCAPath); CHECK_STATE(zmqServer) - serverThread = make_shared(std::bind(&ZMQServer::run, ZMQServer::zmqServer)); + serverThread = make_shared(bind(&ZMQServer::run, ZMQServer::zmqServer)); serverThread->detach(); spdlog::info("Releasing SGX worker threads ..."); @@ -163,7 +163,7 @@ void ZMQServer::initZMQServer(bool _checkSignature, bool _checkKeyOwnership) { spdlog::info("Inited zmq server."); } -shared_ptr ZMQServer::serverThread = nullptr; +shared_ptr ZMQServer::serverThread = nullptr; ZMQServer::~ZMQServer() { exitZMQServer(); @@ -192,38 +192,41 @@ PollResult ZMQServer::poll() { return GOT_INCOMING_MSG; } -string ZMQServer::receiveMessage(zmq::message_t& _identity) { - if (!socket->recv(&_identity)) { +pair> ZMQServer::receiveMessage() { + + auto identity = make_shared(); + + if (!socket->recv(identity.get())) { checkForExit(); // something terrible happened spdlog::error("Fatal error: socket->recv(&identity) returned false. Exiting."); exit(-11); } - if (!_identity.more()) { + if (!identity->more()) { checkForExit(); // something terrible happened spdlog::error("Fatal error: zmq_msg_more(identity) returned false. Existing."); exit(-12); } - zmq::message_t reqMsg; + auto reqMsg = make_shared(); - if (!socket->recv(&reqMsg, 0)) { + if (!socket->recv(reqMsg.get(), 0)) { checkForExit(); // something terrible happened spdlog::error("Fatal error: socket.recv(&reqMsg, 0) returned false. Exiting"); exit(-13); } - auto result = string((char *) reqMsg.data(), reqMsg.size()); + auto result = string((char *) reqMsg->data(), reqMsg->size()); CHECK_STATE(result.front() == '{') CHECK_STATE(result.back() == '}') - return result; + return {result, identity}; } -void ZMQServer::sendToClient(Json::Value& _result, zmq::message_t& _identity ) { +void ZMQServer::sendToClient(Json::Value& _result, shared_ptr& _identity ) { string replyStr; try { Json::FastWriter fastWriter; @@ -235,7 +238,7 @@ void ZMQServer::sendToClient(Json::Value& _result, zmq::message_t& _identity ) CHECK_STATE(replyStr.front() == '{'); CHECK_STATE(replyStr.back() == '}'); - if (!socket->send(_identity, ZMQ_SNDMORE)) { + if (!socket->send(*_identity, ZMQ_SNDMORE)) { exit(-15); } if (!s_send(*socket, replyStr)) { @@ -243,7 +246,7 @@ void ZMQServer::sendToClient(Json::Value& _result, zmq::message_t& _identity ) } } catch (ExitRequestedException) { throw; - } catch (std::exception &e) { + } catch (exception &e) { checkForExit(); spdlog::error("Exception in zmq server worker send :{}", e.what()); exit(-17); @@ -260,14 +263,14 @@ void ZMQServer::doOneServerLoop() { Json::Value result; result["status"] = ZMQ_SERVER_ERROR; - zmq::message_t identity; + shared_ptr identity = nullptr; string msgStr; try { poll(); - msgStr = receiveMessage(identity); + tie(msgStr, identity) = receiveMessage(); auto msg = ZMQMessage::parse( msgStr.c_str(), msgStr.size(), true, checkSignature, checkKeyOwnership); @@ -276,25 +279,24 @@ void ZMQServer::doOneServerLoop() { if ((dynamic_pointer_cast(msg)!= nullptr) || dynamic_pointer_cast(msg)) { - spdlog::info("FUFUFUFUF"); } else { - spdlog::info("HAHAHA"); + } result = msg->process(); } catch (ExitRequestedException) { throw; - } catch (std::exception &e) { + } catch (exception &e) { checkForExit(); result["errorMessage"] = string(e.what()); spdlog::error("Exception in zmq server :{}", e.what()); - spdlog::error("ID:" + string((char *) identity.data(), identity.size())); + spdlog::error("ID:" + string((char *) identity->data(), identity->size())); spdlog::error("Client request :" + msgStr); } catch (...) { checkForExit(); spdlog::error("Error in zmq server "); result["errorMessage"] = "Error in zmq server "; - spdlog::error("ID:" + string((char *) identity.data(), identity.size())); + spdlog::error("ID:" + string((char *) identity->data(), identity->size())); spdlog::error("Client request :" + msgStr); } diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index ae7582fe..bb9dbd4b 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -25,14 +25,22 @@ #ifndef SGXWALLET_ZMQServer_H #define SGXWALLET_ZMQServer_H + +#include "third_party/readerwriterqueue.h" + + #include #include "zhelpers.hpp" #include "Agent.h" #include "WorkerThreadPool.h" +using namespace moodycamel; + typedef enum {GOT_INCOMING_MSG = 0, GOT_OUTFOING_MSG = 1} PollResult; +static const uint64_t NUM_ZMQ_WORKER_THREADS = 2; + class ZMQServer : public Agent{ uint64_t workerThreads; @@ -40,12 +48,16 @@ class ZMQServer : public Agent{ string caCertFile; string caCert; + ReaderWriterQueue>> outgoingQueue; + + vector>>> incomingQueue; + bool checkKeyOwnership = true; shared_ptr ctx; shared_ptr socket; - static std::atomic isExitRequested; + static atomic isExitRequested; void doOneServerLoop(); @@ -57,7 +69,6 @@ class ZMQServer : public Agent{ shared_ptr threadPool = nullptr; - static shared_ptr serverThread; ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string& _caCertFile); @@ -79,9 +90,9 @@ class ZMQServer : public Agent{ PollResult poll(); - string receiveMessage(zmq::message_t& _identity); + pair> receiveMessage(); - void sendToClient(Json::Value& _result, zmq::message_t& _identity); + void sendToClient(Json::Value& _result, shared_ptr& _identity); }; From f457b2011c8e1d34fb2583fa594db785247ab077 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 17:21:38 +0300 Subject: [PATCH 082/100] SKALE-4586 Added Thread Pool --- third_party/atomicops.h | 747 +++++++++--------- third_party/readerwriterqueue.h | 1247 +++++++++++++++---------------- zmq_src/ZMQServer.cpp | 14 +- zmq_src/ZMQServer.h | 6 +- 4 files changed, 1018 insertions(+), 996 deletions(-) diff --git a/third_party/atomicops.h b/third_party/atomicops.h index 696888b4..74221734 100644 --- a/third_party/atomicops.h +++ b/third_party/atomicops.h @@ -136,14 +136,14 @@ namespace moodycamel { AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { - switch (order) { - case memory_order_relaxed: break; - case memory_order_acquire: _ReadBarrier(); break; - case memory_order_release: _WriteBarrier(); break; - case memory_order_acq_rel: _ReadWriteBarrier(); break; - case memory_order_seq_cst: _ReadWriteBarrier(); break; - default: assert(false); - } + switch (order) { + case memory_order_relaxed: break; + case memory_order_acquire: _ReadBarrier(); break; + case memory_order_release: _WriteBarrier(); break; + case memory_order_acq_rel: _ReadWriteBarrier(); break; + case memory_order_seq_cst: _ReadWriteBarrier(); break; + default: assert(false); + } } // x86/x64 have a strong memory model -- all loads and stores have @@ -152,48 +152,48 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN #if defined(AE_ARCH_X86) || defined(AE_ARCH_X64) AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { - switch (order) { - case memory_order_relaxed: break; - case memory_order_acquire: _ReadBarrier(); break; - case memory_order_release: _WriteBarrier(); break; - case memory_order_acq_rel: _ReadWriteBarrier(); break; - case memory_order_seq_cst: - _ReadWriteBarrier(); - AeFullSync(); - _ReadWriteBarrier(); - break; - default: assert(false); - } + switch (order) { + case memory_order_relaxed: break; + case memory_order_acquire: _ReadBarrier(); break; + case memory_order_release: _WriteBarrier(); break; + case memory_order_acq_rel: _ReadWriteBarrier(); break; + case memory_order_seq_cst: + _ReadWriteBarrier(); + AeFullSync(); + _ReadWriteBarrier(); + break; + default: assert(false); + } } #else AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { - // Non-specialized arch, use heavier memory barriers everywhere just in case :-( - switch (order) { - case memory_order_relaxed: - break; - case memory_order_acquire: - _ReadBarrier(); - AeLiteSync(); - _ReadBarrier(); - break; - case memory_order_release: - _WriteBarrier(); - AeLiteSync(); - _WriteBarrier(); - break; - case memory_order_acq_rel: - _ReadWriteBarrier(); - AeLiteSync(); - _ReadWriteBarrier(); - break; - case memory_order_seq_cst: - _ReadWriteBarrier(); - AeFullSync(); - _ReadWriteBarrier(); - break; - default: assert(false); - } + // Non-specialized arch, use heavier memory barriers everywhere just in case :-( + switch (order) { + case memory_order_relaxed: + break; + case memory_order_acquire: + _ReadBarrier(); + AeLiteSync(); + _ReadBarrier(); + break; + case memory_order_release: + _WriteBarrier(); + AeLiteSync(); + _WriteBarrier(); + break; + case memory_order_acq_rel: + _ReadWriteBarrier(); + AeLiteSync(); + _ReadWriteBarrier(); + break; + case memory_order_seq_cst: + _ReadWriteBarrier(); + AeFullSync(); + _ReadWriteBarrier(); + break; + default: assert(false); + } } #endif } // end namespace moodycamel @@ -206,24 +206,48 @@ namespace moodycamel { AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN { switch (order) { - case memory_order_relaxed: break; - case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break; - case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break; - case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break; - case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break; - default: assert(false); + case memory_order_relaxed: + break; + case memory_order_acquire: + std::atomic_signal_fence(std::memory_order_acquire); + break; + case memory_order_release: + std::atomic_signal_fence(std::memory_order_release); + break; + case memory_order_acq_rel: + std::atomic_signal_fence(std::memory_order_acq_rel); + break; + case memory_order_seq_cst: + std::atomic_signal_fence(std::memory_order_seq_cst); + break; + default: + assert(false); } } AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN { switch (order) { - case memory_order_relaxed: break; - case memory_order_acquire: AE_TSAN_ANNOTATE_ACQUIRE(); std::atomic_thread_fence(std::memory_order_acquire); break; - case memory_order_release: AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_release); break; - case memory_order_acq_rel: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_acq_rel); break; - case memory_order_seq_cst: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_seq_cst); break; - default: assert(false); + case memory_order_relaxed: + break; + case memory_order_acquire: + AE_TSAN_ANNOTATE_ACQUIRE(); + std::atomic_thread_fence(std::memory_order_acquire); + break; + case memory_order_release: + AE_TSAN_ANNOTATE_RELEASE(); + std::atomic_thread_fence(std::memory_order_release); + break; + case memory_order_acq_rel: + AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); + std::atomic_thread_fence(std::memory_order_acq_rel); + break; + case memory_order_seq_cst: + AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); + std::atomic_thread_fence(std::memory_order_seq_cst); + break; + default: + assert(false); } } @@ -237,8 +261,11 @@ namespace moodycamel { #endif #ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC + #include + #endif + #include // WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY: @@ -247,21 +274,26 @@ namespace moodycamel { // at the hardware level -- on most platforms this generally means aligned pointers and integers (only). namespace moodycamel { template - class weak_atomic - { + class weak_atomic { public: - AE_NO_TSAN weak_atomic() : value() { } + AE_NO_TSAN weak_atomic() : value() {} + #ifdef AE_VCPP - #pragma warning(push) +#pragma warning(push) #pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning #endif - template AE_NO_TSAN weak_atomic(U&& x) : value(std::forward(x)) { } + + template + AE_NO_TSAN weak_atomic(U &&x) : value(std::forward(x)) {} + #ifdef __cplusplus_cli // Work around bug with universal reference/nullptr combination that only appears when /clr is on - AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) { } + AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) { } #endif - AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) { } - AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) { } + AE_NO_TSAN weak_atomic(weak_atomic const &other) : value(other.load()) {} + + AE_NO_TSAN weak_atomic(weak_atomic &&other) : value(std::move(other.load())) {} + #ifdef AE_VCPP #pragma warning(pop) #endif @@ -271,46 +303,46 @@ namespace moodycamel { #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC template AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { value = std::forward(x); return *this; } - AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { value = other.value; return *this; } + AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { value = other.value; return *this; } - AE_FORCEINLINE T load() const AE_NO_TSAN { return value; } + AE_FORCEINLINE T load() const AE_NO_TSAN { return value; } - AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN - { + AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN + { #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) - if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); + if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); #if defined(_M_AMD64) - else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); + else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); #endif #else #error Unsupported platform #endif - assert(false && "T must be either a 32 or 64 bit type"); - return value; - } + assert(false && "T must be either a 32 or 64 bit type"); + return value; + } - AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN - { + AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN + { #if defined(AE_ARCH_X64) || defined(AE_ARCH_X86) - if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); + if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment); #if defined(_M_AMD64) - else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); + else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment); #endif #else #error Unsupported platform #endif - assert(false && "T must be either a 32 or 64 bit type"); - return value; - } + assert(false && "T must be either a 32 or 64 bit type"); + return value; + } #else + template - AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN - { + AE_FORCEINLINE weak_atomic const &operator=(U &&x) AE_NO_TSAN { value.store(std::forward(x), std::memory_order_relaxed); return *this; } - AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN + AE_FORCEINLINE weak_atomic const &operator=(weak_atomic const &other) AE_NO_TSAN { value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; @@ -327,20 +359,21 @@ namespace moodycamel { { return value.fetch_add(increment, std::memory_order_release); } + #endif private: #ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC // No std::atomic support, but still need to circumvent compiler optimizations. - // `volatile` will make memory access slow, but is guaranteed to be reliable. - volatile T value; + // `volatile` will make memory access slow, but is guaranteed to be reliable. + volatile T value; #else - std::atomic value; + std::atomic value; #endif }; -} // end namespace moodycamel +} // end namespace moodycamel @@ -353,11 +386,11 @@ namespace moodycamel { // I know this is an ugly hack but it still beats polluting the global // namespace with thousands of generic names or adding a .cpp for nothing. extern "C" { - struct _SECURITY_ATTRIBUTES; - __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName); - __declspec(dllimport) int __stdcall CloseHandle(void* hObject); - __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds); - __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount); + struct _SECURITY_ATTRIBUTES; + __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName); + __declspec(dllimport) int __stdcall CloseHandle(void* hObject); + __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds); + __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount); } #elif defined(__MACH__) #include @@ -369,8 +402,7 @@ extern "C" { #include #endif -namespace moodycamel -{ +namespace moodycamel { // Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's // portable + lightweight semaphore implementations, originally from // https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h @@ -392,262 +424,261 @@ namespace moodycamel // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. - namespace spsc_sema - { + namespace spsc_sema { #if defined(_WIN32) class Semaphore - { - private: - void* m_hSema; - - Semaphore(const Semaphore& other); - Semaphore& operator=(const Semaphore& other); - - public: - AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema() - { - assert(initialCount >= 0); - const long maxLong = 0x7fffffff; - m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); - assert(m_hSema); - } - - AE_NO_TSAN ~Semaphore() - { - CloseHandle(m_hSema); - } - - bool wait() AE_NO_TSAN - { - const unsigned long infinite = 0xffffffff; - return WaitForSingleObject(m_hSema, infinite) == 0; - } - - bool try_wait() AE_NO_TSAN - { - return WaitForSingleObject(m_hSema, 0) == 0; - } - - bool timed_wait(std::uint64_t usecs) AE_NO_TSAN - { - return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0; - } - - void signal(int count = 1) AE_NO_TSAN - { - while (!ReleaseSemaphore(m_hSema, count, nullptr)); - } - }; + { + private: + void* m_hSema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema() + { + assert(initialCount >= 0); + const long maxLong = 0x7fffffff; + m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); + assert(m_hSema); + } + + AE_NO_TSAN ~Semaphore() + { + CloseHandle(m_hSema); + } + + bool wait() AE_NO_TSAN + { + const unsigned long infinite = 0xffffffff; + return WaitForSingleObject(m_hSema, infinite) == 0; + } + + bool try_wait() AE_NO_TSAN + { + return WaitForSingleObject(m_hSema, 0) == 0; + } + + bool timed_wait(std::uint64_t usecs) AE_NO_TSAN + { + return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0; + } + + void signal(int count = 1) AE_NO_TSAN + { + while (!ReleaseSemaphore(m_hSema, count, nullptr)); + } + }; #elif defined(__MACH__) //--------------------------------------------------------- - // Semaphore (Apple iOS and OSX) - // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html - //--------------------------------------------------------- - class Semaphore - { - private: - semaphore_t m_sema; - - Semaphore(const Semaphore& other); - Semaphore& operator=(const Semaphore& other); - - public: - AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() - { - assert(initialCount >= 0); - kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount); - assert(rc == KERN_SUCCESS); - AE_UNUSED(rc); - } - - AE_NO_TSAN ~Semaphore() - { - semaphore_destroy(mach_task_self(), m_sema); - } - - bool wait() AE_NO_TSAN - { - return semaphore_wait(m_sema) == KERN_SUCCESS; - } - - bool try_wait() AE_NO_TSAN - { - return timed_wait(0); - } - - bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN - { - mach_timespec_t ts; - ts.tv_sec = static_cast(timeout_usecs / 1000000); - ts.tv_nsec = static_cast((timeout_usecs % 1000000) * 1000); - - // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html - kern_return_t rc = semaphore_timedwait(m_sema, ts); - return rc == KERN_SUCCESS; - } - - void signal() AE_NO_TSAN - { - while (semaphore_signal(m_sema) != KERN_SUCCESS); - } - - void signal(int count) AE_NO_TSAN - { - while (count-- > 0) - { - while (semaphore_signal(m_sema) != KERN_SUCCESS); - } - } - }; + // Semaphore (Apple iOS and OSX) + // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html + //--------------------------------------------------------- + class Semaphore + { + private: + semaphore_t m_sema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() + { + assert(initialCount >= 0); + kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount); + assert(rc == KERN_SUCCESS); + AE_UNUSED(rc); + } + + AE_NO_TSAN ~Semaphore() + { + semaphore_destroy(mach_task_self(), m_sema); + } + + bool wait() AE_NO_TSAN + { + return semaphore_wait(m_sema) == KERN_SUCCESS; + } + + bool try_wait() AE_NO_TSAN + { + return timed_wait(0); + } + + bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN + { + mach_timespec_t ts; + ts.tv_sec = static_cast(timeout_usecs / 1000000); + ts.tv_nsec = static_cast((timeout_usecs % 1000000) * 1000); + + // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html + kern_return_t rc = semaphore_timedwait(m_sema, ts); + return rc == KERN_SUCCESS; + } + + void signal() AE_NO_TSAN + { + while (semaphore_signal(m_sema) != KERN_SUCCESS); + } + + void signal(int count) AE_NO_TSAN + { + while (count-- > 0) + { + while (semaphore_signal(m_sema) != KERN_SUCCESS); + } + } + }; #elif defined(__unix__) //--------------------------------------------------------- - // Semaphore (POSIX, Linux) - //--------------------------------------------------------- - class Semaphore - { - private: - sem_t m_sema; - - Semaphore(const Semaphore& other); - Semaphore& operator=(const Semaphore& other); - - public: - AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() - { - assert(initialCount >= 0); - int rc = sem_init(&m_sema, 0, static_cast(initialCount)); - assert(rc == 0); - AE_UNUSED(rc); - } - - AE_NO_TSAN ~Semaphore() - { - sem_destroy(&m_sema); - } - - bool wait() AE_NO_TSAN - { - // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error - int rc; - do - { - rc = sem_wait(&m_sema); - } - while (rc == -1 && errno == EINTR); - return rc == 0; - } - - bool try_wait() AE_NO_TSAN - { - int rc; - do { - rc = sem_trywait(&m_sema); - } while (rc == -1 && errno == EINTR); - return rc == 0; - } - - bool timed_wait(std::uint64_t usecs) AE_NO_TSAN - { - struct timespec ts; - const int usecs_in_1_sec = 1000000; - const int nsecs_in_1_sec = 1000000000; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += static_cast(usecs / usecs_in_1_sec); - ts.tv_nsec += static_cast(usecs % usecs_in_1_sec) * 1000; - // sem_timedwait bombs if you have more than 1e9 in tv_nsec - // so we have to clean things up before passing it in - if (ts.tv_nsec >= nsecs_in_1_sec) { - ts.tv_nsec -= nsecs_in_1_sec; - ++ts.tv_sec; - } - - int rc; - do { - rc = sem_timedwait(&m_sema, &ts); - } while (rc == -1 && errno == EINTR); - return rc == 0; - } - - void signal() AE_NO_TSAN - { - while (sem_post(&m_sema) == -1); - } - - void signal(int count) AE_NO_TSAN - { - while (count-- > 0) - { - while (sem_post(&m_sema) == -1); - } - } - }; + // Semaphore (POSIX, Linux) + //--------------------------------------------------------- + class Semaphore + { + private: + sem_t m_sema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() + { + assert(initialCount >= 0); + int rc = sem_init(&m_sema, 0, static_cast(initialCount)); + assert(rc == 0); + AE_UNUSED(rc); + } + + AE_NO_TSAN ~Semaphore() + { + sem_destroy(&m_sema); + } + + bool wait() AE_NO_TSAN + { + // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error + int rc; + do + { + rc = sem_wait(&m_sema); + } + while (rc == -1 && errno == EINTR); + return rc == 0; + } + + bool try_wait() AE_NO_TSAN + { + int rc; + do { + rc = sem_trywait(&m_sema); + } while (rc == -1 && errno == EINTR); + return rc == 0; + } + + bool timed_wait(std::uint64_t usecs) AE_NO_TSAN + { + struct timespec ts; + const int usecs_in_1_sec = 1000000; + const int nsecs_in_1_sec = 1000000000; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += static_cast(usecs / usecs_in_1_sec); + ts.tv_nsec += static_cast(usecs % usecs_in_1_sec) * 1000; + // sem_timedwait bombs if you have more than 1e9 in tv_nsec + // so we have to clean things up before passing it in + if (ts.tv_nsec >= nsecs_in_1_sec) { + ts.tv_nsec -= nsecs_in_1_sec; + ++ts.tv_sec; + } + + int rc; + do { + rc = sem_timedwait(&m_sema, &ts); + } while (rc == -1 && errno == EINTR); + return rc == 0; + } + + void signal() AE_NO_TSAN + { + while (sem_post(&m_sema) == -1); + } + + void signal(int count) AE_NO_TSAN + { + while (count-- > 0) + { + while (sem_post(&m_sema) == -1); + } + } + }; #elif defined(FREERTOS) //--------------------------------------------------------- - // Semaphore (FreeRTOS) - //--------------------------------------------------------- - class Semaphore - { - private: - SemaphoreHandle_t m_sema; - - Semaphore(const Semaphore& other); - Semaphore& operator=(const Semaphore& other); - - public: - AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() - { - assert(initialCount >= 0); - m_sema = xSemaphoreCreateCounting(static_cast(~0ull), static_cast(initialCount)); - assert(m_sema); - } - - AE_NO_TSAN ~Semaphore() - { - vSemaphoreDelete(m_sema); - } - - bool wait() AE_NO_TSAN - { - return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE; - } - - bool try_wait() AE_NO_TSAN - { - // Note: In an ISR context, if this causes a task to unblock, - // the caller won't know about it - if (xPortIsInsideInterrupt()) - return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE; - return xSemaphoreTake(m_sema, 0) == pdTRUE; - } - - bool timed_wait(std::uint64_t usecs) AE_NO_TSAN - { - std::uint64_t msecs = usecs / 1000; - TickType_t ticks = static_cast(msecs / portTICK_PERIOD_MS); - if (ticks == 0) - return try_wait(); - return xSemaphoreTake(m_sema, ticks) == pdTRUE; - } - - void signal() AE_NO_TSAN - { - // Note: In an ISR context, if this causes a task to unblock, - // the caller won't know about it - BaseType_t rc; - if (xPortIsInsideInterrupt()) - rc = xSemaphoreGiveFromISR(m_sema, NULL); - else - rc = xSemaphoreGive(m_sema); - assert(rc == pdTRUE); - AE_UNUSED(rc); - } - - void signal(int count) AE_NO_TSAN - { - while (count-- > 0) - signal(); - } - }; + // Semaphore (FreeRTOS) + //--------------------------------------------------------- + class Semaphore + { + private: + SemaphoreHandle_t m_sema; + + Semaphore(const Semaphore& other); + Semaphore& operator=(const Semaphore& other); + + public: + AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema() + { + assert(initialCount >= 0); + m_sema = xSemaphoreCreateCounting(static_cast(~0ull), static_cast(initialCount)); + assert(m_sema); + } + + AE_NO_TSAN ~Semaphore() + { + vSemaphoreDelete(m_sema); + } + + bool wait() AE_NO_TSAN + { + return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE; + } + + bool try_wait() AE_NO_TSAN + { + // Note: In an ISR context, if this causes a task to unblock, + // the caller won't know about it + if (xPortIsInsideInterrupt()) + return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE; + return xSemaphoreTake(m_sema, 0) == pdTRUE; + } + + bool timed_wait(std::uint64_t usecs) AE_NO_TSAN + { + std::uint64_t msecs = usecs / 1000; + TickType_t ticks = static_cast(msecs / portTICK_PERIOD_MS); + if (ticks == 0) + return try_wait(); + return xSemaphoreTake(m_sema, ticks) == pdTRUE; + } + + void signal() AE_NO_TSAN + { + // Note: In an ISR context, if this causes a task to unblock, + // the caller won't know about it + BaseType_t rc; + if (xPortIsInsideInterrupt()) + rc = xSemaphoreGiveFromISR(m_sema, NULL); + else + rc = xSemaphoreGive(m_sema); + assert(rc == pdTRUE); + AE_UNUSED(rc); + } + + void signal(int count) AE_NO_TSAN + { + while (count-- > 0) + signal(); + } + }; #else #error Unsupported platform! (No semaphore wrapper available) #endif @@ -655,8 +686,7 @@ namespace moodycamel //--------------------------------------------------------- // LightweightSemaphore //--------------------------------------------------------- - class LightweightSemaphore - { + class LightweightSemaphore { public: typedef std::make_signed::type ssize_t; @@ -671,10 +701,8 @@ namespace moodycamel // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC, // as threads start hitting the kernel semaphore. int spin = 1024; - while (--spin >= 0) - { - if (m_count.load() > 0) - { + while (--spin >= 0) { + if (m_count.load() > 0) { m_count.fetch_add_acquire(-1); return true; } @@ -683,8 +711,7 @@ namespace moodycamel oldCount = m_count.fetch_add_acquire(-1); if (oldCount > 0) return true; - if (timeout_usecs < 0) - { + if (timeout_usecs < 0) { if (m_sema.wait()) return true; } @@ -695,8 +722,7 @@ namespace moodycamel // it. So we have to re-adjust the count, but only if the semaphore // wasn't signaled enough times for us too since then. If it was, we // need to release the semaphore too. - while (true) - { + while (true) { oldCount = m_count.fetch_add_release(1); if (oldCount < 0) return false; // successfully restored things to the way they were @@ -708,15 +734,13 @@ namespace moodycamel } public: - AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema() - { + AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema() { assert(initialCount >= 0); } bool tryWait() AE_NO_TSAN { - if (m_count.load() > 0) - { + if (m_count.load() > 0) { m_count.fetch_add_acquire(-1); return true; } @@ -738,8 +762,7 @@ namespace moodycamel assert(count >= 0); ssize_t oldCount = m_count.fetch_add_release(count); assert(oldCount >= -1); - if (oldCount < 0) - { + if (oldCount < 0) { m_sema.signal(1); } } @@ -750,8 +773,8 @@ namespace moodycamel return count > 0 ? static_cast(count) : 0; } }; - } // end namespace spsc_sema -} // end namespace moodycamel + } // end namespace spsc_sema +} // end namespace moodycamel #if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli)) #pragma warning(pop) diff --git a/third_party/readerwriterqueue.h b/third_party/readerwriterqueue.h index 8b26556c..8fb1aa66 100644 --- a/third_party/readerwriterqueue.h +++ b/third_party/readerwriterqueue.h @@ -12,8 +12,9 @@ #include #include #include -#include // For malloc/free/abort & size_t +#include // For malloc/free/abort & size_t #include + #if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012 #include #endif @@ -73,219 +74,214 @@ namespace moodycamel { template - class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue -{ - // Design: Based on a queue-of-queues. The low-level queues are just - // circular buffers with front and tail indices indicating where the - // next element to dequeue is and where the next element can be enqueued, - // respectively. Each low-level queue is called a "block". Each block - // wastes exactly one element's worth of space to keep the design simple - // (if front == tail then the queue is empty, and can't be full). - // The high-level queue is a circular linked list of blocks; again there - // is a front and tail, but this time they are pointers to the blocks. - // The front block is where the next element to be dequeued is, provided - // the block is not empty. The back block is where elements are to be - // enqueued, provided the block is not full. - // The producer thread owns all the tail indices/pointers. The consumer - // thread owns all the front indices/pointers. Both threads read each - // other's variables, but only the owning thread updates them. E.g. After - // the consumer reads the producer's tail, the tail may change before the - // consumer is done dequeuing an object, but the consumer knows the tail - // will never go backwards, only forwards. - // If there is no room to enqueue an object, an additional block (of - // equal size to the last block) is added. Blocks are never removed. + class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue { + // Design: Based on a queue-of-queues. The low-level queues are just + // circular buffers with front and tail indices indicating where the + // next element to dequeue is and where the next element can be enqueued, + // respectively. Each low-level queue is called a "block". Each block + // wastes exactly one element's worth of space to keep the design simple + // (if front == tail then the queue is empty, and can't be full). + // The high-level queue is a circular linked list of blocks; again there + // is a front and tail, but this time they are pointers to the blocks. + // The front block is where the next element to be dequeued is, provided + // the block is not empty. The back block is where elements are to be + // enqueued, provided the block is not full. + // The producer thread owns all the tail indices/pointers. The consumer + // thread owns all the front indices/pointers. Both threads read each + // other's variables, but only the owning thread updates them. E.g. After + // the consumer reads the producer's tail, the tail may change before the + // consumer is done dequeuing an object, but the consumer knows the tail + // will never go backwards, only forwards. + // If there is no room to enqueue an object, an additional block (of + // equal size to the last block) is added. Blocks are never removed. public: - typedef T value_type; + typedef T value_type; - // Constructs a queue that can hold at least `size` elements without further - // allocations. If more than MAX_BLOCK_SIZE elements are requested, - // then several blocks of MAX_BLOCK_SIZE each are reserved (including - // at least one extra buffer block). - AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15) + // Constructs a queue that can hold at least `size` elements without further + // allocations. If more than MAX_BLOCK_SIZE elements are requested, + // then several blocks of MAX_BLOCK_SIZE each are reserved (including + // at least one extra buffer block). + AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15) #ifndef NDEBUG - : enqueuing(false) - ,dequeuing(false) + : enqueuing(false), dequeuing(false) #endif -{ - assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2"); - assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); - - Block* firstBlock = nullptr; - - largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block - if (largestBlockSize > MAX_BLOCK_SIZE * 2) { - // We need a spare block in case the producer is writing to a different block the consumer is reading from, and - // wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity - // between front == tail meaning "empty" and "full". - // So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the - // number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying): - size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); - largestBlockSize = MAX_BLOCK_SIZE; - Block* lastBlock = nullptr; - for (size_t i = 0; i != initialBlockCount; ++i) { - auto block = make_block(largestBlockSize); - if (block == nullptr) { + { + assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2"); + assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); + + Block *firstBlock = nullptr; + + largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block + if (largestBlockSize > MAX_BLOCK_SIZE * 2) { + // We need a spare block in case the producer is writing to a different block the consumer is reading from, and + // wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity + // between front == tail meaning "empty" and "full". + // So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the + // number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying): + size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); + largestBlockSize = MAX_BLOCK_SIZE; + Block *lastBlock = nullptr; + for (size_t i = 0; i != initialBlockCount; ++i) { + auto block = make_block(largestBlockSize); + if (block == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED - throw std::bad_alloc(); + throw std::bad_alloc(); #else - abort(); + abort(); #endif -} -if (firstBlock == nullptr) { -firstBlock = block; -} -else { -lastBlock->next = block; -} -lastBlock = block; -block->next = firstBlock; -} -} -else { -firstBlock = make_block(largestBlockSize); -if (firstBlock == nullptr) { + } + if (firstBlock == nullptr) { + firstBlock = block; + } else { + lastBlock->next = block; + } + lastBlock = block; + block->next = firstBlock; + } + } else { + firstBlock = make_block(largestBlockSize); + if (firstBlock == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED -throw std::bad_alloc(); + throw std::bad_alloc(); #else -abort(); + abort(); #endif -} -firstBlock->next = firstBlock; -} -frontBlock = firstBlock; -tailBlock = firstBlock; + } + firstBlock->next = firstBlock; + } + frontBlock = firstBlock; + tailBlock = firstBlock; // Make sure the reader/writer threads will have the initialized memory setup above: -fence(memory_order_sync); -} + fence(memory_order_sync); + } // Note: The queue should not be accessed concurrently while it's // being moved. It's up to the user to synchronize this. -AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other) -: frontBlock(other.frontBlock.load()), -tailBlock(other.tailBlock.load()), -largestBlockSize(other.largestBlockSize) + AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue &&other) + : frontBlock(other.frontBlock.load()), + tailBlock(other.tailBlock.load()), + largestBlockSize(other.largestBlockSize) #ifndef NDEBUG -,enqueuing(false) -,dequeuing(false) + , enqueuing(false), dequeuing(false) #endif -{ -other.largestBlockSize = 32; -Block* b = other.make_block(other.largestBlockSize); -if (b == nullptr) { + { + other.largestBlockSize = 32; + Block *b = other.make_block(other.largestBlockSize); + if (b == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED -throw std::bad_alloc(); + throw std::bad_alloc(); #else -abort(); + abort(); #endif -} -b->next = b; -other.frontBlock = b; -other.tailBlock = b; -} + } + b->next = b; + other.frontBlock = b; + other.tailBlock = b; + } // Note: The queue should not be accessed concurrently while it's // being moved. It's up to the user to synchronize this. -ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN -{ -Block* b = frontBlock.load(); -frontBlock = other.frontBlock.load(); -other.frontBlock = b; -b = tailBlock.load(); -tailBlock = other.tailBlock.load(); -other.tailBlock = b; -std::swap(largestBlockSize, other.largestBlockSize); -return *this; -} + ReaderWriterQueue &operator=(ReaderWriterQueue &&other) AE_NO_TSAN + { + Block *b = frontBlock.load(); + frontBlock = other.frontBlock.load(); + other.frontBlock = b; + b = tailBlock.load(); + tailBlock = other.tailBlock.load(); + other.tailBlock = b; + std::swap(largestBlockSize, other.largestBlockSize); + return *this; + } // Note: The queue should not be accessed concurrently while it's // being deleted. It's up to the user to synchronize this. -AE_NO_TSAN ~ReaderWriterQueue() -{ - // Make sure we get the latest version of all variables from other CPUs: - fence(memory_order_sync); - - // Destroy any remaining objects in queue and free memory - Block* frontBlock_ = frontBlock; - Block* block = frontBlock_; - do { - Block* nextBlock = block->next; - size_t blockFront = block->front; - size_t blockTail = block->tail; - - for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { - auto element = reinterpret_cast(block->data + i * sizeof(T)); - element->~T(); - (void)element; + AE_NO_TSAN ~ReaderWriterQueue() { + // Make sure we get the latest version of all variables from other CPUs: + fence(memory_order_sync); + + // Destroy any remaining objects in queue and free memory + Block *frontBlock_ = frontBlock; + Block *block = frontBlock_; + do { + Block *nextBlock = block->next; + size_t blockFront = block->front; + size_t blockTail = block->tail; + + for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { + auto element = reinterpret_cast(block->data + i * sizeof(T)); + element->~T(); + (void) element; + } + + auto rawBlock = block->rawThis; + block->~Block(); + std::free(rawBlock); + block = nextBlock; + } while (block != frontBlock_); } - auto rawBlock = block->rawThis; - block->~Block(); - std::free(rawBlock); - block = nextBlock; - } while (block != frontBlock_); -} - // Enqueues a copy of element if there is room in the queue. // Returns true if the element was enqueued, false otherwise. // Does not allocate memory. -AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN -{ -return inner_enqueue(element); -} + AE_FORCEINLINE bool try_enqueue(T const &element) AE_NO_TSAN + { + return inner_enqueue(element); + } // Enqueues a moved copy of element if there is room in the queue. // Returns true if the element was enqueued, false otherwise. // Does not allocate memory. -AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN -{ -return inner_enqueue(std::forward(element)); -} + AE_FORCEINLINE bool try_enqueue(T &&element) AE_NO_TSAN + { + return inner_enqueue(std::forward(element)); + } #if MOODYCAMEL_HAS_EMPLACE + // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). -template -AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN -{ -return inner_enqueue(std::forward(args)...); -} + template + AE_FORCEINLINE bool try_emplace(Args &&... args) AE_NO_TSAN { + return inner_enqueue(std::forward(args)...); + } + #endif // Enqueues a copy of element on the queue. // Allocates an additional block of memory if needed. // Only fails (returns false) if memory allocation fails. -AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN -{ -return inner_enqueue(element); -} + AE_FORCEINLINE bool enqueue(T const &element) AE_NO_TSAN + { + return inner_enqueue(element); + } // Enqueues a moved copy of element on the queue. // Allocates an additional block of memory if needed. // Only fails (returns false) if memory allocation fails. -AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN -{ -return inner_enqueue(std::forward(element)); -} + AE_FORCEINLINE bool enqueue(T &&element) AE_NO_TSAN + { + return inner_enqueue(std::forward(element)); + } #if MOODYCAMEL_HAS_EMPLACE + // Like enqueue() but with emplace semantics (i.e. construct-in-place). -template -AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN -{ -return inner_enqueue(std::forward(args)...); -} + template + AE_FORCEINLINE bool emplace(Args &&... args) AE_NO_TSAN { + return inner_enqueue(std::forward(args)...); + } + #endif // Attempts to dequeue an element; if the queue is empty, // returns false instead. If the queue has at least one element, // moves front to result using operator=, then returns true. -template -bool try_dequeue(U& result) AE_NO_TSAN -{ + template + bool try_dequeue(U &result) AE_NO_TSAN { #ifndef NDEBUG -ReentrantGuard guard(this->dequeuing); + ReentrantGuard guard(this->dequeuing); #endif // High-level pseudocode: @@ -305,75 +301,73 @@ ReentrantGuard guard(this->dequeuing); // then re-read the front block and check if it's not empty again, then check if the tail // block has advanced. -Block* frontBlock_ = frontBlock.load(); -size_t blockTail = frontBlock_->localTail; -size_t blockFront = frontBlock_->front.load(); + Block *frontBlock_ = frontBlock.load(); + size_t blockTail = frontBlock_->localTail; + size_t blockFront = frontBlock_->front.load(); -if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { -fence(memory_order_acquire); + if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { + fence(memory_order_acquire); -non_empty_front_block: + non_empty_front_block: // Front block not empty, dequeue from here -auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); -result = std::move(*element); -element->~T(); + auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); + result = std::move(*element); + element->~T(); -blockFront = (blockFront + 1) & frontBlock_->sizeMask; + blockFront = (blockFront + 1) & frontBlock_->sizeMask; -fence(memory_order_release); -frontBlock_->front = blockFront; -} -else if (frontBlock_ != tailBlock.load()) { -fence(memory_order_acquire); + fence(memory_order_release); + frontBlock_->front = blockFront; + } else if (frontBlock_ != tailBlock.load()) { + fence(memory_order_acquire); -frontBlock_ = frontBlock.load(); -blockTail = frontBlock_->localTail = frontBlock_->tail.load(); -blockFront = frontBlock_->front.load(); -fence(memory_order_acquire); + frontBlock_ = frontBlock.load(); + blockTail = frontBlock_->localTail = frontBlock_->tail.load(); + blockFront = frontBlock_->front.load(); + fence(memory_order_acquire); -if (blockFront != blockTail) { + if (blockFront != blockTail) { // Oh look, the front block isn't empty after all -goto non_empty_front_block; -} + goto non_empty_front_block; + } // Front block is empty but there's another block ahead, advance to it -Block* nextBlock = frontBlock_->next; + Block *nextBlock = frontBlock_->next; // Don't need an acquire fence here since next can only ever be set on the tailBlock, // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which // ensures next is up-to-date on this CPU in case we recently were at tailBlock. -size_t nextBlockFront = nextBlock->front.load(); -size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); -fence(memory_order_acquire); + size_t nextBlockFront = nextBlock->front.load(); + size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); + fence(memory_order_acquire); // Since the tailBlock is only ever advanced after being written to, // we know there's for sure an element to dequeue on it -assert(nextBlockFront != nextBlockTail); -AE_UNUSED(nextBlockTail); + assert(nextBlockFront != nextBlockTail); + AE_UNUSED(nextBlockTail); // We're done with this block, let the producer use it if it needs -fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue -frontBlock = frontBlock_ = nextBlock; + fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue + frontBlock = frontBlock_ = nextBlock; -compiler_fence(memory_order_release); // Not strictly needed + compiler_fence(memory_order_release); // Not strictly needed -auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); + auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); -result = std::move(*element); -element->~T(); + result = std::move(*element); + element->~T(); -nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; + nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; -fence(memory_order_release); -frontBlock_->front = nextBlockFront; -} -else { + fence(memory_order_release); + frontBlock_->front = nextBlockFront; + } else { // No elements in current block and no other block to advance to -return false; -} + return false; + } -return true; -} + return true; + } // Returns a pointer to the front element in the queue (the one that @@ -381,129 +375,126 @@ return true; // queue appears empty at the time the method is called, nullptr is // returned instead. // Must be called only from the consumer thread. -T* peek() const AE_NO_TSAN -{ + T *peek() const AE_NO_TSAN + { #ifndef NDEBUG -ReentrantGuard guard(this->dequeuing); + ReentrantGuard guard(this->dequeuing); #endif // See try_dequeue() for reasoning -Block* frontBlock_ = frontBlock.load(); -size_t blockTail = frontBlock_->localTail; -size_t blockFront = frontBlock_->front.load(); + Block *frontBlock_ = frontBlock.load(); + size_t blockTail = frontBlock_->localTail; + size_t blockFront = frontBlock_->front.load(); -if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { -fence(memory_order_acquire); -non_empty_front_block: -return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); -} -else if (frontBlock_ != tailBlock.load()) { -fence(memory_order_acquire); -frontBlock_ = frontBlock.load(); -blockTail = frontBlock_->localTail = frontBlock_->tail.load(); -blockFront = frontBlock_->front.load(); -fence(memory_order_acquire); + if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { + fence(memory_order_acquire); + non_empty_front_block: + return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); + } else if (frontBlock_ != tailBlock.load()) { + fence(memory_order_acquire); + frontBlock_ = frontBlock.load(); + blockTail = frontBlock_->localTail = frontBlock_->tail.load(); + blockFront = frontBlock_->front.load(); + fence(memory_order_acquire); -if (blockFront != blockTail) { -goto non_empty_front_block; -} + if (blockFront != blockTail) { + goto non_empty_front_block; + } -Block* nextBlock = frontBlock_->next; + Block *nextBlock = frontBlock_->next; -size_t nextBlockFront = nextBlock->front.load(); -fence(memory_order_acquire); + size_t nextBlockFront = nextBlock->front.load(); + fence(memory_order_acquire); -assert(nextBlockFront != nextBlock->tail.load()); -return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); -} + assert(nextBlockFront != nextBlock->tail.load()); + return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); + } -return nullptr; -} + return nullptr; + } // Removes the front element from the queue, if any, without returning it. // Returns true on success, or false if the queue appeared empty at the time // `pop` was called. -bool pop() AE_NO_TSAN -{ + bool pop() AE_NO_TSAN + { #ifndef NDEBUG -ReentrantGuard guard(this->dequeuing); + ReentrantGuard guard(this->dequeuing); #endif // See try_dequeue() for reasoning -Block* frontBlock_ = frontBlock.load(); -size_t blockTail = frontBlock_->localTail; -size_t blockFront = frontBlock_->front.load(); + Block *frontBlock_ = frontBlock.load(); + size_t blockTail = frontBlock_->localTail; + size_t blockFront = frontBlock_->front.load(); -if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { -fence(memory_order_acquire); + if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { + fence(memory_order_acquire); -non_empty_front_block: -auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); -element->~T(); + non_empty_front_block: + auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); + element->~T(); -blockFront = (blockFront + 1) & frontBlock_->sizeMask; + blockFront = (blockFront + 1) & frontBlock_->sizeMask; -fence(memory_order_release); -frontBlock_->front = blockFront; -} -else if (frontBlock_ != tailBlock.load()) { -fence(memory_order_acquire); -frontBlock_ = frontBlock.load(); -blockTail = frontBlock_->localTail = frontBlock_->tail.load(); -blockFront = frontBlock_->front.load(); -fence(memory_order_acquire); + fence(memory_order_release); + frontBlock_->front = blockFront; + } else if (frontBlock_ != tailBlock.load()) { + fence(memory_order_acquire); + frontBlock_ = frontBlock.load(); + blockTail = frontBlock_->localTail = frontBlock_->tail.load(); + blockFront = frontBlock_->front.load(); + fence(memory_order_acquire); -if (blockFront != blockTail) { -goto non_empty_front_block; -} + if (blockFront != blockTail) { + goto non_empty_front_block; + } // Front block is empty but there's another block ahead, advance to it -Block* nextBlock = frontBlock_->next; + Block *nextBlock = frontBlock_->next; -size_t nextBlockFront = nextBlock->front.load(); -size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); -fence(memory_order_acquire); + size_t nextBlockFront = nextBlock->front.load(); + size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); + fence(memory_order_acquire); -assert(nextBlockFront != nextBlockTail); -AE_UNUSED(nextBlockTail); + assert(nextBlockFront != nextBlockTail); + AE_UNUSED(nextBlockTail); -fence(memory_order_release); -frontBlock = frontBlock_ = nextBlock; + fence(memory_order_release); + frontBlock = frontBlock_ = nextBlock; -compiler_fence(memory_order_release); + compiler_fence(memory_order_release); -auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); -element->~T(); + auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); + element->~T(); -nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; + nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; -fence(memory_order_release); -frontBlock_->front = nextBlockFront; -} -else { + fence(memory_order_release); + frontBlock_->front = nextBlockFront; + } else { // No elements in current block and no other block to advance to -return false; -} + return false; + } -return true; -} + return true; + } // Returns the approximate number of items currently in the queue. // Safe to call from both the producer and consumer threads. -inline size_t size_approx() const AE_NO_TSAN -{ -size_t result = 0; -Block* frontBlock_ = frontBlock.load(); -Block* block = frontBlock_; -do { -fence(memory_order_acquire); -size_t blockFront = block->front.load(); -size_t blockTail = block->tail.load(); -result += (blockTail - blockFront) & block->sizeMask; -block = block->next.load(); -} while (block != frontBlock_); -return result; -} + inline size_t size_approx() const AE_NO_TSAN + { + size_t result = 0; + Block *frontBlock_ = frontBlock.load(); + Block *block = frontBlock_; + do { + fence(memory_order_acquire); + size_t blockFront = block->front.load(); + size_t blockTail = block->tail.load(); + result += (blockTail - blockFront) & block->sizeMask; + block = block->next.load(); + } while (block != frontBlock_); + return result; + } // Returns the total number of items that could be enqueued without incurring // an allocation when this queue is empty. @@ -514,32 +505,35 @@ return result; // the block the consumer is removing from until it's completely empty, except in // the case where the producer was writing to the same block the consumer was // reading from the whole time. -inline size_t max_capacity() const { - size_t result = 0; - Block* frontBlock_ = frontBlock.load(); - Block* block = frontBlock_; - do { - fence(memory_order_acquire); - result += block->sizeMask; - block = block->next.load(); - } while (block != frontBlock_); - return result; -} - - -private: -enum AllocationMode { CanAlloc, CannotAlloc }; + inline size_t max_capacity() const { + size_t result = 0; + Block *frontBlock_ = frontBlock.load(); + Block *block = frontBlock_; + do { + fence(memory_order_acquire); + result += block->sizeMask; + block = block->next.load(); + } while (block != frontBlock_); + return result; + } + + + private: + enum AllocationMode { + CanAlloc, CannotAlloc + }; #if MOODYCAMEL_HAS_EMPLACE -template -bool inner_enqueue(Args&&... args) AE_NO_TSAN + + template + bool inner_enqueue(Args &&... args) AE_NO_TSAN #else -template - bool inner_enqueue(U&& element) AE_NO_TSAN + template + bool inner_enqueue(U&& element) AE_NO_TSAN #endif -{ + { #ifndef NDEBUG -ReentrantGuard guard(this->enqueuing); + ReentrantGuard guard(this->enqueuing); #endif // High-level pseudocode (assuming we're allowed to alloc a new block): @@ -549,77 +543,75 @@ ReentrantGuard guard(this->enqueuing); // Else create a new block and enqueue there // Advance tail to the block we just enqueued to -Block* tailBlock_ = tailBlock.load(); -size_t blockFront = tailBlock_->localFront; -size_t blockTail = tailBlock_->tail.load(); + Block *tailBlock_ = tailBlock.load(); + size_t blockFront = tailBlock_->localFront; + size_t blockTail = tailBlock_->tail.load(); -size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; -if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { -fence(memory_order_acquire); + size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; + if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { + fence(memory_order_acquire); // This block has room for at least one more element -char* location = tailBlock_->data + blockTail * sizeof(T); + char *location = tailBlock_->data + blockTail * sizeof(T); #if MOODYCAMEL_HAS_EMPLACE -new (location) T(std::forward(args)...); + new(location) T(std::forward(args)...); #else -new (location) T(std::forward(element)); + new (location) T(std::forward(element)); #endif -fence(memory_order_release); -tailBlock_->tail = nextBlockTail; -} -else { -fence(memory_order_acquire); -if (tailBlock_->next.load() != frontBlock) { + fence(memory_order_release); + tailBlock_->tail = nextBlockTail; + } else { + fence(memory_order_acquire); + if (tailBlock_->next.load() != frontBlock) { // Note that the reason we can't advance to the frontBlock and start adding new entries there // is because if we did, then dequeue would stay in that block, eventually reading the new values, // instead of advancing to the next full block (whose values were enqueued first and so should be // consumed first). -fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock + fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock // tailBlock is full, but there's a free block ahead, use it -Block* tailBlockNext = tailBlock_->next.load(); -size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); -nextBlockTail = tailBlockNext->tail.load(); -fence(memory_order_acquire); + Block *tailBlockNext = tailBlock_->next.load(); + size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); + nextBlockTail = tailBlockNext->tail.load(); + fence(memory_order_acquire); // This block must be empty since it's not the head block and we // go through the blocks in a circle -assert(nextBlockFront == nextBlockTail); -tailBlockNext->localFront = nextBlockFront; + assert(nextBlockFront == nextBlockTail); + tailBlockNext->localFront = nextBlockFront; -char* location = tailBlockNext->data + nextBlockTail * sizeof(T); + char *location = tailBlockNext->data + nextBlockTail * sizeof(T); #if MOODYCAMEL_HAS_EMPLACE -new (location) T(std::forward(args)...); + new(location) T(std::forward(args)...); #else -new (location) T(std::forward(element)); + new (location) T(std::forward(element)); #endif -tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; + tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; -fence(memory_order_release); -tailBlock = tailBlockNext; -} -else if (canAlloc == CanAlloc) { + fence(memory_order_release); + tailBlock = tailBlockNext; + } else if (canAlloc == CanAlloc) { // tailBlock is full and there's no free block ahead; create a new block -auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; -auto newBlock = make_block(newBlockSize); -if (newBlock == nullptr) { + auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; + auto newBlock = make_block(newBlockSize); + if (newBlock == nullptr) { // Could not allocate a block! -return false; -} -largestBlockSize = newBlockSize; + return false; + } + largestBlockSize = newBlockSize; #if MOODYCAMEL_HAS_EMPLACE -new (newBlock->data) T(std::forward(args)...); + new(newBlock->data) T(std::forward(args)...); #else -new (newBlock->data) T(std::forward(element)); + new (newBlock->data) T(std::forward(element)); #endif -assert(newBlock->front == 0); -newBlock->tail = newBlock->localTail = 1; + assert(newBlock->front == 0); + newBlock->tail = newBlock->localTail = 1; -newBlock->next = tailBlock_->next.load(); -tailBlock_->next = newBlock; + newBlock->next = tailBlock_->next.load(); + tailBlock_->next = newBlock; // Might be possible for the dequeue thread to see the new tailBlock->next // *without* seeing the new tailBlock value, but this is OK since it can't @@ -627,350 +619,345 @@ tailBlock_->next = newBlock; // case where it could try to read the next is if it's already at the tailBlock, // and it won't advance past tailBlock in any circumstance). -fence(memory_order_release); -tailBlock = newBlock; -} -else if (canAlloc == CannotAlloc) { + fence(memory_order_release); + tailBlock = newBlock; + } else if (canAlloc == CannotAlloc) { // Would have had to allocate a new block to enqueue, but not allowed -return false; -} -else { -assert(false && "Should be unreachable code"); -return false; -} -} + return false; + } else { + assert(false && "Should be unreachable code"); + return false; + } + } -return true; -} + return true; + } // Disable copying -ReaderWriterQueue(ReaderWriterQueue const&) { } + ReaderWriterQueue(ReaderWriterQueue const &) {} // Disable assignment -ReaderWriterQueue& operator=(ReaderWriterQueue const&) { } + ReaderWriterQueue &operator=(ReaderWriterQueue const &) {} -AE_FORCEINLINE static size_t ceilToPow2(size_t x) -{ + AE_FORCEINLINE static size_t ceilToPow2(size_t x) { // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 ---x; -x |= x >> 1; -x |= x >> 2; -x |= x >> 4; -for (size_t i = 1; i < sizeof(size_t); i <<= 1) { -x |= x >> (i << 3); -} -++x; -return x; -} - -template -static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN -{ -const std::size_t alignment = std::alignment_of::value; -return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; -} -private: + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + for (size_t i = 1; i < sizeof(size_t); i <<= 1) { + x |= x >> (i << 3); + } + ++x; + return x; + } + + template + static AE_FORCEINLINE char *align_for(char *ptr) AE_NO_TSAN { + const std::size_t alignment = std::alignment_of::value; + return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; + } + + private: #ifndef NDEBUG -struct ReentrantGuard -{ - AE_NO_TSAN ReentrantGuard(weak_atomic& _inSection) - : inSection(_inSection) - { - assert(!inSection && "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)"); - inSection = true; - } - - AE_NO_TSAN ~ReentrantGuard() { inSection = false; } - -private: - ReentrantGuard& operator=(ReentrantGuard const&); - -private: - weak_atomic& inSection; -}; + + struct ReentrantGuard { + AE_NO_TSAN ReentrantGuard(weak_atomic &_inSection) + : inSection(_inSection) { + assert(!inSection && + "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)"); + inSection = true; + } + + AE_NO_TSAN ~ReentrantGuard() { inSection = false; } + + private: + ReentrantGuard &operator=(ReentrantGuard const &); + + private: + weak_atomic &inSection; + }; + #endif -struct Block -{ - // Avoid false-sharing by putting highly contended variables on their own cache lines - weak_atomic front; // (Atomic) Elements are read from here - size_t localTail; // An uncontended shadow copy of tail, owned by the consumer + struct Block { + // Avoid false-sharing by putting highly contended variables on their own cache lines + weak_atomic front; // (Atomic) Elements are read from here + size_t localTail; // An uncontended shadow copy of tail, owned by the consumer - char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; - weak_atomic tail; // (Atomic) Elements are enqueued here - size_t localFront; + char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; + weak_atomic tail; // (Atomic) Elements are enqueued here + size_t localFront; - char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is) - weak_atomic next; // (Atomic) + char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - + sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is) + weak_atomic next; // (Atomic) - char* data; // Contents (on heap) are aligned to T's alignment + char *data; // Contents (on heap) are aligned to T's alignment - const size_t sizeMask; + const size_t sizeMask; - // size must be a power of two (and greater than 0) - AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data) - : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis) - { - } + // size must be a power of two (and greater than 0) + AE_NO_TSAN Block(size_t const &_size, char *_rawThis, char *_data) + : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), + sizeMask(_size - 1), rawThis(_rawThis) { + } -private: - // C4512 - Assignment operator could not be generated - Block& operator=(Block const&); + private: + // C4512 - Assignment operator could not be generated + Block &operator=(Block const &); -public: - char* rawThis; -}; + public: + char *rawThis; + }; -static Block* make_block(size_t capacity) AE_NO_TSAN -{ + static Block *make_block(size_t capacity) AE_NO_TSAN + { // Allocate enough memory for the block itself, as well as all the elements it will contain -auto size = sizeof(Block) + std::alignment_of::value - 1; -size += sizeof(T) * capacity + std::alignment_of::value - 1; -auto newBlockRaw = static_cast(std::malloc(size)); -if (newBlockRaw == nullptr) { -return nullptr; -} - -auto newBlockAligned = align_for(newBlockRaw); -auto newBlockData = align_for(newBlockAligned + sizeof(Block)); -return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData); -} + auto size = sizeof(Block) + std::alignment_of::value - 1; + size += sizeof(T) * capacity + std::alignment_of::value - 1; + auto newBlockRaw = static_cast(std::malloc(size)); + if (newBlockRaw == nullptr) { + return nullptr; + } + + auto newBlockAligned = align_for(newBlockRaw); + auto newBlockData = align_for(newBlockAligned + sizeof(Block)); + return new(newBlockAligned) Block(capacity, newBlockRaw, newBlockData); + } -private: -weak_atomic frontBlock; // (Atomic) Elements are dequeued from this block + private: + weak_atomic frontBlock; // (Atomic) Elements are dequeued from this block -char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic)]; -weak_atomic tailBlock; // (Atomic) Elements are enqueued to this block + char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic)]; + weak_atomic tailBlock; // (Atomic) Elements are enqueued to this block -size_t largestBlockSize; + size_t largestBlockSize; #ifndef NDEBUG -weak_atomic enqueuing; -mutable weak_atomic dequeuing; + weak_atomic enqueuing; + mutable weak_atomic dequeuing; #endif -}; + }; // Like ReaderWriterQueue, but also providees blocking operations -template -class BlockingReaderWriterQueue -{ -private: - typedef ::moodycamel::ReaderWriterQueue ReaderWriterQueue; - -public: - explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN - : inner(size), sema(new spsc_sema::LightweightSemaphore()) - { } - - BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN - : inner(std::move(other.inner)), sema(std::move(other.sema)) - { } - - BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN - { - std::swap(sema, other.sema); - std::swap(inner, other.inner); - return *this; - } - - - // Enqueues a copy of element if there is room in the queue. - // Returns true if the element was enqueued, false otherwise. - // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN - { - if (inner.try_enqueue(element)) { - sema->signal(); - return true; + template + class BlockingReaderWriterQueue { + private: + typedef ::moodycamel::ReaderWriterQueue ReaderWriterQueue; + + public: + explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN + : inner(size), sema(new spsc_sema::LightweightSemaphore()) {} + + BlockingReaderWriterQueue(BlockingReaderWriterQueue &&other) AE_NO_TSAN + : inner(std::move(other.inner)), sema(std::move(other.sema)) {} + + BlockingReaderWriterQueue &operator=(BlockingReaderWriterQueue &&other) AE_NO_TSAN + { + std::swap(sema, other.sema); + std::swap(inner, other.inner); + return *this; } - return false; - } - // Enqueues a moved copy of element if there is room in the queue. - // Returns true if the element was enqueued, false otherwise. - // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN - { - if (inner.try_enqueue(std::forward(element))) { - sema->signal(); - return true; + + // Enqueues a copy of element if there is room in the queue. + // Returns true if the element was enqueued, false otherwise. + // Does not allocate memory. + AE_FORCEINLINE bool try_enqueue(T const &element) AE_NO_TSAN + { + if (inner.try_enqueue(element)) { + sema->signal(); + return true; + } + return false; + } + + // Enqueues a moved copy of element if there is room in the queue. + // Returns true if the element was enqueued, false otherwise. + // Does not allocate memory. + AE_FORCEINLINE bool try_enqueue(T &&element) AE_NO_TSAN + { + if (inner.try_enqueue(std::forward(element))) { + sema->signal(); + return true; + } + return false; } - return false; - } #if MOODYCAMEL_HAS_EMPLACE - // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN - { - if (inner.try_emplace(std::forward(args)...)) { - sema->signal(); - return true; + + // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). + template + AE_FORCEINLINE bool try_emplace(Args &&... args) AE_NO_TSAN { + if (inner.try_emplace(std::forward(args)...)) { + sema->signal(); + return true; + } + return false; } - return false; - } + #endif - // Enqueues a copy of element on the queue. - // Allocates an additional block of memory if needed. - // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN - { - if (inner.enqueue(element)) { - sema->signal(); - return true; + // Enqueues a copy of element on the queue. + // Allocates an additional block of memory if needed. + // Only fails (returns false) if memory allocation fails. + AE_FORCEINLINE bool enqueue(T const &element) AE_NO_TSAN + { + if (inner.enqueue(element)) { + sema->signal(); + return true; + } + return false; } - return false; - } - // Enqueues a moved copy of element on the queue. - // Allocates an additional block of memory if needed. - // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN - { - if (inner.enqueue(std::forward(element))) { - sema->signal(); - return true; + // Enqueues a moved copy of element on the queue. + // Allocates an additional block of memory if needed. + // Only fails (returns false) if memory allocation fails. + AE_FORCEINLINE bool enqueue(T &&element) AE_NO_TSAN + { + if (inner.enqueue(std::forward(element))) { + sema->signal(); + return true; + } + return false; } - return false; - } #if MOODYCAMEL_HAS_EMPLACE - // Like enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN - { - if (inner.emplace(std::forward(args)...)) { - sema->signal(); - return true; + + // Like enqueue() but with emplace semantics (i.e. construct-in-place). + template + AE_FORCEINLINE bool emplace(Args &&... args) AE_NO_TSAN { + if (inner.emplace(std::forward(args)...)) { + sema->signal(); + return true; + } + return false; } - return false; - } + #endif - // Attempts to dequeue an element; if the queue is empty, - // returns false instead. If the queue has at least one element, - // moves front to result using operator=, then returns true. - template - bool try_dequeue(U& result) AE_NO_TSAN - { - if (sema->tryWait()) { + // Attempts to dequeue an element; if the queue is empty, + // returns false instead. If the queue has at least one element, + // moves front to result using operator=, then returns true. + template + bool try_dequeue(U &result) AE_NO_TSAN { + if (sema->tryWait()) { + bool success = inner.try_dequeue(result); + assert(success); + AE_UNUSED(success); + return true; + } + return false; + } + + + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available, then dequeues it. + template + void wait_dequeue(U &result) AE_NO_TSAN { + while (!sema->wait()); bool success = inner.try_dequeue(result); + AE_UNUSED(result); assert(success); AE_UNUSED(success); - return true; } - return false; - } - - - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available, then dequeues it. - template - void wait_dequeue(U& result) AE_NO_TSAN - { - while (!sema->wait()); - bool success = inner.try_dequeue(result); - AE_UNUSED(result); - assert(success); - AE_UNUSED(success); - } - - - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available up to the specified timeout, - // then dequeues it and returns true, or returns false if the timeout - // expires before an element can be dequeued. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - template - bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN - { - if (!sema->wait(timeout_usecs)) { - return false; + + + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available up to the specified timeout, + // then dequeues it and returns true, or returns false if the timeout + // expires before an element can be dequeued. + // Using a negative timeout indicates an indefinite timeout, + // and is thus functionally equivalent to calling wait_dequeue. + template + bool wait_dequeue_timed(U &result, std::int64_t timeout_usecs) AE_NO_TSAN { + if (!sema->wait(timeout_usecs)) { + return false; + } + bool success = inner.try_dequeue(result); + AE_UNUSED(result); + assert(success); + AE_UNUSED(success); + return true; } - bool success = inner.try_dequeue(result); - AE_UNUSED(result); - assert(success); - AE_UNUSED(success); - return true; - } #if __cplusplus > 199711L || _MSC_VER >= 1700 - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available up to the specified timeout, - // then dequeues it and returns true, or returns false if the timeout - // expires before an element can be dequeued. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - template - inline bool wait_dequeue_timed(U& result, std::chrono::duration const& timeout) AE_NO_TSAN - { - return wait_dequeue_timed(result, std::chrono::duration_cast(timeout).count()); - } + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available up to the specified timeout, + // then dequeues it and returns true, or returns false if the timeout + // expires before an element can be dequeued. + // Using a negative timeout indicates an indefinite timeout, + // and is thus functionally equivalent to calling wait_dequeue. + template + inline bool wait_dequeue_timed(U& result, std::chrono::duration const& timeout) AE_NO_TSAN + { + return wait_dequeue_timed(result, std::chrono::duration_cast(timeout).count()); + } #endif - // Returns a pointer to the front element in the queue (the one that - // would be removed next by a call to `try_dequeue` or `pop`). If the - // queue appears empty at the time the method is called, nullptr is - // returned instead. - // Must be called only from the consumer thread. - AE_FORCEINLINE T* peek() const AE_NO_TSAN - { - return inner.peek(); - } - - // Removes the front element from the queue, if any, without returning it. - // Returns true on success, or false if the queue appeared empty at the time - // `pop` was called. - AE_FORCEINLINE bool pop() AE_NO_TSAN - { - if (sema->tryWait()) { - bool result = inner.pop(); - assert(result); - AE_UNUSED(result); - return true; + // Returns a pointer to the front element in the queue (the one that + // would be removed next by a call to `try_dequeue` or `pop`). If the + // queue appears empty at the time the method is called, nullptr is + // returned instead. + // Must be called only from the consumer thread. + AE_FORCEINLINE T *peek() const AE_NO_TSAN + { + return inner.peek(); + } + + // Removes the front element from the queue, if any, without returning it. + // Returns true on success, or false if the queue appeared empty at the time + // `pop` was called. + AE_FORCEINLINE bool pop() AE_NO_TSAN + { + if (sema->tryWait()) { + bool result = inner.pop(); + assert(result); + AE_UNUSED(result); + return true; + } + return false; + } + + // Returns the approximate number of items currently in the queue. + // Safe to call from both the producer and consumer threads. + AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN + { + return sema->availableApprox(); } - return false; - } - - // Returns the approximate number of items currently in the queue. - // Safe to call from both the producer and consumer threads. - AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN - { - return sema->availableApprox(); - } - - // Returns the total number of items that could be enqueued without incurring - // an allocation when this queue is empty. - // Safe to call from both the producer and consumer threads. - // - // NOTE: The actual capacity during usage may be different depending on the consumer. - // If the consumer is removing elements concurrently, the producer cannot add to - // the block the consumer is removing from until it's completely empty, except in - // the case where the producer was writing to the same block the consumer was - // reading from the whole time. - AE_FORCEINLINE size_t max_capacity() const { - return inner.max_capacity(); - } - -private: - // Disable copying & assignment - BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) { } - BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) { } - -private: - ReaderWriterQueue inner; - std::unique_ptr sema; -}; + + // Returns the total number of items that could be enqueued without incurring + // an allocation when this queue is empty. + // Safe to call from both the producer and consumer threads. + // + // NOTE: The actual capacity during usage may be different depending on the consumer. + // If the consumer is removing elements concurrently, the producer cannot add to + // the block the consumer is removing from until it's completely empty, except in + // the case where the producer was writing to the same block the consumer was + // reading from the whole time. + AE_FORCEINLINE size_t max_capacity() const { + return inner.max_capacity(); + } + + private: + // Disable copying & assignment + BlockingReaderWriterQueue(BlockingReaderWriterQueue const &) {} + + BlockingReaderWriterQueue &operator=(BlockingReaderWriterQueue const &) {} + + private: + ReaderWriterQueue inner; + std::unique_ptr sema; + }; } // end namespace moodycamel diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index d0a513df..5b7673ec 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -42,9 +42,11 @@ using namespace std; shared_ptr ZMQServer::zmqServer = nullptr; ZMQServer::ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string &_caCertFile) - : outgoingQueue(NUM_ZMQ_WORKER_THREADS), checkSignature(_checkSignature), checkKeyOwnership(_checkKeyOwnership), + : incomingQueue(NUM_ZMQ_WORKER_THREADS), checkSignature(_checkSignature), checkKeyOwnership(_checkKeyOwnership), caCertFile(_caCertFile), ctx(make_shared(1)) { + CHECK_STATE(NUM_ZMQ_WORKER_THREADS > 1); + socket = make_shared(*ctx, ZMQ_ROUTER); if (_checkSignature) { @@ -277,12 +279,20 @@ void ZMQServer::doOneServerLoop() { CHECK_STATE2(msg, ZMQ_COULD_NOT_PARSE); + + uint64_t index = 0; + if ((dynamic_pointer_cast(msg)!= nullptr) || dynamic_pointer_cast(msg)) { + index = NUM_ZMQ_WORKER_THREADS - 1; } else { - + index = 0; } + auto element = pair, shared_ptr>(msg, identity); + + incomingQueue.at(index).enqueue(element); + result = msg->process(); } catch (ExitRequestedException) { throw; diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index bb9dbd4b..7bcb42b4 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -34,6 +34,7 @@ #include "Agent.h" #include "WorkerThreadPool.h" +#include "ZMQMessage.h" using namespace moodycamel; @@ -41,6 +42,7 @@ typedef enum {GOT_INCOMING_MSG = 0, GOT_OUTFOING_MSG = 1} PollResult; static const uint64_t NUM_ZMQ_WORKER_THREADS = 2; + class ZMQServer : public Agent{ uint64_t workerThreads; @@ -48,9 +50,9 @@ class ZMQServer : public Agent{ string caCertFile; string caCert; - ReaderWriterQueue>> outgoingQueue; + ReaderWriterQueue>> outgoingQueue; - vector>>> incomingQueue; + vector, shared_ptr>>> incomingQueue; bool checkKeyOwnership = true; From eedcab4637b62e1a99699e041d187e9d001cb834 Mon Sep 17 00:00:00 2001 From: Oleh Date: Thu, 9 Sep 2021 18:09:46 +0300 Subject: [PATCH 083/100] SKALE-4523 fix bufer length --- secure_enclave/AESUtils.c | 55 ++++++++++++++++++--------------- secure_enclave/secure_enclave.c | 31 +++++++++---------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/secure_enclave/AESUtils.c b/secure_enclave/AESUtils.c index b99790be..ae501607 100644 --- a/secure_enclave/AESUtils.c +++ b/secure_enclave/AESUtils.c @@ -49,11 +49,16 @@ int AES_encrypt(char *message, uint8_t *encr_message, uint64_t encrBufLen, unsig return -2; } + if (!resultLen) { + LOG_ERROR("Null resultLen in AES_encrypt"); + return -3; + } + uint64_t len = strlen(message) + 1; if (2 + len + SGX_AESGCM_MAC_SIZE + SGX_AESGCM_IV_SIZE > encrBufLen ) { LOG_ERROR("Output buffer too small"); - return -3; + return -4; } SAFE_CHAR_BUF(fullMessage, len + 2); @@ -97,36 +102,36 @@ int AES_decrypt(uint8_t *encr_message, uint64_t length, char *message, uint64_t return -3; } - if (!encr_message) { + if (!exportable) { LOG_ERROR("Null exportable in AES_encrypt"); return -4; } - if (length < SGX_AESGCM_MAC_SIZE + SGX_AESGCM_IV_SIZE) { - LOG_ERROR("length < SGX_AESGCM_MAC_SIZE - SGX_AESGCM_IV_SIZE"); - return -1; - } + if (length < SGX_AESGCM_MAC_SIZE + SGX_AESGCM_IV_SIZE) { + LOG_ERROR("length < SGX_AESGCM_MAC_SIZE - SGX_AESGCM_IV_SIZE"); + return -5; + } - uint64_t len = length - SGX_AESGCM_MAC_SIZE - SGX_AESGCM_IV_SIZE; + uint64_t len = length - SGX_AESGCM_MAC_SIZE - SGX_AESGCM_IV_SIZE; - if (msgLen < len) { + if (msgLen < len) { LOG_ERROR("Output buffer not large enough"); - return -2; - } - - sgx_status_t status = sgx_rijndael128GCM_decrypt(&(AES_key[512]), - encr_message + SGX_AESGCM_MAC_SIZE + SGX_AESGCM_IV_SIZE, len, - (unsigned char*) message, - encr_message + SGX_AESGCM_MAC_SIZE, SGX_AESGCM_IV_SIZE, - NULL, 0, - (sgx_aes_gcm_128bit_tag_t *)encr_message); - - *type = message[0]; - *exportable = message[1]; - for (int i = 2; i < strlen(message) + 1; i++) { - message[i - 2 ] = message[i]; - } - - return status; + return -6; + } + + sgx_status_t status = sgx_rijndael128GCM_decrypt(&(AES_key[512]), + encr_message + SGX_AESGCM_MAC_SIZE + SGX_AESGCM_IV_SIZE, len, + (unsigned char*) message, + encr_message + SGX_AESGCM_MAC_SIZE, SGX_AESGCM_IV_SIZE, + NULL, 0, + (sgx_aes_gcm_128bit_tag_t *)encr_message); + + *type = message[0]; + *exportable = message[1]; + for (int i = 2; i < strlen(message) + 1; i++) { + message[i - 2 ] = message[i]; + } + + return status; } diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 88423b45..9478dfe7 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -597,7 +597,6 @@ void trustedEcdsaSign(int *errStatus, char *errString, uint8_t *encryptedPrivate LOG_DEBUG("SGX call completed"); } - void trustedDecryptKey(int *errStatus, char *errString, uint8_t *encryptedPrivateKey, uint64_t enc_len, char *key) { @@ -606,24 +605,14 @@ void trustedDecryptKey(int *errStatus, char *errString, uint8_t *encryptedPrivat CHECK_STATE(encryptedPrivateKey); CHECK_STATE(key); + CHECK_STATE( enc_len == strnlen( encryptedPrivateKey, 1024 ) ); *errStatus = -9; uint8_t type = 0; uint8_t exportable = 0; - int status = AES_decrypt(encryptedPrivateKey, enc_len, key, 3072, - &type, &exportable); - - if (exportable != EXPORTABLE) { - while (*key != '\0') { - *key++ = '0'; - } - *errStatus = -11; - snprintf(errString, BUF_LEN, "Key is not exportable"); - LOG_ERROR(errString); - goto clean; - } + int status = AES_decrypt(encryptedPrivateKey, enc_len, key, 1024, &type, &exportable); if (status != 0) { *errStatus = status; @@ -632,22 +621,30 @@ void trustedDecryptKey(int *errStatus, char *errString, uint8_t *encryptedPrivat goto clean; } - *errStatus = -10; - - uint64_t keyLen = strnlen(key, MAX_KEY_LENGTH); + size_t keyLen = strnlen(key, MAX_KEY_LENGTH); if (keyLen == MAX_KEY_LENGTH) { + *errStatus = -10; snprintf(errString, BUF_LEN, "Key is not null terminated"); LOG_ERROR(errString); goto clean; } + if (exportable != EXPORTABLE) { + while (*key != '\0') { + *key++ = '0'; + } + *errStatus = -11; + snprintf(errString, BUF_LEN, "Key is not exportable"); + LOG_ERROR(errString); + goto clean; + } + SET_SUCCESS clean: ; } - void trustedEncryptKey(int *errStatus, char *errString, const char *key, uint8_t *encryptedPrivateKey, uint64_t *enc_len) { LOG_INFO(__FUNCTION__); From 73e2a031c49b2bba46acefe20bc399c0c84d4d2c Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:09:35 +0300 Subject: [PATCH 084/100] SKALE-4586 Added Thread Pool --- third_party/readerwriterqueue.h | 1247 ++++++++++++++++--------------- zmq_src/WorkerThreadPool.cpp | 2 +- zmq_src/ZMQServer.cpp | 77 +- zmq_src/ZMQServer.h | 8 +- 4 files changed, 696 insertions(+), 638 deletions(-) diff --git a/third_party/readerwriterqueue.h b/third_party/readerwriterqueue.h index 8fb1aa66..8b26556c 100644 --- a/third_party/readerwriterqueue.h +++ b/third_party/readerwriterqueue.h @@ -12,9 +12,8 @@ #include #include #include -#include // For malloc/free/abort & size_t +#include // For malloc/free/abort & size_t #include - #if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012 #include #endif @@ -74,214 +73,219 @@ namespace moodycamel { template - class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue { - // Design: Based on a queue-of-queues. The low-level queues are just - // circular buffers with front and tail indices indicating where the - // next element to dequeue is and where the next element can be enqueued, - // respectively. Each low-level queue is called a "block". Each block - // wastes exactly one element's worth of space to keep the design simple - // (if front == tail then the queue is empty, and can't be full). - // The high-level queue is a circular linked list of blocks; again there - // is a front and tail, but this time they are pointers to the blocks. - // The front block is where the next element to be dequeued is, provided - // the block is not empty. The back block is where elements are to be - // enqueued, provided the block is not full. - // The producer thread owns all the tail indices/pointers. The consumer - // thread owns all the front indices/pointers. Both threads read each - // other's variables, but only the owning thread updates them. E.g. After - // the consumer reads the producer's tail, the tail may change before the - // consumer is done dequeuing an object, but the consumer knows the tail - // will never go backwards, only forwards. - // If there is no room to enqueue an object, an additional block (of - // equal size to the last block) is added. Blocks are never removed. + class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue +{ + // Design: Based on a queue-of-queues. The low-level queues are just + // circular buffers with front and tail indices indicating where the + // next element to dequeue is and where the next element can be enqueued, + // respectively. Each low-level queue is called a "block". Each block + // wastes exactly one element's worth of space to keep the design simple + // (if front == tail then the queue is empty, and can't be full). + // The high-level queue is a circular linked list of blocks; again there + // is a front and tail, but this time they are pointers to the blocks. + // The front block is where the next element to be dequeued is, provided + // the block is not empty. The back block is where elements are to be + // enqueued, provided the block is not full. + // The producer thread owns all the tail indices/pointers. The consumer + // thread owns all the front indices/pointers. Both threads read each + // other's variables, but only the owning thread updates them. E.g. After + // the consumer reads the producer's tail, the tail may change before the + // consumer is done dequeuing an object, but the consumer knows the tail + // will never go backwards, only forwards. + // If there is no room to enqueue an object, an additional block (of + // equal size to the last block) is added. Blocks are never removed. public: - typedef T value_type; + typedef T value_type; - // Constructs a queue that can hold at least `size` elements without further - // allocations. If more than MAX_BLOCK_SIZE elements are requested, - // then several blocks of MAX_BLOCK_SIZE each are reserved (including - // at least one extra buffer block). - AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15) + // Constructs a queue that can hold at least `size` elements without further + // allocations. If more than MAX_BLOCK_SIZE elements are requested, + // then several blocks of MAX_BLOCK_SIZE each are reserved (including + // at least one extra buffer block). + AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15) #ifndef NDEBUG - : enqueuing(false), dequeuing(false) + : enqueuing(false) + ,dequeuing(false) #endif - { - assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2"); - assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); - - Block *firstBlock = nullptr; - - largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block - if (largestBlockSize > MAX_BLOCK_SIZE * 2) { - // We need a spare block in case the producer is writing to a different block the consumer is reading from, and - // wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity - // between front == tail meaning "empty" and "full". - // So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the - // number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying): - size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); - largestBlockSize = MAX_BLOCK_SIZE; - Block *lastBlock = nullptr; - for (size_t i = 0; i != initialBlockCount; ++i) { - auto block = make_block(largestBlockSize); - if (block == nullptr) { +{ + assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2"); + assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2"); + + Block* firstBlock = nullptr; + + largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block + if (largestBlockSize > MAX_BLOCK_SIZE * 2) { + // We need a spare block in case the producer is writing to a different block the consumer is reading from, and + // wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity + // between front == tail meaning "empty" and "full". + // So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the + // number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying): + size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1); + largestBlockSize = MAX_BLOCK_SIZE; + Block* lastBlock = nullptr; + for (size_t i = 0; i != initialBlockCount; ++i) { + auto block = make_block(largestBlockSize); + if (block == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED - throw std::bad_alloc(); + throw std::bad_alloc(); #else - abort(); + abort(); #endif - } - if (firstBlock == nullptr) { - firstBlock = block; - } else { - lastBlock->next = block; - } - lastBlock = block; - block->next = firstBlock; - } - } else { - firstBlock = make_block(largestBlockSize); - if (firstBlock == nullptr) { +} +if (firstBlock == nullptr) { +firstBlock = block; +} +else { +lastBlock->next = block; +} +lastBlock = block; +block->next = firstBlock; +} +} +else { +firstBlock = make_block(largestBlockSize); +if (firstBlock == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED - throw std::bad_alloc(); +throw std::bad_alloc(); #else - abort(); +abort(); #endif - } - firstBlock->next = firstBlock; - } - frontBlock = firstBlock; - tailBlock = firstBlock; +} +firstBlock->next = firstBlock; +} +frontBlock = firstBlock; +tailBlock = firstBlock; // Make sure the reader/writer threads will have the initialized memory setup above: - fence(memory_order_sync); - } +fence(memory_order_sync); +} // Note: The queue should not be accessed concurrently while it's // being moved. It's up to the user to synchronize this. - AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue &&other) - : frontBlock(other.frontBlock.load()), - tailBlock(other.tailBlock.load()), - largestBlockSize(other.largestBlockSize) +AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other) +: frontBlock(other.frontBlock.load()), +tailBlock(other.tailBlock.load()), +largestBlockSize(other.largestBlockSize) #ifndef NDEBUG - , enqueuing(false), dequeuing(false) +,enqueuing(false) +,dequeuing(false) #endif - { - other.largestBlockSize = 32; - Block *b = other.make_block(other.largestBlockSize); - if (b == nullptr) { +{ +other.largestBlockSize = 32; +Block* b = other.make_block(other.largestBlockSize); +if (b == nullptr) { #ifdef MOODYCAMEL_EXCEPTIONS_ENABLED - throw std::bad_alloc(); +throw std::bad_alloc(); #else - abort(); +abort(); #endif - } - b->next = b; - other.frontBlock = b; - other.tailBlock = b; - } +} +b->next = b; +other.frontBlock = b; +other.tailBlock = b; +} // Note: The queue should not be accessed concurrently while it's // being moved. It's up to the user to synchronize this. - ReaderWriterQueue &operator=(ReaderWriterQueue &&other) AE_NO_TSAN - { - Block *b = frontBlock.load(); - frontBlock = other.frontBlock.load(); - other.frontBlock = b; - b = tailBlock.load(); - tailBlock = other.tailBlock.load(); - other.tailBlock = b; - std::swap(largestBlockSize, other.largestBlockSize); - return *this; - } +ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN +{ +Block* b = frontBlock.load(); +frontBlock = other.frontBlock.load(); +other.frontBlock = b; +b = tailBlock.load(); +tailBlock = other.tailBlock.load(); +other.tailBlock = b; +std::swap(largestBlockSize, other.largestBlockSize); +return *this; +} // Note: The queue should not be accessed concurrently while it's // being deleted. It's up to the user to synchronize this. - AE_NO_TSAN ~ReaderWriterQueue() { - // Make sure we get the latest version of all variables from other CPUs: - fence(memory_order_sync); - - // Destroy any remaining objects in queue and free memory - Block *frontBlock_ = frontBlock; - Block *block = frontBlock_; - do { - Block *nextBlock = block->next; - size_t blockFront = block->front; - size_t blockTail = block->tail; - - for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { - auto element = reinterpret_cast(block->data + i * sizeof(T)); - element->~T(); - (void) element; - } - - auto rawBlock = block->rawThis; - block->~Block(); - std::free(rawBlock); - block = nextBlock; - } while (block != frontBlock_); +AE_NO_TSAN ~ReaderWriterQueue() +{ + // Make sure we get the latest version of all variables from other CPUs: + fence(memory_order_sync); + + // Destroy any remaining objects in queue and free memory + Block* frontBlock_ = frontBlock; + Block* block = frontBlock_; + do { + Block* nextBlock = block->next; + size_t blockFront = block->front; + size_t blockTail = block->tail; + + for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) { + auto element = reinterpret_cast(block->data + i * sizeof(T)); + element->~T(); + (void)element; } + auto rawBlock = block->rawThis; + block->~Block(); + std::free(rawBlock); + block = nextBlock; + } while (block != frontBlock_); +} + // Enqueues a copy of element if there is room in the queue. // Returns true if the element was enqueued, false otherwise. // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T const &element) AE_NO_TSAN - { - return inner_enqueue(element); - } +AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN +{ +return inner_enqueue(element); +} // Enqueues a moved copy of element if there is room in the queue. // Returns true if the element was enqueued, false otherwise. // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T &&element) AE_NO_TSAN - { - return inner_enqueue(std::forward(element)); - } +AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN +{ +return inner_enqueue(std::forward(element)); +} #if MOODYCAMEL_HAS_EMPLACE - // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool try_emplace(Args &&... args) AE_NO_TSAN { - return inner_enqueue(std::forward(args)...); - } - +template +AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN +{ +return inner_enqueue(std::forward(args)...); +} #endif // Enqueues a copy of element on the queue. // Allocates an additional block of memory if needed. // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T const &element) AE_NO_TSAN - { - return inner_enqueue(element); - } +AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN +{ +return inner_enqueue(element); +} // Enqueues a moved copy of element on the queue. // Allocates an additional block of memory if needed. // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T &&element) AE_NO_TSAN - { - return inner_enqueue(std::forward(element)); - } +AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN +{ +return inner_enqueue(std::forward(element)); +} #if MOODYCAMEL_HAS_EMPLACE - // Like enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool emplace(Args &&... args) AE_NO_TSAN { - return inner_enqueue(std::forward(args)...); - } - +template +AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN +{ +return inner_enqueue(std::forward(args)...); +} #endif // Attempts to dequeue an element; if the queue is empty, // returns false instead. If the queue has at least one element, // moves front to result using operator=, then returns true. - template - bool try_dequeue(U &result) AE_NO_TSAN { +template +bool try_dequeue(U& result) AE_NO_TSAN +{ #ifndef NDEBUG - ReentrantGuard guard(this->dequeuing); +ReentrantGuard guard(this->dequeuing); #endif // High-level pseudocode: @@ -301,73 +305,75 @@ namespace moodycamel { // then re-read the front block and check if it's not empty again, then check if the tail // block has advanced. - Block *frontBlock_ = frontBlock.load(); - size_t blockTail = frontBlock_->localTail; - size_t blockFront = frontBlock_->front.load(); +Block* frontBlock_ = frontBlock.load(); +size_t blockTail = frontBlock_->localTail; +size_t blockFront = frontBlock_->front.load(); - if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { - fence(memory_order_acquire); +if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { +fence(memory_order_acquire); - non_empty_front_block: +non_empty_front_block: // Front block not empty, dequeue from here - auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); - result = std::move(*element); - element->~T(); +auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); +result = std::move(*element); +element->~T(); - blockFront = (blockFront + 1) & frontBlock_->sizeMask; +blockFront = (blockFront + 1) & frontBlock_->sizeMask; - fence(memory_order_release); - frontBlock_->front = blockFront; - } else if (frontBlock_ != tailBlock.load()) { - fence(memory_order_acquire); +fence(memory_order_release); +frontBlock_->front = blockFront; +} +else if (frontBlock_ != tailBlock.load()) { +fence(memory_order_acquire); - frontBlock_ = frontBlock.load(); - blockTail = frontBlock_->localTail = frontBlock_->tail.load(); - blockFront = frontBlock_->front.load(); - fence(memory_order_acquire); +frontBlock_ = frontBlock.load(); +blockTail = frontBlock_->localTail = frontBlock_->tail.load(); +blockFront = frontBlock_->front.load(); +fence(memory_order_acquire); - if (blockFront != blockTail) { +if (blockFront != blockTail) { // Oh look, the front block isn't empty after all - goto non_empty_front_block; - } +goto non_empty_front_block; +} // Front block is empty but there's another block ahead, advance to it - Block *nextBlock = frontBlock_->next; +Block* nextBlock = frontBlock_->next; // Don't need an acquire fence here since next can only ever be set on the tailBlock, // and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which // ensures next is up-to-date on this CPU in case we recently were at tailBlock. - size_t nextBlockFront = nextBlock->front.load(); - size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); - fence(memory_order_acquire); +size_t nextBlockFront = nextBlock->front.load(); +size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); +fence(memory_order_acquire); // Since the tailBlock is only ever advanced after being written to, // we know there's for sure an element to dequeue on it - assert(nextBlockFront != nextBlockTail); - AE_UNUSED(nextBlockTail); +assert(nextBlockFront != nextBlockTail); +AE_UNUSED(nextBlockTail); // We're done with this block, let the producer use it if it needs - fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue - frontBlock = frontBlock_ = nextBlock; +fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue +frontBlock = frontBlock_ = nextBlock; - compiler_fence(memory_order_release); // Not strictly needed +compiler_fence(memory_order_release); // Not strictly needed - auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); +auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); - result = std::move(*element); - element->~T(); +result = std::move(*element); +element->~T(); - nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; +nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; - fence(memory_order_release); - frontBlock_->front = nextBlockFront; - } else { +fence(memory_order_release); +frontBlock_->front = nextBlockFront; +} +else { // No elements in current block and no other block to advance to - return false; - } +return false; +} - return true; - } +return true; +} // Returns a pointer to the front element in the queue (the one that @@ -375,126 +381,129 @@ namespace moodycamel { // queue appears empty at the time the method is called, nullptr is // returned instead. // Must be called only from the consumer thread. - T *peek() const AE_NO_TSAN - { +T* peek() const AE_NO_TSAN +{ #ifndef NDEBUG - ReentrantGuard guard(this->dequeuing); +ReentrantGuard guard(this->dequeuing); #endif // See try_dequeue() for reasoning - Block *frontBlock_ = frontBlock.load(); - size_t blockTail = frontBlock_->localTail; - size_t blockFront = frontBlock_->front.load(); +Block* frontBlock_ = frontBlock.load(); +size_t blockTail = frontBlock_->localTail; +size_t blockFront = frontBlock_->front.load(); - if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { - fence(memory_order_acquire); - non_empty_front_block: - return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); - } else if (frontBlock_ != tailBlock.load()) { - fence(memory_order_acquire); - frontBlock_ = frontBlock.load(); - blockTail = frontBlock_->localTail = frontBlock_->tail.load(); - blockFront = frontBlock_->front.load(); - fence(memory_order_acquire); +if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { +fence(memory_order_acquire); +non_empty_front_block: +return reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); +} +else if (frontBlock_ != tailBlock.load()) { +fence(memory_order_acquire); +frontBlock_ = frontBlock.load(); +blockTail = frontBlock_->localTail = frontBlock_->tail.load(); +blockFront = frontBlock_->front.load(); +fence(memory_order_acquire); - if (blockFront != blockTail) { - goto non_empty_front_block; - } +if (blockFront != blockTail) { +goto non_empty_front_block; +} - Block *nextBlock = frontBlock_->next; +Block* nextBlock = frontBlock_->next; - size_t nextBlockFront = nextBlock->front.load(); - fence(memory_order_acquire); +size_t nextBlockFront = nextBlock->front.load(); +fence(memory_order_acquire); - assert(nextBlockFront != nextBlock->tail.load()); - return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); - } +assert(nextBlockFront != nextBlock->tail.load()); +return reinterpret_cast(nextBlock->data + nextBlockFront * sizeof(T)); +} - return nullptr; - } +return nullptr; +} // Removes the front element from the queue, if any, without returning it. // Returns true on success, or false if the queue appeared empty at the time // `pop` was called. - bool pop() AE_NO_TSAN - { +bool pop() AE_NO_TSAN +{ #ifndef NDEBUG - ReentrantGuard guard(this->dequeuing); +ReentrantGuard guard(this->dequeuing); #endif // See try_dequeue() for reasoning - Block *frontBlock_ = frontBlock.load(); - size_t blockTail = frontBlock_->localTail; - size_t blockFront = frontBlock_->front.load(); +Block* frontBlock_ = frontBlock.load(); +size_t blockTail = frontBlock_->localTail; +size_t blockFront = frontBlock_->front.load(); - if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { - fence(memory_order_acquire); +if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) { +fence(memory_order_acquire); - non_empty_front_block: - auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); - element->~T(); +non_empty_front_block: +auto element = reinterpret_cast(frontBlock_->data + blockFront * sizeof(T)); +element->~T(); - blockFront = (blockFront + 1) & frontBlock_->sizeMask; +blockFront = (blockFront + 1) & frontBlock_->sizeMask; - fence(memory_order_release); - frontBlock_->front = blockFront; - } else if (frontBlock_ != tailBlock.load()) { - fence(memory_order_acquire); - frontBlock_ = frontBlock.load(); - blockTail = frontBlock_->localTail = frontBlock_->tail.load(); - blockFront = frontBlock_->front.load(); - fence(memory_order_acquire); +fence(memory_order_release); +frontBlock_->front = blockFront; +} +else if (frontBlock_ != tailBlock.load()) { +fence(memory_order_acquire); +frontBlock_ = frontBlock.load(); +blockTail = frontBlock_->localTail = frontBlock_->tail.load(); +blockFront = frontBlock_->front.load(); +fence(memory_order_acquire); - if (blockFront != blockTail) { - goto non_empty_front_block; - } +if (blockFront != blockTail) { +goto non_empty_front_block; +} // Front block is empty but there's another block ahead, advance to it - Block *nextBlock = frontBlock_->next; +Block* nextBlock = frontBlock_->next; - size_t nextBlockFront = nextBlock->front.load(); - size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); - fence(memory_order_acquire); +size_t nextBlockFront = nextBlock->front.load(); +size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load(); +fence(memory_order_acquire); - assert(nextBlockFront != nextBlockTail); - AE_UNUSED(nextBlockTail); +assert(nextBlockFront != nextBlockTail); +AE_UNUSED(nextBlockTail); - fence(memory_order_release); - frontBlock = frontBlock_ = nextBlock; +fence(memory_order_release); +frontBlock = frontBlock_ = nextBlock; - compiler_fence(memory_order_release); +compiler_fence(memory_order_release); - auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); - element->~T(); +auto element = reinterpret_cast(frontBlock_->data + nextBlockFront * sizeof(T)); +element->~T(); - nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; +nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask; - fence(memory_order_release); - frontBlock_->front = nextBlockFront; - } else { +fence(memory_order_release); +frontBlock_->front = nextBlockFront; +} +else { // No elements in current block and no other block to advance to - return false; - } +return false; +} - return true; - } +return true; +} // Returns the approximate number of items currently in the queue. // Safe to call from both the producer and consumer threads. - inline size_t size_approx() const AE_NO_TSAN - { - size_t result = 0; - Block *frontBlock_ = frontBlock.load(); - Block *block = frontBlock_; - do { - fence(memory_order_acquire); - size_t blockFront = block->front.load(); - size_t blockTail = block->tail.load(); - result += (blockTail - blockFront) & block->sizeMask; - block = block->next.load(); - } while (block != frontBlock_); - return result; - } +inline size_t size_approx() const AE_NO_TSAN +{ +size_t result = 0; +Block* frontBlock_ = frontBlock.load(); +Block* block = frontBlock_; +do { +fence(memory_order_acquire); +size_t blockFront = block->front.load(); +size_t blockTail = block->tail.load(); +result += (blockTail - blockFront) & block->sizeMask; +block = block->next.load(); +} while (block != frontBlock_); +return result; +} // Returns the total number of items that could be enqueued without incurring // an allocation when this queue is empty. @@ -505,35 +514,32 @@ namespace moodycamel { // the block the consumer is removing from until it's completely empty, except in // the case where the producer was writing to the same block the consumer was // reading from the whole time. - inline size_t max_capacity() const { - size_t result = 0; - Block *frontBlock_ = frontBlock.load(); - Block *block = frontBlock_; - do { - fence(memory_order_acquire); - result += block->sizeMask; - block = block->next.load(); - } while (block != frontBlock_); - return result; - } - - - private: - enum AllocationMode { - CanAlloc, CannotAlloc - }; +inline size_t max_capacity() const { + size_t result = 0; + Block* frontBlock_ = frontBlock.load(); + Block* block = frontBlock_; + do { + fence(memory_order_acquire); + result += block->sizeMask; + block = block->next.load(); + } while (block != frontBlock_); + return result; +} + + +private: +enum AllocationMode { CanAlloc, CannotAlloc }; #if MOODYCAMEL_HAS_EMPLACE - - template - bool inner_enqueue(Args &&... args) AE_NO_TSAN +template +bool inner_enqueue(Args&&... args) AE_NO_TSAN #else - template - bool inner_enqueue(U&& element) AE_NO_TSAN +template + bool inner_enqueue(U&& element) AE_NO_TSAN #endif - { +{ #ifndef NDEBUG - ReentrantGuard guard(this->enqueuing); +ReentrantGuard guard(this->enqueuing); #endif // High-level pseudocode (assuming we're allowed to alloc a new block): @@ -543,75 +549,77 @@ namespace moodycamel { // Else create a new block and enqueue there // Advance tail to the block we just enqueued to - Block *tailBlock_ = tailBlock.load(); - size_t blockFront = tailBlock_->localFront; - size_t blockTail = tailBlock_->tail.load(); +Block* tailBlock_ = tailBlock.load(); +size_t blockFront = tailBlock_->localFront; +size_t blockTail = tailBlock_->tail.load(); - size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; - if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { - fence(memory_order_acquire); +size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask; +if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) { +fence(memory_order_acquire); // This block has room for at least one more element - char *location = tailBlock_->data + blockTail * sizeof(T); +char* location = tailBlock_->data + blockTail * sizeof(T); #if MOODYCAMEL_HAS_EMPLACE - new(location) T(std::forward(args)...); +new (location) T(std::forward(args)...); #else - new (location) T(std::forward(element)); +new (location) T(std::forward(element)); #endif - fence(memory_order_release); - tailBlock_->tail = nextBlockTail; - } else { - fence(memory_order_acquire); - if (tailBlock_->next.load() != frontBlock) { +fence(memory_order_release); +tailBlock_->tail = nextBlockTail; +} +else { +fence(memory_order_acquire); +if (tailBlock_->next.load() != frontBlock) { // Note that the reason we can't advance to the frontBlock and start adding new entries there // is because if we did, then dequeue would stay in that block, eventually reading the new values, // instead of advancing to the next full block (whose values were enqueued first and so should be // consumed first). - fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock +fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock // tailBlock is full, but there's a free block ahead, use it - Block *tailBlockNext = tailBlock_->next.load(); - size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); - nextBlockTail = tailBlockNext->tail.load(); - fence(memory_order_acquire); +Block* tailBlockNext = tailBlock_->next.load(); +size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load(); +nextBlockTail = tailBlockNext->tail.load(); +fence(memory_order_acquire); // This block must be empty since it's not the head block and we // go through the blocks in a circle - assert(nextBlockFront == nextBlockTail); - tailBlockNext->localFront = nextBlockFront; +assert(nextBlockFront == nextBlockTail); +tailBlockNext->localFront = nextBlockFront; - char *location = tailBlockNext->data + nextBlockTail * sizeof(T); +char* location = tailBlockNext->data + nextBlockTail * sizeof(T); #if MOODYCAMEL_HAS_EMPLACE - new(location) T(std::forward(args)...); +new (location) T(std::forward(args)...); #else - new (location) T(std::forward(element)); +new (location) T(std::forward(element)); #endif - tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; +tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask; - fence(memory_order_release); - tailBlock = tailBlockNext; - } else if (canAlloc == CanAlloc) { +fence(memory_order_release); +tailBlock = tailBlockNext; +} +else if (canAlloc == CanAlloc) { // tailBlock is full and there's no free block ahead; create a new block - auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; - auto newBlock = make_block(newBlockSize); - if (newBlock == nullptr) { +auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2; +auto newBlock = make_block(newBlockSize); +if (newBlock == nullptr) { // Could not allocate a block! - return false; - } - largestBlockSize = newBlockSize; +return false; +} +largestBlockSize = newBlockSize; #if MOODYCAMEL_HAS_EMPLACE - new(newBlock->data) T(std::forward(args)...); +new (newBlock->data) T(std::forward(args)...); #else - new (newBlock->data) T(std::forward(element)); +new (newBlock->data) T(std::forward(element)); #endif - assert(newBlock->front == 0); - newBlock->tail = newBlock->localTail = 1; +assert(newBlock->front == 0); +newBlock->tail = newBlock->localTail = 1; - newBlock->next = tailBlock_->next.load(); - tailBlock_->next = newBlock; +newBlock->next = tailBlock_->next.load(); +tailBlock_->next = newBlock; // Might be possible for the dequeue thread to see the new tailBlock->next // *without* seeing the new tailBlock value, but this is OK since it can't @@ -619,345 +627,350 @@ namespace moodycamel { // case where it could try to read the next is if it's already at the tailBlock, // and it won't advance past tailBlock in any circumstance). - fence(memory_order_release); - tailBlock = newBlock; - } else if (canAlloc == CannotAlloc) { +fence(memory_order_release); +tailBlock = newBlock; +} +else if (canAlloc == CannotAlloc) { // Would have had to allocate a new block to enqueue, but not allowed - return false; - } else { - assert(false && "Should be unreachable code"); - return false; - } - } +return false; +} +else { +assert(false && "Should be unreachable code"); +return false; +} +} - return true; - } +return true; +} // Disable copying - ReaderWriterQueue(ReaderWriterQueue const &) {} +ReaderWriterQueue(ReaderWriterQueue const&) { } // Disable assignment - ReaderWriterQueue &operator=(ReaderWriterQueue const &) {} +ReaderWriterQueue& operator=(ReaderWriterQueue const&) { } - AE_FORCEINLINE static size_t ceilToPow2(size_t x) { +AE_FORCEINLINE static size_t ceilToPow2(size_t x) +{ // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - for (size_t i = 1; i < sizeof(size_t); i <<= 1) { - x |= x >> (i << 3); - } - ++x; - return x; - } - - template - static AE_FORCEINLINE char *align_for(char *ptr) AE_NO_TSAN { - const std::size_t alignment = std::alignment_of::value; - return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; - } - - private: +--x; +x |= x >> 1; +x |= x >> 2; +x |= x >> 4; +for (size_t i = 1; i < sizeof(size_t); i <<= 1) { +x |= x >> (i << 3); +} +++x; +return x; +} + +template +static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN +{ +const std::size_t alignment = std::alignment_of::value; +return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; +} +private: #ifndef NDEBUG - - struct ReentrantGuard { - AE_NO_TSAN ReentrantGuard(weak_atomic &_inSection) - : inSection(_inSection) { - assert(!inSection && - "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)"); - inSection = true; - } - - AE_NO_TSAN ~ReentrantGuard() { inSection = false; } - - private: - ReentrantGuard &operator=(ReentrantGuard const &); - - private: - weak_atomic &inSection; - }; - +struct ReentrantGuard +{ + AE_NO_TSAN ReentrantGuard(weak_atomic& _inSection) + : inSection(_inSection) + { + assert(!inSection && "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)"); + inSection = true; + } + + AE_NO_TSAN ~ReentrantGuard() { inSection = false; } + +private: + ReentrantGuard& operator=(ReentrantGuard const&); + +private: + weak_atomic& inSection; +}; #endif - struct Block { - // Avoid false-sharing by putting highly contended variables on their own cache lines - weak_atomic front; // (Atomic) Elements are read from here - size_t localTail; // An uncontended shadow copy of tail, owned by the consumer +struct Block +{ + // Avoid false-sharing by putting highly contended variables on their own cache lines + weak_atomic front; // (Atomic) Elements are read from here + size_t localTail; // An uncontended shadow copy of tail, owned by the consumer - char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; - weak_atomic tail; // (Atomic) Elements are enqueued here - size_t localFront; + char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; + weak_atomic tail; // (Atomic) Elements are enqueued here + size_t localFront; - char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is) - weak_atomic next; // (Atomic) + char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is) + weak_atomic next; // (Atomic) - char *data; // Contents (on heap) are aligned to T's alignment + char* data; // Contents (on heap) are aligned to T's alignment - const size_t sizeMask; + const size_t sizeMask; - // size must be a power of two (and greater than 0) - AE_NO_TSAN Block(size_t const &_size, char *_rawThis, char *_data) - : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), - sizeMask(_size - 1), rawThis(_rawThis) { - } + // size must be a power of two (and greater than 0) + AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data) + : front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis) + { + } - private: - // C4512 - Assignment operator could not be generated - Block &operator=(Block const &); +private: + // C4512 - Assignment operator could not be generated + Block& operator=(Block const&); - public: - char *rawThis; - }; +public: + char* rawThis; +}; - static Block *make_block(size_t capacity) AE_NO_TSAN - { +static Block* make_block(size_t capacity) AE_NO_TSAN +{ // Allocate enough memory for the block itself, as well as all the elements it will contain - auto size = sizeof(Block) + std::alignment_of::value - 1; - size += sizeof(T) * capacity + std::alignment_of::value - 1; - auto newBlockRaw = static_cast(std::malloc(size)); - if (newBlockRaw == nullptr) { - return nullptr; - } - - auto newBlockAligned = align_for(newBlockRaw); - auto newBlockData = align_for(newBlockAligned + sizeof(Block)); - return new(newBlockAligned) Block(capacity, newBlockRaw, newBlockData); - } +auto size = sizeof(Block) + std::alignment_of::value - 1; +size += sizeof(T) * capacity + std::alignment_of::value - 1; +auto newBlockRaw = static_cast(std::malloc(size)); +if (newBlockRaw == nullptr) { +return nullptr; +} + +auto newBlockAligned = align_for(newBlockRaw); +auto newBlockData = align_for(newBlockAligned + sizeof(Block)); +return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData); +} - private: - weak_atomic frontBlock; // (Atomic) Elements are dequeued from this block +private: +weak_atomic frontBlock; // (Atomic) Elements are dequeued from this block - char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic)]; - weak_atomic tailBlock; // (Atomic) Elements are enqueued to this block +char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic)]; +weak_atomic tailBlock; // (Atomic) Elements are enqueued to this block - size_t largestBlockSize; +size_t largestBlockSize; #ifndef NDEBUG - weak_atomic enqueuing; - mutable weak_atomic dequeuing; +weak_atomic enqueuing; +mutable weak_atomic dequeuing; #endif - }; +}; // Like ReaderWriterQueue, but also providees blocking operations - template - class BlockingReaderWriterQueue { - private: - typedef ::moodycamel::ReaderWriterQueue ReaderWriterQueue; - - public: - explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN - : inner(size), sema(new spsc_sema::LightweightSemaphore()) {} - - BlockingReaderWriterQueue(BlockingReaderWriterQueue &&other) AE_NO_TSAN - : inner(std::move(other.inner)), sema(std::move(other.sema)) {} - - BlockingReaderWriterQueue &operator=(BlockingReaderWriterQueue &&other) AE_NO_TSAN - { - std::swap(sema, other.sema); - std::swap(inner, other.inner); - return *this; - } - - - // Enqueues a copy of element if there is room in the queue. - // Returns true if the element was enqueued, false otherwise. - // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T const &element) AE_NO_TSAN - { - if (inner.try_enqueue(element)) { - sema->signal(); - return true; - } - return false; +template +class BlockingReaderWriterQueue +{ +private: + typedef ::moodycamel::ReaderWriterQueue ReaderWriterQueue; + +public: + explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN + : inner(size), sema(new spsc_sema::LightweightSemaphore()) + { } + + BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN + : inner(std::move(other.inner)), sema(std::move(other.sema)) + { } + + BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN + { + std::swap(sema, other.sema); + std::swap(inner, other.inner); + return *this; + } + + + // Enqueues a copy of element if there is room in the queue. + // Returns true if the element was enqueued, false otherwise. + // Does not allocate memory. + AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN + { + if (inner.try_enqueue(element)) { + sema->signal(); + return true; } + return false; + } - // Enqueues a moved copy of element if there is room in the queue. - // Returns true if the element was enqueued, false otherwise. - // Does not allocate memory. - AE_FORCEINLINE bool try_enqueue(T &&element) AE_NO_TSAN - { - if (inner.try_enqueue(std::forward(element))) { - sema->signal(); - return true; - } - return false; + // Enqueues a moved copy of element if there is room in the queue. + // Returns true if the element was enqueued, false otherwise. + // Does not allocate memory. + AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN + { + if (inner.try_enqueue(std::forward(element))) { + sema->signal(); + return true; } + return false; + } #if MOODYCAMEL_HAS_EMPLACE - - // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool try_emplace(Args &&... args) AE_NO_TSAN { - if (inner.try_emplace(std::forward(args)...)) { - sema->signal(); - return true; - } - return false; + // Like try_enqueue() but with emplace semantics (i.e. construct-in-place). + template + AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN + { + if (inner.try_emplace(std::forward(args)...)) { + sema->signal(); + return true; } - + return false; + } #endif - // Enqueues a copy of element on the queue. - // Allocates an additional block of memory if needed. - // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T const &element) AE_NO_TSAN - { - if (inner.enqueue(element)) { - sema->signal(); - return true; - } - return false; + // Enqueues a copy of element on the queue. + // Allocates an additional block of memory if needed. + // Only fails (returns false) if memory allocation fails. + AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN + { + if (inner.enqueue(element)) { + sema->signal(); + return true; } + return false; + } - // Enqueues a moved copy of element on the queue. - // Allocates an additional block of memory if needed. - // Only fails (returns false) if memory allocation fails. - AE_FORCEINLINE bool enqueue(T &&element) AE_NO_TSAN - { - if (inner.enqueue(std::forward(element))) { - sema->signal(); - return true; - } - return false; + // Enqueues a moved copy of element on the queue. + // Allocates an additional block of memory if needed. + // Only fails (returns false) if memory allocation fails. + AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN + { + if (inner.enqueue(std::forward(element))) { + sema->signal(); + return true; } + return false; + } #if MOODYCAMEL_HAS_EMPLACE - - // Like enqueue() but with emplace semantics (i.e. construct-in-place). - template - AE_FORCEINLINE bool emplace(Args &&... args) AE_NO_TSAN { - if (inner.emplace(std::forward(args)...)) { - sema->signal(); - return true; - } - return false; + // Like enqueue() but with emplace semantics (i.e. construct-in-place). + template + AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN + { + if (inner.emplace(std::forward(args)...)) { + sema->signal(); + return true; } - + return false; + } #endif - // Attempts to dequeue an element; if the queue is empty, - // returns false instead. If the queue has at least one element, - // moves front to result using operator=, then returns true. - template - bool try_dequeue(U &result) AE_NO_TSAN { - if (sema->tryWait()) { - bool success = inner.try_dequeue(result); - assert(success); - AE_UNUSED(success); - return true; - } - return false; - } - - - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available, then dequeues it. - template - void wait_dequeue(U &result) AE_NO_TSAN { - while (!sema->wait()); + // Attempts to dequeue an element; if the queue is empty, + // returns false instead. If the queue has at least one element, + // moves front to result using operator=, then returns true. + template + bool try_dequeue(U& result) AE_NO_TSAN + { + if (sema->tryWait()) { bool success = inner.try_dequeue(result); - AE_UNUSED(result); - assert(success); - AE_UNUSED(success); - } - - - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available up to the specified timeout, - // then dequeues it and returns true, or returns false if the timeout - // expires before an element can be dequeued. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - template - bool wait_dequeue_timed(U &result, std::int64_t timeout_usecs) AE_NO_TSAN { - if (!sema->wait(timeout_usecs)) { - return false; - } - bool success = inner.try_dequeue(result); - AE_UNUSED(result); assert(success); AE_UNUSED(success); return true; } + return false; + } + + + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available, then dequeues it. + template + void wait_dequeue(U& result) AE_NO_TSAN + { + while (!sema->wait()); + bool success = inner.try_dequeue(result); + AE_UNUSED(result); + assert(success); + AE_UNUSED(success); + } + + + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available up to the specified timeout, + // then dequeues it and returns true, or returns false if the timeout + // expires before an element can be dequeued. + // Using a negative timeout indicates an indefinite timeout, + // and is thus functionally equivalent to calling wait_dequeue. + template + bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN + { + if (!sema->wait(timeout_usecs)) { + return false; + } + bool success = inner.try_dequeue(result); + AE_UNUSED(result); + assert(success); + AE_UNUSED(success); + return true; + } #if __cplusplus > 199711L || _MSC_VER >= 1700 - // Attempts to dequeue an element; if the queue is empty, - // waits until an element is available up to the specified timeout, - // then dequeues it and returns true, or returns false if the timeout - // expires before an element can be dequeued. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - template - inline bool wait_dequeue_timed(U& result, std::chrono::duration const& timeout) AE_NO_TSAN - { - return wait_dequeue_timed(result, std::chrono::duration_cast(timeout).count()); - } + // Attempts to dequeue an element; if the queue is empty, + // waits until an element is available up to the specified timeout, + // then dequeues it and returns true, or returns false if the timeout + // expires before an element can be dequeued. + // Using a negative timeout indicates an indefinite timeout, + // and is thus functionally equivalent to calling wait_dequeue. + template + inline bool wait_dequeue_timed(U& result, std::chrono::duration const& timeout) AE_NO_TSAN + { + return wait_dequeue_timed(result, std::chrono::duration_cast(timeout).count()); + } #endif - // Returns a pointer to the front element in the queue (the one that - // would be removed next by a call to `try_dequeue` or `pop`). If the - // queue appears empty at the time the method is called, nullptr is - // returned instead. - // Must be called only from the consumer thread. - AE_FORCEINLINE T *peek() const AE_NO_TSAN - { - return inner.peek(); - } - - // Removes the front element from the queue, if any, without returning it. - // Returns true on success, or false if the queue appeared empty at the time - // `pop` was called. - AE_FORCEINLINE bool pop() AE_NO_TSAN - { - if (sema->tryWait()) { - bool result = inner.pop(); - assert(result); - AE_UNUSED(result); - return true; - } - return false; - } - - // Returns the approximate number of items currently in the queue. - // Safe to call from both the producer and consumer threads. - AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN - { - return sema->availableApprox(); - } - - // Returns the total number of items that could be enqueued without incurring - // an allocation when this queue is empty. - // Safe to call from both the producer and consumer threads. - // - // NOTE: The actual capacity during usage may be different depending on the consumer. - // If the consumer is removing elements concurrently, the producer cannot add to - // the block the consumer is removing from until it's completely empty, except in - // the case where the producer was writing to the same block the consumer was - // reading from the whole time. - AE_FORCEINLINE size_t max_capacity() const { - return inner.max_capacity(); + // Returns a pointer to the front element in the queue (the one that + // would be removed next by a call to `try_dequeue` or `pop`). If the + // queue appears empty at the time the method is called, nullptr is + // returned instead. + // Must be called only from the consumer thread. + AE_FORCEINLINE T* peek() const AE_NO_TSAN + { + return inner.peek(); + } + + // Removes the front element from the queue, if any, without returning it. + // Returns true on success, or false if the queue appeared empty at the time + // `pop` was called. + AE_FORCEINLINE bool pop() AE_NO_TSAN + { + if (sema->tryWait()) { + bool result = inner.pop(); + assert(result); + AE_UNUSED(result); + return true; } - - private: - // Disable copying & assignment - BlockingReaderWriterQueue(BlockingReaderWriterQueue const &) {} - - BlockingReaderWriterQueue &operator=(BlockingReaderWriterQueue const &) {} - - private: - ReaderWriterQueue inner; - std::unique_ptr sema; - }; + return false; + } + + // Returns the approximate number of items currently in the queue. + // Safe to call from both the producer and consumer threads. + AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN + { + return sema->availableApprox(); + } + + // Returns the total number of items that could be enqueued without incurring + // an allocation when this queue is empty. + // Safe to call from both the producer and consumer threads. + // + // NOTE: The actual capacity during usage may be different depending on the consumer. + // If the consumer is removing elements concurrently, the producer cannot add to + // the block the consumer is removing from until it's completely empty, except in + // the case where the producer was writing to the same block the consumer was + // reading from the whole time. + AE_FORCEINLINE size_t max_capacity() const { + return inner.max_capacity(); + } + +private: + // Disable copying & assignment + BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) { } + BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) { } + +private: + ReaderWriterQueue inner; + std::unique_ptr sema; +}; } // end namespace moodycamel diff --git a/zmq_src/WorkerThreadPool.cpp b/zmq_src/WorkerThreadPool.cpp index 2d602d8d..c1bf2c13 100644 --- a/zmq_src/WorkerThreadPool.cpp +++ b/zmq_src/WorkerThreadPool.cpp @@ -81,7 +81,7 @@ void WorkerThreadPool::createThread(uint64_t _threadNumber) { spdlog::info("Starting ZMQ worker thread " + to_string(_threadNumber) ); this->threadpool.push_back( - make_shared< thread >( ZMQServer::workerThreadMessageProcessLoop, agent ) ); + make_shared< thread >( ZMQServer::workerThreadMessageProcessLoop, agent, _threadNumber ) ); spdlog::info("Started ZMQ worker thread " + to_string(_threadNumber) ); } diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 5b7673ec..9295787b 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -61,7 +61,7 @@ ZMQServer::ZMQServer(bool _checkSignature, bool _checkKeyOwnership, const string zmq_setsockopt(*socket, ZMQ_LINGER, &linger, sizeof(linger)); - threadPool = make_shared(1, this); + threadPool = make_shared(NUM_ZMQ_WORKER_THREADS, this); } @@ -93,7 +93,7 @@ void ZMQServer::run() { while (!isExitRequested) { try { zmqServer->doOneServerLoop(); - } catch (ExitRequestedException& e) { + } catch (ExitRequestedException &e) { spdlog::info("Exit requested. Exiting server loop"); break; } @@ -178,7 +178,6 @@ void ZMQServer::checkForExit() { } - PollResult ZMQServer::poll() { zmq_pollitem_t items[1]; items[0].socket = *socket; @@ -189,12 +188,19 @@ PollResult ZMQServer::poll() { do { checkForExit(); pollResult = zmq_poll(items, 1, 1); + + pair > element; + + // send all items in outgoing queue + while (outgoingQueue.try_dequeue(element)) { + sendToClient(element.first, element.second); + } } while (pollResult == 0); return GOT_INCOMING_MSG; } -pair> ZMQServer::receiveMessage() { +pair > ZMQServer::receiveMessage() { auto identity = make_shared(); @@ -228,7 +234,7 @@ pair> ZMQServer::receiveMessage() { return {result, identity}; } -void ZMQServer::sendToClient(Json::Value& _result, shared_ptr& _identity ) { +void ZMQServer::sendToClient(Json::Value &_result, shared_ptr &_identity) { string replyStr; try { Json::FastWriter fastWriter; @@ -265,7 +271,7 @@ void ZMQServer::doOneServerLoop() { Json::Value result; result["status"] = ZMQ_SERVER_ERROR; - shared_ptr identity = nullptr; + shared_ptr identity = nullptr; string msgStr; try { @@ -282,18 +288,18 @@ void ZMQServer::doOneServerLoop() { uint64_t index = 0; - if ((dynamic_pointer_cast(msg)!= nullptr) || - dynamic_pointer_cast(msg)) { + if ((dynamic_pointer_cast(msg) != nullptr) || + dynamic_pointer_cast(msg)) { index = NUM_ZMQ_WORKER_THREADS - 1; } else { index = 0; } - auto element = pair, shared_ptr>(msg, identity); + auto element = pair < shared_ptr < ZMQMessage >, shared_ptr> + (msg, identity); incomingQueue.at(index).enqueue(element); - result = msg->process(); } catch (ExitRequestedException) { throw; } catch (exception &e) { @@ -302,30 +308,69 @@ void ZMQServer::doOneServerLoop() { spdlog::error("Exception in zmq server :{}", e.what()); spdlog::error("ID:" + string((char *) identity->data(), identity->size())); spdlog::error("Client request :" + msgStr); + sendToClient(result, identity); } catch (...) { checkForExit(); spdlog::error("Error in zmq server "); result["errorMessage"] = "Error in zmq server "; spdlog::error("ID:" + string((char *) identity->data(), identity->size())); spdlog::error("Client request :" + msgStr); + sendToClient(result, identity); } - sendToClient(result, identity); + + } -void ZMQServer::workerThreadProcessNextMessage() { - usleep(1000000); - cerr << "WORKER LOOP" << endl; +void ZMQServer::workerThreadProcessNextMessage(uint64_t _threadNumber) { + + + Json::Value result; + result["status"] = ZMQ_SERVER_ERROR; + + shared_ptr identity = nullptr; + string msgStr; + + pair , shared_ptr> element; + + try { + + while (!incomingQueue.at(_threadNumber) + .wait_dequeue_timed(element, std::chrono::milliseconds(100))) { + + } + + result = element.first->process(); + + } catch (ExitRequestedException) { + throw; + } catch (exception &e) { + checkForExit(); + result["errorMessage"] = string(e.what()); + spdlog::error("Exception in zmq server :{}", e.what()); + spdlog::error("ID:" + string((char *) identity->data(), identity->size())); + spdlog::error("Client request :" + msgStr); + } catch (...) { + checkForExit(); + spdlog::error("Error in zmq server "); + result["errorMessage"] = "Error in zmq server "; + spdlog::error("ID:" + string((char *) identity->data(), identity->size())); + spdlog::error("Client request :" + msgStr); + } + + pair > fullResult(result, element.second); + + outgoingQueue.enqueue(fullResult); } -void ZMQServer::workerThreadMessageProcessLoop(ZMQServer *_agent) { +void ZMQServer::workerThreadMessageProcessLoop(ZMQServer *_agent, uint64_t _threadNumber) { CHECK_STATE(_agent); _agent->waitOnGlobalStartBarrier(); // do work forever until told to exit while (!isExitRequested) { try { - _agent->workerThreadProcessNextMessage(); + _agent->workerThreadProcessNextMessage(_threadNumber); } catch (ExitRequestedException &e) { break; } catch (Exception &e) { diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 7bcb42b4..f4b08901 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -50,9 +50,9 @@ class ZMQServer : public Agent{ string caCertFile; string caCert; - ReaderWriterQueue>> outgoingQueue; + BlockingReaderWriterQueue>> outgoingQueue; - vector, shared_ptr>>> incomingQueue; + vector, shared_ptr>>> incomingQueue; bool checkKeyOwnership = true; @@ -84,9 +84,9 @@ class ZMQServer : public Agent{ static void initZMQServer(bool _checkSignature, bool _checkKeyOwnership); static void exitZMQServer(); - static void workerThreadMessageProcessLoop(ZMQServer* agent ); + static void workerThreadMessageProcessLoop(ZMQServer* agent, uint64_t _threadNumber ); - void workerThreadProcessNextMessage(); + void workerThreadProcessNextMessage(uint64_t _threadNumber); void checkForExit(); From f2bd2ae86c4bac63ce61c9c3fcc5c68332863568 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:21:51 +0300 Subject: [PATCH 085/100] SKALE-4586 Added Thread Pool --- zmq_src/ZMQServer.cpp | 12 ++++++++++-- zmq_src/ZMQServer.h | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 9295787b..88691669 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "third_party/spdlog/spdlog.h" @@ -290,14 +291,21 @@ void ZMQServer::doOneServerLoop() { if ((dynamic_pointer_cast(msg) != nullptr) || dynamic_pointer_cast(msg)) { - index = NUM_ZMQ_WORKER_THREADS - 1; + + boost::hash string_hash; + + auto hash = string_hash(string((const char*) identity->data())); + + index = hash % (NUM_ZMQ_WORKER_THREADS - 1); } else { - index = 0; + index = NUM_ZMQ_WORKER_THREADS - 1; } auto element = pair < shared_ptr < ZMQMessage >, shared_ptr> (msg, identity); + + incomingQueue.at(index).enqueue(element); } catch (ExitRequestedException) { diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index f4b08901..25cd7691 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -40,7 +40,7 @@ using namespace moodycamel; typedef enum {GOT_INCOMING_MSG = 0, GOT_OUTFOING_MSG = 1} PollResult; -static const uint64_t NUM_ZMQ_WORKER_THREADS = 2; +static const uint64_t NUM_ZMQ_WORKER_THREADS = 16; class ZMQServer : public Agent{ From 70c496f42d69c01dee90d2e5832d5b3108f786c3 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:25:09 +0300 Subject: [PATCH 086/100] SKALE-4586 Added Thread Pool --- zmq_src/ZMQServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 88691669..4520cd14 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -345,8 +345,8 @@ void ZMQServer::workerThreadProcessNextMessage(uint64_t _threadNumber) { try { while (!incomingQueue.at(_threadNumber) - .wait_dequeue_timed(element, std::chrono::milliseconds(100))) { - + .wait_dequeue_timed(element, std::chrono::milliseconds(1000))) { + checkForExit(); } result = element.first->process(); From acbb854d3d0c7387fac8359b78b1fe5d67883f9b Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:28:38 +0300 Subject: [PATCH 087/100] SKALE-4586 Added concurrentqueue --- third_party/concurrentqueue.h | 3955 +++++++++++++++++++++++++++++++++ zmq_src/ZMQServer.h | 3 +- 2 files changed, 3957 insertions(+), 1 deletion(-) create mode 100644 third_party/concurrentqueue.h diff --git a/third_party/concurrentqueue.h b/third_party/concurrentqueue.h new file mode 100644 index 00000000..13724a51 --- /dev/null +++ b/third_party/concurrentqueue.h @@ -0,0 +1,3955 @@ +// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue. +// An overview, including benchmark results, is provided here: +// http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++ +// The full design is also described in excruciating detail at: +// http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue + +// Simplified BSD license: +// Copyright (c) 2013-2020, Cameron Desrochers. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// - Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Also dual-licensed under the Boost Software License (see LICENSE.md) + +#pragma once + +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and +// Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings +// upon assigning any computed values) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" + +#ifdef MCDBGQ_USE_RELACY +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" +#endif +#endif + +#if defined(_MSC_VER) && (!defined(_HAS_CXX17) || !_HAS_CXX17) +// VS2019 with /W4 warns about constant conditional expressions but unless /std=c++17 or higher +// does not support `if constexpr`, so we have no choice but to simply disable the warning +#pragma warning(push) +#pragma warning(disable: 4127) // conditional expression is constant +#endif + +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif + +#ifdef MCDBGQ_USE_RELACY +#include "relacy/relacy_std.hpp" +#include "relacy_shims.h" +// We only use malloc/free anyway, and the delete macro messes up `= delete` method declarations. +// We'll override the default trait malloc ourselves without a macro. +#undef new +#undef delete +#undef malloc +#undef free +#else + +#include // Requires C++11. Sorry VS2010. +#include + +#endif + +#include // for max_align_t +#include +#include +#include +#include +#include +#include +#include // for CHAR_BIT +#include +#include // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading + +// Platform-specific definitions of a numeric thread ID type and an invalid value +namespace moodycamel { + namespace details { + template + struct thread_id_converter { + typedef thread_id_t thread_id_numeric_size_t; + typedef thread_id_t thread_id_hash_t; + + static thread_id_hash_t prehash(thread_id_t const &x) { return x; } + }; + } +} +#if defined(MCDBGQ_USE_RELACY) +namespace moodycamel { namespace details { + typedef std::uint32_t thread_id_t; + static const thread_id_t invalid_thread_id = 0xFFFFFFFFU; + static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU; + static inline thread_id_t thread_id() { return rl::thread_index(); } +} } +#elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__) +// No sense pulling in windows.h in a header, we'll manually declare the function +// we use and rely on backwards-compatibility for this not to break +extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void); +namespace moodycamel { namespace details { + static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows"); + typedef std::uint32_t thread_id_t; + static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx + static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU; // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4. + static inline thread_id_t thread_id() { return static_cast(::GetCurrentThreadId()); } +} } +#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE) || defined(MOODYCAMEL_NO_THREAD_LOCAL) +namespace moodycamel { namespace details { + static_assert(sizeof(std::thread::id) == 4 || sizeof(std::thread::id) == 8, "std::thread::id is expected to be either 4 or 8 bytes"); + + typedef std::thread::id thread_id_t; + static const thread_id_t invalid_thread_id; // Default ctor creates invalid ID + + // Note we don't define a invalid_thread_id2 since std::thread::id doesn't have one; it's + // only used if MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is defined anyway, which it won't + // be. + static inline thread_id_t thread_id() { return std::this_thread::get_id(); } + + template struct thread_id_size { }; + template<> struct thread_id_size<4> { typedef std::uint32_t numeric_t; }; + template<> struct thread_id_size<8> { typedef std::uint64_t numeric_t; }; + + template<> struct thread_id_converter { + typedef thread_id_size::numeric_t thread_id_numeric_size_t; +#ifndef __APPLE__ + typedef std::size_t thread_id_hash_t; +#else + typedef thread_id_numeric_size_t thread_id_hash_t; +#endif + + static thread_id_hash_t prehash(thread_id_t const& x) + { +#ifndef __APPLE__ + return std::hash()(x); +#else + return *reinterpret_cast(&x); +#endif + } + }; +} } +#else +// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475 +// In order to get a numeric thread ID in a platform-independent way, we use a thread-local +// static variable's address as a thread identifier :-) +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +#define MOODYCAMEL_THREADLOCAL __thread +#elif defined(_MSC_VER) +#define MOODYCAMEL_THREADLOCAL __declspec(thread) +#else +// Assume C++11 compliant compiler +#define MOODYCAMEL_THREADLOCAL thread_local +#endif +namespace moodycamel { + namespace details { + typedef std::uintptr_t thread_id_t; + static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr + static const thread_id_t invalid_thread_id2 = 1; // Member accesses off a null pointer are also generally invalid. Plus it's not aligned. + inline thread_id_t thread_id() { + static MOODYCAMEL_THREADLOCAL int x; + return reinterpret_cast(&x); + } + } +} +#endif + +// Constexpr if +#ifndef MOODYCAMEL_CONSTEXPR_IF +#if (defined(_MSC_VER) && defined(_HAS_CXX17) && _HAS_CXX17) || __cplusplus > 201402L +#define MOODYCAMEL_CONSTEXPR_IF if constexpr +#define MOODYCAMEL_MAYBE_UNUSED [[maybe_unused]] +#else +#define MOODYCAMEL_CONSTEXPR_IF if +#define MOODYCAMEL_MAYBE_UNUSED +#endif +#endif + +// Exceptions +#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED +#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) +#define MOODYCAMEL_EXCEPTIONS_ENABLED +#endif +#endif +#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED +#define MOODYCAMEL_TRY try +#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__) +#define MOODYCAMEL_RETHROW throw +#define MOODYCAMEL_THROW(expr) throw (expr) +#else +#define MOODYCAMEL_TRY MOODYCAMEL_CONSTEXPR_IF (true) +#define MOODYCAMEL_CATCH(...) else MOODYCAMEL_CONSTEXPR_IF (false) +#define MOODYCAMEL_RETHROW +#define MOODYCAMEL_THROW(expr) +#endif + +#ifndef MOODYCAMEL_NOEXCEPT +#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED) +#define MOODYCAMEL_NOEXCEPT +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true +#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800 +// VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-( +// We have to assume *all* non-trivial constructors may throw on VS2012! +#define MOODYCAMEL_NOEXCEPT _NOEXCEPT +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value : std::is_trivially_copy_constructible::value) +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) +#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900 +#define MOODYCAMEL_NOEXCEPT _NOEXCEPT +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value || std::is_nothrow_move_constructible::value : std::is_trivially_copy_constructible::value || std::is_nothrow_copy_constructible::value) +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) +#else +#define MOODYCAMEL_NOEXCEPT noexcept +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr) +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr) +#endif +#endif + +#ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED +#ifdef MCDBGQ_USE_RELACY +#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED +#else +// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445 +// g++ <=4.7 doesn't support thread_local either. +// Finally, iOS/ARM doesn't have support for it either, and g++/ARM allows it to compile but it's unconfirmed to actually work +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && (!defined(__APPLE__) || !TARGET_OS_IPHONE) && !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__) +// Assume `thread_local` is fully supported in all other C++11 compilers/platforms +//#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED // always disabled for now since several users report having problems with it on +#endif +#endif +#endif + +// VS2012 doesn't support deleted functions. +// In this case, we declare the function normally but don't define it. A link error will be generated if the function is called. +#ifndef MOODYCAMEL_DELETE_FUNCTION +#if defined(_MSC_VER) && _MSC_VER < 1800 +#define MOODYCAMEL_DELETE_FUNCTION +#else +#define MOODYCAMEL_DELETE_FUNCTION = delete +#endif +#endif + +namespace moodycamel { + namespace details { +#ifndef MOODYCAMEL_ALIGNAS +// VS2013 doesn't support alignas or alignof, and align() requires a constant literal +#if defined(_MSC_VER) && _MSC_VER <= 1800 +#define MOODYCAMEL_ALIGNAS(alignment) __declspec(align(alignment)) +#define MOODYCAMEL_ALIGNOF(obj) __alignof(obj) +#define MOODYCAMEL_ALIGNED_TYPE_LIKE(T, obj) typename details::Vs2013Aligned::value, T>::type + template struct Vs2013Aligned { }; // default, unsupported alignment + template struct Vs2013Aligned<1, T> { typedef __declspec(align(1)) T type; }; + template struct Vs2013Aligned<2, T> { typedef __declspec(align(2)) T type; }; + template struct Vs2013Aligned<4, T> { typedef __declspec(align(4)) T type; }; + template struct Vs2013Aligned<8, T> { typedef __declspec(align(8)) T type; }; + template struct Vs2013Aligned<16, T> { typedef __declspec(align(16)) T type; }; + template struct Vs2013Aligned<32, T> { typedef __declspec(align(32)) T type; }; + template struct Vs2013Aligned<64, T> { typedef __declspec(align(64)) T type; }; + template struct Vs2013Aligned<128, T> { typedef __declspec(align(128)) T type; }; + template struct Vs2013Aligned<256, T> { typedef __declspec(align(256)) T type; }; +#else + template + struct identity { + typedef T type; + }; +#define MOODYCAMEL_ALIGNAS(alignment) alignas(alignment) +#define MOODYCAMEL_ALIGNOF(obj) alignof(obj) +#define MOODYCAMEL_ALIGNED_TYPE_LIKE(T, obj) alignas(alignof(obj)) typename details::identity::type +#endif +#endif + } +} + + +// TSAN can false report races in lock-free code. To enable TSAN to be used from projects that use this one, +// we can apply per-function compile-time suppression. +// See https://clang.llvm.org/docs/ThreadSanitizer.html#has-feature-thread-sanitizer +#define MOODYCAMEL_NO_TSAN +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#undef MOODYCAMEL_NO_TSAN +#define MOODYCAMEL_NO_TSAN __attribute__((no_sanitize("thread"))) +#endif // TSAN +#endif // TSAN + +// Compiler-specific likely/unlikely hints +namespace moodycamel { + namespace details { +#if defined(__GNUC__) + static inline bool (likely)(bool x) { return __builtin_expect((x), true); } + static inline bool (unlikely)(bool x) { return __builtin_expect((x), false); } +#else + + static inline bool (likely)(bool x) { return x; } + + static inline bool (unlikely)(bool x) { return x; } + +#endif + } +} + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG +#include "internal/concurrentqueue_internal_debug.h" +#endif + +namespace moodycamel { + namespace details { + template + struct const_numeric_max { + static_assert(std::is_integral::value, "const_numeric_max can only be used with integers"); + static const T value = std::numeric_limits::is_signed + ? (static_cast(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast(1) + : static_cast(-1); + }; + +#if defined(__GLIBCXX__) + typedef ::max_align_t std_max_align_t; // libstdc++ forgot to add it to std:: for a while +#else + typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std:: +#endif + + // Some platforms have incorrectly set max_align_t to a type with <8 bytes alignment even while supporting + // 8-byte aligned scalar values (*cough* 32-bit iOS). Work around this with our own union. See issue #64. + typedef union { + std_max_align_t x; + long long y; + void *z; + } max_align_t; + } + +// Default traits for the ConcurrentQueue. To change some of the +// traits without re-implementing all of them, inherit from this +// struct and shadow the declarations you wish to be different; +// since the traits are used as a template type parameter, the +// shadowed declarations will be used where defined, and the defaults +// otherwise. + struct ConcurrentQueueDefaultTraits { + // General-purpose size type. std::size_t is strongly recommended. + typedef std::size_t size_t; + + // The type used for the enqueue and dequeue indices. Must be at least as + // large as size_t. Should be significantly larger than the number of elements + // you expect to hold at once, especially if you have a high turnover rate; + // for example, on 32-bit x86, if you expect to have over a hundred million + // elements or pump several million elements through your queue in a very + // short space of time, using a 32-bit type *may* trigger a race condition. + // A 64-bit int type is recommended in that case, and in practice will + // prevent a race condition no matter the usage of the queue. Note that + // whether the queue is lock-free with a 64-int type depends on the whether + // std::atomic is lock-free, which is platform-specific. + typedef std::size_t index_t; + + // Internally, all elements are enqueued and dequeued from multi-element + // blocks; this is the smallest controllable unit. If you expect few elements + // but many producers, a smaller block size should be favoured. For few producers + // and/or many elements, a larger block size is preferred. A sane default + // is provided. Must be a power of 2. + static const size_t BLOCK_SIZE = 32; + + // For explicit producers (i.e. when using a producer token), the block is + // checked for being empty by iterating through a list of flags, one per element. + // For large block sizes, this is too inefficient, and switching to an atomic + // counter-based approach is faster. The switch is made for block sizes strictly + // larger than this threshold. + static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32; + + // How many full blocks can be expected for a single explicit producer? This should + // reflect that number's maximum for optimal performance. Must be a power of 2. + static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32; + + // How many full blocks can be expected for a single implicit producer? This should + // reflect that number's maximum for optimal performance. Must be a power of 2. + static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32; + + // The initial size of the hash table mapping thread IDs to implicit producers. + // Note that the hash is resized every time it becomes half full. + // Must be a power of two, and either 0 or at least 1. If 0, implicit production + // (using the enqueue methods without an explicit producer token) is disabled. + static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32; + + // Controls the number of items that an explicit consumer (i.e. one with a token) + // must consume before it causes all consumers to rotate and move on to the next + // internal queue. + static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256; + + // The maximum number of elements (inclusive) that can be enqueued to a sub-queue. + // Enqueue operations that would cause this limit to be surpassed will fail. Note + // that this limit is enforced at the block level (for performance reasons), i.e. + // it's rounded up to the nearest block size. + static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max::value; + + // The number of times to spin before sleeping when waiting on a semaphore. + // Recommended values are on the order of 1000-10000 unless the number of + // consumer threads exceeds the number of idle cores (in which case try 0-100). + // Only affects instances of the BlockingConcurrentQueue. + static const int MAX_SEMA_SPINS = 10000; + + +#ifndef MCDBGQ_USE_RELACY + // Memory allocation can be customized if needed. + // malloc should return nullptr on failure, and handle alignment like std::malloc. +#if defined(malloc) || defined(free) + // Gah, this is 2015, stop defining macros that break standard code already! + // Work around malloc/free being special macros: + static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); } + static inline void WORKAROUND_free(void* ptr) { return free(ptr); } + static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); } + static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); } +#else + + static inline void *malloc(size_t size) { return std::malloc(size); } + + static inline void free(void *ptr) { return std::free(ptr); } + +#endif +#else + // Debug versions when running under the Relacy race detector (ignore + // these in user code) + static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); } + static inline void free(void* ptr) { return rl::rl_free(ptr, $); } +#endif + }; + + +// When producing or consuming many elements, the most efficient way is to: +// 1) Use one of the bulk-operation methods of the queue with a token +// 2) Failing that, use the bulk-operation methods without a token +// 3) Failing that, create a token and use that with the single-item methods +// 4) Failing that, use the single-parameter methods of the queue +// Having said that, don't create tokens willy-nilly -- ideally there should be +// a maximum of one token per thread (of each kind). + struct ProducerToken; + struct ConsumerToken; + + template + class ConcurrentQueue; + + template + class BlockingConcurrentQueue; + + class ConcurrentQueueTests; + + + namespace details { + struct ConcurrentQueueProducerTypelessBase { + ConcurrentQueueProducerTypelessBase *next; + std::atomic inactive; + ProducerToken *token; + + ConcurrentQueueProducerTypelessBase() + : next(nullptr), inactive(false), token(nullptr) { + } + }; + + template + struct _hash_32_or_64 { + static inline std::uint32_t hash(std::uint32_t h) { + // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + // Since the thread ID is already unique, all we really want to do is propagate that + // uniqueness evenly across all the bits, so that we can use a subset of the bits while + // reducing collisions significantly + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + return h ^ (h >> 16); + } + }; + + template<> + struct _hash_32_or_64<1> { + static inline std::uint64_t hash(std::uint64_t h) { + h ^= h >> 33; + h *= 0xff51afd7ed558ccd; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53; + return h ^ (h >> 33); + } + }; + + template + struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { + }; + + static inline size_t hash_thread_id(thread_id_t id) { + static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values"); + return static_cast(hash_32_or_64::thread_id_hash_t)>::hash( + thread_id_converter::prehash(id))); + } + + template + static inline bool circular_less_than(T a, T b) { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4554) +#endif + static_assert(std::is_integral::value && !std::numeric_limits::is_signed, + "circular_less_than is intended to be used only with unsigned integer types"); + return static_cast(a - b) > + static_cast(static_cast(1) << static_cast(sizeof(T) * CHAR_BIT - 1)); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + + template + static inline char *align_for(char *ptr) { + const std::size_t alignment = std::alignment_of::value; + return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; + } + + template + static inline T ceil_to_pow_2(T x) { + static_assert(std::is_integral::value && !std::numeric_limits::is_signed, + "ceil_to_pow_2 is intended to be used only with unsigned integer types"); + + // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + for (std::size_t i = 1; i < sizeof(T); i <<= 1) { + x |= x >> (i << 3); + } + ++x; + return x; + } + + template + static inline void swap_relaxed(std::atomic &left, std::atomic &right) { + T temp = std::move(left.load(std::memory_order_relaxed)); + left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed); + right.store(std::move(temp), std::memory_order_relaxed); + } + + template + static inline T const &nomove(T const &x) { + return x; + } + + template + struct nomove_if { + template + static inline T const &eval(T const &x) { + return x; + } + }; + + template<> + struct nomove_if { + template + static inline auto eval(U &&x) + -> + + decltype (std::forward(x)) { + return std::forward(x); + } + }; + + template + static inline auto deref_noexcept(It &it) + + MOODYCAMEL_NOEXCEPT -> + decltype(*it) + { + return *it; + } + +#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + template + struct is_trivially_destructible : std::is_trivially_destructible { + }; +#else + template struct is_trivially_destructible : std::has_trivial_destructor { }; +#endif + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED +#ifdef MCDBGQ_USE_RELACY + typedef RelacyThreadExitListener ThreadExitListener; + typedef RelacyThreadExitNotifier ThreadExitNotifier; +#else + struct ThreadExitListener + { + typedef void (*callback_t)(void*); + callback_t callback; + void* userData; + + ThreadExitListener* next; // reserved for use by the ThreadExitNotifier + }; + + + class ThreadExitNotifier + { + public: + static void subscribe(ThreadExitListener* listener) + { + auto& tlsInst = instance(); + listener->next = tlsInst.tail; + tlsInst.tail = listener; + } + + static void unsubscribe(ThreadExitListener* listener) + { + auto& tlsInst = instance(); + ThreadExitListener** prev = &tlsInst.tail; + for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) { + if (ptr == listener) { + *prev = ptr->next; + break; + } + prev = &ptr->next; + } + } + + private: + ThreadExitNotifier() : tail(nullptr) { } + ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; + ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; + + ~ThreadExitNotifier() + { + // This thread is about to exit, let everyone know! + assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined."); + for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) { + ptr->callback(ptr->userData); + } + } + + // Thread-local + static inline ThreadExitNotifier& instance() + { + static thread_local ThreadExitNotifier notifier; + return notifier; + } + + private: + ThreadExitListener* tail; + }; +#endif +#endif + + template + struct static_is_lock_free_num { + enum { + value = 0 + }; + }; + template<> + struct static_is_lock_free_num { + enum { + value = ATOMIC_CHAR_LOCK_FREE + }; + }; + template<> + struct static_is_lock_free_num { + enum { + value = ATOMIC_SHORT_LOCK_FREE + }; + }; + template<> + struct static_is_lock_free_num { + enum { + value = ATOMIC_INT_LOCK_FREE + }; + }; + template<> + struct static_is_lock_free_num { + enum { + value = ATOMIC_LONG_LOCK_FREE + }; + }; + template<> + struct static_is_lock_free_num { + enum { + value = ATOMIC_LLONG_LOCK_FREE + }; + }; + template + struct static_is_lock_free : static_is_lock_free_num::type> { + }; + template<> + struct static_is_lock_free { + enum { + value = ATOMIC_BOOL_LOCK_FREE + }; + }; + template + struct static_is_lock_free { + enum { + value = ATOMIC_POINTER_LOCK_FREE + }; + }; + } + + + struct ProducerToken { + template + explicit ProducerToken(ConcurrentQueue &queue); + + template + explicit ProducerToken(BlockingConcurrentQueue &queue); + + ProducerToken(ProducerToken &&other) + + MOODYCAMEL_NOEXCEPT + : producer(other.producer) + { + other.producer = nullptr; + if (producer != nullptr) { + producer->token = this; + } + } + + inline ProducerToken &operator=(ProducerToken &&other) + + MOODYCAMEL_NOEXCEPT + { + swap(other); + return *this; + } + + void swap(ProducerToken &other) + + MOODYCAMEL_NOEXCEPT + { + std::swap(producer, other.producer); + if (producer != nullptr) { + producer->token = this; + } + if (other.producer != nullptr) { + other.producer->token = &other; + } + } + + // A token is always valid unless: + // 1) Memory allocation failed during construction + // 2) It was moved via the move constructor + // (Note: assignment does a swap, leaving both potentially valid) + // 3) The associated queue was destroyed + // Note that if valid() returns true, that only indicates + // that the token is valid for use with a specific queue, + // but not which one; that's up to the user to track. + inline bool valid() const { return producer != nullptr; } + + ~ProducerToken() { + if (producer != nullptr) { + producer->token = nullptr; + producer->inactive.store(true, std::memory_order_release); + } + } + + // Disable copying and assignment + ProducerToken(ProducerToken const &) MOODYCAMEL_DELETE_FUNCTION; + + ProducerToken &operator=(ProducerToken const &) MOODYCAMEL_DELETE_FUNCTION; + + private: + template friend + class ConcurrentQueue; + + friend class ConcurrentQueueTests; + + protected: + details::ConcurrentQueueProducerTypelessBase *producer; + }; + + + struct ConsumerToken { + template + explicit ConsumerToken(ConcurrentQueue &q); + + template + explicit ConsumerToken(BlockingConcurrentQueue &q); + + ConsumerToken(ConsumerToken &&other) + + MOODYCAMEL_NOEXCEPT + : initialOffset(other.initialOffset), lastKnownGlobalOffset(other + .lastKnownGlobalOffset), + itemsConsumedFromCurrent(other + .itemsConsumedFromCurrent), + currentProducer(other + .currentProducer), + desiredProducer(other + .desiredProducer) + { + } + + inline ConsumerToken &operator=(ConsumerToken &&other) + + MOODYCAMEL_NOEXCEPT + { + swap(other); + return *this; + } + + void swap(ConsumerToken &other) + + MOODYCAMEL_NOEXCEPT + { + std::swap(initialOffset, other.initialOffset); + std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset); + std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent); + std::swap(currentProducer, other.currentProducer); + std::swap(desiredProducer, other.desiredProducer); + } + + // Disable copying and assignment + ConsumerToken(ConsumerToken const &) MOODYCAMEL_DELETE_FUNCTION; + + ConsumerToken &operator=(ConsumerToken const &) MOODYCAMEL_DELETE_FUNCTION; + + private: + template friend + class ConcurrentQueue; + + friend class ConcurrentQueueTests; + + private: // but shared with ConcurrentQueue + std::uint32_t initialOffset; + std::uint32_t lastKnownGlobalOffset; + std::uint32_t itemsConsumedFromCurrent; + details::ConcurrentQueueProducerTypelessBase *currentProducer; + details::ConcurrentQueueProducerTypelessBase *desiredProducer; + }; + +// Need to forward-declare this swap because it's in a namespace. +// See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces + template + inline void swap(typename ConcurrentQueue::ImplicitProducerKVP &a, + typename ConcurrentQueue::ImplicitProducerKVP &b) + + MOODYCAMEL_NOEXCEPT; + + + template + class ConcurrentQueue { + public: + typedef ::moodycamel::ProducerToken producer_token_t; + typedef ::moodycamel::ConsumerToken consumer_token_t; + + typedef typename Traits::index_t index_t; + typedef typename Traits::size_t size_t; + + static const size_t BLOCK_SIZE = static_cast(Traits::BLOCK_SIZE); + static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD); + static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::EXPLICIT_INITIAL_INDEX_SIZE); + static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::IMPLICIT_INITIAL_INDEX_SIZE); + static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE); + static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE); +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!) +#pragma warning(disable: 4309) // static_cast: Truncation of constant value +#endif + static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max::value - + static_cast(Traits::MAX_SUBQUEUE_SIZE) < BLOCK_SIZE) + ? details::const_numeric_max::value : ( + (static_cast(Traits::MAX_SUBQUEUE_SIZE) + + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + static_assert(! + std::numeric_limits::is_signed &&std::is_integral::value, + "Traits::size_t must be an unsigned integral type"); + static_assert(! + std::numeric_limits::is_signed &&std::is_integral::value, + "Traits::index_t must be an unsigned integral type"); + static_assert(sizeof(index_t) >= sizeof(size_t), "Traits::index_t must be at least as wide as Traits::size_t"); + static_assert((BLOCK_SIZE + > 1) && !( + BLOCK_SIZE &(BLOCK_SIZE + - 1)), "Traits::BLOCK_SIZE must be a power of 2 (and at least 2)"); + static_assert((EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD + > 1) && !( + EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD &(EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD + - 1)), "Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD must be a power of 2 (and greater than 1)"); + static_assert((EXPLICIT_INITIAL_INDEX_SIZE + > 1) && !( + EXPLICIT_INITIAL_INDEX_SIZE &(EXPLICIT_INITIAL_INDEX_SIZE + - 1)), "Traits::EXPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); + static_assert((IMPLICIT_INITIAL_INDEX_SIZE + > 1) && !( + IMPLICIT_INITIAL_INDEX_SIZE &(IMPLICIT_INITIAL_INDEX_SIZE + - 1)), "Traits::IMPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); + static_assert((INITIAL_IMPLICIT_PRODUCER_HASH_SIZE + == 0) || !( + INITIAL_IMPLICIT_PRODUCER_HASH_SIZE &(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE + - 1)), "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be a power of 2"); + static_assert(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE + == 0 || INITIAL_IMPLICIT_PRODUCER_HASH_SIZE >= 1, "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be at least 1 (or 0 to disable implicit enqueueing)"); + + public: + // Creates a queue with at least `capacity` element slots; note that the + // actual number of elements that can be inserted without additional memory + // allocation depends on the number of producers and the block size (e.g. if + // the block size is equal to `capacity`, only a single block will be allocated + // up-front, which means only a single producer will be able to enqueue elements + // without an extra allocation -- blocks aren't shared between producers). + // This method is not thread safe -- it is up to the user to ensure that the + // queue is fully constructed before it starts being used by other threads (this + // includes making the memory effects of construction visible, possibly with a + // memory barrier). + explicit ConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE) + : producerListTail(nullptr), + producerCount(0), + initialBlockPoolIndex(0), + nextExplicitConsumerId(0), + globalExplicitConsumerOffset(0) { + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + populate_initial_implicit_producer_hash(); + populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1)); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + // Track all the producers using a fully-resolved typed list for + // each kind; this makes it possible to debug them starting from + // the root queue object (otherwise wacky casts are needed that + // don't compile in the debugger's expression evaluator). + explicitProducers.store(nullptr, std::memory_order_relaxed); + implicitProducers.store(nullptr, std::memory_order_relaxed); +#endif + } + + // Computes the correct amount of pre-allocated blocks for you based + // on the minimum number of elements you want available at any given + // time, and the maximum concurrent number of each type of producer. + ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers) + : producerListTail(nullptr), + producerCount(0), + initialBlockPoolIndex(0), + nextExplicitConsumerId(0), + globalExplicitConsumerOffset(0) { + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + populate_initial_implicit_producer_hash(); + size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + + 2 * (maxExplicitProducers + maxImplicitProducers); + populate_initial_block_list(blocks); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + explicitProducers.store(nullptr, std::memory_order_relaxed); + implicitProducers.store(nullptr, std::memory_order_relaxed); +#endif + } + + // Note: The queue should not be accessed concurrently while it's + // being deleted. It's up to the user to synchronize this. + // This method is not thread safe. + ~ConcurrentQueue() { + // Destroy producers + auto ptr = producerListTail.load(std::memory_order_relaxed); + while (ptr != nullptr) { + auto next = ptr->next_prod(); + if (ptr->token != nullptr) { + ptr->token->producer = nullptr; + } + destroy(ptr); + ptr = next; + } + + // Destroy implicit producer hash tables + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) { + auto hash = implicitProducerHash.load(std::memory_order_relaxed); + while (hash != nullptr) { + auto prev = hash->prev; + if (prev != + nullptr) { // The last hash is part of this object and was not allocated dynamically + for (size_t i = 0; i != hash->capacity; ++i) { + hash->entries[i].~ImplicitProducerKVP(); + } + hash->~ImplicitProducerHash(); + (Traits::free)(hash); + } + hash = prev; + } + } + + // Destroy global free list + auto block = freeList.head_unsafe(); + while (block != nullptr) { + auto next = block->freeListNext.load(std::memory_order_relaxed); + if (block->dynamicallyAllocated) { + destroy(block); + } + block = next; + } + + // Destroy initial free list + destroy_array(initialBlockPool, initialBlockPoolSize); + } + + // Disable copying and copy assignment + ConcurrentQueue(ConcurrentQueue const &) MOODYCAMEL_DELETE_FUNCTION; + + ConcurrentQueue &operator=(ConcurrentQueue const &) MOODYCAMEL_DELETE_FUNCTION; + + // Moving is supported, but note that it is *not* a thread-safe operation. + // Nobody can use the queue while it's being moved, and the memory effects + // of that move must be propagated to other threads before they can use it. + // Note: When a queue is moved, its tokens are still valid but can only be + // used with the destination queue (i.e. semantically they are moved along + // with the queue itself). + ConcurrentQueue(ConcurrentQueue &&other) + + MOODYCAMEL_NOEXCEPT + : producerListTail(other.producerListTail.load(std::memory_order_relaxed)), + producerCount(other + .producerCount. + load(std::memory_order_relaxed) + ), + initialBlockPoolIndex(other + .initialBlockPoolIndex. + load(std::memory_order_relaxed) + ), + initialBlockPool(other + .initialBlockPool), + initialBlockPoolSize(other + .initialBlockPoolSize), + + freeList (std::move(other + + .freeList)), + nextExplicitConsumerId(other + .nextExplicitConsumerId. + load(std::memory_order_relaxed) + ), + globalExplicitConsumerOffset(other + .globalExplicitConsumerOffset. + load(std::memory_order_relaxed) + ) + { + // Move the other one into this, and leave the other one as an empty queue + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + populate_initial_implicit_producer_hash(); + swap_implicit_producer_hashes(other); + + other.producerListTail.store(nullptr, std::memory_order_relaxed); + other.producerCount.store(0, std::memory_order_relaxed); + other.nextExplicitConsumerId.store(0, std::memory_order_relaxed); + other.globalExplicitConsumerOffset.store(0, std::memory_order_relaxed); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + explicitProducers.store(other.explicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); + other.explicitProducers.store(nullptr, std::memory_order_relaxed); + implicitProducers.store(other.implicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); + other.implicitProducers.store(nullptr, std::memory_order_relaxed); +#endif + + other.initialBlockPoolIndex.store(0, std::memory_order_relaxed); + other.initialBlockPoolSize = 0; + other.initialBlockPool = nullptr; + + reown_producers(); + } + + inline ConcurrentQueue &operator=(ConcurrentQueue &&other) + + MOODYCAMEL_NOEXCEPT + { + return swap_internal(other); + } + + // Swaps this queue's state with the other's. Not thread-safe. + // Swapping two queues does not invalidate their tokens, however + // the tokens that were created for one queue must be used with + // only the swapped queue (i.e. the tokens are tied to the + // queue's movable state, not the object itself). + inline void swap(ConcurrentQueue &other) + + MOODYCAMEL_NOEXCEPT + { + swap_internal(other); + } + + private: + ConcurrentQueue &swap_internal(ConcurrentQueue &other) { + if (this == &other) { + return *this; + } + + details::swap_relaxed(producerListTail, other.producerListTail); + details::swap_relaxed(producerCount, other.producerCount); + details::swap_relaxed(initialBlockPoolIndex, other.initialBlockPoolIndex); + std::swap(initialBlockPool, other.initialBlockPool); + std::swap(initialBlockPoolSize, other.initialBlockPoolSize); + freeList.swap(other.freeList); + details::swap_relaxed(nextExplicitConsumerId, other.nextExplicitConsumerId); + details::swap_relaxed(globalExplicitConsumerOffset, other.globalExplicitConsumerOffset); + + swap_implicit_producer_hashes(other); + + reown_producers(); + other.reown_producers(); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + details::swap_relaxed(explicitProducers, other.explicitProducers); + details::swap_relaxed(implicitProducers, other.implicitProducers); +#endif + + return *this; + } + + public: + // Enqueues a single item (by copying it). + // Allocates memory if required. Only fails if memory allocation fails (or implicit + // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, + // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(T const &item) { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + else return inner_enqueue(item); + } + + // Enqueues a single item (by moving it, if possible). + // Allocates memory if required. Only fails if memory allocation fails (or implicit + // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, + // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(T &&item) { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + else return inner_enqueue(std::move(item)); + } + + // Enqueues a single item (by copying it) using an explicit producer token. + // Allocates memory if required. Only fails if memory allocation fails (or + // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(producer_token_t const &token, T const &item) { + return inner_enqueue(token, item); + } + + // Enqueues a single item (by moving it, if possible) using an explicit producer token. + // Allocates memory if required. Only fails if memory allocation fails (or + // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(producer_token_t const &token, T &&item) { + return inner_enqueue(token, std::move(item)); + } + + // Enqueues several items. + // Allocates memory if required. Only fails if memory allocation fails (or + // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE + // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Note: Use std::make_move_iterator if the elements should be moved instead of copied. + // Thread-safe. + template + bool enqueue_bulk(It itemFirst, size_t count) { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + else return inner_enqueue_bulk(itemFirst, count); + } + + // Enqueues several items using an explicit producer token. + // Allocates memory if required. Only fails if memory allocation fails + // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Note: Use std::make_move_iterator if the elements should be moved + // instead of copied. + // Thread-safe. + template + bool enqueue_bulk(producer_token_t const &token, It itemFirst, size_t count) { + return inner_enqueue_bulk(token, itemFirst, count); + } + + // Enqueues a single item (by copying it). + // Does not allocate memory. Fails if not enough room to enqueue (or implicit + // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE + // is 0). + // Thread-safe. + inline bool try_enqueue(T const &item) { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + else return inner_enqueue(item); + } + + // Enqueues a single item (by moving it, if possible). + // Does not allocate memory (except for one-time implicit producer). + // Fails if not enough room to enqueue (or implicit production is + // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). + // Thread-safe. + inline bool try_enqueue(T &&item) { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + else return inner_enqueue(std::move(item)); + } + + // Enqueues a single item (by copying it) using an explicit producer token. + // Does not allocate memory. Fails if not enough room to enqueue. + // Thread-safe. + inline bool try_enqueue(producer_token_t const &token, T const &item) { + return inner_enqueue(token, item); + } + + // Enqueues a single item (by moving it, if possible) using an explicit producer token. + // Does not allocate memory. Fails if not enough room to enqueue. + // Thread-safe. + inline bool try_enqueue(producer_token_t const &token, T &&item) { + return inner_enqueue(token, std::move(item)); + } + + // Enqueues several items. + // Does not allocate memory (except for one-time implicit producer). + // Fails if not enough room to enqueue (or implicit production is + // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). + // Note: Use std::make_move_iterator if the elements should be moved + // instead of copied. + // Thread-safe. + template + bool try_enqueue_bulk(It itemFirst, size_t count) { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + else return inner_enqueue_bulk(itemFirst, count); + } + + // Enqueues several items using an explicit producer token. + // Does not allocate memory. Fails if not enough room to enqueue. + // Note: Use std::make_move_iterator if the elements should be moved + // instead of copied. + // Thread-safe. + template + bool try_enqueue_bulk(producer_token_t const &token, It itemFirst, size_t count) { + return inner_enqueue_bulk(token, itemFirst, count); + } + + + // Attempts to dequeue from the queue. + // Returns false if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + bool try_dequeue(U &item) { + // Instead of simply trying each producer in turn (which could cause needless contention on the first + // producer), we score them heuristically. + size_t nonEmptyCount = 0; + ProducerBase *best = nullptr; + size_t bestSize = 0; + for (auto ptr = producerListTail.load(std::memory_order_acquire); + nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) { + auto size = ptr->size_approx(); + if (size > 0) { + if (size > bestSize) { + bestSize = size; + best = ptr; + } + ++nonEmptyCount; + } + } + + // If there was at least one non-empty queue but it appears empty at the time + // we try to dequeue from it, we need to make sure every queue's been tried + if (nonEmptyCount > 0) { + if ((details::likely)(best->dequeue(item))) { + return true; + } + for (auto ptr = producerListTail.load(std::memory_order_acquire); + ptr != nullptr; ptr = ptr->next_prod()) { + if (ptr != best && ptr->dequeue(item)) { + return true; + } + } + } + return false; + } + + // Attempts to dequeue from the queue. + // Returns false if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // This differs from the try_dequeue(item) method in that this one does + // not attempt to reduce contention by interleaving the order that producer + // streams are dequeued from. So, using this method can reduce overall throughput + // under contention, but will give more predictable results in single-threaded + // consumer scenarios. This is mostly only useful for internal unit tests. + // Never allocates. Thread-safe. + template + bool try_dequeue_non_interleaved(U &item) { + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + if (ptr->dequeue(item)) { + return true; + } + } + return false; + } + + // Attempts to dequeue from the queue using an explicit consumer token. + // Returns false if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + bool try_dequeue(consumer_token_t &token, U &item) { + // The idea is roughly as follows: + // Every 256 items from one producer, make everyone rotate (increase the global offset) -> this means the highest efficiency consumer dictates the rotation speed of everyone else, more or less + // If you see that the global offset has changed, you must reset your consumption counter and move to your designated place + // If there's no items where you're supposed to be, keep moving until you find a producer with some items + // If the global offset has not changed but you've run out of items to consume, move over from your current position until you find an producer with something in it + + if (token.desiredProducer == nullptr || + token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { + if (!update_current_producer_after_rotation(token)) { + return false; + } + } + + // If there was at least one non-empty queue but it appears empty at the time + // we try to dequeue from it, we need to make sure every queue's been tried + if (static_cast(token.currentProducer)->dequeue(item)) { + if (++token.itemsConsumedFromCurrent == EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { + globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); + } + return true; + } + + auto tail = producerListTail.load(std::memory_order_acquire); + auto ptr = static_cast(token.currentProducer)->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + while (ptr != static_cast(token.currentProducer)) { + if (ptr->dequeue(item)) { + token.currentProducer = ptr; + token.itemsConsumedFromCurrent = 1; + return true; + } + ptr = ptr->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + } + return false; + } + + // Attempts to dequeue several elements from the queue. + // Returns the number of items actually dequeued. + // Returns 0 if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + size_t try_dequeue_bulk(It itemFirst, size_t max) { + size_t count = 0; + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + count += ptr->dequeue_bulk(itemFirst, max - count); + if (count == max) { + break; + } + } + return count; + } + + // Attempts to dequeue several elements from the queue using an explicit consumer token. + // Returns the number of items actually dequeued. + // Returns 0 if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + size_t try_dequeue_bulk(consumer_token_t &token, It itemFirst, size_t max) { + if (token.desiredProducer == nullptr || + token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { + if (!update_current_producer_after_rotation(token)) { + return 0; + } + } + + size_t count = static_cast(token.currentProducer)->dequeue_bulk(itemFirst, max); + if (count == max) { + if ((token.itemsConsumedFromCurrent += static_cast(max)) >= + EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { + globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); + } + return max; + } + token.itemsConsumedFromCurrent += static_cast(count); + max -= count; + + auto tail = producerListTail.load(std::memory_order_acquire); + auto ptr = static_cast(token.currentProducer)->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + while (ptr != static_cast(token.currentProducer)) { + auto dequeued = ptr->dequeue_bulk(itemFirst, max); + count += dequeued; + if (dequeued != 0) { + token.currentProducer = ptr; + token.itemsConsumedFromCurrent = static_cast(dequeued); + } + if (dequeued == max) { + break; + } + max -= dequeued; + ptr = ptr->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + } + return count; + } + + + // Attempts to dequeue from a specific producer's inner queue. + // If you happen to know which producer you want to dequeue from, this + // is significantly faster than using the general-case try_dequeue methods. + // Returns false if the producer's queue appeared empty at the time it + // was checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + inline bool try_dequeue_from_producer(producer_token_t const &producer, U &item) { + return static_cast(producer.producer)->dequeue(item); + } + + // Attempts to dequeue several elements from a specific producer's inner queue. + // Returns the number of items actually dequeued. + // If you happen to know which producer you want to dequeue from, this + // is significantly faster than using the general-case try_dequeue methods. + // Returns 0 if the producer's queue appeared empty at the time it + // was checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + inline size_t try_dequeue_bulk_from_producer(producer_token_t const &producer, It itemFirst, size_t max) { + return static_cast(producer.producer)->dequeue_bulk(itemFirst, max); + } + + + // Returns an estimate of the total number of elements currently in the queue. This + // estimate is only accurate if the queue has completely stabilized before it is called + // (i.e. all enqueue and dequeue operations have completed and their memory effects are + // visible on the calling thread, and no further operations start while this method is + // being called). + // Thread-safe. + size_t size_approx() const { + size_t size = 0; + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + size += ptr->size_approx(); + } + return size; + } + + + // Returns true if the underlying atomic variables used by + // the queue are lock-free (they should be on most platforms). + // Thread-safe. + static bool is_lock_free() { + return + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::thread_id_numeric_size_t>::value == + 2; + } + + + private: + friend struct ProducerToken; + friend struct ConsumerToken; + struct ExplicitProducer; + friend struct ExplicitProducer; + struct ImplicitProducer; + friend struct ImplicitProducer; + + friend class ConcurrentQueueTests; + + enum AllocationMode { + CanAlloc, CannotAlloc + }; + + + /////////////////////////////// + // Queue methods + /////////////////////////////// + + template + inline bool inner_enqueue(producer_token_t const &token, U &&element) { + return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue( + std::forward(element)); + } + + template + inline bool inner_enqueue(U &&element) { + auto producer = get_or_add_implicit_producer(); + return producer == nullptr ? false + : producer->ConcurrentQueue::ImplicitProducer::template enqueue( + std::forward(element)); + } + + template + inline bool inner_enqueue_bulk(producer_token_t const &token, It itemFirst, size_t count) { + return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue_bulk( + itemFirst, count); + } + + template + inline bool inner_enqueue_bulk(It itemFirst, size_t count) { + auto producer = get_or_add_implicit_producer(); + return producer == nullptr ? false + : producer->ConcurrentQueue::ImplicitProducer::template enqueue_bulk( + itemFirst, count); + } + + inline bool update_current_producer_after_rotation(consumer_token_t &token) { + // Ah, there's been a rotation, figure out where we should be! + auto tail = producerListTail.load(std::memory_order_acquire); + if (token.desiredProducer == nullptr && tail == nullptr) { + return false; + } + auto prodCount = producerCount.load(std::memory_order_relaxed); + auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed); + if ((details::unlikely)(token.desiredProducer == nullptr)) { + // Aha, first time we're dequeueing anything. + // Figure out our local position + // Note: offset is from start, not end, but we're traversing from end -- subtract from count first + std::uint32_t offset = prodCount - 1 - (token.initialOffset % prodCount); + token.desiredProducer = tail; + for (std::uint32_t i = 0; i != offset; ++i) { + token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); + if (token.desiredProducer == nullptr) { + token.desiredProducer = tail; + } + } + } + + std::uint32_t delta = globalOffset - token.lastKnownGlobalOffset; + if (delta >= prodCount) { + delta = delta % prodCount; + } + for (std::uint32_t i = 0; i != delta; ++i) { + token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); + if (token.desiredProducer == nullptr) { + token.desiredProducer = tail; + } + } + + token.lastKnownGlobalOffset = globalOffset; + token.currentProducer = token.desiredProducer; + token.itemsConsumedFromCurrent = 0; + return true; + } + + + /////////////////////////// + // Free list + /////////////////////////// + + template + struct FreeListNode { + FreeListNode() : freeListRefs(0), freeListNext(nullptr) {} + + std::atomic freeListRefs; + std::atomic freeListNext; + }; + + // A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention, but + // simple and correct (assuming nodes are never freed until after the free list is destroyed), and fairly + // speedy under low contention. + template // N must inherit FreeListNode or have the same fields (and initialization of them) + struct FreeList { + FreeList() : freeListHead(nullptr) {} + + FreeList(FreeList &&other) : freeListHead(other.freeListHead.load(std::memory_order_relaxed)) { + other.freeListHead.store(nullptr, std::memory_order_relaxed); + } + + void swap(FreeList &other) { details::swap_relaxed(freeListHead, other.freeListHead); } + + FreeList(FreeList const &) MOODYCAMEL_DELETE_FUNCTION; + + FreeList &operator=(FreeList const &) MOODYCAMEL_DELETE_FUNCTION; + + inline void add(N *node) { +#ifdef MCDBGQ_NOLOCKFREE_FREELIST + debug::DebugLock lock(mutex); +#endif + // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to + // set it using a fetch_add + if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) { + // Oh look! We were the last ones referencing this node, and we know + // we want to add it to the free list, so let's do it! + add_knowing_refcount_is_zero(node); + } + } + + inline N *try_get() { +#ifdef MCDBGQ_NOLOCKFREE_FREELIST + debug::DebugLock lock(mutex); +#endif + auto head = freeListHead.load(std::memory_order_acquire); + while (head != nullptr) { + auto prevHead = head; + auto refs = head->freeListRefs.load(std::memory_order_relaxed); + if ((refs & REFS_MASK) == 0 || + !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, + std::memory_order_relaxed)) { + head = freeListHead.load(std::memory_order_acquire); + continue; + } + + // Good, reference count has been incremented (it wasn't at zero), which means we can read the + // next and not worry about it changing between now and the time we do the CAS + auto next = head->freeListNext.load(std::memory_order_relaxed); + if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, + std::memory_order_relaxed)) { + // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no + // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on). + assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0); + + // Decrease refcount twice, once for our ref, and once for the list's ref + head->freeListRefs.fetch_sub(2, std::memory_order_release); + return head; + } + + // OK, the head must have changed on us, but we still need to decrease the refcount we increased. + // Note that we don't need to release any memory effects, but we do need to ensure that the reference + // count decrement happens-after the CAS on the head. + refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel); + if (refs == SHOULD_BE_ON_FREELIST + 1) { + add_knowing_refcount_is_zero(prevHead); + } + } + + return nullptr; + } + + // Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes) + N *head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); } + + private: + inline void add_knowing_refcount_is_zero(N *node) { + // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run + // only one copy of this method per node at a time, i.e. the single thread case), then we know + // we can safely change the next pointer of the node; however, once the refcount is back above + // zero, then other threads could increase it (happens under heavy contention, when the refcount + // goes to zero in between a load and a refcount increment of a node in try_get, then back up to + // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS + // to add the node to the actual list fails, decrease the refcount and leave the add operation to + // the next thread who puts the refcount back at zero (which could be us, hence the loop). + auto head = freeListHead.load(std::memory_order_relaxed); + while (true) { + node->freeListNext.store(head, std::memory_order_relaxed); + node->freeListRefs.store(1, std::memory_order_release); + if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, + std::memory_order_relaxed)) { + // Hmm, the add failed, but we can only try again when the refcount goes back to zero + if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) { + continue; + } + } + return; + } + } + + private: + // Implemented like a stack, but where node order doesn't matter (nodes are inserted out of order under contention) + std::atomic freeListHead; + + static const std::uint32_t REFS_MASK = 0x7FFFFFFF; + static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000; + +#ifdef MCDBGQ_NOLOCKFREE_FREELIST + debug::DebugMutex mutex; +#endif + }; + + + /////////////////////////// + // Block + /////////////////////////// + + enum InnerQueueContext { + implicit_context = 0, explicit_context = 1 + }; + + struct Block { + Block() + : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), + shouldBeOnFreeList(false), dynamicallyAllocated(true) { +#ifdef MCDBGQ_TRACKMEM + owner = nullptr; +#endif + } + + template + inline bool is_empty() const { + MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && + BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Check flags + for (size_t i = 0; i < BLOCK_SIZE; ++i) { + if (!emptyFlags[i].load(std::memory_order_relaxed)) { + return false; + } + } + + // Aha, empty; make sure we have all other memory effects that happened before the empty flags were set + std::atomic_thread_fence(std::memory_order_acquire); + return true; + } else { + // Check counter + if (elementsCompletelyDequeued.load(std::memory_order_relaxed) == BLOCK_SIZE) { + std::atomic_thread_fence(std::memory_order_acquire); + return true; + } + assert(elementsCompletelyDequeued.load(std::memory_order_relaxed) <= BLOCK_SIZE); + return false; + } + } + + // Returns true if the block is now empty (does not apply in explicit context) + template + inline bool set_empty(MOODYCAMEL_MAYBE_UNUSED index_t i) { + MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && + BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Set flag + assert(!emptyFlags[BLOCK_SIZE - 1 - + static_cast(i & static_cast(BLOCK_SIZE - 1))].load( + std::memory_order_relaxed)); + emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].store( + true, std::memory_order_release); + return false; + } else { + // Increment counter + auto prevVal = elementsCompletelyDequeued.fetch_add(1, std::memory_order_release); + assert(prevVal < BLOCK_SIZE); + return prevVal == BLOCK_SIZE - 1; + } + } + + // Sets multiple contiguous item statuses to 'empty' (assumes no wrapping and count > 0). + // Returns true if the block is now empty (does not apply in explicit context). + template + inline bool set_many_empty(MOODYCAMEL_MAYBE_UNUSED index_t i, size_t count) { + MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && + BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Set flags + std::atomic_thread_fence(std::memory_order_release); + i = BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1)) - count + 1; + for (size_t j = 0; j != count; ++j) { + assert(!emptyFlags[i + j].load(std::memory_order_relaxed)); + emptyFlags[i + j].store(true, std::memory_order_relaxed); + } + return false; + } else { + // Increment counter + auto prevVal = elementsCompletelyDequeued.fetch_add(count, std::memory_order_release); + assert(prevVal + count <= BLOCK_SIZE); + return prevVal + count == BLOCK_SIZE; + } + } + + template + inline void set_all_empty() { + MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && + BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Set all flags + for (size_t i = 0; i != BLOCK_SIZE; ++i) { + emptyFlags[i].store(true, std::memory_order_relaxed); + } + } else { + // Reset counter + elementsCompletelyDequeued.store(BLOCK_SIZE, std::memory_order_relaxed); + } + } + + template + inline void reset_empty() { + MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && + BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Reset flags + for (size_t i = 0; i != BLOCK_SIZE; ++i) { + emptyFlags[i].store(false, std::memory_order_relaxed); + } + } else { + // Reset counter + elementsCompletelyDequeued.store(0, std::memory_order_relaxed); + } + } + + inline T *operator[](index_t idx) + + MOODYCAMEL_NOEXCEPT { + return static_cast(static_cast(elements)) + + static_cast(idx & static_cast(BLOCK_SIZE - 1)); + } + + inline T const *operator[](index_t idx) const + + MOODYCAMEL_NOEXCEPT { + return static_cast(static_cast(elements)) + + static_cast(idx & static_cast(BLOCK_SIZE - 1)); + } + + private: + static_assert(std::alignment_of::value + <= sizeof(T), "The queue does not support types with an alignment greater than their size at this time"); + + MOODYCAMEL_ALIGNED_TYPE_LIKE(char[sizeof(T) * BLOCK_SIZE], T) elements; + public: + Block *next; + std::atomic elementsCompletelyDequeued; + std::atomic emptyFlags[BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD ? BLOCK_SIZE : 1]; + public: + std::atomic freeListRefs; + std::atomic freeListNext; + std::atomic shouldBeOnFreeList; + bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool' + +#ifdef MCDBGQ_TRACKMEM + void* owner; +#endif + }; + + static_assert(std::alignment_of::value + >= std::alignment_of::value, "Internal error: Blocks must be at least as aligned as the type they are wrapping"); + + +#ifdef MCDBGQ_TRACKMEM + public: + struct MemStats; +private: +#endif + + /////////////////////////// + // Producer base + /////////////////////////// + + struct ProducerBase : public details::ConcurrentQueueProducerTypelessBase { + ProducerBase(ConcurrentQueue *parent_, bool isExplicit_) : + tailIndex(0), + headIndex(0), + dequeueOptimisticCount(0), + dequeueOvercommit(0), + tailBlock(nullptr), + isExplicit(isExplicit_), + parent(parent_) { + } + + virtual ~ProducerBase() {} + + template + inline bool dequeue(U &element) { + if (isExplicit) { + return static_cast(this)->dequeue(element); + } else { + return static_cast(this)->dequeue(element); + } + } + + template + inline size_t dequeue_bulk(It &itemFirst, size_t max) { + if (isExplicit) { + return static_cast(this)->dequeue_bulk(itemFirst, max); + } else { + return static_cast(this)->dequeue_bulk(itemFirst, max); + } + } + + inline ProducerBase *next_prod() const { return static_cast(next); } + + inline size_t size_approx() const { + auto tail = tailIndex.load(std::memory_order_relaxed); + auto head = headIndex.load(std::memory_order_relaxed); + return details::circular_less_than(head, tail) ? static_cast(tail - head) : 0; + } + + inline index_t getTail() const { return tailIndex.load(std::memory_order_relaxed); } + + protected: + std::atomic tailIndex; // Where to enqueue to next + std::atomic headIndex; // Where to dequeue from next + + std::atomic dequeueOptimisticCount; + std::atomic dequeueOvercommit; + + Block *tailBlock; + + public: + bool isExplicit; + ConcurrentQueue *parent; + + protected: +#ifdef MCDBGQ_TRACKMEM + friend struct MemStats; +#endif + }; + + + /////////////////////////// + // Explicit queue + /////////////////////////// + + struct ExplicitProducer : public ProducerBase { + explicit ExplicitProducer(ConcurrentQueue *parent_) : + ProducerBase(parent_, true), + blockIndex(nullptr), + pr_blockIndexSlotsUsed(0), + pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1), + pr_blockIndexFront(0), + pr_blockIndexEntries(nullptr), + pr_blockIndexRaw(nullptr) { + size_t poolBasedIndexSize = details::ceil_to_pow_2(parent_->initialBlockPoolSize) >> 1; + if (poolBasedIndexSize > pr_blockIndexSize) { + pr_blockIndexSize = poolBasedIndexSize; + } + + new_block_index( + 0); // This creates an index with double the number of current entries, i.e. EXPLICIT_INITIAL_INDEX_SIZE + } + + ~ExplicitProducer() { + // Destruct any elements not yet dequeued. + // Since we're in the destructor, we can assume all elements + // are either completely dequeued or completely not (no halfways). + if (this->tailBlock != nullptr) { // Note this means there must be a block index too + // First find the block that's partially dequeued, if any + Block *halfDequeuedBlock = nullptr; + if ((this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) != 0) { + // The head's not on a block boundary, meaning a block somewhere is partially dequeued + // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary) + size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1); + while (details::circular_less_than(pr_blockIndexEntries[i].base + BLOCK_SIZE, + this->headIndex.load(std::memory_order_relaxed))) { + i = (i + 1) & (pr_blockIndexSize - 1); + } + assert(details::circular_less_than(pr_blockIndexEntries[i].base, + this->headIndex.load(std::memory_order_relaxed))); + halfDequeuedBlock = pr_blockIndexEntries[i].block; + } + + // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration) + auto block = this->tailBlock; + do { + block = block->next; + if (block->ConcurrentQueue::Block::template is_empty()) { + continue; + } + + size_t i = 0; // Offset into block + if (block == halfDequeuedBlock) { + i = static_cast(this->headIndex.load(std::memory_order_relaxed) & + static_cast(BLOCK_SIZE - 1)); + } + + // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index + auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & + static_cast(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE + : static_cast( + this->tailIndex.load(std::memory_order_relaxed) & + static_cast(BLOCK_SIZE - 1)); + while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) { + (*block)[i++]->~T(); + } + } while (block != this->tailBlock); + } + + // Destroy all blocks that we own + if (this->tailBlock != nullptr) { + auto block = this->tailBlock; + do { + auto nextBlock = block->next; + if (block->dynamicallyAllocated) { + destroy(block); + } else { + this->parent->add_block_to_free_list(block); + } + block = nextBlock; + } while (block != this->tailBlock); + } + + // Destroy the block indices + auto header = static_cast(pr_blockIndexRaw); + while (header != nullptr) { + auto prev = static_cast(header->prev); + header->~BlockIndexHeader(); + (Traits::free)(header); + header = prev; + } + } + + template + inline bool enqueue(U &&element) { + index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); + index_t newTailIndex = 1 + currentTailIndex; + if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + // We reached the end of a block, start a new one + auto startBlock = this->tailBlock; + auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; + if (this->tailBlock != nullptr && + this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { + // We can re-use the block ahead of us, it's empty! + this->tailBlock = this->tailBlock->next; + this->tailBlock->ConcurrentQueue::Block::template reset_empty(); + + // We'll put the block on the block index (guaranteed to be room since we're conceptually removing the + // last block from it first -- except instead of removing then adding, we can just overwrite). + // Note that there must be a valid block index here, since even if allocation failed in the ctor, + // it would have been re-attempted when adding the first block to the queue; since there is such + // a block, a block index must have been successfully allocated. + } else { + // Whatever head value we see here is >= the last value we saw here (relatively), + // and <= its current value. Since we have the most recent tail, the head must be + // <= to it. + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) + || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && + (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { + // We can't enqueue in another block because there's not enough leeway -- the + // tail could surpass the head by the time the block fills up! (Or we'll exceed + // the size limit, if the second part of the condition was true.) + return false; + } + // We're going to need a new block; check that the block index has room + if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize) { + // Hmm, the circular block index is already full -- we'll need + // to allocate a new index. Note pr_blockIndexRaw can only be nullptr if + // the initial allocation failed in the constructor. + + MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { + return false; + } else if (!new_block_index(pr_blockIndexSlotsUsed)) { + return false; + } + } + + // Insert a new block in the circular linked list + auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); + if (newBlock == nullptr) { + return false; + } +#ifdef MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template reset_empty(); + if (this->tailBlock == nullptr) { + newBlock->next = newBlock; + } else { + newBlock->next = this->tailBlock->next; + this->tailBlock->next = newBlock; + } + this->tailBlock = newBlock; + ++pr_blockIndexSlotsUsed; + } + + MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new(static_cast(nullptr)) T( + std::forward(element)))) { + // The constructor may throw. We want the element not to appear in the queue in + // that case (without corrupting the queue): + MOODYCAMEL_TRY { + new((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); + } + MOODYCAMEL_CATCH (...) { + // Revert change to the current block, but leave the new block available + // for next time + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? this->tailBlock : startBlock; + MOODYCAMEL_RETHROW; + } + } else { + (void) startBlock; + (void) originalBlockIndexSlotsUsed; + } + + // Add block to block index + auto &entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; + entry.base = currentTailIndex; + entry.block = this->tailBlock; + blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, + std::memory_order_release); + pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); + + MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new(static_cast(nullptr)) T( + std::forward(element)))) { + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + } + + // Enqueue + new((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); + + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + + template + bool dequeue(U &element) { + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + if (details::circular_less_than( + this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { + // Might be something to dequeue, let's give it a try + + // Note that this if is purely for performance purposes in the common case when the queue is + // empty and the values are eventually consistent -- we may enter here spuriously. + + // Note that whatever the values of overcommit and tail are, they are not going to change (unless we + // change them) and must be the same value at this point (inside the if) as when the if condition was + // evaluated. + + // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below. + // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in + // the fetch_add below will result in a value at least as recent as that (and therefore at least as large). + // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all + // read-modify-write operations are guaranteed to work on the latest value in the modification order), but + // unfortunately that can't be shown to be correct using only the C++11 standard. + // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case + std::atomic_thread_fence(std::memory_order_acquire); + + // Increment optimistic counter, then check if it went over the boundary + auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); + + // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever + // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now + // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon + // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount. + // However, we can't assert this since both dequeueOptimisticCount and dequeueOvercommit may (independently) + // overflow; in such a case, though, the logic still holds since the difference between the two is maintained. + + // Note that we reload tail here in case it changed; it will be the same value as before or greater, since + // this load is sequenced after (happens after) the earlier load above. This is supported by read-read + // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order + tail = this->tailIndex.load(std::memory_order_acquire); + if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { + // Guaranteed to be at least one element to dequeue! + + // Get the index. Note that since there's guaranteed to be at least one element, this + // will never exceed tail. We need to do an acquire-release fence here since it's possible + // that whatever condition got us to this point was for an earlier enqueued element (that + // we already see the memory effects for), but that by the time we increment somebody else + // has incremented it, and we need to see the memory effects for *that* element, which is + // in such a case is necessarily visible on the thread that incremented it in the first + // place with the more current condition (they must have acquired a tail that is at least + // as recent). + auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); + + + // Determine which block the element is in + + auto localBlockIndex = blockIndex.load(std::memory_order_acquire); + auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); + + // We need to be careful here about subtracting and dividing because of index wrap-around. + // When an index wraps, we need to preserve the sign of the offset when dividing it by the + // block size (in order to get a correct signed block count offset in all cases): + auto headBase = localBlockIndex->entries[localBlockIndexHead].base; + auto blockBaseIndex = index & ~static_cast(BLOCK_SIZE - 1); + auto offset = static_cast( + static_cast::type>(blockBaseIndex - headBase) / + BLOCK_SIZE); + auto block = localBlockIndex->entries[(localBlockIndexHead + offset) & + (localBlockIndex->size - 1)].block; + + // Dequeue + auto &el = *((*block)[index]); + if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T &&, element = std::move(el))) { + // Make sure the element is still fully dequeued and destroyed even if the assignment + // throws + struct Guard { + Block *block; + index_t index; + + ~Guard() { + (*block)[index]->~T(); + block->ConcurrentQueue::Block::template set_empty(index); + } + } guard = {block, index}; + + element = std::move(el); // NOLINT + } else { + element = std::move(el); // NOLINT + el.~T(); // NOLINT + block->ConcurrentQueue::Block::template set_empty(index); + } + + return true; + } else { + // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent + this->dequeueOvercommit.fetch_add(1, + std::memory_order_release); // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write + } + } + + return false; + } + + template + bool MOODYCAMEL_NO_TSAN enqueue_bulk(It itemFirst, size_t count) { + // First, we need to make sure we have enough room to enqueue all of the elements; + // this means pre-allocating blocks and putting them in the block index (but only if + // all the allocations succeeded). + index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); + auto startBlock = this->tailBlock; + auto originalBlockIndexFront = pr_blockIndexFront; + auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; + + Block *firstAllocatedBlock = nullptr; + + // Figure out how many blocks we'll need to allocate, and do so + size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - + ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); + index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + if (blockBaseDiff > 0) { + // Allocate as many blocks as possible from ahead + while (blockBaseDiff > 0 && this->tailBlock != nullptr && + this->tailBlock->next != firstAllocatedBlock && + this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { + blockBaseDiff -= static_cast(BLOCK_SIZE); + currentTailIndex += static_cast(BLOCK_SIZE); + + this->tailBlock = this->tailBlock->next; + firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; + + auto &entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; + entry.base = currentTailIndex; + entry.block = this->tailBlock; + pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); + } + + // Now allocate as many blocks as necessary from the block pool + while (blockBaseDiff > 0) { + blockBaseDiff -= static_cast(BLOCK_SIZE); + currentTailIndex += static_cast(BLOCK_SIZE); + + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || + (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && + (MAX_SUBQUEUE_SIZE == 0 || + MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); + if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) { + MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { + // Failed to allocate, undo changes (but keep injected blocks) + pr_blockIndexFront = originalBlockIndexFront; + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; + return false; + } else if (full || !new_block_index(originalBlockIndexSlotsUsed)) { + // Failed to allocate, undo changes (but keep injected blocks) + pr_blockIndexFront = originalBlockIndexFront; + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; + return false; + } + + // pr_blockIndexFront is updated inside new_block_index, so we need to + // update our fallback value too (since we keep the new index even if we + // later fail) + originalBlockIndexFront = originalBlockIndexSlotsUsed; + } + + // Insert a new block in the circular linked list + auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); + if (newBlock == nullptr) { + pr_blockIndexFront = originalBlockIndexFront; + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; + return false; + } + +#ifdef MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template set_all_empty(); + if (this->tailBlock == nullptr) { + newBlock->next = newBlock; + } else { + newBlock->next = this->tailBlock->next; + this->tailBlock->next = newBlock; + } + this->tailBlock = newBlock; + firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; + + ++pr_blockIndexSlotsUsed; + + auto &entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; + entry.base = currentTailIndex; + entry.block = this->tailBlock; + pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); + } + + // Excellent, all allocations succeeded. Reset each block's emptiness before we fill them up, and + // publish the new block index front + auto block = firstAllocatedBlock; + while (true) { + block->ConcurrentQueue::Block::template reset_empty(); + if (block == this->tailBlock) { + break; + } + block = block->next; + } + + MOODYCAMEL_CONSTEXPR_IF (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), + new(static_cast(nullptr)) T( + details::deref_noexcept(itemFirst)))) { + blockIndex.load(std::memory_order_relaxed)->front.store( + (pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); + } + } + + // Enqueue, one block at a time + index_t newTailIndex = startTailIndex + static_cast(count); + currentTailIndex = startTailIndex; + auto endBlock = this->tailBlock; + this->tailBlock = startBlock; + assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || + count == 0); + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { + this->tailBlock = firstAllocatedBlock; + } + while (true) { + index_t stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + + static_cast(BLOCK_SIZE); + if (details::circular_less_than(newTailIndex, stopIndex)) { + stopIndex = newTailIndex; + } + MOODYCAMEL_CONSTEXPR_IF (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), + new(static_cast(nullptr)) T( + details::deref_noexcept(itemFirst)))) { + while (currentTailIndex != stopIndex) { + new((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); + } + } else { + MOODYCAMEL_TRY { + while (currentTailIndex != stopIndex) { + // Must use copy constructor even if move constructor is available + // because we may have to revert if there's an exception. + // Sorry about the horrible templated next line, but it was the only way + // to disable moving *at compile time*, which is important because a type + // may only define a (noexcept) move constructor, and so calls to the + // cctor will not compile, even if they are in an if branch that will never + // be executed + new((*this->tailBlock)[currentTailIndex]) T( + details::nomove_if(nullptr)) T( + details::deref_noexcept( + itemFirst)))>::eval( + *itemFirst)); + ++currentTailIndex; + ++itemFirst; + } + } + MOODYCAMEL_CATCH (...) { + // Oh dear, an exception's been thrown -- destroy the elements that + // were enqueued so far and revert the entire bulk operation (we'll keep + // any allocated blocks in our linked list for later, though). + auto constructedStopIndex = currentTailIndex; + auto lastBlockEnqueued = this->tailBlock; + + pr_blockIndexFront = originalBlockIndexFront; + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; + + if (!details::is_trivially_destructible::value) { + auto block = startBlock; + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + block = firstAllocatedBlock; + } + currentTailIndex = startTailIndex; + while (true) { + stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + + static_cast(BLOCK_SIZE); + if (details::circular_less_than(constructedStopIndex, stopIndex)) { + stopIndex = constructedStopIndex; + } + while (currentTailIndex != stopIndex) { + (*block)[currentTailIndex++]->~T(); + } + if (block == lastBlockEnqueued) { + break; + } + block = block->next; + } + } + MOODYCAMEL_RETHROW; + } + } + + if (this->tailBlock == endBlock) { + assert(currentTailIndex == newTailIndex); + break; + } + this->tailBlock = this->tailBlock->next; + } + + MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), + new(static_cast(nullptr)) T( + details::deref_noexcept(itemFirst)))) { + if (firstAllocatedBlock != nullptr) + blockIndex.load(std::memory_order_relaxed)->front.store( + (pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); + } + + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + + template + size_t dequeue_bulk(It &itemFirst, size_t max) { + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + auto desiredCount = static_cast(tail - + (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - + overcommit)); + if (details::circular_less_than(0, desiredCount)) { + desiredCount = desiredCount < max ? desiredCount : max; + std::atomic_thread_fence(std::memory_order_acquire); + + auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, + std::memory_order_relaxed); + + tail = this->tailIndex.load(std::memory_order_acquire); + auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); + if (details::circular_less_than(0, actualCount)) { + actualCount = desiredCount < actualCount ? desiredCount : actualCount; + if (actualCount < desiredCount) { + this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); + } + + // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this + // will never exceed tail. + auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); + + // Determine which block the first element is in + auto localBlockIndex = blockIndex.load(std::memory_order_acquire); + auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); + + auto headBase = localBlockIndex->entries[localBlockIndexHead].base; + auto firstBlockBaseIndex = firstIndex & ~static_cast(BLOCK_SIZE - 1); + auto offset = static_cast( + static_cast::type>(firstBlockBaseIndex - headBase) / + BLOCK_SIZE); + auto indexIndex = (localBlockIndexHead + offset) & (localBlockIndex->size - 1); + + // Iterate the blocks and dequeue + auto index = firstIndex; + do { + auto firstIndexInBlock = index; + index_t endIndex = + (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than( + firstIndex + static_cast(actualCount), endIndex) ? firstIndex + + static_cast(actualCount) + : endIndex; + auto block = localBlockIndex->entries[indexIndex].block; + if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T &&, details::deref_noexcept(itemFirst) = std::move( + (*(*block)[index])))) { + while (index != endIndex) { + auto &el = *((*block)[index]); + *itemFirst++ = std::move(el); + el.~T(); + ++index; + } + } else { + MOODYCAMEL_TRY { + while (index != endIndex) { + auto &el = *((*block)[index]); + *itemFirst = std::move(el); + ++itemFirst; + el.~T(); + ++index; + } + } + MOODYCAMEL_CATCH (...) { + // It's too late to revert the dequeue, but we can make sure that all + // the dequeued objects are properly destroyed and the block index + // (and empty count) are properly updated before we propagate the exception + do { + block = localBlockIndex->entries[indexIndex].block; + while (index != endIndex) { + (*block)[index++]->~T(); + } + block->ConcurrentQueue::Block::template set_many_empty( + firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); + indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); + + firstIndexInBlock = index; + endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than( + firstIndex + static_cast(actualCount), endIndex) ? firstIndex + + static_cast(actualCount) + : endIndex; + } while (index != firstIndex + actualCount); + + MOODYCAMEL_RETHROW; + } + } + block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, + static_cast( + endIndex - + firstIndexInBlock)); + indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); + } while (index != firstIndex + actualCount); + + return actualCount; + } else { + // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent + this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); + } + } + + return 0; + } + + private: + struct BlockIndexEntry { + index_t base; + Block *block; + }; + + struct BlockIndexHeader { + size_t size; + std::atomic front; // Current slot (not next, like pr_blockIndexFront) + BlockIndexEntry *entries; + void *prev; + }; + + + bool new_block_index(size_t numberOfFilledSlotsToExpose) { + auto prevBlockSizeMask = pr_blockIndexSize - 1; + + // Create the new block + pr_blockIndexSize <<= 1; + auto newRawPtr = static_cast((Traits::malloc)( + sizeof(BlockIndexHeader) + std::alignment_of::value - 1 + + sizeof(BlockIndexEntry) * pr_blockIndexSize)); + if (newRawPtr == nullptr) { + pr_blockIndexSize >>= 1; // Reset to allow graceful retry + return false; + } + + auto newBlockIndexEntries = reinterpret_cast(details::align_for( + newRawPtr + sizeof(BlockIndexHeader))); + + // Copy in all the old indices, if any + size_t j = 0; + if (pr_blockIndexSlotsUsed != 0) { + auto i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & prevBlockSizeMask; + do { + newBlockIndexEntries[j++] = pr_blockIndexEntries[i]; + i = (i + 1) & prevBlockSizeMask; + } while (i != pr_blockIndexFront); + } + + // Update everything + auto header = new(newRawPtr) BlockIndexHeader; + header->size = pr_blockIndexSize; + header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed); + header->entries = newBlockIndexEntries; + header->prev = pr_blockIndexRaw; // we link the new block to the old one so we can free it later + + pr_blockIndexFront = j; + pr_blockIndexEntries = newBlockIndexEntries; + pr_blockIndexRaw = newRawPtr; + blockIndex.store(header, std::memory_order_release); + + return true; + } + + private: + std::atomic blockIndex; + + // To be used by producer only -- consumer must use the ones in referenced by blockIndex + size_t pr_blockIndexSlotsUsed; + size_t pr_blockIndexSize; + size_t pr_blockIndexFront; // Next slot (not current) + BlockIndexEntry *pr_blockIndexEntries; + void *pr_blockIndexRaw; + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + public: + ExplicitProducer* nextExplicitProducer; + private: +#endif + +#ifdef MCDBGQ_TRACKMEM + friend struct MemStats; +#endif + }; + + + ////////////////////////////////// + // Implicit queue + ////////////////////////////////// + + struct ImplicitProducer : public ProducerBase { + ImplicitProducer(ConcurrentQueue *parent_) : + ProducerBase(parent_, false), + nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE), + blockIndex(nullptr) { + new_block_index(); + } + + ~ImplicitProducer() { + // Note that since we're in the destructor we can assume that all enqueue/dequeue operations + // completed already; this means that all undequeued elements are placed contiguously across + // contiguous blocks, and that only the first and last remaining blocks can be only partially + // empty (all other remaining blocks must be completely full). + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + // Unregister ourselves for thread termination notification + if (!this->inactive.load(std::memory_order_relaxed)) { + details::ThreadExitNotifier::unsubscribe(&threadExitListener); + } +#endif + + // Destroy all remaining elements! + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto index = this->headIndex.load(std::memory_order_relaxed); + Block *block = nullptr; + assert(index == tail || details::circular_less_than(index, tail)); + bool forceFreeLastBlock = + index != tail; // If we enter the loop, then the last (tail) block will not be freed + while (index != tail) { + if ((index & static_cast(BLOCK_SIZE - 1)) == 0 || block == nullptr) { + if (block != nullptr) { + // Free the old block + this->parent->add_block_to_free_list(block); + } + + block = get_block_index_entry_for_index(index)->value.load(std::memory_order_relaxed); + } + + ((*block)[index])->~T(); + ++index; + } + // Even if the queue is empty, there's still one block that's not on the free list + // (unless the head index reached the end of it, in which case the tail will be poised + // to create a new block). + if (this->tailBlock != nullptr && + (forceFreeLastBlock || (tail & static_cast(BLOCK_SIZE - 1)) != 0)) { + this->parent->add_block_to_free_list(this->tailBlock); + } + + // Destroy block index + auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); + if (localBlockIndex != nullptr) { + for (size_t i = 0; i != localBlockIndex->capacity; ++i) { + localBlockIndex->index[i]->~BlockIndexEntry(); + } + do { + auto prev = localBlockIndex->prev; + localBlockIndex->~BlockIndexHeader(); + (Traits::free)(localBlockIndex); + localBlockIndex = prev; + } while (localBlockIndex != nullptr); + } + } + + template + inline bool enqueue(U &&element) { + index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); + index_t newTailIndex = 1 + currentTailIndex; + if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + // We reached the end of a block, start a new one + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || + (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && + (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { + return false; + } +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + // Find out where we'll be inserting this block in the block index + BlockIndexEntry *idxEntry; + if (!insert_block_index_entry(idxEntry, currentTailIndex)) { + return false; + } + + // Get ahold of a new block + auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); + if (newBlock == nullptr) { + rewind_block_index_tail(); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + return false; + } +#ifdef MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template reset_empty(); + + MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new(static_cast(nullptr)) T( + std::forward(element)))) { + // May throw, try to insert now before we publish the fact that we have this new block + MOODYCAMEL_TRY { + new((*newBlock)[currentTailIndex]) T(std::forward(element)); + } + MOODYCAMEL_CATCH (...) { + rewind_block_index_tail(); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + this->parent->add_block_to_free_list(newBlock); + MOODYCAMEL_RETHROW; + } + } + + // Insert the new block into the index + idxEntry->value.store(newBlock, std::memory_order_relaxed); + + this->tailBlock = newBlock; + + MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new(static_cast(nullptr)) T( + std::forward(element)))) { + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + } + + // Enqueue + new((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); + + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + + template + bool dequeue(U &element) { + // See ExplicitProducer::dequeue for rationale and explanation + index_t tail = this->tailIndex.load(std::memory_order_relaxed); + index_t overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + if (details::circular_less_than( + this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { + std::atomic_thread_fence(std::memory_order_acquire); + + index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); + tail = this->tailIndex.load(std::memory_order_acquire); + if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { + index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); + + // Determine which block the element is in + auto entry = get_block_index_entry_for_index(index); + + // Dequeue + auto block = entry->value.load(std::memory_order_relaxed); + auto &el = *((*block)[index]); + + if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T &&, element = std::move(el))) { +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + // Note: Acquiring the mutex with every dequeue instead of only when a block + // is released is very sub-optimal, but it is, after all, purely debug code. + debug::DebugLock lock(producer->mutex); +#endif + struct Guard { + Block *block; + index_t index; + BlockIndexEntry *entry; + ConcurrentQueue *parent; + + ~Guard() { + (*block)[index]->~T(); + if (block->ConcurrentQueue::Block::template set_empty(index)) { + entry->value.store(nullptr, std::memory_order_relaxed); + parent->add_block_to_free_list(block); + } + } + } guard = {block, index, entry, this->parent}; + + element = std::move(el); // NOLINT + } else { + element = std::move(el); // NOLINT + el.~T(); // NOLINT + + if (block->ConcurrentQueue::Block::template set_empty(index)) { + { +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + // Add the block back into the global free pool (and remove from block index) + entry->value.store(nullptr, std::memory_order_relaxed); + } + this->parent->add_block_to_free_list(block); // releases the above store + } + } + + return true; + } else { + this->dequeueOvercommit.fetch_add(1, std::memory_order_release); + } + } + + return false; + } + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4706) // assignment within conditional expression +#endif + + template + bool enqueue_bulk(It itemFirst, size_t count) { + // First, we need to make sure we have enough room to enqueue all of the elements; + // this means pre-allocating blocks and putting them in the block index (but only if + // all the allocations succeeded). + + // Note that the tailBlock we start off with may not be owned by us any more; + // this happens if it was filled up exactly to the top (setting tailIndex to + // the first index of the next block which is not yet allocated), then dequeued + // completely (putting it on the free list) before we enqueue again. + + index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); + auto startBlock = this->tailBlock; + Block *firstAllocatedBlock = nullptr; + auto endBlock = this->tailBlock; + + // Figure out how many blocks we'll need to allocate, and do so + size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - + ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); + index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + if (blockBaseDiff > 0) { +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + do { + blockBaseDiff -= static_cast(BLOCK_SIZE); + currentTailIndex += static_cast(BLOCK_SIZE); + + // Find out where we'll be inserting this block in the block index + BlockIndexEntry *idxEntry = nullptr; // initialization here unnecessary but compiler can't always tell + Block *newBlock; + bool indexInserted = false; + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || + (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && + (MAX_SUBQUEUE_SIZE == 0 || + MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); + + if (full || + !(indexInserted = insert_block_index_entry(idxEntry, currentTailIndex)) || + (newBlock = this->parent->ConcurrentQueue::template requisition_block()) == + nullptr) { + // Index allocation or block allocation failed; revert any other allocations + // and index insertions done so far for this operation + if (indexInserted) { + rewind_block_index_tail(); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + } + currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { + currentTailIndex += static_cast(BLOCK_SIZE); + idxEntry = get_block_index_entry_for_index(currentTailIndex); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + rewind_block_index_tail(); + } + this->parent->add_blocks_to_free_list(firstAllocatedBlock); + this->tailBlock = startBlock; + + return false; + } + +#ifdef MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template reset_empty(); + newBlock->next = nullptr; + + // Insert the new block into the index + idxEntry->value.store(newBlock, std::memory_order_relaxed); + + // Store the chain of blocks so that we can undo if later allocations fail, + // and so that we can find the blocks when we do the actual enqueueing + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || + firstAllocatedBlock != nullptr) { + assert(this->tailBlock != nullptr); + this->tailBlock->next = newBlock; + } + this->tailBlock = newBlock; + endBlock = newBlock; + firstAllocatedBlock = firstAllocatedBlock == nullptr ? newBlock : firstAllocatedBlock; + } while (blockBaseDiff > 0); + } + + // Enqueue, one block at a time + index_t newTailIndex = startTailIndex + static_cast(count); + currentTailIndex = startTailIndex; + this->tailBlock = startBlock; + assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || + count == 0); + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { + this->tailBlock = firstAllocatedBlock; + } + while (true) { + index_t stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + + static_cast(BLOCK_SIZE); + if (details::circular_less_than(newTailIndex, stopIndex)) { + stopIndex = newTailIndex; + } + MOODYCAMEL_CONSTEXPR_IF (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), + new(static_cast(nullptr)) T( + details::deref_noexcept(itemFirst)))) { + while (currentTailIndex != stopIndex) { + new((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); + } + } else { + MOODYCAMEL_TRY { + while (currentTailIndex != stopIndex) { + new((*this->tailBlock)[currentTailIndex]) T( + details::nomove_if(nullptr)) T( + details::deref_noexcept( + itemFirst)))>::eval( + *itemFirst)); + ++currentTailIndex; + ++itemFirst; + } + } + MOODYCAMEL_CATCH (...) { + auto constructedStopIndex = currentTailIndex; + auto lastBlockEnqueued = this->tailBlock; + + if (!details::is_trivially_destructible::value) { + auto block = startBlock; + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + block = firstAllocatedBlock; + } + currentTailIndex = startTailIndex; + while (true) { + stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + + static_cast(BLOCK_SIZE); + if (details::circular_less_than(constructedStopIndex, stopIndex)) { + stopIndex = constructedStopIndex; + } + while (currentTailIndex != stopIndex) { + (*block)[currentTailIndex++]->~T(); + } + if (block == lastBlockEnqueued) { + break; + } + block = block->next; + } + } + + currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { + currentTailIndex += static_cast(BLOCK_SIZE); + auto idxEntry = get_block_index_entry_for_index(currentTailIndex); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + rewind_block_index_tail(); + } + this->parent->add_blocks_to_free_list(firstAllocatedBlock); + this->tailBlock = startBlock; + MOODYCAMEL_RETHROW; + } + } + + if (this->tailBlock == endBlock) { + assert(currentTailIndex == newTailIndex); + break; + } + this->tailBlock = this->tailBlock->next; + } + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + template + size_t dequeue_bulk(It &itemFirst, size_t max) { + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + auto desiredCount = static_cast(tail - + (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - + overcommit)); + if (details::circular_less_than(0, desiredCount)) { + desiredCount = desiredCount < max ? desiredCount : max; + std::atomic_thread_fence(std::memory_order_acquire); + + auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, + std::memory_order_relaxed); + + tail = this->tailIndex.load(std::memory_order_acquire); + auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); + if (details::circular_less_than(0, actualCount)) { + actualCount = desiredCount < actualCount ? desiredCount : actualCount; + if (actualCount < desiredCount) { + this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); + } + + // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this + // will never exceed tail. + auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); + + // Iterate the blocks and dequeue + auto index = firstIndex; + BlockIndexHeader *localBlockIndex; + auto indexIndex = get_block_index_index_for_index(index, localBlockIndex); + do { + auto blockStartIndex = index; + index_t endIndex = + (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than( + firstIndex + static_cast(actualCount), endIndex) ? firstIndex + + static_cast(actualCount) + : endIndex; + + auto entry = localBlockIndex->index[indexIndex]; + auto block = entry->value.load(std::memory_order_relaxed); + if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T &&, details::deref_noexcept(itemFirst) = std::move( + (*(*block)[index])))) { + while (index != endIndex) { + auto &el = *((*block)[index]); + *itemFirst++ = std::move(el); + el.~T(); + ++index; + } + } else { + MOODYCAMEL_TRY { + while (index != endIndex) { + auto &el = *((*block)[index]); + *itemFirst = std::move(el); + ++itemFirst; + el.~T(); + ++index; + } + } + MOODYCAMEL_CATCH (...) { + do { + entry = localBlockIndex->index[indexIndex]; + block = entry->value.load(std::memory_order_relaxed); + while (index != endIndex) { + (*block)[index++]->~T(); + } + + if (block->ConcurrentQueue::Block::template set_many_empty( + blockStartIndex, static_cast(endIndex - blockStartIndex))) { +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + entry->value.store(nullptr, std::memory_order_relaxed); + this->parent->add_block_to_free_list(block); + } + indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); + + blockStartIndex = index; + endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than( + firstIndex + static_cast(actualCount), endIndex) ? firstIndex + + static_cast(actualCount) + : endIndex; + } while (index != firstIndex + actualCount); + + MOODYCAMEL_RETHROW; + } + } + if (block->ConcurrentQueue::Block::template set_many_empty( + blockStartIndex, static_cast(endIndex - blockStartIndex))) { + { +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + // Note that the set_many_empty above did a release, meaning that anybody who acquires the block + // we're about to free can use it safely since our writes (and reads!) will have happened-before then. + entry->value.store(nullptr, std::memory_order_relaxed); + } + this->parent->add_block_to_free_list(block); // releases the above store + } + indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); + } while (index != firstIndex + actualCount); + + return actualCount; + } else { + this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); + } + } + + return 0; + } + + private: + // The block size must be > 1, so any number with the low bit set is an invalid block base index + static const index_t INVALID_BLOCK_BASE = 1; + + struct BlockIndexEntry { + std::atomic key; + std::atomic value; + }; + + struct BlockIndexHeader { + size_t capacity; + std::atomic tail; + BlockIndexEntry *entries; + BlockIndexEntry **index; + BlockIndexHeader *prev; + }; + + template + inline bool insert_block_index_entry(BlockIndexEntry *&idxEntry, index_t blockStartIndex) { + auto localBlockIndex = blockIndex.load( + std::memory_order_relaxed); // We're the only writer thread, relaxed is OK + if (localBlockIndex == nullptr) { + return false; // this can happen if new_block_index failed in the constructor + } + size_t newTail = + (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); + idxEntry = localBlockIndex->index[newTail]; + if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE || + idxEntry->value.load(std::memory_order_relaxed) == nullptr) { + + idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); + localBlockIndex->tail.store(newTail, std::memory_order_release); + return true; + } + + // No room in the old block index, try to allocate another one! + MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { + return false; + } else if (!new_block_index()) { + return false; + } + localBlockIndex = blockIndex.load(std::memory_order_relaxed); + newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); + idxEntry = localBlockIndex->index[newTail]; + assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE); + idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); + localBlockIndex->tail.store(newTail, std::memory_order_release); + return true; + } + + inline void rewind_block_index_tail() { + auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); + localBlockIndex->tail.store( + (localBlockIndex->tail.load(std::memory_order_relaxed) - 1) & (localBlockIndex->capacity - 1), + std::memory_order_relaxed); + } + + inline BlockIndexEntry *get_block_index_entry_for_index(index_t index) const { + BlockIndexHeader *localBlockIndex; + auto idx = get_block_index_index_for_index(index, localBlockIndex); + return localBlockIndex->index[idx]; + } + + inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader *&localBlockIndex) const { +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + index &= ~static_cast(BLOCK_SIZE - 1); + localBlockIndex = blockIndex.load(std::memory_order_acquire); + auto tail = localBlockIndex->tail.load(std::memory_order_acquire); + auto tailBase = localBlockIndex->index[tail]->key.load(std::memory_order_relaxed); + assert(tailBase != INVALID_BLOCK_BASE); + // Note: Must use division instead of shift because the index may wrap around, causing a negative + // offset, whose negativity we want to preserve + auto offset = static_cast( + static_cast::type>(index - tailBase) / BLOCK_SIZE); + size_t idx = (tail + offset) & (localBlockIndex->capacity - 1); + assert(localBlockIndex->index[idx]->key.load(std::memory_order_relaxed) == index && + localBlockIndex->index[idx]->value.load(std::memory_order_relaxed) != nullptr); + return idx; + } + + bool new_block_index() { + auto prev = blockIndex.load(std::memory_order_relaxed); + size_t prevCapacity = prev == nullptr ? 0 : prev->capacity; + auto entryCount = prev == nullptr ? nextBlockIndexCapacity : prevCapacity; + auto raw = static_cast((Traits::malloc)( + sizeof(BlockIndexHeader) + + std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * entryCount + + std::alignment_of::value - 1 + + sizeof(BlockIndexEntry *) * nextBlockIndexCapacity)); + if (raw == nullptr) { + return false; + } + + auto header = new(raw) BlockIndexHeader; + auto entries = reinterpret_cast(details::align_for( + raw + sizeof(BlockIndexHeader))); + auto index = reinterpret_cast(details::align_for( + reinterpret_cast(entries) + sizeof(BlockIndexEntry) * entryCount)); + if (prev != nullptr) { + auto prevTail = prev->tail.load(std::memory_order_relaxed); + auto prevPos = prevTail; + size_t i = 0; + do { + prevPos = (prevPos + 1) & (prev->capacity - 1); + index[i++] = prev->index[prevPos]; + } while (prevPos != prevTail); + assert(i == prevCapacity); + } + for (size_t i = 0; i != entryCount; ++i) { + new(entries + i) BlockIndexEntry; + entries[i].key.store(INVALID_BLOCK_BASE, std::memory_order_relaxed); + index[prevCapacity + i] = entries + i; + } + header->prev = prev; + header->entries = entries; + header->index = index; + header->capacity = nextBlockIndexCapacity; + header->tail.store((prevCapacity - 1) & (nextBlockIndexCapacity - 1), std::memory_order_relaxed); + + blockIndex.store(header, std::memory_order_release); + + nextBlockIndexCapacity <<= 1; + + return true; + } + + private: + size_t nextBlockIndexCapacity; + std::atomic blockIndex; + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + public: + details::ThreadExitListener threadExitListener; + private: +#endif + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + public: + ImplicitProducer* nextImplicitProducer; + private: +#endif + +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + mutable debug::DebugMutex mutex; +#endif +#ifdef MCDBGQ_TRACKMEM + friend struct MemStats; +#endif + }; + + + ////////////////////////////////// + // Block pool manipulation + ////////////////////////////////// + + void populate_initial_block_list(size_t blockCount) { + initialBlockPoolSize = blockCount; + if (initialBlockPoolSize == 0) { + initialBlockPool = nullptr; + return; + } + + initialBlockPool = create_array(blockCount); + if (initialBlockPool == nullptr) { + initialBlockPoolSize = 0; + } + for (size_t i = 0; i < initialBlockPoolSize; ++i) { + initialBlockPool[i].dynamicallyAllocated = false; + } + } + + inline Block *try_get_block_from_initial_pool() { + if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) { + return nullptr; + } + + auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed); + + return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr; + } + + inline void add_block_to_free_list(Block *block) { +#ifdef MCDBGQ_TRACKMEM + block->owner = nullptr; +#endif + freeList.add(block); + } + + inline void add_blocks_to_free_list(Block *block) { + while (block != nullptr) { + auto next = block->next; + add_block_to_free_list(block); + block = next; + } + } + + inline Block *try_get_block_from_free_list() { + return freeList.try_get(); + } + + // Gets a free block from one of the memory pools, or allocates a new one (if applicable) + template + Block *requisition_block() { + auto block = try_get_block_from_initial_pool(); + if (block != nullptr) { + return block; + } + + block = try_get_block_from_free_list(); + if (block != nullptr) { + return block; + } + + MOODYCAMEL_CONSTEXPR_IF (canAlloc == CanAlloc) { + return create(); + } else { + return nullptr; + } + } + + +#ifdef MCDBGQ_TRACKMEM + public: + struct MemStats { + size_t allocatedBlocks; + size_t usedBlocks; + size_t freeBlocks; + size_t ownedBlocksExplicit; + size_t ownedBlocksImplicit; + size_t implicitProducers; + size_t explicitProducers; + size_t elementsEnqueued; + size_t blockClassBytes; + size_t queueClassBytes; + size_t implicitBlockIndexBytes; + size_t explicitBlockIndexBytes; + + friend class ConcurrentQueue; + + private: + static MemStats getFor(ConcurrentQueue* q) + { + MemStats stats = { 0 }; + + stats.elementsEnqueued = q->size_approx(); + + auto block = q->freeList.head_unsafe(); + while (block != nullptr) { + ++stats.allocatedBlocks; + ++stats.freeBlocks; + block = block->freeListNext.load(std::memory_order_relaxed); + } + + for (auto ptr = q->producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + bool implicit = dynamic_cast(ptr) != nullptr; + stats.implicitProducers += implicit ? 1 : 0; + stats.explicitProducers += implicit ? 0 : 1; + + if (implicit) { + auto prod = static_cast(ptr); + stats.queueClassBytes += sizeof(ImplicitProducer); + auto head = prod->headIndex.load(std::memory_order_relaxed); + auto tail = prod->tailIndex.load(std::memory_order_relaxed); + auto hash = prod->blockIndex.load(std::memory_order_relaxed); + if (hash != nullptr) { + for (size_t i = 0; i != hash->capacity; ++i) { + if (hash->index[i]->key.load(std::memory_order_relaxed) != ImplicitProducer::INVALID_BLOCK_BASE && hash->index[i]->value.load(std::memory_order_relaxed) != nullptr) { + ++stats.allocatedBlocks; + ++stats.ownedBlocksImplicit; + } + } + stats.implicitBlockIndexBytes += hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry); + for (; hash != nullptr; hash = hash->prev) { + stats.implicitBlockIndexBytes += sizeof(typename ImplicitProducer::BlockIndexHeader) + hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry*); + } + } + for (; details::circular_less_than(head, tail); head += BLOCK_SIZE) { + //auto block = prod->get_block_index_entry_for_index(head); + ++stats.usedBlocks; + } + } + else { + auto prod = static_cast(ptr); + stats.queueClassBytes += sizeof(ExplicitProducer); + auto tailBlock = prod->tailBlock; + bool wasNonEmpty = false; + if (tailBlock != nullptr) { + auto block = tailBlock; + do { + ++stats.allocatedBlocks; + if (!block->ConcurrentQueue::Block::template is_empty() || wasNonEmpty) { + ++stats.usedBlocks; + wasNonEmpty = wasNonEmpty || block != tailBlock; + } + ++stats.ownedBlocksExplicit; + block = block->next; + } while (block != tailBlock); + } + auto index = prod->blockIndex.load(std::memory_order_relaxed); + while (index != nullptr) { + stats.explicitBlockIndexBytes += sizeof(typename ExplicitProducer::BlockIndexHeader) + index->size * sizeof(typename ExplicitProducer::BlockIndexEntry); + index = static_cast(index->prev); + } + } + } + + auto freeOnInitialPool = q->initialBlockPoolIndex.load(std::memory_order_relaxed) >= q->initialBlockPoolSize ? 0 : q->initialBlockPoolSize - q->initialBlockPoolIndex.load(std::memory_order_relaxed); + stats.allocatedBlocks += freeOnInitialPool; + stats.freeBlocks += freeOnInitialPool; + + stats.blockClassBytes = sizeof(Block) * stats.allocatedBlocks; + stats.queueClassBytes += sizeof(ConcurrentQueue); + + return stats; + } + }; + + // For debugging only. Not thread-safe. + MemStats getMemStats() + { + return MemStats::getFor(this); + } + private: + friend struct MemStats; +#endif + + + ////////////////////////////////// + // Producer list manipulation + ////////////////////////////////// + + ProducerBase *recycle_or_create_producer(bool isExplicit) { + bool recycled; + return recycle_or_create_producer(isExplicit, recycled); + } + + ProducerBase *recycle_or_create_producer(bool isExplicit, bool &recycled) { +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugLock lock(implicitProdMutex); +#endif + // Try to re-use one first + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) { + bool expected = true; + if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, + std::memory_order_relaxed)) { + // We caught one! It's been marked as activated, the caller can have it + recycled = true; + return ptr; + } + } + } + + recycled = false; + return add_producer( + isExplicit ? static_cast(create(this)) : create( + this)); + } + + ProducerBase *add_producer(ProducerBase *producer) { + // Handle failed memory allocation + if (producer == nullptr) { + return nullptr; + } + + producerCount.fetch_add(1, std::memory_order_relaxed); + + // Add it to the lock-free list + auto prevTail = producerListTail.load(std::memory_order_relaxed); + do { + producer->next = prevTail; + } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, + std::memory_order_relaxed)); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + if (producer->isExplicit) { + auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed); + do { + static_cast(producer)->nextExplicitProducer = prevTailExplicit; + } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); + } + else { + auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed); + do { + static_cast(producer)->nextImplicitProducer = prevTailImplicit; + } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); + } +#endif + + return producer; + } + + void reown_producers() { + // After another instance is moved-into/swapped-with this one, all the + // producers we stole still think their parents are the other queue. + // So fix them up! + for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) { + ptr->parent = this; + } + } + + + ////////////////////////////////// + // Implicit producer hash + ////////////////////////////////// + + struct ImplicitProducerKVP { + std::atomic key; + ImplicitProducer *value; // No need for atomicity since it's only read by the thread that sets it in the first place + + ImplicitProducerKVP() : value(nullptr) {} + + ImplicitProducerKVP(ImplicitProducerKVP &&other) + + MOODYCAMEL_NOEXCEPT + { + key.store(other.key.load(std::memory_order_relaxed), std::memory_order_relaxed); + value = other.value; + } + + inline ImplicitProducerKVP &operator=(ImplicitProducerKVP &&other) + + MOODYCAMEL_NOEXCEPT + { + swap(other); + return *this; + } + + inline void swap(ImplicitProducerKVP &other) + + MOODYCAMEL_NOEXCEPT + { + if (this != &other) { + details::swap_relaxed(key, other.key); + std::swap(value, other.value); + } + } + }; + + template + friend void moodycamel::swap(typename ConcurrentQueue::ImplicitProducerKVP &, + typename ConcurrentQueue::ImplicitProducerKVP &) + + MOODYCAMEL_NOEXCEPT; + + struct ImplicitProducerHash { + size_t capacity; + ImplicitProducerKVP *entries; + ImplicitProducerHash *prev; + }; + + inline void populate_initial_implicit_producer_hash() { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) { + return; + } else { + implicitProducerHashCount.store(0, std::memory_order_relaxed); + auto hash = &initialImplicitProducerHash; + hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; + hash->entries = &initialImplicitProducerHashEntries[0]; + for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) { + initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, + std::memory_order_relaxed); + } + hash->prev = nullptr; + implicitProducerHash.store(hash, std::memory_order_relaxed); + } + } + + void swap_implicit_producer_hashes(ConcurrentQueue &other) { + MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) { + return; + } else { + // Swap (assumes our implicit producer hash is initialized) + initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries); + initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0]; + other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0]; + + details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount); + + details::swap_relaxed(implicitProducerHash, other.implicitProducerHash); + if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) { + implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed); + } else { + ImplicitProducerHash *hash; + for (hash = implicitProducerHash.load(std::memory_order_relaxed); + hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) { + continue; + } + hash->prev = &initialImplicitProducerHash; + } + if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) { + other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed); + } else { + ImplicitProducerHash *hash; + for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); + hash->prev != &initialImplicitProducerHash; hash = hash->prev) { + continue; + } + hash->prev = &other.initialImplicitProducerHash; + } + } + } + + // Only fails (returns nullptr) if memory allocation fails + ImplicitProducer *get_or_add_implicit_producer() { + // Note that since the data is essentially thread-local (key is thread ID), + // there's a reduced need for fences (memory ordering is already consistent + // for any individual thread), except for the current table itself. + + // Start by looking for the thread ID in the current and all previous hash tables. + // If it's not found, it must not be in there yet, since this same thread would + // have added it previously to one of the tables that we traversed. + + // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table + +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugLock lock(implicitProdMutex); +#endif + + auto id = details::thread_id(); + auto hashedId = details::hash_thread_id(id); + + auto mainHash = implicitProducerHash.load(std::memory_order_acquire); + assert(mainHash != nullptr); // silence clang-tidy and MSVC warnings (hash cannot be null) + for (auto hash = mainHash; hash != nullptr; hash = hash->prev) { + // Look for the id in this hash + auto index = hashedId; + while (true) { // Not an infinite loop because at least one slot is free in the hash table + index &= hash->capacity - 1; + + auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed); + if (probedKey == id) { + // Found it! If we had to search several hashes deep, though, we should lazily add it + // to the current main hash table to avoid the extended search next time. + // Note there's guaranteed to be room in the current hash table since every subsequent + // table implicitly reserves space for all previous tables (there's only one + // implicitProducerHashCount). + auto value = hash->entries[index].value; + if (hash != mainHash) { + index = hashedId; + while (true) { + index &= mainHash->capacity - 1; + probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); + auto empty = details::invalid_thread_id; +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + auto reusable = details::invalid_thread_id2; + if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed)) || + (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) { +#else + if ((probedKey == empty && + mainHash->entries[index].key.compare_exchange_strong(empty, id, + std::memory_order_relaxed, + std::memory_order_relaxed))) { +#endif + mainHash->entries[index].value = value; + break; + } + ++index; + } + } + + return value; + } + if (probedKey == details::invalid_thread_id) { + break; // Not in this hash table + } + ++index; + } + } + + // Insert! + auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed); + while (true) { + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) + if (newCount >= (mainHash->capacity >> 1) && + !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) { + // We've acquired the resize lock, try to allocate a bigger hash table. + // Note the acquire fence synchronizes with the release fence at the end of this block, and hence when + // we reload implicitProducerHash it must be the most recent version (it only gets changed within this + // locked block). + mainHash = implicitProducerHash.load(std::memory_order_acquire); + if (newCount >= (mainHash->capacity >> 1)) { + auto newCapacity = mainHash->capacity << 1; + while (newCount >= (newCapacity >> 1)) { + newCapacity <<= 1; + } + auto raw = static_cast((Traits::malloc)( + sizeof(ImplicitProducerHash) + std::alignment_of::value - 1 + + sizeof(ImplicitProducerKVP) * newCapacity)); + if (raw == nullptr) { + // Allocation failed + implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + return nullptr; + } + + auto newHash = new(raw) ImplicitProducerHash; + newHash->capacity = static_cast(newCapacity); + newHash->entries = reinterpret_cast(details::align_for( + raw + sizeof(ImplicitProducerHash))); + for (size_t i = 0; i != newCapacity; ++i) { + new(newHash->entries + i) ImplicitProducerKVP; + newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); + } + newHash->prev = mainHash; + implicitProducerHash.store(newHash, std::memory_order_release); + implicitProducerHashResizeInProgress.clear(std::memory_order_release); + mainHash = newHash; + } else { + implicitProducerHashResizeInProgress.clear(std::memory_order_release); + } + } + + // If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table + // to finish being allocated by another thread (and if we just finished allocating above, the condition will + // always be true) + if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) { + bool recycled; + auto producer = static_cast(recycle_or_create_producer(false, recycled)); + if (producer == nullptr) { + implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); + return nullptr; + } + if (recycled) { + implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); + } + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + producer->threadExitListener.callback = &ConcurrentQueue::implicit_producer_thread_exited_callback; + producer->threadExitListener.userData = producer; + details::ThreadExitNotifier::subscribe(&producer->threadExitListener); +#endif + + auto index = hashedId; + while (true) { + index &= mainHash->capacity - 1; + auto probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); + + auto empty = details::invalid_thread_id; +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + auto reusable = details::invalid_thread_id2; + if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed)) || + (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) { +#else + if ((probedKey == empty && + mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, + std::memory_order_relaxed))) { +#endif + mainHash->entries[index].value = producer; + break; + } + ++index; + } + return producer; + } + + // Hmm, the old hash is quite full and somebody else is busy allocating a new one. + // We need to wait for the allocating thread to finish (if it succeeds, we add, if not, + // we try to allocate ourselves). + mainHash = implicitProducerHash.load(std::memory_order_acquire); + } + } + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + void implicit_producer_thread_exited(ImplicitProducer* producer) + { + // Remove from thread exit listeners + details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener); + + // Remove from hash +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugLock lock(implicitProdMutex); +#endif + auto hash = implicitProducerHash.load(std::memory_order_acquire); + assert(hash != nullptr); // The thread exit listener is only registered if we were added to a hash in the first place + auto id = details::thread_id(); + auto hashedId = details::hash_thread_id(id); + details::thread_id_t probedKey; + + // We need to traverse all the hashes just in case other threads aren't on the current one yet and are + // trying to add an entry thinking there's a free slot (because they reused a producer) + for (; hash != nullptr; hash = hash->prev) { + auto index = hashedId; + do { + index &= hash->capacity - 1; + probedKey = hash->entries[index].key.load(std::memory_order_relaxed); + if (probedKey == id) { + hash->entries[index].key.store(details::invalid_thread_id2, std::memory_order_release); + break; + } + ++index; + } while (probedKey != details::invalid_thread_id); // Can happen if the hash has changed but we weren't put back in it yet, or if we weren't added to this hash in the first place + } + + // Mark the queue as being recyclable + producer->inactive.store(true, std::memory_order_release); + } + + static void implicit_producer_thread_exited_callback(void* userData) + { + auto producer = static_cast(userData); + auto queue = producer->parent; + queue->implicit_producer_thread_exited(producer); + } +#endif + + ////////////////////////////////// + // Utility functions + ////////////////////////////////// + + template + static inline void *aligned_malloc(size_t size) { + MOODYCAMEL_CONSTEXPR_IF (std::alignment_of::value <= + std::alignment_of::value)return (Traits::malloc)(size); + else { + size_t alignment = std::alignment_of::value; + void *raw = (Traits::malloc)(size + alignment - 1 + sizeof(void *)); + if (!raw) + return nullptr; + char *ptr = details::align_for(reinterpret_cast(raw) + sizeof(void *)); + *(reinterpret_cast(ptr) - 1) = raw; + return ptr; + } + } + + template + static inline void aligned_free(void *ptr) { + MOODYCAMEL_CONSTEXPR_IF (std::alignment_of::value <= + std::alignment_of::value)return (Traits::free)(ptr); + else + (Traits::free)(ptr ? *(reinterpret_cast(ptr) - 1) : nullptr); + } + + template + static inline U *create_array(size_t count) { + assert(count > 0); + U *p = static_cast(aligned_malloc(sizeof(U) * count)); + if (p == nullptr) + return nullptr; + + for (size_t i = 0; i != count; ++i) + new(p + i) U(); + return p; + } + + template + static inline void destroy_array(U *p, size_t count) { + if (p != nullptr) { + assert(count > 0); + for (size_t i = count; i != 0;) + (p + --i)->~U(); + } + aligned_free(p); + } + + template + static inline U *create() { + void *p = aligned_malloc(sizeof(U)); + return p != nullptr ? new(p) U : nullptr; + } + + template + static inline U *create(A1 &&a1) { + void *p = aligned_malloc(sizeof(U)); + return p != nullptr ? new(p) U(std::forward(a1)) : nullptr; + } + + template + static inline void destroy(U *p) { + if (p != nullptr) + p->~U(); + aligned_free(p); + } + + private: + std::atomic producerListTail; + std::atomic producerCount; + + std::atomic initialBlockPoolIndex; + Block *initialBlockPool; + size_t initialBlockPoolSize; + +#ifndef MCDBGQ_USEDEBUGFREELIST + FreeList freeList; +#else + debug::DebugFreeList freeList; +#endif + + std::atomic implicitProducerHash; + std::atomic implicitProducerHashCount; // Number of slots logically used + ImplicitProducerHash initialImplicitProducerHash; + std::array initialImplicitProducerHashEntries; + std::atomic_flag implicitProducerHashResizeInProgress; + + std::atomic nextExplicitConsumerId; + std::atomic globalExplicitConsumerOffset; + +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugMutex implicitProdMutex; +#endif + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + std::atomic explicitProducers; + std::atomic implicitProducers; +#endif + }; + + + template + ProducerToken::ProducerToken(ConcurrentQueue &queue) + : producer(queue.recycle_or_create_producer(true)) { + if (producer != nullptr) { + producer->token = this; + } + } + + template + ProducerToken::ProducerToken(BlockingConcurrentQueue &queue) + : producer(reinterpret_cast *>(&queue)->recycle_or_create_producer(true)) { + if (producer != nullptr) { + producer->token = this; + } + } + + template + ConsumerToken::ConsumerToken(ConcurrentQueue &queue) + : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) { + initialOffset = queue.nextExplicitConsumerId.fetch_add(1, std::memory_order_release); + lastKnownGlobalOffset = static_cast(-1); + } + + template + ConsumerToken::ConsumerToken(BlockingConcurrentQueue &queue) + : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) { + initialOffset = reinterpret_cast *>(&queue)->nextExplicitConsumerId.fetch_add(1, + std::memory_order_release); + lastKnownGlobalOffset = static_cast(-1); + } + + template + inline void swap(ConcurrentQueue &a, ConcurrentQueue &b) + + MOODYCAMEL_NOEXCEPT { + a. + swap(b); +} + +inline void swap(ProducerToken &a, ProducerToken &b) + +MOODYCAMEL_NOEXCEPT +{ +a. +swap(b); +} + +inline void swap(ConsumerToken &a, ConsumerToken &b) + +MOODYCAMEL_NOEXCEPT +{ +a. +swap(b); +} + +template +inline void swap(typename ConcurrentQueue::ImplicitProducerKVP &a, + typename ConcurrentQueue::ImplicitProducerKVP &b) + +MOODYCAMEL_NOEXCEPT +{ +a. +swap(b); +} + +} + +#if defined(_MSC_VER) && (!defined(_HAS_CXX17) || !_HAS_CXX17) +#pragma warning(pop) +#endif + +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +#pragma GCC diagnostic pop +#endif \ No newline at end of file diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 25cd7691..8c1c87ac 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -27,6 +27,7 @@ #include "third_party/readerwriterqueue.h" +#include "third_party/concurrentqueue.h" #include @@ -50,7 +51,7 @@ class ZMQServer : public Agent{ string caCertFile; string caCert; - BlockingReaderWriterQueue>> outgoingQueue; + ConcurrentQueue>> outgoingQueue; vector, shared_ptr>>> incomingQueue; From 597ecc4d62b9ad351c2de700d0431597fbff1abc Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:34:55 +0300 Subject: [PATCH 088/100] SKALE-4586 Added concurrentqueue --- zmq_src/ZMQServer.cpp | 6 +++--- zmq_src/ZMQServer.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 4520cd14..8ca6a317 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -179,7 +179,7 @@ void ZMQServer::checkForExit() { } -PollResult ZMQServer::poll() { +PollResult ZMQServer::pollIncomingAndSendOutgoing() { zmq_pollitem_t items[1]; items[0].socket = *socket; items[0].events = ZMQ_POLLIN; @@ -289,7 +289,7 @@ void ZMQServer::doOneServerLoop() { uint64_t index = 0; - if ((dynamic_pointer_cast(msg) != nullptr) || + if ((dynamic_pointer_cast(msg)) || dynamic_pointer_cast(msg)) { boost::hash string_hash; @@ -386,5 +386,5 @@ void ZMQServer::workerThreadMessageProcessLoop(ZMQServer *_agent, uint64_t _thre } } - spdlog::info("Exit requested. Exiting worker thread."); + spdlog::info("Exit requested. Exiting worker thread:" + to_string(_threadNumber)); } \ No newline at end of file diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 8c1c87ac..7a73d2f4 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -91,7 +91,7 @@ class ZMQServer : public Agent{ void checkForExit(); - PollResult poll(); + PollResult pollIncomingAndSendOutGoing(); pair> receiveMessage(); From 5319afa7cd1ff2855466cdde1f6513936285f4fd Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Fri, 10 Sep 2021 15:24:58 +0300 Subject: [PATCH 089/100] SKALE-4586 Added concurrent queue --- zmq_src/ZMQServer.cpp | 34 ++++++++++++++++++++-------------- zmq_src/ZMQServer.h | 4 +--- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 8ca6a317..23e5d087 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -179,7 +179,7 @@ void ZMQServer::checkForExit() { } -PollResult ZMQServer::pollIncomingAndSendOutgoing() { +void ZMQServer::waitForIncomingAndProcessOutgoingMessages() { zmq_pollitem_t items[1]; items[0].socket = *socket; items[0].events = ZMQ_POLLIN; @@ -198,7 +198,6 @@ PollResult ZMQServer::pollIncomingAndSendOutgoing() { } } while (pollResult == 0); - return GOT_INCOMING_MSG; } pair > ZMQServer::receiveMessage() { @@ -277,7 +276,7 @@ void ZMQServer::doOneServerLoop() { try { - poll(); + waitForIncomingAndProcessOutgoingMessages(); tie(msgStr, identity) = receiveMessage(); @@ -292,9 +291,9 @@ void ZMQServer::doOneServerLoop() { if ((dynamic_pointer_cast(msg)) || dynamic_pointer_cast(msg)) { - boost::hash string_hash; + boost::hash string_hash; - auto hash = string_hash(string((const char*) identity->data())); + auto hash = string_hash(string((const char *) identity->data())); index = hash % (NUM_ZMQ_WORKER_THREADS - 1); } else { @@ -305,7 +304,6 @@ void ZMQServer::doOneServerLoop() { (msg, identity); - incomingQueue.at(index).enqueue(element); } catch (ExitRequestedException) { @@ -327,8 +325,6 @@ void ZMQServer::doOneServerLoop() { } - - } void ZMQServer::workerThreadProcessNextMessage(uint64_t _threadNumber) { @@ -336,34 +332,44 @@ void ZMQServer::workerThreadProcessNextMessage(uint64_t _threadNumber) { Json::Value result; result["status"] = ZMQ_SERVER_ERROR; - - shared_ptr identity = nullptr; string msgStr; pair , shared_ptr> element; try { - while (!incomingQueue.at(_threadNumber) .wait_dequeue_timed(element, std::chrono::milliseconds(1000))) { checkForExit(); } + } catch (ExitRequestedException) { + throw; + } catch (exception &e) { + checkForExit(); + result["errorMessage"] = string(e.what()); + spdlog::error("Exception in zmq server :{}", e.what()); + spdlog::error("Client request :" + msgStr); + } catch (...) { + checkForExit(); + spdlog::error("Error in zmq server "); + result["errorMessage"] = "Error in zmq server "; + spdlog::error("Client request :" + msgStr); + } + try { result = element.first->process(); - } catch (ExitRequestedException) { throw; } catch (exception &e) { checkForExit(); result["errorMessage"] = string(e.what()); spdlog::error("Exception in zmq server :{}", e.what()); - spdlog::error("ID:" + string((char *) identity->data(), identity->size())); + spdlog::error("ID:" + string((char *) element.second->data(), element.second->size())); spdlog::error("Client request :" + msgStr); } catch (...) { checkForExit(); spdlog::error("Error in zmq server "); result["errorMessage"] = "Error in zmq server "; - spdlog::error("ID:" + string((char *) identity->data(), identity->size())); + spdlog::error("ID:" + string((char *) element.second->data(), element.second->size())); spdlog::error("Client request :" + msgStr); } diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index 7a73d2f4..c0708d63 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -39,8 +39,6 @@ using namespace moodycamel; -typedef enum {GOT_INCOMING_MSG = 0, GOT_OUTFOING_MSG = 1} PollResult; - static const uint64_t NUM_ZMQ_WORKER_THREADS = 16; @@ -91,7 +89,7 @@ class ZMQServer : public Agent{ void checkForExit(); - PollResult pollIncomingAndSendOutGoing(); + void waitForIncomingAndProcessOutgoingMessages(); pair> receiveMessage(); From 61cf7127e6347c20a237f9487229878ee5f84167 Mon Sep 17 00:00:00 2001 From: kladko <13399135+kladkogex@users.noreply.github.com> Date: Fri, 10 Sep 2021 15:32:53 +0300 Subject: [PATCH 090/100] SKALE-4586 Added concurrentqueue --- zmq_src/ZMQServer.cpp | 47 +++++++++++++++++++++++++------------------ zmq_src/ZMQServer.h | 2 ++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/zmq_src/ZMQServer.cpp b/zmq_src/ZMQServer.cpp index 23e5d087..44de4491 100644 --- a/zmq_src/ZMQServer.cpp +++ b/zmq_src/ZMQServer.cpp @@ -179,6 +179,15 @@ void ZMQServer::checkForExit() { } +void ZMQServer::sendMessagesInOutgoingMessageQueueIfAny() { + pair > element; + + // send all items in outgoing queue + while (outgoingQueue.try_dequeue(element)) { + sendToClient(element.first, element.second); + } +} + void ZMQServer::waitForIncomingAndProcessOutgoingMessages() { zmq_pollitem_t items[1]; items[0].socket = *socket; @@ -190,12 +199,8 @@ void ZMQServer::waitForIncomingAndProcessOutgoingMessages() { checkForExit(); pollResult = zmq_poll(items, 1, 1); - pair > element; + sendMessagesInOutgoingMessageQueueIfAny(); - // send all items in outgoing queue - while (outgoingQueue.try_dequeue(element)) { - sendToClient(element.first, element.second); - } } while (pollResult == 0); } @@ -280,31 +285,33 @@ void ZMQServer::doOneServerLoop() { tie(msgStr, identity) = receiveMessage(); - auto msg = ZMQMessage::parse( - msgStr.c_str(), msgStr.size(), true, checkSignature, checkKeyOwnership); + { - CHECK_STATE2(msg, ZMQ_COULD_NOT_PARSE); + auto msg = ZMQMessage::parse( + msgStr.c_str(), msgStr.size(), true, checkSignature, checkKeyOwnership); + CHECK_STATE2(msg, ZMQ_COULD_NOT_PARSE); - uint64_t index = 0; + uint64_t index = 0; - if ((dynamic_pointer_cast(msg)) || - dynamic_pointer_cast(msg)) { + if ((dynamic_pointer_cast(msg)) || + dynamic_pointer_cast(msg)) { - boost::hash string_hash; + boost::hash string_hash; - auto hash = string_hash(string((const char *) identity->data())); + auto hash = string_hash(string((const char *) identity->data())); - index = hash % (NUM_ZMQ_WORKER_THREADS - 1); - } else { - index = NUM_ZMQ_WORKER_THREADS - 1; - } + index = hash % (NUM_ZMQ_WORKER_THREADS - 1); + } else { + index = NUM_ZMQ_WORKER_THREADS - 1; + } - auto element = pair < shared_ptr < ZMQMessage >, shared_ptr> - (msg, identity); + auto element = pair < shared_ptr < ZMQMessage >, shared_ptr> + (msg, identity); - incomingQueue.at(index).enqueue(element); + incomingQueue.at(index).enqueue(element); + } } catch (ExitRequestedException) { throw; diff --git a/zmq_src/ZMQServer.h b/zmq_src/ZMQServer.h index c0708d63..c5e95fcb 100644 --- a/zmq_src/ZMQServer.h +++ b/zmq_src/ZMQServer.h @@ -95,6 +95,8 @@ class ZMQServer : public Agent{ void sendToClient(Json::Value& _result, shared_ptr& _identity); + void sendMessagesInOutgoingMessageQueueIfAny(); + }; From a9fdcbaebb3eae2fd5238c2704a9061a8a882480 Mon Sep 17 00:00:00 2001 From: Oleh Date: Mon, 13 Sep 2021 17:00:24 +0300 Subject: [PATCH 091/100] SKALE-4402 update job file for new release procedure --- .github/workflows/dockerimagerelease.yml | 18 +++++++++++------- DockerfileRelease | 6 +++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dockerimagerelease.yml b/.github/workflows/dockerimagerelease.yml index 15788f99..d5beb5ea 100644 --- a/.github/workflows/dockerimagerelease.yml +++ b/.github/workflows/dockerimagerelease.yml @@ -26,12 +26,6 @@ jobs: shell: bash - name: Generate public key run: openssl rsa -in signed_enclaves/skale_sgx_private_key0.pem -pubout -out signed_enclaves/skale_sgx_public_key0.pem - - name: Build enclave - run: ./autoconf.bash && ./configure --with-sgx-build=release && bash -c "make -j$(nproc)" - - name: Sign enclave - run: cd scripts && ./sign_enclave.bash - - name: Delete secrets - run: rm -f signed_enclaves/skale_sgx_private_key0.pem signed_enclaves/skale_sgx_public_key0.pem - name: Build and deploy docker image run : | export BRANCH=${GITHUB_REF##*/} @@ -47,6 +41,16 @@ jobs: bash ./scripts/publish_image.sh sgxwallet_release env: ACTIONS_ALLOW_UNSECURE_COMMANDS: true + - name: Delete secrets + run: rm -f signed_enclaves/skale_sgx_private_key0.pem signed_enclaves/skale_sgx_public_key0.pem + - name: Copy secure_enclave.signed.so + run: | + export IMAGE_NAME=skalenetwork/sgxwallet_release:$VERSION + docker create --name extract $IMAGE_NAME + docker cp extract:/usr/src/sdk/secure_enclave/secure_enclave.signed.so signed_enclaves/secure_enclave_signed.so + docker rm extract + - name: Check signed enclaves dir + run: ls signed_enclaves - name: Create Release id: create_release uses: actions/create-release@v1 @@ -63,6 +67,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: signed_enclaves/secure_enclave_signed0.so + asset_path: signed_enclaves/secure_enclave_signed.so asset_name: signed_enclave.so asset_content_type: application/octet-stream diff --git a/DockerfileRelease b/DockerfileRelease index 7b817e3a..3fa72652 100644 --- a/DockerfileRelease +++ b/DockerfileRelease @@ -5,11 +5,15 @@ WORKDIR /usr/src/sdk RUN cp -f secure_enclave/secure_enclave.config.xml.release secure_enclave/secure_enclave.config.xml RUN apt update && apt install -y curl secure-delete RUN touch /var/hwmode +RUN ./autoconf.bash +RUN ./configure --with-sgx-build=release +RUN bash -c "make -j$(nproc)" RUN ccache -sz +RUN cd scripts && ./sign_enclave.bash RUN mkdir -p /usr/src/sdk/sgx_data COPY docker/start.sh ./ RUN rm -rf /usr/src/sdk/sgx-sdk-build/ RUN rm /opt/intel/sgxsdk/lib64/*_sim.so RUN rm /usr/src/sdk/secure_enclave/secure_enclave*.so -RUN cp signed_enclaves/secure_enclave_signed0.so secure_enclabve/secure_enclave.signed.so +RUN cp signed_enclaves/secure_enclave_signed0.so secure_enclave/secure_enclave.signed.so ENTRYPOINT ["/usr/src/sdk/start.sh"] From fc88b9c410aa403377c1cdbe4f3256c8c85de83d Mon Sep 17 00:00:00 2001 From: Oleh Date: Wed, 15 Sep 2021 15:01:48 +0300 Subject: [PATCH 092/100] SKALE-4262 add registerKeyOwner --- SGXWalletServer.cpp | 22 ++++++++++++++++++++++ SGXWalletServer.hpp | 2 ++ sgxwallet_common.h | 2 ++ zmq_src/ReqMessage.cpp | 8 ++++++++ zmq_src/ReqMessage.h | 7 +++++++ zmq_src/ZMQMessage.cpp | 6 ++++++ zmq_src/ZMQMessage.h | 6 ++++-- 7 files changed, 51 insertions(+), 2 deletions(-) diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index e47dfbcf..d8c17765 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -1009,6 +1009,28 @@ Json::Value SGXWalletServer::getDecryptionShareImpl(const std::string& blsKeyNam RETURN_SUCCESS(result) } +Json::Value SGXWalletServer::registerKeyOwnerImpl(const std::string& keyName, const std::string& cert) { + spdlog::info("Entering {}", __FUNCTION__); + INIT_RESULT(result) + + try { + if (!checkName(keyName, "BLS_KEY") && !checkECDSAKeyName(keyName)) { + throw SGXException(INVALID_KEY_FORMAT, string(__FUNCTION__) + ":Invalid key format"); + } + + std::string ownerKeyName = keyName + ":OWNER"; + if ( !LevelDB::getLevelDb()->readString( ownerKeyName ) ) { + spdlog::info("Cert {} registers key {}", cert, keyName); + LevelDB::getLevelDb()->writeString( ownerKeyName, cert ); + } else { + spdlog::error("The key {} already registered", keyName); + throw SGXException(KEY_ALREADY_REGISTERED, string(__FUNCTION__) + ":The key is already registered"); + } + } HANDLE_SGX_EXCEPTION(result) + + RETURN_SUCCESS(result) +} + Json::Value SGXWalletServer::generateDKGPoly(const string &_polyName, int _t) { return generateDKGPolyImpl(_polyName, _t); } diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index bcec3f59..fe095ca7 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -175,6 +175,8 @@ class SGXWalletServer : public AbstractStubServer { static Json::Value getDecryptionShareImpl(const std::string& KeyName, const std::string& publicDecryptionValue); + static Json::Value registerKeyOwnerImpl(const std::string& keyName, const std::string& cert); + static void printDB(); static void initHttpServer(); diff --git a/sgxwallet_common.h b/sgxwallet_common.h index dd80cda2..cbc4bd43 100644 --- a/sgxwallet_common.h +++ b/sgxwallet_common.h @@ -185,6 +185,8 @@ extern bool autoconfirm; #define CORRUPT_DATABASE -112 #define INVALID_SEK -113 #define INVALID_DECRYPTION_VALUE_FORMAT -114 +#define INVALID_KEY_FORMAT -115 +#define KEY_ALREADY_REGISTERED -116 #define SGX_ENCLAVE_ERROR -666 diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index 38948845..a0f816f0 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -261,3 +261,11 @@ Json::Value GetDecryptionShareReqMessage::process() { result["type"] = ZMQMessage::GET_DECRYPTION_SHARE_RSP; return result; } + +Json::Value RegisterKeyOwnerReqMessage::process() { + auto keyName = getStringRapid("keyName"); + auto cert = getStringRapid("cert"); + auto result = SGXWalletServer::getDecryptionShareImpl(keyName, cert); + result["type"] = ZMQMessage::REGISTER_KEY_OWNER_RSP; + return result; +} diff --git a/zmq_src/ReqMessage.h b/zmq_src/ReqMessage.h index cb1815fc..b27dfdbd 100644 --- a/zmq_src/ReqMessage.h +++ b/zmq_src/ReqMessage.h @@ -185,4 +185,11 @@ class GetDecryptionShareReqMessage : public ZMQMessage { virtual Json::Value process(); }; +class RegisterKeyOwnerReqMessage : public ZMQMessage { +public: + RegisterKeyOwnerReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); +}; + #endif //SGXWALLET_REQMESSAGE_H diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 2703c0b1..932bec16 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -230,6 +230,9 @@ shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr (_d); break; + case ENUM_REGISTER_KEY_OWNER_REQ: + ret = make_shared(_d); + break; default: break; } @@ -314,6 +317,9 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr (_d); break; + case ENUM_REGISTER_KEY_OWNER_RSP: + ret = make_shared(_d); + break; default: break; } diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index e4908047..7a9786db 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -99,6 +99,8 @@ class ZMQMessage { static constexpr const char *DELETE_BLS_KEY_RSP = "deleteBLSKeyRsp"; static constexpr const char *GET_DECRYPTION_SHARE_REQ = "getDecryptionShareReq"; static constexpr const char *GET_DECRYPTION_SHARE_RSP = "getDecryptionShareRsp"; + static constexpr const char *REGISTER_KEY_OWNER_REQ = "registerKeyOwnerReq"; + static constexpr const char *REGISTER_KEY_OWNER_RSP = "registerKeyOwnerRsp"; static const std::map requests; static const std::map responses; @@ -106,11 +108,11 @@ class ZMQMessage { enum Requests { ENUM_BLS_SIGN_REQ, ENUM_ECDSA_SIGN_REQ, ENUM_IMPORT_BLS_REQ, ENUM_IMPORT_ECDSA_REQ, ENUM_GENERATE_ECDSA_REQ, ENUM_GET_PUBLIC_ECDSA_REQ, ENUM_GENERATE_DKG_POLY_REQ, ENUM_GET_VV_REQ, ENUM_GET_SECRET_SHARE_REQ, ENUM_DKG_VERIFY_REQ, ENUM_CREATE_BLS_PRIVATE_REQ, ENUM_GET_BLS_PUBLIC_REQ, ENUM_GET_ALL_BLS_PUBLIC_REQ, ENUM_COMPLAINT_RESPONSE_REQ, ENUM_MULT_G2_REQ, ENUM_IS_POLY_EXISTS_REQ, - ENUM_GET_SERVER_STATUS_REQ, ENUM_GET_SERVER_VERSION_REQ, ENUM_DELETE_BLS_KEY_REQ, ENUM_GET_DECRYPTION_SHARE_REQ }; + ENUM_GET_SERVER_STATUS_REQ, ENUM_GET_SERVER_VERSION_REQ, ENUM_DELETE_BLS_KEY_REQ, ENUM_GET_DECRYPTION_SHARE_REQ, ENUM_REGISTER_KEY_OWNER_REQ }; enum Responses { ENUM_BLS_SIGN_RSP, ENUM_ECDSA_SIGN_RSP, ENUM_IMPORT_BLS_RSP, ENUM_IMPORT_ECDSA_RSP, ENUM_GENERATE_ECDSA_RSP, ENUM_GET_PUBLIC_ECDSA_RSP, ENUM_GENERATE_DKG_POLY_RSP, ENUM_GET_VV_RSP, ENUM_GET_SECRET_SHARE_RSP, ENUM_DKG_VERIFY_RSP, ENUM_CREATE_BLS_PRIVATE_RSP, ENUM_GET_BLS_PUBLIC_RSP, ENUM_GET_ALL_BLS_PUBLIC_RSP, ENUM_COMPLAINT_RESPONSE_RSP, ENUM_MULT_G2_RSP, ENUM_IS_POLY_EXISTS_RSP, - ENUM_GET_SERVER_STATUS_RSP, ENUM_GET_SERVER_VERSION_RSP, ENUM_DELETE_BLS_KEY_RSP, ENUM_GET_DECRYPTION_SHARE_RSP }; + ENUM_GET_SERVER_STATUS_RSP, ENUM_GET_SERVER_VERSION_RSP, ENUM_DELETE_BLS_KEY_RSP, ENUM_GET_DECRYPTION_SHARE_RSP, ENUM_REGISTER_KEY_OWNER_RSP }; explicit ZMQMessage(shared_ptr &_d) : d(_d) {}; From 318bac356bb739237e185b8de9fb87ab4d7b182e Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 15 Sep 2021 15:32:57 +0300 Subject: [PATCH 093/100] SKALE-4262 fix build --- zmq_src/ReqMessage.cpp | 2 +- zmq_src/RspMessage.cpp | 4 ++++ zmq_src/RspMessage.h | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index a0f816f0..f4827528 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -265,7 +265,7 @@ Json::Value GetDecryptionShareReqMessage::process() { Json::Value RegisterKeyOwnerReqMessage::process() { auto keyName = getStringRapid("keyName"); auto cert = getStringRapid("cert"); - auto result = SGXWalletServer::getDecryptionShareImpl(keyName, cert); + auto result = SGXWalletServer::registerKeyOwnerImpl(keyName, cert); result["type"] = ZMQMessage::REGISTER_KEY_OWNER_RSP; return result; } diff --git a/zmq_src/RspMessage.cpp b/zmq_src/RspMessage.cpp index 61840678..f17ac214 100644 --- a/zmq_src/RspMessage.cpp +++ b/zmq_src/RspMessage.cpp @@ -114,3 +114,7 @@ Json::Value deleteBLSKeyRspMessage::process() { Json::Value GetDecryptionShareRspMessage::process() { assert(false); } + +Json::Value RegisterKeyOwnerRspMessage::process() { + assert(false); +} diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h index 09c026aa..2546225b 100644 --- a/zmq_src/RspMessage.h +++ b/zmq_src/RspMessage.h @@ -259,5 +259,16 @@ class GetDecryptionShareRspMessage : public ZMQMessage { } }; +class RegisterKeyOwnerRspMessage : public ZMQMessage { +public: + RegisterKeyOwnerRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; + + virtual Json::Value process(); + + Json::Value getShare() { + return getJsonValueRapid("decryptionShare"); + } +}; + #endif //SGXWALLET_RSPMESSAGE_H From 147896153793df02e1d44ba2d2b40620ff38db2b Mon Sep 17 00:00:00 2001 From: Oleh Date: Wed, 15 Sep 2021 17:45:37 +0300 Subject: [PATCH 094/100] SKALE-4262 fix tests --- zmq_src/ZMQMessage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 932bec16..71cb3bd9 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -349,7 +349,7 @@ const std::map ZMQMessage::requests{ {CREATE_BLS_PRIVATE_REQ, 10}, {GET_BLS_PUBLIC_REQ, 11}, {GET_ALL_BLS_PUBLIC_REQ, 12}, {COMPLAINT_RESPONSE_REQ, 13}, {MULT_G2_REQ, 14}, {IS_POLY_EXISTS_REQ, 15}, {GET_SERVER_STATUS_REQ, 16}, {GET_SERVER_VERSION_REQ, 17}, {DELETE_BLS_KEY_REQ, 18}, - {GET_DECRYPTION_SHARE_REQ, 19} + {GET_DECRYPTION_SHARE_REQ, 19}, {REGISTER_KEY_OWNER_REQ, 20} }; const std::map ZMQMessage::responses { @@ -359,5 +359,5 @@ const std::map ZMQMessage::responses { {CREATE_BLS_PRIVATE_RSP, 10}, {GET_BLS_PUBLIC_RSP, 11}, {GET_ALL_BLS_PUBLIC_RSP, 12}, {COMPLAINT_RESPONSE_RSP, 13}, {MULT_G2_RSP, 14}, {IS_POLY_EXISTS_RSP, 15}, {GET_SERVER_STATUS_RSP, 16}, {GET_SERVER_VERSION_RSP, 17}, {DELETE_BLS_KEY_RSP, 18}, - {GET_DECRYPTION_SHARE_RSP, 19} + {GET_DECRYPTION_SHARE_RSP, 19}, {REGISTER_KEY_OWNER_RSP, 20} }; From c3e9ed102f86bc1439cb369fa375815222c6a464 Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 15 Sep 2021 19:30:53 +0300 Subject: [PATCH 095/100] SKALE-4262 clean up log --- SGXWalletServer.cpp | 4 +++- zmq_src/RspMessage.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index d8c17765..3e3370de 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -1024,8 +1024,10 @@ Json::Value SGXWalletServer::registerKeyOwnerImpl(const std::string& keyName, co LevelDB::getLevelDb()->writeString( ownerKeyName, cert ); } else { spdlog::error("The key {} already registered", keyName); - throw SGXException(KEY_ALREADY_REGISTERED, string(__FUNCTION__) + ":The key is already registered"); + throw SGXException(KEY_ALREADY_REGISTERED, "The key owner is already registered"); } + + result["Registered"] = true; } HANDLE_SGX_EXCEPTION(result) RETURN_SUCCESS(result) diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h index 2546225b..44fcddec 100644 --- a/zmq_src/RspMessage.h +++ b/zmq_src/RspMessage.h @@ -265,8 +265,8 @@ class RegisterKeyOwnerRspMessage : public ZMQMessage { virtual Json::Value process(); - Json::Value getShare() { - return getJsonValueRapid("decryptionShare"); + Json::Value isRegistered() { + return getBoolRapid("Registered"); } }; From 7a98a6ef3ced983c4b9c315d00c819360533f979 Mon Sep 17 00:00:00 2001 From: Oleh Date: Thu, 16 Sep 2021 16:11:21 +0300 Subject: [PATCH 096/100] SKALE-4262 automatically add key owners --- SGXWalletServer.cpp | 22 ---------------------- SGXWalletServer.hpp | 2 -- zmq_src/ReqMessage.cpp | 32 ++++++++++++++++++-------------- zmq_src/ReqMessage.h | 7 ------- zmq_src/RspMessage.cpp | 4 ---- zmq_src/RspMessage.h | 12 ------------ zmq_src/ZMQMessage.cpp | 14 ++++++-------- zmq_src/ZMQMessage.h | 8 ++++---- 8 files changed, 28 insertions(+), 73 deletions(-) diff --git a/SGXWalletServer.cpp b/SGXWalletServer.cpp index d8c17765..e47dfbcf 100644 --- a/SGXWalletServer.cpp +++ b/SGXWalletServer.cpp @@ -1009,28 +1009,6 @@ Json::Value SGXWalletServer::getDecryptionShareImpl(const std::string& blsKeyNam RETURN_SUCCESS(result) } -Json::Value SGXWalletServer::registerKeyOwnerImpl(const std::string& keyName, const std::string& cert) { - spdlog::info("Entering {}", __FUNCTION__); - INIT_RESULT(result) - - try { - if (!checkName(keyName, "BLS_KEY") && !checkECDSAKeyName(keyName)) { - throw SGXException(INVALID_KEY_FORMAT, string(__FUNCTION__) + ":Invalid key format"); - } - - std::string ownerKeyName = keyName + ":OWNER"; - if ( !LevelDB::getLevelDb()->readString( ownerKeyName ) ) { - spdlog::info("Cert {} registers key {}", cert, keyName); - LevelDB::getLevelDb()->writeString( ownerKeyName, cert ); - } else { - spdlog::error("The key {} already registered", keyName); - throw SGXException(KEY_ALREADY_REGISTERED, string(__FUNCTION__) + ":The key is already registered"); - } - } HANDLE_SGX_EXCEPTION(result) - - RETURN_SUCCESS(result) -} - Json::Value SGXWalletServer::generateDKGPoly(const string &_polyName, int _t) { return generateDKGPolyImpl(_polyName, _t); } diff --git a/SGXWalletServer.hpp b/SGXWalletServer.hpp index fe095ca7..bcec3f59 100644 --- a/SGXWalletServer.hpp +++ b/SGXWalletServer.hpp @@ -175,8 +175,6 @@ class SGXWalletServer : public AbstractStubServer { static Json::Value getDecryptionShareImpl(const std::string& KeyName, const std::string& publicDecryptionValue); - static Json::Value registerKeyOwnerImpl(const std::string& keyName, const std::string& cert); - static void printDB(); static void initHttpServer(); diff --git a/zmq_src/ReqMessage.cpp b/zmq_src/ReqMessage.cpp index f4827528..413d01eb 100644 --- a/zmq_src/ReqMessage.cpp +++ b/zmq_src/ReqMessage.cpp @@ -32,9 +32,15 @@ Json::Value ECDSASignReqMessage::process() { auto base = getInt64Rapid("base"); auto keyName = getStringRapid("keyName"); auto hash = getStringRapid("messageHash"); - if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { - spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), keyName); - throw std::invalid_argument("Only owner of the key can access it"); + if (checkKeyOwnership) { + if (!isKeyRegistered(keyName)) { + addKeyByOwner(keyName, getStringRapid("cert")); + } else { + if (!isKeyByOwner(keyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), keyName); + throw std::invalid_argument("Only owner of the key can access it"); + } + } } auto result = SGXWalletServer::ecdsaSignMessageHashImpl(base, keyName, hash); result["type"] = ZMQMessage::ECDSA_SIGN_RSP; @@ -46,9 +52,15 @@ Json::Value BLSSignReqMessage::process() { auto hash = getStringRapid("messageHash"); auto t = getInt64Rapid("t"); auto n = getInt64Rapid("n"); - if (checkKeyOwnership && !isKeyByOwner(keyName, getStringRapid("cert"))) { - spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), keyName); - throw std::invalid_argument("Only owner of the key can access it"); + if (checkKeyOwnership) { + if (!isKeyRegistered(keyName)) { + addKeyByOwner(keyName, getStringRapid("cert")); + } else { + if (!isKeyByOwner(keyName, getStringRapid("cert"))) { + spdlog::error("Cert {} try to access key {} which does not belong to it", getStringRapid("cert"), keyName); + throw std::invalid_argument("Only owner of the key can access it"); + } + } } auto result = SGXWalletServer::blsSignMessageHashImpl(keyName, hash, t, n); result["type"] = ZMQMessage::BLS_SIGN_RSP; @@ -261,11 +273,3 @@ Json::Value GetDecryptionShareReqMessage::process() { result["type"] = ZMQMessage::GET_DECRYPTION_SHARE_RSP; return result; } - -Json::Value RegisterKeyOwnerReqMessage::process() { - auto keyName = getStringRapid("keyName"); - auto cert = getStringRapid("cert"); - auto result = SGXWalletServer::registerKeyOwnerImpl(keyName, cert); - result["type"] = ZMQMessage::REGISTER_KEY_OWNER_RSP; - return result; -} diff --git a/zmq_src/ReqMessage.h b/zmq_src/ReqMessage.h index b27dfdbd..cb1815fc 100644 --- a/zmq_src/ReqMessage.h +++ b/zmq_src/ReqMessage.h @@ -185,11 +185,4 @@ class GetDecryptionShareReqMessage : public ZMQMessage { virtual Json::Value process(); }; -class RegisterKeyOwnerReqMessage : public ZMQMessage { -public: - RegisterKeyOwnerReqMessage(shared_ptr& _d) : ZMQMessage(_d) {}; - - virtual Json::Value process(); -}; - #endif //SGXWALLET_REQMESSAGE_H diff --git a/zmq_src/RspMessage.cpp b/zmq_src/RspMessage.cpp index f17ac214..61840678 100644 --- a/zmq_src/RspMessage.cpp +++ b/zmq_src/RspMessage.cpp @@ -114,7 +114,3 @@ Json::Value deleteBLSKeyRspMessage::process() { Json::Value GetDecryptionShareRspMessage::process() { assert(false); } - -Json::Value RegisterKeyOwnerRspMessage::process() { - assert(false); -} diff --git a/zmq_src/RspMessage.h b/zmq_src/RspMessage.h index 2546225b..cb7e045e 100644 --- a/zmq_src/RspMessage.h +++ b/zmq_src/RspMessage.h @@ -259,16 +259,4 @@ class GetDecryptionShareRspMessage : public ZMQMessage { } }; -class RegisterKeyOwnerRspMessage : public ZMQMessage { -public: - RegisterKeyOwnerRspMessage(shared_ptr& _d) : ZMQMessage(_d) {}; - - virtual Json::Value process(); - - Json::Value getShare() { - return getJsonValueRapid("decryptionShare"); - } -}; - - #endif //SGXWALLET_RSPMESSAGE_H diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index 71cb3bd9..ac2cb13b 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -230,9 +230,6 @@ shared_ptr ZMQMessage::buildRequest(string &_type, shared_ptr (_d); break; - case ENUM_REGISTER_KEY_OWNER_REQ: - ret = make_shared(_d); - break; default: break; } @@ -317,9 +314,6 @@ shared_ptr ZMQMessage::buildResponse(string &_type, shared_ptr (_d); break; - case ENUM_REGISTER_KEY_OWNER_RSP: - ret = make_shared(_d); - break; default: break; } @@ -340,6 +334,10 @@ void ZMQMessage::addKeyByOwner(const string& keyName, const string& cert) { SGXWalletServer::writeDataToDB(keyName + ":OWNER", cert); } +bool ZMQMessage::isKeyRegistered(const string& keyName) { + return LevelDB::getLevelDb()->readString(keyName + ":OWNER") != nullptr; +} + cache::lru_cache> ZMQMessage::verifiedCerts(256); const std::map ZMQMessage::requests{ @@ -349,7 +347,7 @@ const std::map ZMQMessage::requests{ {CREATE_BLS_PRIVATE_REQ, 10}, {GET_BLS_PUBLIC_REQ, 11}, {GET_ALL_BLS_PUBLIC_REQ, 12}, {COMPLAINT_RESPONSE_REQ, 13}, {MULT_G2_REQ, 14}, {IS_POLY_EXISTS_REQ, 15}, {GET_SERVER_STATUS_REQ, 16}, {GET_SERVER_VERSION_REQ, 17}, {DELETE_BLS_KEY_REQ, 18}, - {GET_DECRYPTION_SHARE_REQ, 19}, {REGISTER_KEY_OWNER_REQ, 20} + {GET_DECRYPTION_SHARE_REQ, 19} } }; const std::map ZMQMessage::responses { @@ -359,5 +357,5 @@ const std::map ZMQMessage::responses { {CREATE_BLS_PRIVATE_RSP, 10}, {GET_BLS_PUBLIC_RSP, 11}, {GET_ALL_BLS_PUBLIC_RSP, 12}, {COMPLAINT_RESPONSE_RSP, 13}, {MULT_G2_RSP, 14}, {IS_POLY_EXISTS_RSP, 15}, {GET_SERVER_STATUS_RSP, 16}, {GET_SERVER_VERSION_RSP, 17}, {DELETE_BLS_KEY_RSP, 18}, - {GET_DECRYPTION_SHARE_RSP, 19}, {REGISTER_KEY_OWNER_RSP, 20} + {GET_DECRYPTION_SHARE_RSP, 19} } }; diff --git a/zmq_src/ZMQMessage.h b/zmq_src/ZMQMessage.h index 7a9786db..45051634 100644 --- a/zmq_src/ZMQMessage.h +++ b/zmq_src/ZMQMessage.h @@ -57,6 +57,8 @@ class ZMQMessage { static void addKeyByOwner(const string& keyName, const string& cert); + static bool isKeyRegistered(const std::string& keyName); + public: static constexpr const char *BLS_SIGN_REQ = "BLSSignReq"; @@ -99,8 +101,6 @@ class ZMQMessage { static constexpr const char *DELETE_BLS_KEY_RSP = "deleteBLSKeyRsp"; static constexpr const char *GET_DECRYPTION_SHARE_REQ = "getDecryptionShareReq"; static constexpr const char *GET_DECRYPTION_SHARE_RSP = "getDecryptionShareRsp"; - static constexpr const char *REGISTER_KEY_OWNER_REQ = "registerKeyOwnerReq"; - static constexpr const char *REGISTER_KEY_OWNER_RSP = "registerKeyOwnerRsp"; static const std::map requests; static const std::map responses; @@ -108,11 +108,11 @@ class ZMQMessage { enum Requests { ENUM_BLS_SIGN_REQ, ENUM_ECDSA_SIGN_REQ, ENUM_IMPORT_BLS_REQ, ENUM_IMPORT_ECDSA_REQ, ENUM_GENERATE_ECDSA_REQ, ENUM_GET_PUBLIC_ECDSA_REQ, ENUM_GENERATE_DKG_POLY_REQ, ENUM_GET_VV_REQ, ENUM_GET_SECRET_SHARE_REQ, ENUM_DKG_VERIFY_REQ, ENUM_CREATE_BLS_PRIVATE_REQ, ENUM_GET_BLS_PUBLIC_REQ, ENUM_GET_ALL_BLS_PUBLIC_REQ, ENUM_COMPLAINT_RESPONSE_REQ, ENUM_MULT_G2_REQ, ENUM_IS_POLY_EXISTS_REQ, - ENUM_GET_SERVER_STATUS_REQ, ENUM_GET_SERVER_VERSION_REQ, ENUM_DELETE_BLS_KEY_REQ, ENUM_GET_DECRYPTION_SHARE_REQ, ENUM_REGISTER_KEY_OWNER_REQ }; + ENUM_GET_SERVER_STATUS_REQ, ENUM_GET_SERVER_VERSION_REQ, ENUM_DELETE_BLS_KEY_REQ, ENUM_GET_DECRYPTION_SHARE_REQ }; enum Responses { ENUM_BLS_SIGN_RSP, ENUM_ECDSA_SIGN_RSP, ENUM_IMPORT_BLS_RSP, ENUM_IMPORT_ECDSA_RSP, ENUM_GENERATE_ECDSA_RSP, ENUM_GET_PUBLIC_ECDSA_RSP, ENUM_GENERATE_DKG_POLY_RSP, ENUM_GET_VV_RSP, ENUM_GET_SECRET_SHARE_RSP, ENUM_DKG_VERIFY_RSP, ENUM_CREATE_BLS_PRIVATE_RSP, ENUM_GET_BLS_PUBLIC_RSP, ENUM_GET_ALL_BLS_PUBLIC_RSP, ENUM_COMPLAINT_RESPONSE_RSP, ENUM_MULT_G2_RSP, ENUM_IS_POLY_EXISTS_RSP, - ENUM_GET_SERVER_STATUS_RSP, ENUM_GET_SERVER_VERSION_RSP, ENUM_DELETE_BLS_KEY_RSP, ENUM_GET_DECRYPTION_SHARE_RSP, ENUM_REGISTER_KEY_OWNER_RSP }; + ENUM_GET_SERVER_STATUS_RSP, ENUM_GET_SERVER_VERSION_RSP, ENUM_DELETE_BLS_KEY_RSP, ENUM_GET_DECRYPTION_SHARE_RSP }; explicit ZMQMessage(shared_ptr &_d) : d(_d) {}; From f2e72ede10e968f38ba73b2216cfcf26f22805df Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Thu, 16 Sep 2021 16:50:12 +0300 Subject: [PATCH 097/100] SKALE-4262 fix build --- zmq_src/ZMQMessage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zmq_src/ZMQMessage.cpp b/zmq_src/ZMQMessage.cpp index ac2cb13b..82fb8c00 100644 --- a/zmq_src/ZMQMessage.cpp +++ b/zmq_src/ZMQMessage.cpp @@ -347,7 +347,7 @@ const std::map ZMQMessage::requests{ {CREATE_BLS_PRIVATE_REQ, 10}, {GET_BLS_PUBLIC_REQ, 11}, {GET_ALL_BLS_PUBLIC_REQ, 12}, {COMPLAINT_RESPONSE_REQ, 13}, {MULT_G2_REQ, 14}, {IS_POLY_EXISTS_REQ, 15}, {GET_SERVER_STATUS_REQ, 16}, {GET_SERVER_VERSION_REQ, 17}, {DELETE_BLS_KEY_REQ, 18}, - {GET_DECRYPTION_SHARE_REQ, 19} } + {GET_DECRYPTION_SHARE_REQ, 19} }; const std::map ZMQMessage::responses { @@ -357,5 +357,5 @@ const std::map ZMQMessage::responses { {CREATE_BLS_PRIVATE_RSP, 10}, {GET_BLS_PUBLIC_RSP, 11}, {GET_ALL_BLS_PUBLIC_RSP, 12}, {COMPLAINT_RESPONSE_RSP, 13}, {MULT_G2_RSP, 14}, {IS_POLY_EXISTS_RSP, 15}, {GET_SERVER_STATUS_RSP, 16}, {GET_SERVER_VERSION_RSP, 17}, {DELETE_BLS_KEY_RSP, 18}, - {GET_DECRYPTION_SHARE_RSP, 19} } + {GET_DECRYPTION_SHARE_RSP, 19} }; From e3f7cbf11101af8b831b510c4742ef869e081f5a Mon Sep 17 00:00:00 2001 From: Chadwick Strange Date: Wed, 29 Sep 2021 18:31:50 -0400 Subject: [PATCH 098/100] Update CODEOWNERS --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2d8747c6..be7e9bde 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,3 @@ * @olehnikolaiev @kladkogex *.md @skalenetwork/docowners +/docs/ @skalenetwork/docowners From 2fa40a7525eeb68582d75c1a1fca3837615b618c Mon Sep 17 00:00:00 2001 From: Oleh Nikolaiev Date: Wed, 6 Oct 2021 11:01:53 +0300 Subject: [PATCH 099/100] add zmq port --- docs/run-in-hardware-mode.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/run-in-hardware-mode.md b/docs/run-in-hardware-mode.md index ab8125d2..07fe9e18 100644 --- a/docs/run-in-hardware-mode.md +++ b/docs/run-in-hardware-mode.md @@ -53,6 +53,7 @@ sgxwallet operates on the following network ports: - 1028 (localhost for admin ) - 1029 (http only operation) - 1030 (localhost for informational requests) +- 1031 (zmq) If operating with a firewall, please make sure these ports are open so clients are able to connect to the server. From 5b4a1cea6820daf52f2108f4a64a775cead88e7f Mon Sep 17 00:00:00 2001 From: Oleh Date: Tue, 9 Nov 2021 13:06:48 +0200 Subject: [PATCH 100/100] SKALE-4751 remove extra check --- secure_enclave/secure_enclave.c | 1 - 1 file changed, 1 deletion(-) diff --git a/secure_enclave/secure_enclave.c b/secure_enclave/secure_enclave.c index 9478dfe7..78b84a5d 100644 --- a/secure_enclave/secure_enclave.c +++ b/secure_enclave/secure_enclave.c @@ -605,7 +605,6 @@ void trustedDecryptKey(int *errStatus, char *errString, uint8_t *encryptedPrivat CHECK_STATE(encryptedPrivateKey); CHECK_STATE(key); - CHECK_STATE( enc_len == strnlen( encryptedPrivateKey, 1024 ) ); *errStatus = -9;