diff --git a/.bazelrc b/.bazelrc index 2c18cd7c0..0566a7c9c 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,4 +1,4 @@ build --cxxopt='-std=c++17' --copt=-O3 --jobs=40 -build --action_env=PYTHON_BIN_PATH="/usr/bin/python3.10" -build --action_env=PYTHON_LIB_PATH="/usr/include/python3.10" +#build --action_env=PYTHON_BIN_PATH="/usr/bin/python3.10" +#build --action_env=PYTHON_LIB_PATH="/usr/include/python3.10" diff --git a/application/utils/BUILD b/application/utils/BUILD index 9544cca80..39b292280 100644 --- a/application/utils/BUILD +++ b/application/utils/BUILD @@ -6,6 +6,7 @@ cc_library( hdrs = ["server_factory.h"], deps = [ "//config:resdb_config_utils", + "//execution:custom_query", "//ordering/pbft:consensus_service_pbft", "//server:resdb_server", ], diff --git a/application/utils/server_factory.h b/application/utils/server_factory.h index 3f6a0b814..d1db54854 100644 --- a/application/utils/server_factory.h +++ b/application/utils/server_factory.h @@ -26,6 +26,7 @@ #pragma once #include "config/resdb_config_utils.h" +#include "execution/custom_query.h" #include "execution/transaction_executor_impl.h" #include "ordering/pbft/consensus_service_pbft.h" #include "server/resdb_server.h" @@ -44,6 +45,13 @@ class ServerFactory { char* config_file, char* private_key_file, char* cert_file, std::unique_ptr executor, char* logging_dir, std::function config_handler); + + template + std::unique_ptr CustomCreateResDBServer( + char* config_file, char* private_key_file, char* cert_file, + std::unique_ptr executor, + std::unique_ptr query_executor, + std::function config_handler); }; std::unique_ptr GenerateResDBServer( @@ -59,6 +67,13 @@ std::unique_ptr CustomGenerateResDBServer( char* logging_dir = nullptr, std::function config_handler = nullptr); +template +std::unique_ptr CustomGenerateResDBServer( + char* config_file, char* private_key_file, char* cert_file, + std::unique_ptr executor, + std::unique_ptr query_executor, + std::function config_handler = nullptr); + // =================================================================== template std::unique_ptr ServerFactory::CustomCreateResDBServer( @@ -76,6 +91,23 @@ std::unique_ptr ServerFactory::CustomCreateResDBServer( std::make_unique(*config, std::move(executor))); } +template +std::unique_ptr ServerFactory::CustomCreateResDBServer( + char* config_file, char* private_key_file, char* cert_file, + std::unique_ptr executor, + std::unique_ptr query_executor, + std::function config_handler) { + std::unique_ptr config = + GenerateResDBConfig(config_file, private_key_file, cert_file); + + if (config_handler) { + config_handler(config.get()); + } + return std::make_unique( + *config, std::make_unique(*config, std::move(executor), + std::move(query_executor))); +} + template std::unique_ptr CustomGenerateResDBServer( char* config_file, char* private_key_file, char* cert_file, @@ -86,4 +118,15 @@ std::unique_ptr CustomGenerateResDBServer( logging_dir, config_handler); } +template +std::unique_ptr CustomGenerateResDBServer( + char* config_file, char* private_key_file, char* cert_file, + std::unique_ptr executor, + std::unique_ptr query_executor, + std::function config_handler) { + return ServerFactory().CustomCreateResDBServer( + config_file, private_key_file, cert_file, std::move(executor), + std::move(query_executor), config_handler); +} + } // namespace resdb diff --git a/application/utxo/client/BUILD b/application/utxo/client/BUILD new file mode 100644 index 000000000..2835a7af1 --- /dev/null +++ b/application/utxo/client/BUILD @@ -0,0 +1,12 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "utxo_client", + srcs = ["utxo_client.cpp"], + hdrs = ["utxo_client.h"], + deps = [ + "//application/utxo/proto:rpc_cc_proto", + "//application/utxo/proto:utxo_cc_proto", + "//client:resdb_user_client", + ], +) diff --git a/application/utxo/client/utxo_client.cpp b/application/utxo/client/utxo_client.cpp new file mode 100644 index 000000000..080cb087d --- /dev/null +++ b/application/utxo/client/utxo_client.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/client/utxo_client.h" + +#include + +#include "application/utxo/proto/rpc.pb.h" + +namespace resdb { +namespace utxo { + +UTXOClient::UTXOClient(const ResDBConfig& config) : ResDBUserClient(config) {} + +int UTXOClient::Transfer(const UTXO& utxo) { + UTXORequest request; + UTXOResponse response; + *request.mutable_utxo() = utxo; + + int ret = SendRequest(request, &response); + if (ret != 0 || response.ret() < 0) { + return -1; + } + return response.ret(); +} + +std::vector UTXOClient::GetList(int64_t end_id, int num) { + UTXOQuery query; + query.set_query_transaction(true); + query.set_end_id(end_id); + query.set_num(num); + + CustomQueryResponse response; + + int ret = SendRequest(query, Request::TYPE_CUSTOM_QUERY); + if (ret) { + LOG(ERROR) << "send request fail"; + return std::vector(); + } + + ret = RecvRawMessage(&response); + if (ret) { + LOG(ERROR) << "recv response fail"; + return std::vector(); + } + + UTXOQueryResponse utxo_response; + utxo_response.ParseFromString(response.resp_str()); + std::vector utxo_list; + for (const auto utxo : utxo_response.utxos()) { + utxo_list.push_back(utxo); + } + return utxo_list; +} + +int64_t UTXOClient::GetWallet(const std::string& address) { + UTXOQuery query; + query.set_query_transaction(false); + query.set_address(address); + + CustomQueryResponse response; + + int ret = SendRequest(query, Request::TYPE_CUSTOM_QUERY); + if (ret) { + LOG(ERROR) << "send request fail"; + return -1; + } + + ret = RecvRawMessage(&response); + if (ret) { + LOG(ERROR) << "recv response fail"; + return -1; + } + + UTXOQueryResponse utxo_response; + utxo_response.ParseFromString(response.resp_str()); + return utxo_response.value(); +} + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/client/utxo_client.h b/application/utxo/client/utxo_client.h new file mode 100644 index 000000000..9c3726572 --- /dev/null +++ b/application/utxo/client/utxo_client.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include "application/utxo/proto/utxo.pb.h" +#include "client/resdb_user_client.h" + +namespace resdb { +namespace utxo { + +class UTXOClient : public ResDBUserClient { + public: + UTXOClient(const ResDBConfig& config); + + int Transfer(const UTXO& utxo); + + std::vector GetList(int64_t end_id, int num); + + int64_t GetWallet(const std::string& address); +}; + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/proto/BUILD b/application/utxo/proto/BUILD new file mode 100644 index 000000000..cf9f2823d --- /dev/null +++ b/application/utxo/proto/BUILD @@ -0,0 +1,36 @@ +package(default_visibility = ["//visibility:public"]) + +load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +proto_library( + name = "utxo_proto", + srcs = ["utxo.proto"], +) + +cc_proto_library( + name = "utxo_cc_proto", + deps = [":utxo_proto"], +) + +proto_library( + name = "config_proto", + srcs = ["config.proto"], + deps = ["utxo_proto"], +) + +cc_proto_library( + name = "config_cc_proto", + deps = [":config_proto"], +) + +proto_library( + name = "rpc_proto", + srcs = ["rpc.proto"], + deps = ["utxo_proto"], +) + +cc_proto_library( + name = "rpc_cc_proto", + deps = [":rpc_proto"], +) diff --git a/application/utxo/proto/config.proto b/application/utxo/proto/config.proto new file mode 100644 index 000000000..6ba6c3afe --- /dev/null +++ b/application/utxo/proto/config.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package resdb.utxo; + +import "application/utxo/proto/utxo.proto"; + +message GenesisUTXO { + repeated UTXO transactions = 1; +} + +message Config { + GenesisUTXO genesis_transactions = 1; +} + + diff --git a/application/utxo/proto/rpc.proto b/application/utxo/proto/rpc.proto new file mode 100644 index 000000000..c13f30beb --- /dev/null +++ b/application/utxo/proto/rpc.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package resdb.utxo; + +import "application/utxo/proto/utxo.proto"; + +message UTXORequest { + UTXO utxo = 1; +} + +message UTXOResponse { + int64 ret = 1; +} + +message UTXOQuery { + bool query_transaction = 1; + int64 end_id = 2; + int32 num = 3; + string address = 4; +} + +message UTXOQueryResponse { + repeated UTXO utxos = 1; + int64 value = 2; +} diff --git a/application/utxo/proto/utxo.proto b/application/utxo/proto/utxo.proto new file mode 100644 index 000000000..4fd417905 --- /dev/null +++ b/application/utxo/proto/utxo.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package resdb.utxo; + +message UTXOIn { + int64 prev_id = 1; + int32 out_idx = 2; +} + +message UTXOOut{ + string address = 1; + int64 value = 2; + bool spent = 3; + string pub_key = 4; +} + +message UTXO { + repeated UTXOIn in = 1; + repeated UTXOOut out = 2; + string sig = 3; // signed by the owner. + string address = 4; + int64 transaction_id = 5; +} diff --git a/application/utxo/server/BUILD b/application/utxo/server/BUILD new file mode 100644 index 000000000..a36e340da --- /dev/null +++ b/application/utxo/server/BUILD @@ -0,0 +1,12 @@ +package(default_visibility = ["//visibility:public"]) + +cc_binary( + name = "utxo_server", + srcs = ["utxo_server.cpp"], + deps = [ + "//application/utils:server_factory", + "//application/utxo/service:utxo_executor", + "//config:resdb_config_utils", + "//ordering/pbft:consensus_service_pbft", + ], +) diff --git a/application/utxo/server/config/server_config.config b/application/utxo/server/config/server_config.config new file mode 100644 index 000000000..ec284753a --- /dev/null +++ b/application/utxo/server/config/server_config.config @@ -0,0 +1,28 @@ +{ + region : { + replica_info : { + id:1, + ip:"127.0.0.1", + port: 10001, + }, + replica_info : { + id:2, + ip:"127.0.0.1", + port: 10002, + }, + replica_info : { + id:3, + ip:"127.0.0.1", + port: 10003, + }, + replica_info : { + id:4, + ip:"127.0.0.1", + port: 10004, + }, + region_id: 1, + }, + self_region_id:1, +} + + diff --git a/application/utxo/server/config/utxo_config.config b/application/utxo/server/config/utxo_config.config new file mode 100644 index 000000000..237ad2822 --- /dev/null +++ b/application/utxo/server/config/utxo_config.config @@ -0,0 +1,12 @@ + +{ + genesis_transactions: { + transactions: { + out: { + address : "bc1q09tk54hqfz5muzn9rgalfkdjfey8qpuhmzs5zn", + value : 1000, + pub_key: "3056301006072A8648CE3D020106052B8104000A034200049C8FBD86EA4E38FD607CD3AC49FEB75E364B0C694EFB2E6DDD33ABED0BB1017575A79CC53EC6A052F839B4876E96FF9E4B08ECF23EC9CD495B82ECF9D95303BD" + } + } + } +} diff --git a/application/utxo/server/start_contract_server.sh b/application/utxo/server/start_contract_server.sh new file mode 100755 index 000000000..785f82cdf --- /dev/null +++ b/application/utxo/server/start_contract_server.sh @@ -0,0 +1,14 @@ +killall -9 utxo_server + +SERVER_PATH=./bazel-bin/application/utxo/server/utxo_server +SERVER_CONFIG=application/utxo/server/config/server_config.config +UTXO_CONFIG=application/utxo/server/config/utxo_config.config +WORK_PATH=$PWD + +bazel build //application/utxo/server:utxo_server +nohup $SERVER_PATH $SERVER_CONFIG $WORK_PATH/cert/node1.key.pri $WORK_PATH/cert/cert_1.cert ${UTXO_CONFIG} > server0.log & +nohup $SERVER_PATH $SERVER_CONFIG $WORK_PATH/cert/node2.key.pri $WORK_PATH/cert/cert_2.cert ${UTXO_CONFIG} > server1.log & +nohup $SERVER_PATH $SERVER_CONFIG $WORK_PATH/cert/node3.key.pri $WORK_PATH/cert/cert_3.cert ${UTXO_CONFIG} > server2.log & +nohup $SERVER_PATH $SERVER_CONFIG $WORK_PATH/cert/node4.key.pri $WORK_PATH/cert/cert_4.cert ${UTXO_CONFIG} > server3.log & + +nohup $SERVER_PATH $SERVER_CONFIG $WORK_PATH/cert/node5.key.pri $WORK_PATH/cert/cert_5.cert ${UTXO_CONFIG} > client.log & diff --git a/application/utxo/server/utxo_server.cpp b/application/utxo/server/utxo_server.cpp new file mode 100644 index 000000000..9272e3bef --- /dev/null +++ b/application/utxo/server/utxo_server.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include + +#include + +#include "application/utils/server_factory.h" +#include "application/utxo/service/utxo_executor.h" +#include "config/resdb_config_utils.h" +#include "ordering/pbft/consensus_service_pbft.h" +#include "statistic/stats.h" + +using google::protobuf::util::JsonParseOptions; +using resdb::ConsensusServicePBFT; +using resdb::CustomGenerateResDBServer; +using resdb::GenerateResDBConfig; +using resdb::ResConfigData; +using resdb::ResDBConfig; +using resdb::ResDBServer; +using resdb::Stats; +using resdb::utxo::QueryExecutor; +using resdb::utxo::Transaction; +using resdb::utxo::UTXOExecutor; +using resdb::utxo::Wallet; + +resdb::utxo::Config ReadConfigFromFile(const std::string& file_name) { + std::stringstream json_data; + std::ifstream infile(file_name.c_str()); + json_data << infile.rdbuf(); + + resdb::utxo::Config config_data; + JsonParseOptions options; + auto status = JsonStringToMessage(json_data.str(), &config_data, options); + if (!status.ok()) { + LOG(ERROR) << "parse json :" << file_name << " fail:" << status.message(); + } + assert(status.ok()); + return config_data; +} + +void ShowUsage() { + printf( + " [logging_dir]\n"); +} + +int main(int argc, char** argv) { + if (argc < 4) { + ShowUsage(); + exit(0); + } + + char* config_file = argv[1]; + char* private_key_file = argv[2]; + char* cert_file = argv[3]; + + char* utxo_config_file = argv[4]; + + if (argc >= 6) { + auto monitor_port = Stats::GetGlobalStats(5); + monitor_port->SetPrometheus(argv[5]); + } + + resdb::utxo::Config utxo_config = ReadConfigFromFile(utxo_config_file); + + std::unique_ptr config = + GenerateResDBConfig(config_file, private_key_file, cert_file); + ResConfigData config_data = config->GetConfigData(); + + std::unique_ptr wallet = std::make_unique(); + std::unique_ptr transaction = + std::make_unique(utxo_config, wallet.get()); + + auto server = CustomGenerateResDBServer( + config_file, private_key_file, cert_file, + std::make_unique(utxo_config, transaction.get(), + wallet.get()), + std::make_unique(transaction.get(), wallet.get())); + + server->Run(); +} diff --git a/application/utxo/service/BUILD b/application/utxo/service/BUILD new file mode 100644 index 000000000..8ff57f7b5 --- /dev/null +++ b/application/utxo/service/BUILD @@ -0,0 +1,28 @@ +package(default_visibility = ["//application/utxo/server:__pkg__"]) + +cc_library( + name = "utxo_executor", + srcs = ["utxo_executor.cpp"], + hdrs = ["utxo_executor.h"], + deps = [ + "//application/utxo/proto:config_cc_proto", + "//application/utxo/proto:rpc_cc_proto", + "//application/utxo/utxo:transaction", + "//application/utxo/utxo:wallet", + "//config:resdb_config_utils", + "//execution:custom_query", + "//ordering/pbft:consensus_service_pbft", + ], +) + +cc_test( + name = "utxo_executor_test", + srcs = ["utxo_executor_test.cpp"], + deps = [ + ":utxo_executor", + "//common/test:test_main", + "//crypto:hash", + "//crypto:key_generator", + "//crypto:signature_utils", + ], +) diff --git a/application/utxo/service/utxo_executor.cpp b/application/utxo/service/utxo_executor.cpp new file mode 100644 index 000000000..53f3f4182 --- /dev/null +++ b/application/utxo/service/utxo_executor.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/service/utxo_executor.h" + +#include + +#include "application/utxo/proto/rpc.pb.h" + +namespace resdb { +namespace utxo { + +UTXOExecutor::UTXOExecutor(const Config& config, Transaction* transaction, + Wallet* wallet) + : transaction_(transaction), wallet_(wallet) {} + +UTXOExecutor::~UTXOExecutor() {} + +std::unique_ptr UTXOExecutor::ExecuteData( + const std::string& client_request) { + UTXORequest utxo_request; + UTXOResponse response; + + if (!utxo_request.ParseFromString(client_request)) { + LOG(ERROR) << "parse data fail"; + return nullptr; + } + + int64_t ret = transaction_->AddTransaction(utxo_request.utxo()); + response.set_ret(ret); + std::unique_ptr ret_str = std::make_unique(); + response.SerializeToString(ret_str.get()); + return ret_str; +} + +QueryExecutor::QueryExecutor(Transaction* transaction, Wallet* wallet) + : transaction_(transaction), wallet_(wallet) {} + +std::unique_ptr QueryExecutor::Query( + const std::string& request_str) { + UTXOQuery query; + if (!query.ParseFromString(request_str)) { + LOG(ERROR) << "parse query fail"; + return nullptr; + } + + UTXOQueryResponse resp; + if (query.query_transaction()) { + std::vector utxos = + transaction_->GetUTXO(query.end_id(), query.num()); + for (const UTXO& utxo : utxos) { + *resp.add_utxos() = utxo; + } + } else { + resp.set_value(wallet_->GetCoin(query.address())); + } + std::unique_ptr resp_str = std::make_unique(); + resp.SerializeToString(resp_str.get()); + return resp_str; +} + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/service/utxo_executor.h b/application/utxo/service/utxo_executor.h new file mode 100644 index 000000000..c4cdb7c58 --- /dev/null +++ b/application/utxo/service/utxo_executor.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include "application/utxo/proto/config.pb.h" +#include "application/utxo/utxo/transaction.h" +#include "application/utxo/utxo/wallet.h" +#include "config/resdb_config_utils.h" +#include "execution/custom_query.h" +#include "execution/transaction_executor_impl.h" + +namespace resdb { +namespace utxo { + +class UTXOExecutor : public TransactionExecutorImpl { + public: + UTXOExecutor(const Config& config, Transaction* transaction, Wallet* wallet); + ~UTXOExecutor(); + + std::unique_ptr ExecuteData(const std::string& request) override; + + private: + Transaction* transaction_; + Wallet* wallet_; +}; + +class QueryExecutor : public CustomQuery { + public: + QueryExecutor(Transaction* transaction, Wallet* wallet); + virtual ~QueryExecutor() = default; + + virtual std::unique_ptr Query( + const std::string& request_str) override; + + private: + Transaction* transaction_; + Wallet* wallet_; +}; + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/service/utxo_executor_test.cpp b/application/utxo/service/utxo_executor_test.cpp new file mode 100644 index 000000000..87e47d25b --- /dev/null +++ b/application/utxo/service/utxo_executor_test.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/service/utxo_executor.h" + +#include +#include + +#include "application/utxo/proto/rpc.pb.h" +#include "crypto/key_generator.h" +#include "crypto/signature_utils.h" + +namespace resdb { +namespace utxo { +namespace { + +using ::testing::Test; + +TEST(UTXOExecutorTest, AddUTXO) { + Config config; + + SecretKey key = KeyGenerator ::GeneratorKeys(SignatureInfo::ECDSA); + + { + auto* gensis_txn = config.mutable_genesis_transactions(); + UTXO* utxo = gensis_txn->add_transactions(); + UTXOOut* out = utxo->add_out(); + out->set_address("0001"); + out->set_value(1234); + out->set_pub_key(key.public_key()); + } + Wallet wallet; + Transaction transaction(config, &wallet); + UTXOExecutor executor(config, &transaction, &wallet); + + { + UTXO utxo; + UTXOIn* in = utxo.add_in(); + in->set_prev_id(0); + in->set_out_idx(0); + + UTXOOut* out = utxo.add_out(); + out->set_address("1234"); + out->set_value(234); + utxo.set_address("0001"); + + std::string signature = utils::ECDSASignString( + key.private_key(), utxo.address() + std::to_string(0)); + utxo.set_sig(signature); + + UTXORequest request; + *request.mutable_utxo() = utxo; + + std::string request_str; + request.SerializeToString(&request_str); + + auto ret_str = executor.ExecuteData(request_str); + EXPECT_TRUE(ret_str != nullptr); + } +} + +} // namespace +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/BUILD b/application/utxo/utxo/BUILD new file mode 100644 index 000000000..2621d8408 --- /dev/null +++ b/application/utxo/utxo/BUILD @@ -0,0 +1,64 @@ +package(default_visibility = ["//application/utxo:__subpackages__"]) + +cc_library( + name = "tx_mempool", + srcs = ["tx_mempool.cpp"], + hdrs = ["tx_mempool.h"], + deps = [ + "//application/utxo/proto:utxo_cc_proto", + "//common:comm", + ], +) + +cc_test( + name = "tx_mempool_test", + srcs = ["tx_mempool_test.cpp"], + deps = [ + ":tx_mempool", + "//common/test:test_main", + ], +) + +cc_library( + name = "wallet", + srcs = ["wallet.cpp"], + hdrs = ["wallet.h"], + deps = [ + "//application/utxo/proto:utxo_cc_proto", + "//common:comm", + ], +) + +cc_test( + name = "wallet_test", + srcs = ["wallet_test.cpp"], + deps = [ + ":wallet", + "//common/test:test_main", + ], +) + +cc_library( + name = "transaction", + srcs = ["transaction.cpp"], + hdrs = ["transaction.h"], + deps = [ + ":tx_mempool", + ":wallet", + "//application/utxo/proto:config_cc_proto", + "//application/utxo/proto:utxo_cc_proto", + "//common:comm", + "//crypto:hash", + "//crypto:signature_utils", + ], +) + +cc_test( + name = "transaction_test", + srcs = ["transaction_test.cpp"], + deps = [ + ":transaction", + "//common/test:test_main", + "//crypto:key_generator", + ], +) diff --git a/application/utxo/utxo/transaction.cpp b/application/utxo/utxo/transaction.cpp new file mode 100644 index 000000000..65e86d3a9 --- /dev/null +++ b/application/utxo/utxo/transaction.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/utxo/transaction.h" + +#include + +#include +#include + +#include "crypto/hash.h" +#include "crypto/signature_utils.h" + +namespace resdb { +namespace utxo { + +Transaction::Transaction(const Config& config, Wallet* wallet) + : config_(config), wallet_(wallet) { + tx_mempool_ = std::make_unique(); + for (const UTXO& trans : config_.genesis_transactions().transactions()) { + int64_t transaction_id = tx_mempool_->AddUTXO(trans); + + for (const UTXOOut& output : trans.out()) { + int ret = wallet_->AddCoin(output.address(), output.value()); + if (ret) { + LOG(ERROR) << "add coin fail"; + assert(ret == 0); + } + } + + LOG(ERROR) << "get genesis utxo:" << trans.DebugString() + << " transaction id:" << transaction_id; + } +} + +Transaction::~Transaction() {} + +int64_t Transaction::AddTransaction(const UTXO& utxo) { + absl::StatusOr> ins_or = GetInput(utxo); + if (!ins_or.ok()) { + LOG(ERROR) << "get input fail:" << ins_or.status().message(); + return -1; + } + + if (!VerifyUTXO(utxo, *ins_or)) { + LOG(ERROR) << "transaction is not valid"; + return -1; + } + + if (AddCoin(utxo)) { + LOG(ERROR) << "add coin fail"; + return -1; + } + return tx_mempool_->AddUTXO(utxo); +} + +int64_t Transaction::AddTransaction(const std::string& utxo_str) { + UTXO utxo; + if (!utxo.ParseFromString(utxo_str)) { + LOG(ERROR) << "parse utxo fail"; + return -1; + } + return AddTransaction(utxo); +} + +absl::StatusOr> Transaction::GetInput(const UTXO& utxo) { + std::vector utxos; + if (utxo.in_size() == 0) { + if (utxo.address() == "0000") { + return utxos; + } + } + + int64_t verify_nonce = 0; + std::string public_key; + for (const UTXOIn& input : utxo.in()) { + absl::StatusOr utxo_or = + tx_mempool_->GetUTXO(input.prev_id(), input.out_idx(), utxo.address()); + if (!utxo_or.ok()) { + LOG(ERROR) << "get pre utxo id fail:" << input.prev_id(); + return utxo_or.status(); + } + utxos.push_back(*utxo_or); + if (public_key.empty()) { + public_key = (*utxo_or).pub_key(); + } + verify_nonce += input.prev_id(); + } + + if (utxos.empty()) { + LOG(ERROR) << "no input"; + return absl::InvalidArgumentError("Input invalid."); + } + + if (utxo.sig().empty()) { + LOG(ERROR) << "utxo no sig, invalid"; + return absl::InvalidArgumentError("Input invalid."); + } + + LOG(ERROR) << "get public key:" << public_key << " nonce:" << verify_nonce; + + bool valid = utils::ECDSAVerifyString( + utxo.address() + std::to_string(verify_nonce), public_key, utxo.sig()); + if (!valid) { + LOG(ERROR) << "key not valid"; + return absl::InvalidArgumentError("Key invalid."); + } + + for (const UTXOOut& utxo : utxos) { + if (utxo.pub_key() != public_key) { + LOG(ERROR) << "public key not match"; + return absl::InvalidArgumentError("Key invalid."); + } + } + + return utxos; +} + +bool Transaction::VerifyUTXO(const UTXO& utxo, + const std::vector& inputs) { + int64_t total_input_v = 0; + for (const UTXOOut& input : inputs) { + int64_t value = input.value(); + total_input_v += value; + } + + for (const UTXOOut& output : utxo.out()) { + total_input_v -= output.value(); + } + + if (total_input_v <= 0) { + LOG(ERROR) << "input is not enough"; + return false; + } + + return true; +} + +int64_t Transaction::GetUTXOOutValue(int64_t transaction_id, int out_idx, + const std::string& address) { + return tx_mempool_->GetUTXOOutValue(transaction_id, out_idx, address); +} + +int Transaction::AddCoin(const UTXO& utxo) { + int64_t total_value = 0; + for (const UTXOIn& input : utxo.in()) { + int64_t value = tx_mempool_->MarkSpend(input.prev_id(), input.out_idx(), + utxo.address()); + assert(value >= 0); + total_value += value; + } + + int ret = wallet_->AddCoin(utxo.address(), -total_value); + if (ret) { + LOG(ERROR) << "add coin fail"; + return -1; + } + + for (const UTXOOut& output : utxo.out()) { + int ret = wallet_->AddCoin(output.address(), output.value()); + if (ret) { + LOG(ERROR) << "add coin fail"; + return -1; + } + } + return 0; +} + +std::vector Transaction::GetUTXO(int64_t end_id, int num) { + return tx_mempool_->GetUTXO(end_id, num); +} + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/transaction.h b/application/utxo/utxo/transaction.h new file mode 100644 index 000000000..2cea0c40d --- /dev/null +++ b/application/utxo/utxo/transaction.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include "absl/status/statusor.h" +#include "application/utxo/proto/config.pb.h" +#include "application/utxo/proto/utxo.pb.h" +#include "application/utxo/utxo/tx_mempool.h" +#include "application/utxo/utxo/wallet.h" + +namespace resdb { +namespace utxo { + +class Transaction { + public: + Transaction(const Config& config, Wallet* wallet); + ~Transaction(); + + int64_t AddTransaction(const std::string& utxo_string); + int64_t AddTransaction(const UTXO& utxo); + + std::vector GetUTXO(int64_t end_id, int num); + + private: + int AddCoin(const UTXO& utxo); + bool VerifyUTXO(const UTXO& utxo, const std::vector& ins); + int64_t GetUTXOOutValue(int64_t tx_id, int out_idx, + const std::string& address); + + absl::StatusOr> GetInput(const UTXO& utxo); + + private: + std::unique_ptr tx_mempool_; + Config config_; + Wallet* wallet_; +}; + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/transaction_test.cpp b/application/utxo/utxo/transaction_test.cpp new file mode 100644 index 000000000..f34d9dac5 --- /dev/null +++ b/application/utxo/utxo/transaction_test.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/utxo/transaction.h" + +#include +#include + +#include "crypto/hash.h" +#include "crypto/key_generator.h" +#include "crypto/signature_utils.h" + +namespace resdb { +namespace utxo { +namespace { + +using ::testing::Test; + +TEST(TransanctionTest, InvalidInputString) { + Wallet wallet; + Transaction transaction(Config(), &wallet); + EXPECT_EQ(transaction.AddTransaction("123"), -1); +} + +TEST(TransanctionTest, NoAddress) { + UTXO utxo; + + UTXOOut* out = utxo.add_out(); + out->set_address("1234"); + out->set_value(1234); + + std::string str; + utxo.SerializeToString(&str); + + Wallet wallet; + Transaction transaction(Config(), &wallet); + + EXPECT_EQ(transaction.AddTransaction(str), -1); +} + +TEST(TransanctionTest, EmptyInputUtxo) { + UTXO utxo; + + UTXOOut* out = utxo.add_out(); + out->set_address("1234"); + out->set_value(1234); + + utxo.set_address("0001"); + + std::string str; + utxo.SerializeToString(&str); + + Wallet wallet; + Transaction transaction(Config(), &wallet); + + EXPECT_EQ(transaction.AddTransaction(str), -1); +} + +TEST(TransanctionTest, DefaultUTXO) { + SecretKey key = KeyGenerator ::GeneratorKeys(SignatureInfo::ECDSA); + + Config config; + { + auto* gensis_txn = config.mutable_genesis_transactions(); + UTXO* utxo = gensis_txn->add_transactions(); + UTXOOut* out = utxo->add_out(); + out->set_address("0001"); + out->set_value(1234); + out->set_pub_key(key.public_key()); + } + + Wallet wallet; + Transaction transaction(config, &wallet); + + { + UTXO utxo; + UTXOIn* in = utxo.add_in(); + in->set_prev_id(0); + in->set_out_idx(0); + + UTXOOut* out = utxo.add_out(); + out->set_address("1234"); + out->set_value(234); + utxo.set_address("0001"); + + std::string signature = utils::ECDSASignString( + key.private_key(), utxo.address() + std::to_string(0)); + utxo.set_sig(signature); + + std::string str; + utxo.SerializeToString(&str); + + EXPECT_EQ(transaction.AddTransaction(str), 1); + } + + EXPECT_EQ(wallet.GetCoin("1234"), 234); +} + +} // namespace +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/tx_mempool.cpp b/application/utxo/utxo/tx_mempool.cpp new file mode 100644 index 000000000..7ed29dac5 --- /dev/null +++ b/application/utxo/utxo/tx_mempool.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/utxo/tx_mempool.h" + +#include + +namespace resdb { +namespace utxo { + +TxMempool::TxMempool() : id_(0) {} +TxMempool::~TxMempool() {} + +int64_t TxMempool::AddUTXO(const UTXO& utxo) { + LOG(ERROR) << "add utxo id:" << id_; + int64_t cur_id = id_++; + txs_[cur_id] = std::make_unique(utxo); + return cur_id; +} + +absl::StatusOr TxMempool::GetUTXO(int64_t id, int out_idx, + const std::string& address) { + if (txs_.find(id) == txs_.end()) { + LOG(ERROR) << "no utxo:" << id; + return absl::InvalidArgumentError("id invalid."); + } + + UTXO* utxo = txs_[id].get(); + + if (utxo->out_size() <= out_idx) { + LOG(ERROR) << " idx not found:" << out_idx << " id:" << id; + return absl::InvalidArgumentError("id invalid."); + } + + if (utxo->out(out_idx).spent()) { + LOG(ERROR) << " value has been spent:" << id << " idx:" << out_idx; + return absl::InvalidArgumentError("id has been spent."); + } + + if (utxo->out(out_idx).address() != address) { + LOG(ERROR) << " address not match:" << address + << " utxo addr:" << utxo->out(out_idx).address(); + return absl::InvalidArgumentError("address invalid."); + } + return utxo->out(out_idx); +} + +int64_t TxMempool::GetUTXOOutValue(int64_t id, int out_idx, + const std::string& address) { + if (txs_.find(id) == txs_.end()) { + LOG(ERROR) << "no utxo:" << id; + return -1; + } + + UTXO* utxo = txs_[id].get(); + + if (utxo->out_size() <= out_idx) { + LOG(ERROR) << " idx not found:" << out_idx << " id:" << id; + return -1; + } + + if (utxo->out(out_idx).spent()) { + LOG(ERROR) << " value has been spent:" << id << " idx:" << out_idx; + return -1; + } + if (utxo->out(out_idx).address() != address) { + LOG(ERROR) << " address not match:" << address + << " utxo addr:" << utxo->out(out_idx).address(); + return -1; + } + return utxo->out(out_idx).value(); +} + +int64_t TxMempool::MarkSpend(int64_t id, int out_idx, + const std::string& address) { + if (txs_.find(id) == txs_.end()) { + LOG(ERROR) << "no utxo:" << id; + return -1; + } + + UTXO* utxo = txs_[id].get(); + if (utxo->out_size() <= out_idx) { + LOG(ERROR) << " idx not found:" << out_idx << " id:" << id; + return -1; + } + + utxo->mutable_out(out_idx)->set_spent(true); + return utxo->out(out_idx).value(); +} + +std::vector TxMempool::GetUTXO(int64_t end_idx, int num) { + if (end_idx == -1) { + int64_t max_id = ((--txs_.end())->first); + end_idx = max_id; + } + + std::vector resp; + for (int i = 0; i < num; ++i) { + if (txs_.find(end_idx - i) == txs_.end()) { + break; + } + resp.push_back(*txs_[end_idx - i]); + resp.back().set_transaction_id(end_idx - i); + } + + std::reverse(resp.begin(), resp.end()); + + return resp; +} + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/tx_mempool.h b/application/utxo/utxo/tx_mempool.h new file mode 100644 index 000000000..fcc468980 --- /dev/null +++ b/application/utxo/utxo/tx_mempool.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include "absl/status/statusor.h" +#include "application/utxo/proto/utxo.pb.h" + +namespace resdb { +namespace utxo { + +class TxMempool { + public: + TxMempool(); + ~TxMempool(); + + // Add a new utxo and return the transaction id. + int64_t AddUTXO(const UTXO& utxo); + + // get the transfer value from a transantion "id" in its output[out_idx]. + // if the address not match or the transaction does not exist, return -1 + int64_t GetUTXOOutValue(int64_t id, int out_idx, const std::string& address); + + absl::StatusOr GetUTXO(int64_t id, int out_idx, + const std::string& address); + + // Mark the output of a trans has been spent. + // Return the out value. + int64_t MarkSpend(int64_t id, int out_idx, const std::string& address); + + std::vector GetUTXO(int64_t end_idx, int num); + + private: + std::map > txs_; + std::atomic id_; +}; + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/tx_mempool_test.cpp b/application/utxo/utxo/tx_mempool_test.cpp new file mode 100644 index 000000000..2176c3e00 --- /dev/null +++ b/application/utxo/utxo/tx_mempool_test.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/utxo/tx_mempool.h" + +#include +#include + +#include "common/test/test_macros.h" + +namespace resdb { +namespace utxo { +namespace { + +using ::resdb::testing::EqualsProto; +using ::testing::Test; + +TEST(TxMempoolTest, AddTx) { + UTXO utxo; + auto out = utxo.add_out(); + out->set_value(100); + out->set_address("0000"); + utxo.set_address("0001"); + + TxMempool pool; + EXPECT_EQ(pool.AddUTXO(utxo), 0); + + int64_t value = pool.GetUTXOOutValue(0, 0, "0000"); + EXPECT_EQ(value, 100); +} + +TEST(TxMempoolTest, AddTxNoIdx) { + UTXO utxo; + auto out = utxo.add_out(); + out->set_value(100); + out->set_address("0000"); + + utxo.set_address("0001"); + + TxMempool pool; + EXPECT_EQ(pool.AddUTXO(utxo), 0); + + int64_t value = pool.GetUTXOOutValue(1, 1, "0000"); + EXPECT_EQ(value, -1); +} + +TEST(TxMempoolTest, AddTxNoOutAddr) { + UTXO utxo; + auto out = utxo.add_out(); + out->set_value(100); + out->set_address("0000"); + + utxo.set_address("0001"); + + TxMempool pool; + EXPECT_EQ(pool.AddUTXO(utxo), 0); + + int64_t value = pool.GetUTXOOutValue(1, 0, "0002"); + EXPECT_EQ(value, -1); +} + +TEST(TxMempoolTest, AddTxNoAddr) { + UTXO utxo; + auto out = utxo.add_out(); + out->set_value(100); + out->set_address("0000"); + + utxo.set_address("0001"); + + TxMempool pool; + EXPECT_EQ(pool.AddUTXO(utxo), 0); + + int64_t value = pool.GetUTXOOutValue(2, 0, "0000"); + EXPECT_EQ(value, -1); +} + +TEST(TxMempoolTest, AddTxMarkSpent) { + UTXO utxo; + auto out = utxo.add_out(); + out->set_value(100); + out->set_address("0000"); + + utxo.set_address("0001"); + + TxMempool pool; + EXPECT_EQ(pool.AddUTXO(utxo), 0); + + int64_t value = pool.MarkSpend(0, 0, "0000"); + EXPECT_EQ(value, 100); + + EXPECT_EQ(pool.GetUTXOOutValue(0, 0, "0000"), -1); +} + +TEST(TxMempoolTest, AddTxAndGet) { + UTXO utxo; + auto out = utxo.add_out(); + out->set_value(100); + out->set_address("0000"); + + utxo.set_address("0001"); + + TxMempool pool; + EXPECT_EQ(pool.AddUTXO(utxo), 0); + + auto value_or = pool.GetUTXO(0, 0, "0000"); + EXPECT_TRUE(value_or.ok()); + + EXPECT_EQ((*value_or).value(), 100); +} + +TEST(TxMempoolTest, GetList) { + UTXO utxo1; + UTXO utxo2; + UTXO utxo3; + + { + auto out = utxo1.add_out(); + out->set_value(100); + out->set_address("0002"); + utxo1.set_address("0001"); + } + + { + auto in = utxo2.add_in(); + in->set_prev_id(0); + + auto out = utxo2.add_out(); + out->set_value(100); + out->set_address("0003"); + utxo2.set_address("0002"); + } + { + auto in = utxo3.add_in(); + in->set_prev_id(1); + + auto out = utxo3.add_out(); + out->set_value(100); + out->set_address("0004"); + utxo3.set_address("0003"); + } + + TxMempool pool; + EXPECT_EQ(pool.AddUTXO(utxo1), 0); + EXPECT_EQ(pool.AddUTXO(utxo2), 1); + EXPECT_EQ(pool.AddUTXO(utxo3), 2); + + utxo1.set_transaction_id(0); + utxo2.set_transaction_id(1); + utxo3.set_transaction_id(2); + { + auto list = pool.GetUTXO(-1, 2); + EXPECT_EQ(list.size(), 2); + EXPECT_THAT(list[0], EqualsProto(utxo2)); + EXPECT_THAT(list[1], EqualsProto(utxo3)); + } + { + auto list = pool.GetUTXO(-1, 3); + EXPECT_EQ(list.size(), 3); + EXPECT_THAT(list[0], EqualsProto(utxo1)); + EXPECT_THAT(list[1], EqualsProto(utxo2)); + EXPECT_THAT(list[2], EqualsProto(utxo3)); + } + { + auto list = pool.GetUTXO(1, 2); + EXPECT_EQ(list.size(), 2); + EXPECT_THAT(list[0], EqualsProto(utxo1)); + EXPECT_THAT(list[1], EqualsProto(utxo2)); + } + { + auto list = pool.GetUTXO(2, 4); + EXPECT_EQ(list.size(), 3); + EXPECT_THAT(list[0], EqualsProto(utxo1)); + EXPECT_THAT(list[1], EqualsProto(utxo2)); + EXPECT_THAT(list[2], EqualsProto(utxo3)); + } +} + +} // namespace +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/wallet.cpp b/application/utxo/utxo/wallet.cpp new file mode 100644 index 000000000..dc7614c10 --- /dev/null +++ b/application/utxo/utxo/wallet.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/utxo/wallet.h" + +#include + +namespace resdb { +namespace utxo { + +Wallet::Wallet() {} +Wallet::~Wallet() {} + +int Wallet::AddCoin(const std::string& address, int64_t value) { + wallet_[address] += value; + return 0; +} + +int64_t Wallet::GetCoin(const std::string& address) { return wallet_[address]; } + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/wallet.h b/application/utxo/utxo/wallet.h new file mode 100644 index 000000000..a70117683 --- /dev/null +++ b/application/utxo/utxo/wallet.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include "application/utxo/proto/utxo.pb.h" + +namespace resdb { +namespace utxo { + +class Wallet { + public: + Wallet(); + ~Wallet(); + + int AddCoin(const std::string& address, int64_t value); + + int64_t GetCoin(const std::string& address); + + private: + std::map wallet_; +}; + +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/utxo/wallet_test.cpp b/application/utxo/utxo/wallet_test.cpp new file mode 100644 index 000000000..4eb808412 --- /dev/null +++ b/application/utxo/utxo/wallet_test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "application/utxo/utxo/wallet.h" + +#include +#include + +namespace resdb { +namespace utxo { +namespace { + +using ::testing::Test; + +TEST(WalletTest, AddCoin) { + Wallet coin; + coin.AddCoin("1234", 100); + EXPECT_EQ(coin.GetCoin("1234"), 100); + EXPECT_EQ(coin.GetCoin("4321"), 0); +} + +} // namespace +} // namespace utxo +} // namespace resdb diff --git a/application/utxo/wallet_tool/cpp/BUILD b/application/utxo/wallet_tool/cpp/BUILD new file mode 100644 index 000000000..6a918fc06 --- /dev/null +++ b/application/utxo/wallet_tool/cpp/BUILD @@ -0,0 +1,29 @@ +package(default_visibility = ["//application/utxo/wallet_tool/pybind:__subpackages__"]) + +cc_library( + name = "addr_utils", + srcs = ["addr_utils.cpp"], + hdrs = ["addr_utils.h"], + deps = [ + "//crypto:hash", + ], +) + +cc_library( + name = "key_utils", + srcs = ["key_utils.cpp"], + hdrs = ["key_utils.h"], + deps = [ + "//crypto:key_generator", + ], +) + +cc_binary( + name = "utxo_client_tools", + srcs = ["utxo_client_tools.cpp"], + deps = [ + "//application/utxo/client:utxo_client", + "//config:resdb_config_utils", + "//crypto:signature_utils", + ], +) diff --git a/application/utxo/wallet_tool/cpp/addr_utils.cpp b/application/utxo/wallet_tool/cpp/addr_utils.cpp new file mode 100644 index 000000000..783cb8a2d --- /dev/null +++ b/application/utxo/wallet_tool/cpp/addr_utils.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "application/utxo/wallet_tool/cpp/addr_utils.h" + +#include + +#include "crypto/hash.h" + +namespace resdb { +namespace coin { +namespace utils { + +using resdb::utils::CalculateRIPEMD160Hash; +using resdb::utils::CalculateSHA256Hash; + +std::string GenAddr(const std::string& public_key) { + std::string sha256_hash = CalculateSHA256Hash(public_key); + std::string ripemd160_hash = CalculateRIPEMD160Hash(sha256_hash); + return ripemd160_hash; +} + +} // namespace utils +} // namespace coin +} // namespace resdb diff --git a/application/utxo/wallet_tool/cpp/addr_utils.h b/application/utxo/wallet_tool/cpp/addr_utils.h new file mode 100644 index 000000000..e0904d57f --- /dev/null +++ b/application/utxo/wallet_tool/cpp/addr_utils.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +namespace resdb { +namespace coin { +namespace utils { + +// Generate an address based on a public key +// key = ripemd160( sha256 ( public key ) ) +std::string GenAddr(const std::string& public_key); + +} // namespace utils +} // namespace coin +} // namespace resdb diff --git a/application/utxo/wallet_tool/cpp/client_config.config b/application/utxo/wallet_tool/cpp/client_config.config new file mode 100644 index 000000000..e1bcc9054 --- /dev/null +++ b/application/utxo/wallet_tool/cpp/client_config.config @@ -0,0 +1 @@ +5 127.0.0.1 10005 diff --git a/application/utxo/wallet_tool/cpp/key_utils.cpp b/application/utxo/wallet_tool/cpp/key_utils.cpp new file mode 100644 index 000000000..8fe643460 --- /dev/null +++ b/application/utxo/wallet_tool/cpp/key_utils.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "application/utxo/wallet_tool/cpp/key_utils.h" + +#include "crypto/key_generator.h" + +namespace resdb { +namespace coin { +namespace utils { + +std::pair GenECDSAKeys() { + resdb::SecretKey key = + resdb::KeyGenerator::GeneratorKeys(resdb::SignatureInfo::ECDSA); + return std::make_pair(key.private_key(), key.public_key()); +} + +} // namespace utils +} // namespace coin +} // namespace resdb diff --git a/application/utxo/wallet_tool/cpp/key_utils.h b/application/utxo/wallet_tool/cpp/key_utils.h new file mode 100644 index 000000000..783813063 --- /dev/null +++ b/application/utxo/wallet_tool/cpp/key_utils.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +namespace resdb { +namespace coin { +namespace utils { + +std::pair GenECDSAKeys(); + +} +} // namespace coin +} // namespace resdb diff --git a/application/utxo/wallet_tool/cpp/server_config0.config b/application/utxo/wallet_tool/cpp/server_config0.config new file mode 100644 index 000000000..ee48298e4 --- /dev/null +++ b/application/utxo/wallet_tool/cpp/server_config0.config @@ -0,0 +1 @@ +1 127.0.0.1 10001 diff --git a/application/utxo/wallet_tool/cpp/utxo_client_tools.cpp b/application/utxo/wallet_tool/cpp/utxo_client_tools.cpp new file mode 100644 index 000000000..950748df1 --- /dev/null +++ b/application/utxo/wallet_tool/cpp/utxo_client_tools.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include + +#include "application/utxo/client/utxo_client.h" +#include "config/resdb_config_utils.h" +#include "crypto/signature_utils.h" + +using google::protobuf::util::MessageToJsonString; +using resdb::GenerateResDBConfig; +using resdb::ResDBConfig; +using resdb::utxo::UTXO; +using resdb::utxo::UTXOClient; +using resdb::utxo::UTXOIn; +using resdb::utxo::UTXOOut; + +void ShowUsage() { + printf( + " -c -m -n -p -a \n"); + exit(0); +} + +void Transfer(UTXOClient* client, int64_t transaction_id, + const std::string& address, const std::string& to_address, + const int value, const std::string& private_key, + const std::string& to_pub_key) { + if (private_key.empty() || to_pub_key.empty()) { + printf("no private key or public key\n"); + return; + } + UTXO utxo; + int64_t nonce = 0; + UTXOIn* in = utxo.add_in(); + in->set_prev_id(transaction_id); + in->set_out_idx(0); + nonce += transaction_id; + + UTXOOut* out = utxo.add_out(); + out->set_address(to_address); + out->set_value(value); + out->set_pub_key(to_pub_key); + utxo.set_address(address); + utxo.set_sig(resdb::utils::ECDSASignString(private_key, + address + std::to_string(nonce))); + + auto output = client->Transfer(utxo); + LOG(ERROR) << "execute result:\n" << output; +} + +void GetList(UTXOClient* client, int64_t end_id, int num) { + std::vector list = client->GetList(end_id, num); + LOG(ERROR) << "get utxo:"; + for (const UTXO& utxo : list) { + std::string str; + MessageToJsonString(utxo, &str); + printf("%s\n", str.c_str()); + } +} + +void GetWallet(UTXOClient* client, const std::string& address) { + int64_t ret = client->GetWallet(address); + LOG(ERROR) << "address:" << address << " get wallet value:" << ret; +} + +int main(int argc, char** argv) { + if (argc < 3) { + printf("-d -c [config]\n"); + return 0; + } + + int64_t transaction_id = 0; + std::string address, to_address, params, contract_address, func_name, + private_key, to_pub_key; + int64_t end_id = 0; + int num = 10; + int c; + std::string cmd; + int64_t value = 0; + std::string client_config_file; + while ((c = getopt(argc, argv, "c:d:t:x:m:h:e:v:n:p:b:")) != -1) { + switch (c) { + case 'c': + client_config_file = optarg; + break; + case 'd': + address = optarg; + break; + case 't': + to_address = optarg; + break; + case 'x': + transaction_id = strtoull(optarg, NULL, 10); + break; + case 'm': + cmd = optarg; + break; + case 'e': + end_id = strtoull(optarg, NULL, 10); + break; + case 'n': + num = strtoull(optarg, NULL, 10); + break; + case 'v': + value = strtoull(optarg, NULL, 10); + break; + case 'p': + private_key = optarg; + break; + case 'b': + to_pub_key = optarg; + break; + case 'h': + ShowUsage(); + break; + } + } + + ResDBConfig config = GenerateResDBConfig(client_config_file); + config.SetClientTimeoutMs(100000); + + UTXOClient client(config); + if (cmd == "transfer") { + Transfer(&client, transaction_id, address, to_address, value, private_key, + to_pub_key); + } else if (cmd == "list") { + GetList(&client, end_id, num); + } else if (cmd == "wallet") { + GetWallet(&client, to_address); + } +} diff --git a/application/utxo/wallet_tool/py/BUILD b/application/utxo/wallet_tool/py/BUILD new file mode 100644 index 000000000..d6cdc2def --- /dev/null +++ b/application/utxo/wallet_tool/py/BUILD @@ -0,0 +1,21 @@ +package(default_visibility = ["//visibility:public"]) + +py_binary( + name = "keys", + srcs = ["keys.py"], + legacy_create_init = False, + python_version = "PY3", + deps = [ + "//application/utxo/wallet_tool/pybind:wallet_tools_py_so", # a py_library + ], +) + +py_binary( + name = "addr", + srcs = ["addr.py"], + legacy_create_init = False, + python_version = "PY3", + deps = [ + "//application/utxo/wallet_tool/pybind:wallet_tools_py_so", # a py_library + ], +) diff --git a/application/utxo/wallet_tool/py/addr.py b/application/utxo/wallet_tool/py/addr.py new file mode 100644 index 000000000..f4cae5eb1 --- /dev/null +++ b/application/utxo/wallet_tool/py/addr.py @@ -0,0 +1,37 @@ +# Copyright (c) 2019-2022 ExpoLab, UC Davis +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +import sys +import wallet_tools_py +import binascii +import bech32 + +pub_key = sys.argv[1] + +res = wallet_tools_py.GenAddr(pub_key) + +witprog = bytes.fromhex(res.hex()) +witver = 0x00 +hrp = 'bc' +address = bech32.encode(hrp, witver, witprog) +print("address:",address) + diff --git a/application/utxo/wallet_tool/py/keys.py b/application/utxo/wallet_tool/py/keys.py new file mode 100644 index 000000000..72c4fdb74 --- /dev/null +++ b/application/utxo/wallet_tool/py/keys.py @@ -0,0 +1,30 @@ +# Copyright (c) 2019-2022 ExpoLab, UC Davis +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +import wallet_tools_py + +res = wallet_tools_py.GenECDSAKeys() +pri_key = res[0] +pub_key = res[1] + +print("private key:{}".format(pri_key)) +print("public key:{}".format(pub_key)) diff --git a/application/utxo/wallet_tool/pybind/BUILD b/application/utxo/wallet_tool/pybind/BUILD new file mode 100644 index 000000000..5cbdf67bf --- /dev/null +++ b/application/utxo/wallet_tool/pybind/BUILD @@ -0,0 +1,19 @@ +package(default_visibility = ["//visibility:public"]) + +cc_binary( + name = "wallet_tools_py.so", + srcs = ["wallet_tools_py.cpp"], + linkshared = 1, + linkstatic = 1, + deps = [ + "//application/utxo/wallet_tool/cpp:addr_utils", + "//application/utxo/wallet_tool/cpp:key_utils", + "@pybind11", + ], +) + +py_library( + name = "wallet_tools_py_so", + data = [":wallet_tools_py.so"], + imports = ["."], +) diff --git a/application/utxo/wallet_tool/pybind/wallet_tools_py.cpp b/application/utxo/wallet_tool/pybind/wallet_tools_py.cpp new file mode 100644 index 000000000..c90cac023 --- /dev/null +++ b/application/utxo/wallet_tool/pybind/wallet_tools_py.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "application/utxo/wallet_tool/cpp/addr_utils.h" +#include "application/utxo/wallet_tool/cpp/key_utils.h" + +namespace py = pybind11; + +PYBIND11_MODULE(wallet_tools_py, m) { + m.doc() = "Nexres Wallet Key Generator"; + m.def("GenECDSAKeys", &resdb::coin::utils::GenECDSAKeys, + "Generate a ecdsa key pair."); + m.def( + "GenAddr", + [](const std::string& input) { + std::string output = resdb::coin::utils::GenAddr(input); + return py::bytes(output.data(), output.size()); + }, + "Generate a addr for a wallet by a public key."); +} diff --git a/application/utxo/wallet_tool/test/BUILD b/application/utxo/wallet_tool/test/BUILD new file mode 100644 index 000000000..e9f9b241b --- /dev/null +++ b/application/utxo/wallet_tool/test/BUILD @@ -0,0 +1,31 @@ +package(default_visibility = ["//visibility:private"]) + +cc_binary( + name = "key_tester_utils.so", + srcs = ["key_tester_utils.cpp"], + linkshared = 1, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + "//crypto:signature_utils", + "@pybind11", + ], +) + +py_library( + name = "key_tester_utils_so", + data = [":key_tester_utils.so"], + imports = ["."], + visibility = ["//visibility:public"], +) + +py_binary( + name = "key_tester", + srcs = ["key_tester.py"], + legacy_create_init = False, + python_version = "PY3", + deps = [ + ":key_tester_utils_so", + "//application/utxo/wallet_tool/pybind:wallet_tools_py_so", + ], +) diff --git a/application/utxo/wallet_tool/test/key_tester.py b/application/utxo/wallet_tool/test/key_tester.py new file mode 100644 index 000000000..ca06f1409 --- /dev/null +++ b/application/utxo/wallet_tool/test/key_tester.py @@ -0,0 +1,31 @@ +# Copyright (c) 2019-2022 ExpoLab, UC Davis +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +import wallet_tools_py +import key_tester_utils + +res = wallet_tools_py.GenECDSAKeys() +pri_key = res[0] +pub_key = res[1] + +res = key_tester_utils.TestKeyPairs(pri_key, pub_key) +print("test key:",res) diff --git a/application/utxo/wallet_tool/test/key_tester_utils.cpp b/application/utxo/wallet_tool/test/key_tester_utils.cpp new file mode 100644 index 000000000..418d0b0d2 --- /dev/null +++ b/application/utxo/wallet_tool/test/key_tester_utils.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include + +#include "crypto/signature_utils.h" + +namespace resdb { +namespace coin { + +using utils::ECDSASignString; +using utils::ECDSAVerifyString; + +bool TestKeyPairs(std::string private_key, std::string public_key) { + LOG(ERROR) << " private key:" << private_key << " public key:" << public_key; + std::string message = "hello world"; + std::string sign = ECDSASignString(private_key, message); + LOG(ERROR) << "sign done:" << sign; + bool res = ECDSAVerifyString(message, public_key, sign); + LOG(ERROR) << "verify done. res:" << (res == true); + return res; +} + +} // namespace coin +} // namespace resdb + +PYBIND11_MODULE(key_tester_utils, m) { + m.doc() = "ECDSA Key Tester"; + m.def("TestKeyPairs", &resdb::coin::TestKeyPairs, "Test a ecdsa key pair."); +} diff --git a/crypto/BUILD b/crypto/BUILD index 2ac4672ed..e07c169a4 100644 --- a/crypto/BUILD +++ b/crypto/BUILD @@ -11,11 +11,42 @@ cc_library( ], ) +cc_library( + name = "hash", + srcs = ["hash.cpp"], + hdrs = ["hash.h"], + deps = [ + "//:cryptopp_lib", + "//common:comm", + ], +) + +cc_test( + name = "hash_test", + srcs = ["hash_test.cpp"], + deps = [ + ":hash", + "//common/test:test_main", + ], +) + +cc_library( + name = "signature_utils", + srcs = ["signature_utils.cpp"], + hdrs = ["signature_utils.h"], + deps = [ + "//:cryptopp_lib", + "//common:comm", + "//proto:signature_info_cc_proto", + ], +) + cc_library( name = "signature_verifier", srcs = ["signature_verifier.cpp"], hdrs = ["signature_verifier.h"], deps = [ + ":signature_utils", "//:cryptopp_lib", "//common:comm", "//proto:signature_info_cc_proto", diff --git a/crypto/hash.cpp b/crypto/hash.cpp new file mode 100644 index 000000000..9ac4daa0f --- /dev/null +++ b/crypto/hash.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "crypto/hash.h" + +#include +#include +#include + +namespace resdb { +namespace utils { +// Funtion to calculate hash of a string. +std::string CalculateSHA256Hash(const std::string& str) { + CryptoPP::byte const* pData = (CryptoPP::byte*)str.data(); + unsigned int nDataLen = str.size(); + CryptoPP::byte aDigest[CryptoPP::SHA256::DIGESTSIZE]; + + CryptoPP::SHA256().CalculateDigest(aDigest, pData, nDataLen); + return std::string((char*)aDigest, CryptoPP::SHA256::DIGESTSIZE); +} + +std::string CalculateRIPEMD160Hash(const std::string& str) { + CryptoPP::byte const* pData = (CryptoPP::byte*)str.data(); + unsigned int nDataLen = str.size(); + CryptoPP::byte aDigest[CryptoPP::RIPEMD160::DIGESTSIZE]; + + CryptoPP::RIPEMD160().CalculateDigest(aDigest, pData, nDataLen); + return std::string((char*)aDigest, CryptoPP::RIPEMD160::DIGESTSIZE); +} + +} // namespace utils +} // namespace resdb diff --git a/crypto/hash.h b/crypto/hash.h new file mode 100644 index 000000000..10d028cba --- /dev/null +++ b/crypto/hash.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include + +namespace resdb { +namespace utils { + +std::string CalculateSHA256Hash(const std::string& str); +std::string CalculateRIPEMD160Hash(const std::string& str); + +} // namespace utils +} // namespace resdb diff --git a/crypto/hash_test.cpp b/crypto/hash_test.cpp new file mode 100644 index 000000000..2c3d509da --- /dev/null +++ b/crypto/hash_test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "crypto/hash.h" + +#include +#include + +#include "common/test/test_macros.h" + +namespace resdb { +namespace utils { +namespace { + +using ::resdb::testing::EqualsProto; + +TEST(SignatureVerifyTest, CalculateSHA256) { + std::string expected_str = + "\x9F\x86\xD0\x81\x88L}e\x9A/" + "\xEA\xA0\xC5Z\xD0\x15\xA3\xBFO\x1B+\v\x82,\xD1]l\x15\xB0\xF0\n\b"; + EXPECT_EQ(CalculateSHA256Hash("test"), expected_str); +} + +TEST(SignatureVerifyTest, CalculateRIPEMD160) { + std::string expected_str = + "^R\xFE\xE4~k\a\x5" + "e\xF7" + "CrF\x8C\xDCi\x9D\xE8\x91\a"; + EXPECT_EQ(CalculateRIPEMD160Hash("test"), expected_str); +} + +} // namespace +} // namespace utils +} // namespace resdb diff --git a/crypto/key_generator.cpp b/crypto/key_generator.cpp index 41dadc6f1..7cf5342fa 100644 --- a/crypto/key_generator.cpp +++ b/crypto/key_generator.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include #include #include @@ -105,6 +107,28 @@ SecretKey CmacGenerateHexKey(unsigned int key_size) { return key_pair; } +SecretKey ECDSAGenerateKeys() { + CryptoPP::AutoSeededRandomPool prng; + CryptoPP::ECDSA::PrivateKey privateKey; + CryptoPP::DL_GroupParameters_EC params( + CryptoPP::ASN1::secp256k1()); + privateKey.Initialize(prng, params); + + CryptoPP::ECDSA::PublicKey publicKey; + privateKey.MakePublicKey(publicKey); + + SecretKey key_pair; + + // save keys + publicKey.Save(CryptoPP::HexEncoder( + new CryptoPP::StringSink(*key_pair.mutable_public_key())) + .Ref()); + privateKey.Save(CryptoPP::HexEncoder( + new CryptoPP::StringSink(*key_pair.mutable_private_key())) + .Ref()); + return key_pair; +} + } // namespace SecretKey KeyGenerator::GeneratorKeys(SignatureInfo::HashType type) { @@ -121,6 +145,10 @@ SecretKey KeyGenerator::GeneratorKeys(SignatureInfo::HashType type) { key = CmacGenerateHexKey(16); break; } + case SignatureInfo::ECDSA: { + key = ECDSAGenerateKeys(); + break; + } default: break; } diff --git a/crypto/signature_utils.cpp b/crypto/signature_utils.cpp new file mode 100644 index 000000000..8aca18dec --- /dev/null +++ b/crypto/signature_utils.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "crypto/signature_utils.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace resdb { +namespace utils { + +bool RsaVerifyString(const std::string& message, const std::string& public_key, + const std::string& signature) { + try { + // decode and load public key (using pipeline) + CryptoPP::RSA::PublicKey rsa_public_key; + rsa_public_key.Load( + CryptoPP::StringSource(public_key, true, new CryptoPP::HexDecoder()) + .Ref()); + + // decode signature + std::string decoded_signature; + CryptoPP::StringSource ss( + signature, true, + new CryptoPP::HexDecoder(new CryptoPP::StringSink(decoded_signature))); + + // verify message + bool result = false; + CryptoPP::RSASS::Verifier verifier( + rsa_public_key); + CryptoPP::StringSource ss2( + decoded_signature + message, true, + new CryptoPP::SignatureVerificationFilter( + verifier, + new CryptoPP::ArraySink((CryptoPP::byte*)&result, sizeof(result)))); + + return result; + } catch (...) { + LOG(ERROR) << "key not valid"; + return false; + } +} + +bool ECDSAVerifyString(const std::string& message, + const std::string& public_key, + const std::string& signature) { + std::string decoded; + std::string output; + CryptoPP::StringSource( + signature, true, + new CryptoPP::HexDecoder(new CryptoPP::StringSink(decoded)) // StringSink + ); // StringSource + + CryptoPP::ECDSA::PublicKey dsa_public_key; + dsa_public_key.Load( + CryptoPP::StringSource(public_key, true, new CryptoPP::HexDecoder()) + .Ref()); + CryptoPP::ECDSA::Verifier verifier( + dsa_public_key); + + bool valid = true; + CryptoPP::StringSource( + decoded + message, true, + new CryptoPP::SignatureVerificationFilter( + verifier, + new CryptoPP::ArraySink((CryptoPP::byte*)&valid, sizeof(valid)))); + if (!valid) { + LOG(ERROR) << "signature invalid. signature len:" << signature.size() + << " message len:" << message.size(); + } + return valid; +} + +std::string RsaSignString(const std::string& private_key, + const std::string& message) { + // decode and load private key (using pipeline) + CryptoPP::RSA::PrivateKey rsa_private_key; + rsa_private_key.Load( + CryptoPP::StringSource(private_key, true, new CryptoPP::HexDecoder()) + .Ref()); + + // sign message + std::string signature; + CryptoPP::RSASS::Signer signer( + rsa_private_key); + CryptoPP::AutoSeededRandomPool rng; + CryptoPP::StringSource ss( + message, true, + new CryptoPP::SignerFilter( + rng, signer, + new CryptoPP::HexEncoder(new CryptoPP::StringSink(signature)))); + return signature; +} + +std::string ECDSASignString(const std::string& private_key, + const std::string& message) { + try { + CryptoPP::ECDSA::PrivateKey privateKey; + privateKey.Load( + CryptoPP::StringSource(private_key, true, new CryptoPP::HexDecoder()) + .Ref()); + CryptoPP::AutoSeededRandomPool prng; + std::string signature; + + CryptoPP::StringSource ss1( + message, true /*pump all*/, + new CryptoPP::SignerFilter( + prng, + CryptoPP::ECDSA::Signer( + privateKey), + new CryptoPP::StringSink(signature))); + + std::string output; + CryptoPP::StringSource(signature, true, + new CryptoPP::HexEncoder( + new CryptoPP::StringSink(output)) // HexEncoder + ); // StringSource + return output; + } catch (...) { + LOG(ERROR) << "sign error"; + return ""; + } +} + +} // namespace utils +} // namespace resdb diff --git a/crypto/signature_utils.h b/crypto/signature_utils.h new file mode 100644 index 000000000..18415eaa4 --- /dev/null +++ b/crypto/signature_utils.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#include + +namespace resdb { +namespace utils { + +bool RsaVerifyString(const std::string& message, const std::string& public_key, + const std::string& signature); +bool ECDSAVerifyString(const std::string& message, + const std::string& public_key, + const std::string& signature); + +std::string RsaSignString(const std::string& private_key, + const std::string& message); + +std::string ECDSASignString(const std::string& private_key, + const std::string& message); + +} // namespace utils +} // namespace resdb diff --git a/crypto/signature_verifier.cpp b/crypto/signature_verifier.cpp index 23c78367b..9c45e596d 100644 --- a/crypto/signature_verifier.cpp +++ b/crypto/signature_verifier.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include #include #include @@ -36,41 +38,13 @@ #include #include -//#include -//#include +#include "crypto/signature_utils.h" namespace resdb { namespace { // ================== for verify ==================================== -bool RsaVerifyString(const std::string& message, const std::string& public_key, - const std::string& signature) { - // decode and load public key (using pipeline) - CryptoPP::RSA::PublicKey rsa_public_key; - rsa_public_key.Load( - CryptoPP::StringSource(public_key, true, new CryptoPP::HexDecoder()) - .Ref()); - - // decode signature - std::string decoded_signature; - CryptoPP::StringSource ss( - signature, true, - new CryptoPP::HexDecoder(new CryptoPP::StringSink(decoded_signature))); - - // verify message - bool result = false; - CryptoPP::RSASS::Verifier verifier( - rsa_public_key); - CryptoPP::StringSource ss2( - decoded_signature + message, true, - new CryptoPP::SignatureVerificationFilter( - verifier, - new CryptoPP::ArraySink((CryptoPP::byte*)&result, sizeof(result)))); - - return result; -} - bool ED25519verifyString(const std::string& message, const std::string& public_key, const std::string& signature) { @@ -95,28 +69,6 @@ bool ED25519verifyString(const std::string& message, return valid; } -// ================== for sign ==================================== -std::string RsaSignString(const std::string& private_key, - const std::string& message) { - // decode and load private key (using pipeline) - CryptoPP::RSA::PrivateKey rsa_private_key; - rsa_private_key.Load( - CryptoPP::StringSource(private_key, true, new CryptoPP::HexDecoder()) - .Ref()); - - // sign message - std::string signature; - CryptoPP::RSASS::Signer signer( - rsa_private_key); - CryptoPP::AutoSeededRandomPool rng; - CryptoPP::StringSource ss( - message, true, - new CryptoPP::SignerFilter( - rng, signer, - new CryptoPP::HexEncoder(new CryptoPP::StringSink(signature)))); - return signature; -} - bool CmacVerifyString(const std::string& message, const std::string& public_key, const std::string& signature) { bool res = false; @@ -137,6 +89,8 @@ bool CmacVerifyString(const std::string& message, const std::string& public_key, return res; } +// ================== for sign ==================================== + std::string ED25519signString(const std::string& message, CryptoPP::ed25519::Signer* signer) { CryptoPP::AutoSeededRandomPool prng; @@ -244,7 +198,7 @@ absl::StatusOr SignatureVerifier::SignMessage( info.set_node_id(node_id_); switch (private_key_.hash_type()) { case SignatureInfo::RSA: - info.set_signature(RsaSignString(private_key_.key(), message)); + info.set_signature(utils::RsaSignString(private_key_.key(), message)); break; case SignatureInfo::ED25519: info.set_signature(ED25519signString(message, signer_.get())); @@ -313,7 +267,7 @@ bool SignatureVerifier::VerifyMessage(const std::string& message, // << " msg size:" << message.size(); switch (public_key.hash_type()) { case SignatureInfo::RSA: - return RsaVerifyString(message, public_key.key(), signature); + return utils::RsaVerifyString(message, public_key.key(), signature); case SignatureInfo::ED25519: return ED25519verifyString(message, public_key.key(), signature); case SignatureInfo::CMAC_AES: diff --git a/crypto/signature_verifier.h b/crypto/signature_verifier.h index bfb977b09..81ddfae37 100644 --- a/crypto/signature_verifier.h +++ b/crypto/signature_verifier.h @@ -42,6 +42,7 @@ class SignatureVerifier { public: SignatureVerifier(const KeyInfo& private_key, const CertificateInfo& certificate_info); + virtual ~SignatureVerifier() = default; // Set the public key that contains the public key, node id, // and its certificate. diff --git a/execution/BUILD b/execution/BUILD index 8a1410fbe..21a4140b2 100644 --- a/execution/BUILD +++ b/execution/BUILD @@ -120,3 +120,20 @@ cc_test( "//server:mock_resdb_replica_client", ], ) + +cc_library( + name = "custom_query", + hdrs = ["custom_query.h"], + visibility = ["//visibility:public"], + deps = [ + ], +) + +cc_library( + name = "mock_custom_query", + hdrs = ["mock_custom_query.h"], + deps = [ + ":custom_query", + ], +) + diff --git a/execution/custom_query.h b/execution/custom_query.h new file mode 100644 index 000000000..9ae59e0a2 --- /dev/null +++ b/execution/custom_query.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2022 ExpoLab, UC Davis + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +namespace resdb { + +// Execute a query request which does not process consensus protocol. +class CustomQuery { + public: + virtual ~CustomQuery() = default; + + virtual std::unique_ptr Query(const std::string& request_str) { + return nullptr; + } +}; + +} // namespace resdb diff --git a/ordering/pbft/BUILD b/ordering/pbft/BUILD index 84c445834..4463750c4 100644 --- a/ordering/pbft/BUILD +++ b/ordering/pbft/BUILD @@ -172,6 +172,7 @@ cc_library( deps = [ ":transaction_manager", "//config:resdb_config", + "//execution:custom_query", "//proto:resdb_cc_proto", ], ) @@ -186,6 +187,7 @@ cc_test( "//common/test:test_main", "//config:resdb_config_utils", "//crypto:mock_signature_verifier", + "//execution:mock_custom_query", "//server:mock_resdb_replica_client", ], ) diff --git a/ordering/pbft/consensus_service_pbft.cpp b/ordering/pbft/consensus_service_pbft.cpp index 454d5917f..69a7c8093 100644 --- a/ordering/pbft/consensus_service_pbft.cpp +++ b/ordering/pbft/consensus_service_pbft.cpp @@ -34,7 +34,8 @@ namespace resdb { ConsensusServicePBFT::ConsensusServicePBFT( const ResDBConfig& config, - std::unique_ptr executor) + std::unique_ptr executor, + std::unique_ptr query_executor) : ConsensusService(config), system_info_(std::make_unique(config)), checkpoint_manager_(std::make_unique( @@ -45,7 +46,8 @@ ConsensusServicePBFT::ConsensusServicePBFT( commitment_(std::make_unique( config_, transaction_manager_.get(), GetBroadCastClient(), GetSignatureVerifier())), - query_(std::make_unique(config_, transaction_manager_.get())), + query_(std::make_unique(config_, transaction_manager_.get(), + std::move(query_executor))), response_manager_(config_.IsPerformanceRunning() ? nullptr : std::make_unique( @@ -184,6 +186,8 @@ int ConsensusServicePBFT::InternalConsensusCommit( case Request::TYPE_REPLICA_STATE: return query_->ProcessGetReplicaState(std::move(context), std::move(request)); + case Request::TYPE_CUSTOM_QUERY: + return query_->ProcessCustomQuery(std::move(context), std::move(request)); } return 0; } diff --git a/ordering/pbft/consensus_service_pbft.h b/ordering/pbft/consensus_service_pbft.h index a9f845210..0629caf24 100644 --- a/ordering/pbft/consensus_service_pbft.h +++ b/ordering/pbft/consensus_service_pbft.h @@ -26,6 +26,7 @@ #pragma once #include "config/resdb_config.h" +#include "execution/custom_query.h" #include "ordering/pbft/checkpoint_manager.h" #include "ordering/pbft/commitment.h" #include "ordering/pbft/performance_manager.h" @@ -40,7 +41,8 @@ namespace resdb { class ConsensusServicePBFT : public ConsensusService { public: ConsensusServicePBFT(const ResDBConfig& config, - std::unique_ptr executor); + std::unique_ptr executor, + std::unique_ptr query_executor = nullptr); virtual ~ConsensusServicePBFT() = default; int ConsensusCommit(std::unique_ptr context, diff --git a/ordering/pbft/query.cpp b/ordering/pbft/query.cpp index bb4653bb5..56c218d6a 100644 --- a/ordering/pbft/query.cpp +++ b/ordering/pbft/query.cpp @@ -31,8 +31,11 @@ namespace resdb { -Query::Query(const ResDBConfig& config, TransactionManager* transaction_manager) - : config_(config), transaction_manager_(transaction_manager) {} +Query::Query(const ResDBConfig& config, TransactionManager* transaction_manager, + std::unique_ptr executor) + : config_(config), + transaction_manager_(transaction_manager), + custom_query_executor_(std::move(executor)) {} Query::~Query() {} @@ -83,4 +86,28 @@ int Query::ProcessQuery(std::unique_ptr context, return 0; } +int Query::ProcessCustomQuery(std::unique_ptr context, + std::unique_ptr request) { + if (custom_query_executor_ == nullptr) { + LOG(ERROR) << "no custom executor"; + return -1; + } + + std::unique_ptr resp_str = + custom_query_executor_->Query(request->data()); + + CustomQueryResponse response; + if (resp_str != nullptr) { + response.set_resp_str(*resp_str); + } + + if (context != nullptr && context->client != nullptr) { + int ret = context->client->SendRawMessage(response); + if (ret) { + LOG(ERROR) << "send resp fail ret:" << ret; + } + } + return 0; +} + } // namespace resdb diff --git a/ordering/pbft/query.h b/ordering/pbft/query.h index df799dc05..7535b741a 100644 --- a/ordering/pbft/query.h +++ b/ordering/pbft/query.h @@ -26,13 +26,15 @@ #pragma once #include "config/resdb_config.h" +#include "execution/custom_query.h" #include "ordering/pbft/transaction_manager.h" namespace resdb { class Query { public: - Query(const ResDBConfig& config, TransactionManager* transaction_manager); + Query(const ResDBConfig& config, TransactionManager* transaction_manager, + std::unique_ptr executor = nullptr); virtual ~Query(); virtual int ProcessGetReplicaState(std::unique_ptr context, @@ -40,9 +42,13 @@ class Query { virtual int ProcessQuery(std::unique_ptr context, std::unique_ptr request); + virtual int ProcessCustomQuery(std::unique_ptr context, + std::unique_ptr request); + protected: ResDBConfig config_; TransactionManager* transaction_manager_; + std::unique_ptr custom_query_executor_; }; } // namespace resdb diff --git a/ordering/pbft/query_test.cpp b/ordering/pbft/query_test.cpp index 0610d4ef8..2a9dd060a 100644 --- a/ordering/pbft/query_test.cpp +++ b/ordering/pbft/query_test.cpp @@ -35,6 +35,7 @@ #include "common/test/test_macros.h" #include "config/resdb_config_utils.h" #include "crypto/mock_signature_verifier.h" +#include "execution/mock_custom_query.h" #include "ordering/pbft/commitment.h" #include "ordering/pbft/transaction_manager.h" #include "server/mock_resdb_replica_client.h" @@ -189,6 +190,35 @@ TEST_F(QueryTest, QueryTxn) { EXPECT_EQ(ret, 0); } +TEST_F(QueryTest, CustomQuery) { + CustomQueryResponse response; + response.set_resp_str("custom_response"); + + std::unique_ptr resp_client = + std::make_unique("127.0.0.1", 0); + EXPECT_CALL(*resp_client, SendRawMessage(EqualsProto(response))).Times(1); + + auto context = std::make_unique(); + context->client = std::move(resp_client); + + context->signature.set_signature("signature"); + + Request request; + request.set_data("request"); + + auto custom_query_executor = std::make_unique(); + EXPECT_CALL(*custom_query_executor, Query("request")) + .WillOnce(Invoke([&](const std::string& str) { + return std::make_unique("custom_response"); + })); + + Query cus_query(config_, nullptr, std::move(custom_query_executor)); + + int ret = cus_query.ProcessCustomQuery(std::move(context), + std::make_unique(request)); + EXPECT_EQ(ret, 0); +} + } // namespace } // namespace resdb diff --git a/proto/resdb.proto b/proto/resdb.proto index f369d8b4c..77ac0c9e6 100644 --- a/proto/resdb.proto +++ b/proto/resdb.proto @@ -36,8 +36,9 @@ message Request { TYPE_GEO_REQUEST = 15; TYPE_VIEWCHANGE = 16; TYPE_NEWVIEW= 17; + TYPE_CUSTOM_QUERY = 18; - NUM_OF_TYPE = 18; // the total number of types. + NUM_OF_TYPE = 19; // the total number of types. // Used to create the collector. }; int32 type = 1; @@ -158,7 +159,12 @@ message QueryRequest { uint64 min_seq = 1; uint64 max_seq = 2; } + message QueryResponse { repeated Request transactions = 1; } +message CustomQueryResponse { + bytes resp_str = 1; +} + diff --git a/proto/signature_info.proto b/proto/signature_info.proto index cded70848..607fe4ac7 100644 --- a/proto/signature_info.proto +++ b/proto/signature_info.proto @@ -8,6 +8,7 @@ message SignatureInfo { RSA = 1; ED25519 = 2; CMAC_AES = 3; + ECDSA = 4; }; HashType hash_type = 1;