From 157e6c2fd82298662f7acb1bf890c5ab54c8ba9a Mon Sep 17 00:00:00 2001 From: serkixenos Date: Wed, 20 Apr 2022 23:24:06 +0200 Subject: [PATCH 001/106] Docker file for Ubuntu 18.04 --- Dockerfile | 2 +- Dockerfile.18.04 | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.18.04 diff --git a/Dockerfile b/Dockerfile index 38c96a3c..a0494813 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM ubuntu:20.04 -MAINTAINER PeerPlays Blockchain Standards Association +MAINTAINER Peerplays Blockchain Standards Association #=============================================================================== # Ubuntu setup diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 new file mode 100644 index 00000000..ecf668e3 --- /dev/null +++ b/Dockerfile.18.04 @@ -0,0 +1,116 @@ +FROM ubuntu:18.04 +MAINTAINER Peerplays Blockchain Standards Association + +#=============================================================================== +# Ubuntu setup +#=============================================================================== + +RUN \ + apt-get update -y && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + apt-utils \ + autoconf \ + bash \ + build-essential \ + ca-certificates \ + cmake \ + dnsutils \ + doxygen \ + expect \ + git \ + graphviz \ + libbz2-dev \ + libcurl4-openssl-dev \ + libncurses-dev \ + libreadline-dev \ + libsnappy-dev \ + libssl-dev \ + libtool \ + libzip-dev \ + libzmq3-dev \ + locales \ + mc \ + nano \ + net-tools \ + ntp \ + openssh-server \ + pkg-config \ + perl \ + python3 \ + python3-jinja2 \ + sudo \ + wget + +ENV HOME /home/peerplays +RUN useradd -rm -d /home/peerplays -s /bin/bash -g root -G sudo -u 1000 peerplays +RUN echo "peerplays ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/peerplays +RUN chmod 440 /etc/sudoers.d/peerplays + +RUN service ssh start +RUN echo 'peerplays:peerplays' | chpasswd + +# SSH +EXPOSE 22 + +#=============================================================================== +# Boost setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 && \ + tar xjf boost_1_67_0.tar.bz2 && \ + cd boost_1_67_0/ && \ + ./bootstrap.sh && \ + ./b2 install + +#=============================================================================== +# Peerplays setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +## Clone Peerplays +#RUN \ +# git clone https://gitlab.com/PBSA/peerplays.git && \ +# cd peerplays && \ +# git checkout develop && \ +# git submodule update --init --recursive && \ +# git branch --show-current && \ +# git log --oneline -n 5 + +# Add local source +ADD . peerplays + +# Configure Peerplays +RUN \ + cd peerplays && \ + mkdir build && \ + cd build && \ + cmake -DCMAKE_BUILD_TYPE=Release .. + +# Build Peerplays +RUN \ + cd peerplays/build && \ + make -j$(nproc) cli_wallet witness_node + +WORKDIR /home/peerplays/peerplays-network + +# Setup Peerplays runimage +RUN \ + ln -s /home/peerplays/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ + ln -s /home/peerplays/peerplays/build/programs/witness_node/witness_node ./ + +RUN ./witness_node --create-genesis-json genesis.json && \ + rm genesis.json + +RUN chown peerplays:root -R /home/peerplays/peerplays-network + +# Peerplays RPC +EXPOSE 8090 +# Peerplays P2P: +EXPOSE 9777 + +# Peerplays +CMD ["./witness_node", "-d", "./witness_node_data_dir"] From b0c7a527fad3f0976f5f4e20f3f2c4bae3c152a1 Mon Sep 17 00:00:00 2001 From: Rily Dunlap Date: Mon, 25 Apr 2022 11:29:22 +0000 Subject: [PATCH 002/106] Resolve "Missing \ after cmake in dependencies installation - QOL" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 391218b7..6883659d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Officially supported OS is Ubuntu 20.04. Following dependencies are needed for a clean install of Ubuntu 20.04: ``` sudo apt-get install \ - apt-utils autoconf bash build-essential ca-certificates clang-format cmake + apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ libcurl4-openssl-dev libncurses-dev libreadline-dev libsnappy-dev \ libssl-dev libtool libzip-dev libzmq3-dev locales mc nano net-tools ntp \ From 13c782ccd675e3bc2d90182c6d7452811545d88e Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 26 Apr 2022 13:09:41 +0000 Subject: [PATCH 003/106] #345 double-free-or-corruption --- .../graphene/peerplays_sidechain/sidechain_net_handler.hpp | 2 ++ .../plugins/peerplays_sidechain/sidechain_net_handler.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index 3aa4465c..a9257e54 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -60,6 +61,7 @@ protected: std::map private_keys; std::vector son_listener_log; + std::mutex son_listener_log_mutex; void on_applied_block(const signed_block &b); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 45fab4ee..7c87a9ef 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -619,13 +619,15 @@ void sidechain_net_handler::settle_sidechain_transactions() { } void sidechain_net_handler::add_to_son_listener_log(std::string trx_id) { + const std::lock_guard lock(son_listener_log_mutex); son_listener_log.insert(son_listener_log.begin(), trx_id); if (son_listener_log.size() > 33) { - son_listener_log.erase(son_listener_log.end()); + son_listener_log.pop_back(); } } std::vector sidechain_net_handler::get_son_listener_log() { + const std::lock_guard lock(son_listener_log_mutex); return son_listener_log; } From bd6f26540970f464ab116ecccc6f74946f726b55 Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Tue, 26 Apr 2022 19:04:30 +0000 Subject: [PATCH 004/106] Disconnect from non updated witness --- libraries/app/application.cpp | 4 + libraries/chain/db_block.cpp | 2 +- libraries/chain/db_init.cpp | 42 +++++ libraries/chain/db_management.cpp | 1 + libraries/chain/db_notify.cpp | 10 +- libraries/chain/hardfork.d/CORE_210.hf | 10 -- .../chain/include/graphene/chain/database.hpp | 4 + libraries/chain/proposal_evaluator.cpp | 6 +- libraries/chain/proposal_object.cpp | 2 +- libraries/net/include/graphene/net/node.hpp | 2 + .../include/graphene/net/peer_connection.hpp | 2 + libraries/net/node.cpp | 149 +++++++++++++----- .../account_history_plugin.cpp | 6 +- .../elasticsearch/elasticsearch_plugin.cpp | 6 +- 14 files changed, 178 insertions(+), 68 deletions(-) delete mode 100644 libraries/chain/hardfork.d/CORE_210.hf diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 19f1ed10..56878eaf 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -795,6 +795,10 @@ public: FC_CAPTURE_AND_RETHROW((block_id)) } + virtual fc::time_point_sec get_last_known_hardfork_time() override { + return _chain_db->_hardfork_times[_chain_db->_hardfork_times.size() - 1]; + } + /** * Returns the time a block was produced (if block_id = 0, returns genesis time). * If we don't know about the block, returns time_point_sec::min() diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 67601c2e..6c58e58b 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -806,7 +806,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx return get_account_custom_authorities(id, op); }; trx.verify_authority( chain_id, get_active, get_owner, get_custom, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(head_block_time()), + true, get_global_properties().parameters.max_authority_depth ); } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 724cad85..c4aabfa8 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -329,6 +329,48 @@ void database::initialize_evaluators() register_evaluator(); } + +void database::initialize_hardforks() +{ + _hardfork_times.emplace_back(HARDFORK_357_TIME); + _hardfork_times.emplace_back(HARDFORK_359_TIME); + _hardfork_times.emplace_back(HARDFORK_385_TIME); + _hardfork_times.emplace_back(HARDFORK_409_TIME); + _hardfork_times.emplace_back(HARDFORK_413_TIME); + _hardfork_times.emplace_back(HARDFORK_415_TIME); + _hardfork_times.emplace_back(HARDFORK_416_TIME); + _hardfork_times.emplace_back(HARDFORK_419_TIME); + _hardfork_times.emplace_back(HARDFORK_436_TIME); + _hardfork_times.emplace_back(HARDFORK_445_TIME); + _hardfork_times.emplace_back(HARDFORK_453_TIME); + _hardfork_times.emplace_back(HARDFORK_480_TIME); + _hardfork_times.emplace_back(HARDFORK_483_TIME); + _hardfork_times.emplace_back(HARDFORK_516_TIME); + _hardfork_times.emplace_back(HARDFORK_533_TIME); + _hardfork_times.emplace_back(HARDFORK_538_TIME); + _hardfork_times.emplace_back(HARDFORK_555_TIME); + _hardfork_times.emplace_back(HARDFORK_563_TIME); + _hardfork_times.emplace_back(HARDFORK_572_TIME); + _hardfork_times.emplace_back(HARDFORK_599_TIME); + _hardfork_times.emplace_back(HARDFORK_607_TIME); + _hardfork_times.emplace_back(HARDFORK_613_TIME); + _hardfork_times.emplace_back(HARDFORK_615_TIME); + _hardfork_times.emplace_back(HARDFORK_999_TIME); + _hardfork_times.emplace_back(HARDFORK_1000_TIME); + _hardfork_times.emplace_back(HARDFORK_1001_TIME); + _hardfork_times.emplace_back(HARDFORK_5050_1_TIME); + _hardfork_times.emplace_back(HARDFORK_CORE_429_TIME); + _hardfork_times.emplace_back(HARDFORK_GPOS_TIME); + _hardfork_times.emplace_back(HARDFORK_NFT_TIME); + _hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME); + _hardfork_times.emplace_back(HARDFORK_SON_TIME); + _hardfork_times.emplace_back(HARDFORK_SON2_TIME); + _hardfork_times.emplace_back(HARDFORK_SON3_TIME); + _hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME); + + std::sort(_hardfork_times.begin(), _hardfork_times.end()); +} + void database::initialize_indexes() { reset_indexes(); diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 2ca49c4c..4a3b519f 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -44,6 +44,7 @@ database::database() : { initialize_indexes(); initialize_evaluators(); + initialize_hardforks(); } database::~database() diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index c5986fad..62d6d2bf 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -625,7 +625,6 @@ void database::notify_changed_objects() if( _undo_db.enabled() ) { const auto& head_undo = _undo_db.head(); - auto chain_time = head_block_time(); // New if( !new_objects.empty() ) @@ -637,8 +636,7 @@ void database::notify_changed_objects() new_ids.push_back(item); auto obj = find_object(item); if(obj != nullptr) - get_relevant_accounts(obj, new_accounts_impacted, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time)); + get_relevant_accounts(obj, new_accounts_impacted, true); } GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted) @@ -652,8 +650,7 @@ void database::notify_changed_objects() for( const auto& item : head_undo.old_values ) { changed_ids.push_back(item.first); - get_relevant_accounts(item.second.get(), changed_accounts_impacted, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time)); + get_relevant_accounts(item.second.get(), changed_accounts_impacted, true); } GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted) @@ -670,8 +667,7 @@ void database::notify_changed_objects() removed_ids.emplace_back( item.first ); auto obj = item.second.get(); removed.emplace_back( obj ); - get_relevant_accounts(obj, removed_accounts_impacted, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time)); + get_relevant_accounts(obj, removed_accounts_impacted, true); } GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted) diff --git a/libraries/chain/hardfork.d/CORE_210.hf b/libraries/chain/hardfork.d/CORE_210.hf deleted file mode 100644 index cf3bdade..00000000 --- a/libraries/chain/hardfork.d/CORE_210.hf +++ /dev/null @@ -1,10 +0,0 @@ -// #210 Check authorities on custom_operation -#ifndef HARDFORK_CORE_210_TIME -#ifdef BUILD_PEERPLAYS_TESTNET -#define HARDFORK_CORE_210_TIME (fc::time_point_sec::from_iso_string("2030-01-01T00:00:00")) // (Not yet scheduled) -#else -#define HARDFORK_CORE_210_TIME (fc::time_point_sec::from_iso_string("2030-01-01T00:00:00")) // (Not yet scheduled) -#endif -// Bugfix: pre-HF 210, custom_operation's required_auths field was ignored. -#define MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) (chain_time <= HARDFORK_CORE_210_TIME) -#endif diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 50975174..f62df938 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -66,6 +66,8 @@ namespace graphene { namespace chain { database(); ~database(); + std::vector _hardfork_times; + enum validation_steps { skip_nothing = 0, @@ -332,6 +334,8 @@ namespace graphene { namespace chain { void initialize_evaluators(); /// Reset the object graph in-memory void initialize_indexes(); + void initialize_hardforks(); + void init_genesis(const genesis_state_type& genesis_state = genesis_state_type()); template diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index ac8ef601..28500304 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -302,8 +302,7 @@ void_result proposal_create_evaluator::do_evaluate( const proposal_create_operat vector other; for( auto& op : o.proposed_ops ) { - operation_get_required_authorities( op.op, auths, auths, other, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(block_time) ); + operation_get_required_authorities( op.op, auths, auths, other, true ); } FC_ASSERT( other.size() == 0 ); // TODO: what about other??? @@ -352,8 +351,7 @@ object_id_type proposal_create_evaluator::do_apply( const proposal_create_operat // TODO: consider caching values from evaluate? for( auto& op : _proposed_trx.operations ) - operation_get_required_authorities( op, required_active, proposal.required_owner_approvals, other, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) ); + operation_get_required_authorities( op, required_active, proposal.required_owner_approvals, other, true); //All accounts which must provide both owner and active authority should be omitted from the active authority set; //owner authority approval implies active authority approval. diff --git a/libraries/chain/proposal_object.cpp b/libraries/chain/proposal_object.cpp index 662c700a..67eef6c7 100644 --- a/libraries/chain/proposal_object.cpp +++ b/libraries/chain/proposal_object.cpp @@ -39,7 +39,7 @@ bool proposal_object::is_authorized_to_execute( database& db ) const [&]( account_id_type id ){ return &id(db).owner; }, [&]( account_id_type id, const operation& op ){ return db.get_account_custom_authorities(id, op); }, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ), + true, db.get_global_properties().parameters.max_authority_depth, true, /* allow committee */ available_active_approvals, diff --git a/libraries/net/include/graphene/net/node.hpp b/libraries/net/include/graphene/net/node.hpp index e17af148..adbaf262 100644 --- a/libraries/net/include/graphene/net/node.hpp +++ b/libraries/net/include/graphene/net/node.hpp @@ -152,6 +152,8 @@ namespace graphene { namespace net { virtual uint32_t get_block_number(const item_hash_t& block_id) = 0; + virtual fc::time_point_sec get_last_known_hardfork_time() = 0; + /** * Returns the time a block was produced (if block_id = 0, returns genesis time). * If we don't know about the block, returns time_point_sec::min() diff --git a/libraries/net/include/graphene/net/peer_connection.hpp b/libraries/net/include/graphene/net/peer_connection.hpp index 61f1cef5..0cd0288f 100644 --- a/libraries/net/include/graphene/net/peer_connection.hpp +++ b/libraries/net/include/graphene/net/peer_connection.hpp @@ -258,6 +258,8 @@ namespace graphene { namespace net uint32_t last_known_fork_block_number = 0; + fc::time_point_sec last_known_hardfork_time; + fc::future accept_or_connect_task_done; firewall_check_state_data *firewall_check_state = nullptr; diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 3222da08..3fab0db3 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -296,6 +296,7 @@ namespace graphene { namespace net { namespace detail { (sync_status) \ (connection_count_changed) \ (get_block_number) \ + (get_last_known_hardfork_time) \ (get_block_time) \ (get_head_block_id) \ (estimate_last_known_fork_from_git_revision_timestamp) \ @@ -395,6 +396,7 @@ namespace graphene { namespace net { namespace detail { void sync_status( uint32_t item_type, uint32_t item_count ) override; void connection_count_changed( uint32_t c ) override; uint32_t get_block_number(const item_hash_t& block_id) override; + fc::time_point_sec get_last_known_hardfork_time() override; fc::time_point_sec get_block_time(const item_hash_t& block_id) override; item_hash_t get_head_block_id() const override; uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const override; @@ -1864,6 +1866,10 @@ namespace graphene { namespace net { namespace detail { user_data["last_known_block_hash"] = fc::variant( head_block_id, 1 ); user_data["last_known_block_number"] = _delegate->get_block_number(head_block_id); user_data["last_known_block_time"] = _delegate->get_block_time(head_block_id); + user_data["last_known_hardfork_time"] = _delegate->get_last_known_hardfork_time().sec_since_epoch(); + + wlog("on generate hello message, hardfork: ${hardfork}", ("hardfork", _delegate->get_last_known_hardfork_time().sec_since_epoch())); + std::cout<<"on generate hello message :"<get_last_known_hardfork_time())<node_id = user_data["node_id"].as(1); if (user_data.contains("last_known_fork_block_number")) originating_peer->last_known_fork_block_number = user_data["last_known_fork_block_number"].as(1); + if (user_data.contains("last_known_hardfork_time")){ + originating_peer->last_known_hardfork_time = fc::time_point_sec(user_data["last_known_hardfork_time"].as(1)); + std::cout<<"on get helllo message: "<last_known_hardfork_time.to_iso_string()<last_known_fork_block_number != 0) - { - uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(originating_peer->last_known_fork_block_number); - if (next_fork_block_number != 0) - { - // we know about a fork they don't. See if we've already passed that block. If we have, don't let them - // connect because we won't be able to give them anything useful - uint32_t head_block_num = _delegate->get_block_number(_delegate->get_head_block_id()); - if (next_fork_block_number < head_block_num) - { -#ifdef ENABLE_DEBUG_ULOGS + + auto disconnet_peer = [&](const std::ostringstream& rejection_message) { + #ifdef ENABLE_DEBUG_ULOGS ulog("Rejecting connection from peer because their version is too old. Their version date: ${date}", ("date", originating_peer->graphene_git_revision_unix_timestamp)); -#endif - wlog("Received hello message from peer running a version of that can only understand blocks up to #${their_hard_fork}, but I'm at head block number #${my_block_number}", - ("their_hard_fork", next_fork_block_number)("my_block_number", head_block_num)); - std::ostringstream rejection_message; - rejection_message << "Your client is outdated -- you can only understand blocks up to #" << next_fork_block_number << ", but I'm already on block #" << head_block_num; + #endif connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version, originating_peer->get_socket().remote_endpoint(), rejection_reason_code::unspecified, @@ -1997,10 +1996,42 @@ namespace graphene { namespace net { namespace detail { // allowing her to ask us for peers (any of our peers will be on the same chain as us, so there's no // benefit of sharing them) disconnect_from_peer(originating_peer, "Your client is too old, please upgrade"); + }; + + if (originating_peer->last_known_fork_block_number != 0) + { + uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(originating_peer->last_known_fork_block_number); + if (next_fork_block_number != 0) + { + // we know about a fork they don't. See if we've already passed that block. If we have, don't let them + // connect because we won't be able to give them anything useful + uint32_t head_block_num = _delegate->get_block_number(_delegate->get_head_block_id()); + if (next_fork_block_number < head_block_num) + { + wlog("Received hello message from peer running a version of that can only understand blocks up to #${their_hard_fork}, but I'm at head block number #${my_block_number}", + ("their_hard_fork", next_fork_block_number)("my_block_number", head_block_num)); + std::ostringstream rejection_message; + rejection_message << "Your client is outdated -- you can only understand blocks up to #" << next_fork_block_number << ", but I'm already on block #" << head_block_num; + disconnet_peer(rejection_message); return; } } } + + // we wan't to disconnect from the peer that didn't updated the software. With the last hardforks we could + // indetify if peer's are not compatible due the hardforks + if ( _delegate->get_last_known_hardfork_time() != originating_peer->last_known_hardfork_time) + { + if (_delegate->get_block_time(_delegate->get_head_block_id()).sec_since_epoch() > originating_peer->last_known_hardfork_time.sec_since_epoch()) + { + std::ostringstream rejection_message; + rejection_message << "Your client is outdated -- you can only understand blocks up to #" << originating_peer->last_known_hardfork_time.to_iso_string() << ", but I'm already on block #" << _delegate->get_block_time(_delegate->get_head_block_id()).to_iso_string(); + std::cout<<"Reject connection due the hardforks on hello_message"<graphene_git_revision_unix_timestamp)); + #endif + disconnecting_this_peer = true; + }; + bool is_fork_block = is_hard_fork_block(block_message_to_send.block.block_num()); for (const peer_connection_ptr& peer : _active_connections) { ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections bool disconnecting_this_peer = false; + + // if connected peer doesn't have the same version of witness which is fully indetified + // with last hardfork time received and block timestamp is grater than peers last known hardfork + // time disconnect that peer, since he will not be capable of handling already pushed block + if(peer->last_known_hardfork_time != _delegate->get_last_known_hardfork_time()) + { + if( block_message_to_send.block.timestamp.sec_since_epoch() > peer->last_known_hardfork_time.sec_since_epoch() ) + { + std::cout<<"disconnect peer from resync method"<graphene_git_revision_unix_timestamp)); -#endif - disconnecting_this_peer = true; + disconnet_peer(disconnect_reason_stream, peer, disconnecting_this_peer); } } } + if (!disconnecting_this_peer && peer->ids_of_items_to_get.empty() && peer->ids_of_items_being_processed.empty()) { @@ -3459,11 +3512,10 @@ namespace graphene { namespace net { namespace detail { broadcast( block_message_to_process, propagation_data ); _message_cache.block_accepted(); - if (is_hard_fork_block(block_number)) + for (const peer_connection_ptr& peer : _active_connections) { - // we just pushed a hard fork block. Find out if any of our peers are running clients - // that will be unable to process future blocks - for (const peer_connection_ptr& peer : _active_connections) + bool disconnect_this_peer = false; + if (is_hard_fork_block(block_number) ) { if (peer->last_known_fork_block_number != 0) { @@ -3471,21 +3523,36 @@ namespace graphene { namespace net { namespace detail { if (next_fork_block_number != 0 && next_fork_block_number <= block_number) { - peers_to_disconnect.insert(peer); -#ifdef ENABLE_DEBUG_ULOGS - ulog("Disconnecting from peer because their version is too old. Their version date: ${date}", ("date", peer->graphene_git_revision_unix_timestamp)); -#endif + disconnect_this_peer = true; } } } - if (!peers_to_disconnect.empty()) + + if(peer->last_known_hardfork_time != _delegate->get_last_known_hardfork_time()) { - std::ostringstream disconnect_reason_stream; - disconnect_reason_stream << "You need to upgrade your client due to hard fork at block " << block_number; - disconnect_reason = disconnect_reason_stream.str(); - disconnect_exception = fc::exception(FC_LOG_MESSAGE(error, "You need to upgrade your client due to hard fork at block ${block_number}", - ("block_number", block_number))); + if(block_message_to_process.block.timestamp.sec_since_epoch() > peer->last_known_hardfork_time.sec_since_epoch()) + { + std::cout<<"disconnect peer on processing block during normal operation"<graphene_git_revision_unix_timestamp)); +#endif + } + } + + if (!peers_to_disconnect.empty()) + { + std::ostringstream disconnect_reason_stream; + disconnect_reason_stream << "You need to upgrade your client due to hard fork at block " << block_number; + disconnect_reason = disconnect_reason_stream.str(); + disconnect_exception = fc::exception(FC_LOG_MESSAGE(error, "You need to upgrade your client due to hard fork at block ${block_number}", + ("block_number", block_number))); } } catch (const fc::canceled_exception&) @@ -5542,6 +5609,14 @@ namespace graphene { namespace net { namespace detail { return _node_delegate->get_block_number(block_id); } + fc::time_point_sec statistics_gathering_node_delegate_wrapper::get_last_known_hardfork_time() + { + // this function doesn't need to block, + ASSERT_TASK_NOT_PREEMPTED(); + return _node_delegate->get_last_known_hardfork_time(); + } + + fc::time_point_sec statistics_gathering_node_delegate_wrapper::get_block_time(const item_hash_t& block_id) { INVOKE_AND_COLLECT_STATISTICS(get_block_time, block_id); diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index db3fa464..60ce64f8 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -126,14 +126,12 @@ void account_history_plugin_impl::update_account_histories( const signed_block& flat_set impacted; vector other; // fee payer is added here - operation_get_required_authorities( op.op, impacted, impacted, other, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); + operation_get_required_authorities( op.op, impacted, impacted, other, true ); if( op.op.which() == operation::tag< account_create_operation >::value ) impacted.insert( op.result.get() ); else - graphene::chain::operation_get_impacted_accounts( op.op, impacted, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(db.head_block_time()) ); + graphene::chain::operation_get_impacted_accounts( op.op, impacted, true ); if( op.op.which() == operation::tag< lottery_end_operation >::value ) { auto lop = op.op.get< lottery_end_operation >(); diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp index 491c011d..e306054a 100644 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -173,14 +173,12 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b flat_set impacted; vector other; // fee_payer is added here - operation_get_required_authorities( op.op, impacted, impacted, other, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); + operation_get_required_authorities( op.op, impacted, impacted, other, true ); if( op.op.which() == operation::tag< account_create_operation >::value ) impacted.insert( op.result.get() ); else - operation_get_impacted_accounts( op.op, impacted, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); + operation_get_impacted_accounts( op.op, impacted, true ); for( auto& a : other ) for( auto& item : a.account_auths ) From c1048e1509d3e495d62896e0155ac5b0da7e5d33 Mon Sep 17 00:00:00 2001 From: Rily Dunlap Date: Tue, 26 Apr 2022 19:12:52 +0000 Subject: [PATCH 005/106] Manual build in pipeline job for testnet cmake --- .gitlab-ci.yml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e65c81c8..83fa21f4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,6 +29,28 @@ build: tags: - builder +build-testnet: + stage: build + script: + - rm -rf .git/modules/docs .git/modules/libraries/fc ./docs ./libraries/fc + - git submodule sync + - git submodule update --init --recursive + - rm -rf build + - mkdir build + - cd build + - cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1 .. + - make -j$(nproc) + artifacts: + untracked: true + paths: + - build/libraries/ + - build/programs/ + - build/tests/ + tags: + - builder-testnet + when: + manual + dockerize: stage: build variables: @@ -41,8 +63,10 @@ dockerize: - docker push $IMAGE tags: - builder - when: manual - timeout: 3h + when: + manual + timeout: + 3h test: stage: test From 520505b667b5d5b0320db9fda14915f974afeac7 Mon Sep 17 00:00:00 2001 From: Pavel Baykov Date: Wed, 4 May 2022 16:46:48 +0000 Subject: [PATCH 006/106] libzmq v4.3.4, cppzmq v4.8.1, cmake v3.23 for Docker Ubuntu 18.04 --- CMakeLists.txt | 31 ++++++++++++++ Dockerfile | 33 ++++++++++++++- Dockerfile.18.04 | 47 ++++++++++++++++++++- README.md | 106 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 212 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 27c29861..695881be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,37 @@ endif() list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" ) +function(get_linux_lsb_release_information) + find_program(LSB_RELEASE_EXEC lsb_release) + if(NOT LSB_RELEASE_EXEC) + message(FATAL_ERROR "Could not detect lsb_release executable, can not gather required information") + endif() + + execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --id OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --release OUTPUT_VARIABLE LSB_RELEASE_VERSION_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --codename OUTPUT_VARIABLE LSB_RELEASE_CODENAME_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(LSB_RELEASE_ID_SHORT "${LSB_RELEASE_ID_SHORT}" PARENT_SCOPE) + set(LSB_RELEASE_VERSION_SHORT "${LSB_RELEASE_VERSION_SHORT}" PARENT_SCOPE) + set(LSB_RELEASE_CODENAME_SHORT "${LSB_RELEASE_CODENAME_SHORT}" PARENT_SCOPE) +endfunction() + +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + find_package(cppzmq) + target_link_libraries(cppzmq) + + get_linux_lsb_release_information() + message(STATUS "Linux ${LSB_RELEASE_ID_SHORT} ${LSB_RELEASE_VERSION_SHORT} ${LSB_RELEASE_CODENAME_SHORT}") + string(REGEX MATCHALL "([0-9]+)" arg_list ${LSB_RELEASE_VERSION_SHORT}) + list( LENGTH arg_list listlen ) + if (NOT listlen) + message(FATAL_ERROR "Could not detect Ubuntu version") + endif() + list(GET arg_list 0 output) + message("Ubuntu version is: ${output}") + add_definitions(-DPEERPLAYS_UBUNTU_VERSION=${output}) +endif() + # function to help with cUrl macro(FIND_CURL) if (NOT WIN32 AND NOT APPLE AND CURL_STATICLIB) diff --git a/Dockerfile b/Dockerfile index a0494813..ffe89d9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,8 +28,8 @@ RUN \ libssl-dev \ libtool \ libzip-dev \ - libzmq3-dev \ locales \ + lsb-release \ mc \ nano \ net-tools \ @@ -40,6 +40,7 @@ RUN \ python3 \ python3-jinja2 \ sudo \ + systemd-coredump \ wget ENV HOME /home/peerplays @@ -53,6 +54,36 @@ RUN echo 'peerplays:peerplays' | chpasswd # SSH EXPOSE 22 +#=============================================================================== +# libzmq setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ + unzip v4.3.4.zip && \ + cd libzmq-4.3.4 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install + +#=============================================================================== +# cppzmq setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ + unzip v4.8.1.zip && \ + cd cppzmq-4.8.1 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install + #=============================================================================== # Peerplays setup #=============================================================================== diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index ecf668e3..7a82fbe7 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -13,7 +13,6 @@ RUN \ bash \ build-essential \ ca-certificates \ - cmake \ dnsutils \ doxygen \ expect \ @@ -27,8 +26,8 @@ RUN \ libssl-dev \ libtool \ libzip-dev \ - libzmq3-dev \ locales \ + lsb-release \ mc \ nano \ net-tools \ @@ -39,6 +38,7 @@ RUN \ python3 \ python3-jinja2 \ sudo \ + systemd-coredump \ wget ENV HOME /home/peerplays @@ -65,6 +65,49 @@ RUN \ ./bootstrap.sh && \ ./b2 install +#=============================================================================== +# cmake setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + cd src && \ + wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ + chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ + ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ + cmake --version + +#=============================================================================== +# libzmq setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ + unzip v4.3.4.zip && \ + cd libzmq-4.3.4 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install + +#=============================================================================== +# cppzmq setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ + unzip v4.8.1.zip && \ + cd cppzmq-4.8.1 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install + #=============================================================================== # Peerplays setup #=============================================================================== diff --git a/README.md b/README.md index 6883659d..58f42cb9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,88 @@ This is a quick introduction to get new developers and witnesses up to speed on # Building and Installation Instructions -Officially supported OS is Ubuntu 20.04. +Officially supported OS is Ubuntu 18.04 and 20.04. + +# To build for Ubuntu 18.04: + +Following dependencies are needed for a clean install of Ubuntu 18.04: +``` +sudo apt-get install \ + apt-utils autoconf bash build-essential ca-certificates dnsutils doxygen \ + expect git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ + libreadline-dev libsnappy-dev libssl-dev libtool libzip-dev locales \ + lsb-release mc nano net-tools ntp openssh-server pkg-config perl \ + python3 python3-jinja2 sudo systemd-coredump wget +``` +# Boost setup +``` +mkdir $HOME/src +cd $HOME/src +export BOOST_ROOT=$HOME/src/boost_1_67_0 +sudo apt-get update +sudo apt-get install -y autotools-dev build-essential libbz2-dev libicu-dev python-dev +wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download'\ + -O boost_1_67_0.tar.bz2 +tar xjf boost_1_67_0.tar.bz2 +cd boost_1_67_0/ +./bootstrap.sh "--prefix=$BOOST_ROOT" +./b2 install +``` +# cmake setup +``` +cd $HOME/src && \ +wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ +chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ +./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ +``` +# libzmq setup +``` +cd $HOME/src && \ +wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ +unzip v4.3.4.zip && \ +cd libzmq-4.3.4 && \ +mkdir build && \ +cd build && \ +cmake .. && \ +make -j$(nproc) install +``` +# cppzmq setup +``` +cd $HOME/src && \ +wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ +unzip v4.8.1.zip && \ +cd cppzmq-4.8.1 && \ +mkdir build && \ +cd build && \ +cmake .. && \ +make -j$(nproc) install +``` +## Building Peerplays + +``` +mkdir $HOME/src +cd $HOME/src +git clone https://gitlab.com/PBSA/peerplays.git +cd peerplays +git submodule update --init --recursive + +# If you want to build Mainnet node +cmake -DCMAKE_BUILD_TYPE=Release + +# If you want to build Testnet node +cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1 + +# Update -j flag depending on your current system specs; +# Recommended 4GB of RAM per 1 CPU core +# make -j2 for 8GB RAM +# make -j4 for 16GB RAM +# make -j8 for 32GB RAM +make -j$(nproc) + +make install # this can install the executable files under /usr/local +``` + +# To build for Ubuntu 20.04: Following dependencies are needed for a clean install of Ubuntu 20.04: ``` @@ -14,10 +95,31 @@ sudo apt-get install \ apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ libcurl4-openssl-dev libncurses-dev libreadline-dev libsnappy-dev \ - libssl-dev libtool libzip-dev libzmq3-dev locales mc nano net-tools ntp \ + libssl-dev libtool libzip-dev libzmq3-dev locales lsb-release mc nano net-tools ntp \ openssh-server pkg-config perl python3 python3-jinja2 sudo wget ``` +# libzmq setup +``` + wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ + unzip v4.3.4.zip && \ + cd libzmq-4.3.4 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install +``` +# cppzmq setup + +``` + wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ + unzip v4.8.1.zip && \ + cd cppzmq-4.8.1 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install +``` ## Building Peerplays From c973131ed21d1cdb154ee6c7cd3d842c10a75be6 Mon Sep 17 00:00:00 2001 From: Pavel Baykov Date: Thu, 5 May 2022 11:10:01 -0300 Subject: [PATCH 007/106] fix bug 360, use zmq_setsockopt --- .../peerplays_sidechain/sidechain_net_handler_bitcoin.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 583effde..bc7989f7 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1079,8 +1079,10 @@ std::vector zmq_listener::receive_multipart() { void zmq_listener::handle_zmq() { int linger = 0; - socket.setsockopt(ZMQ_SUBSCRIBE, "hashblock", 9); - socket.setsockopt(ZMQ_LINGER, &linger, sizeof(linger)); + auto rc = zmq_setsockopt(socket, ZMQ_SUBSCRIBE, "hashblock", 9); + FC_ASSERT(rc); + rc = zmq_setsockopt(socket, ZMQ_LINGER, &linger, sizeof(linger)); + FC_ASSERT(rc); //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); From 223d2a528da00b2e072283fe4b93c9d9be534598 Mon Sep 17 00:00:00 2001 From: hirunda Date: Fri, 6 May 2022 13:08:34 +0200 Subject: [PATCH 008/106] Replace fc::random with std random generator --- libraries/net/node.cpp | 9 +++++++-- libraries/wallet/wallet.cpp | 10 +++++++--- tests/common/tournament_helper.cpp | 9 ++++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 3fab0db3..c9f74101 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -30,6 +30,8 @@ #include #include #include +#include + #include #include @@ -67,7 +69,6 @@ #include #include #include -#include #include #include @@ -829,7 +830,11 @@ namespace graphene { namespace net { namespace detail { _maximum_blocks_per_peer_during_syncing(GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING) { _rate_limiter.set_actual_rate_time_constant(fc::seconds(2)); - fc::rand_bytes(&_node_id.data[0], (int)_node_id.size()); + + using bytes_randomizer = std::independent_bits_engine; + std::random_device rd; + bytes_randomizer br(rd()); + std::generate(std::begin(_node_id.data), std::end(_node_id.data), std::ref(br)); } node_impl::~node_impl() diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 0232bc44..d2fac215 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -62,7 +63,6 @@ #include #include #include -#include #include #include @@ -7365,8 +7365,12 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id, // construct the complete throw, the commit, and reveal rock_paper_scissors_throw full_throw; - fc::rand_bytes((char*)&full_throw.nonce1, sizeof(full_throw.nonce1)); - fc::rand_bytes((char*)&full_throw.nonce2, sizeof(full_throw.nonce2)); + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dis; + full_throw.nonce1 = dis(gen); + full_throw.nonce2 = dis(gen); + full_throw.gesture = gesture; rock_paper_scissors_throw_commit commit_throw; diff --git a/tests/common/tournament_helper.cpp b/tests/common/tournament_helper.cpp index 4cb27b08..b3172d8e 100644 --- a/tests/common/tournament_helper.cpp +++ b/tests/common/tournament_helper.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include using namespace graphene::chain; @@ -276,8 +276,11 @@ void tournaments_helper::rps_throw(const game_id_type& game_id, // construct the complete throw, the commit, and reveal rock_paper_scissors_throw full_throw; - fc::rand_bytes((char*)&full_throw.nonce1, sizeof(full_throw.nonce1)); - fc::rand_bytes((char*)&full_throw.nonce2, sizeof(full_throw.nonce2)); + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dis; + full_throw.nonce1 = dis(gen); + full_throw.nonce2 = dis(gen); full_throw.gesture = gesture; rock_paper_scissors_throw_commit commit_throw; From c79c8987dce9d7ceb5103de9eac237f75b86ffbc Mon Sep 17 00:00:00 2001 From: serkixenos Date: Fri, 6 May 2022 04:14:36 +0200 Subject: [PATCH 009/106] Ubuntu 18.04 build support --- Dockerfile | 6 +- Dockerfile.18.04 | 7 +- README.md | 180 +++++++++--------- .../chain/son_wallet_deposit_object.hpp | 2 +- .../chain/son_wallet_withdraw_object.hpp | 2 +- 5 files changed, 102 insertions(+), 95 deletions(-) diff --git a/Dockerfile b/Dockerfile index ffe89d9f..e90e5380 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,7 +67,8 @@ RUN \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install + make -j$(nproc) install && \ + ldconfig #=============================================================================== # cppzmq setup @@ -82,7 +83,8 @@ RUN \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install + make -j$(nproc) install && \ + ldconfig #=============================================================================== # Peerplays setup diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index 7a82fbe7..5e928263 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -72,7 +72,6 @@ RUN \ WORKDIR /home/peerplays/ RUN \ - cd src && \ wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ @@ -91,7 +90,8 @@ RUN \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install + make -j$(nproc) install && \ + ldconfig #=============================================================================== # cppzmq setup @@ -106,7 +106,8 @@ RUN \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install + make -j$(nproc) install && \ + ldconfig #=============================================================================== # Peerplays setup diff --git a/README.md b/README.md index 58f42cb9..b1333d8e 100644 --- a/README.md +++ b/README.md @@ -6,67 +6,49 @@ This is a quick introduction to get new developers and witnesses up to speed on # Building and Installation Instructions -Officially supported OS is Ubuntu 18.04 and 20.04. +Officially supported OS are Ubuntu 20.04 and Ubuntu 18.04. -# To build for Ubuntu 18.04: +## Ubuntu 20.04 -Following dependencies are needed for a clean install of Ubuntu 18.04: +Following dependencies are needed for a clean install of Ubuntu 20.04: ``` -sudo apt-get install \ - apt-utils autoconf bash build-essential ca-certificates dnsutils doxygen \ - expect git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ - libreadline-dev libsnappy-dev libssl-dev libtool libzip-dev locales \ - lsb-release mc nano net-tools ntp openssh-server pkg-config perl \ - python3 python3-jinja2 sudo systemd-coredump wget +sudo apt-get install \ + apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ + dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ + libcurl4-openssl-dev libncurses-dev libreadline-dev libsnappy-dev \ + libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \ + openssh-server pkg-config perl python3 python3-jinja2 sudo \ + systemd-coredump wget ``` -# Boost setup -``` -mkdir $HOME/src -cd $HOME/src -export BOOST_ROOT=$HOME/src/boost_1_67_0 -sudo apt-get update -sudo apt-get install -y autotools-dev build-essential libbz2-dev libicu-dev python-dev -wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download'\ - -O boost_1_67_0.tar.bz2 -tar xjf boost_1_67_0.tar.bz2 -cd boost_1_67_0/ -./bootstrap.sh "--prefix=$BOOST_ROOT" -./b2 install -``` -# cmake setup -``` -cd $HOME/src && \ -wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ -chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ -./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ -``` -# libzmq setup -``` -cd $HOME/src && \ -wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ -unzip v4.3.4.zip && \ -cd libzmq-4.3.4 && \ -mkdir build && \ -cd build && \ -cmake .. && \ -make -j$(nproc) install -``` -# cppzmq setup -``` -cd $HOME/src && \ -wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ -unzip v4.8.1.zip && \ -cd cppzmq-4.8.1 && \ -mkdir build && \ -cd build && \ -cmake .. && \ -make -j$(nproc) install -``` -## Building Peerplays +Install libzmq from source: +``` +wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip +unzip v4.3.4.zip +cd libzmq-4.3.4 +mkdir build +cd build +cmake .. +make -j$(nproc) +sudo make install +sudo ldconfig +``` + +Install cppzmq from source: +``` +wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip +unzip v4.8.1.zip +cd cppzmq-4.8.1 +mkdir build +cd build +cmake .. +make -j$(nproc) +sudo make install +sudo ldconfig +``` + +Building Peerplays ``` -mkdir $HOME/src -cd $HOME/src git clone https://gitlab.com/PBSA/peerplays.git cd peerplays git submodule update --init --recursive @@ -87,45 +69,62 @@ make -j$(nproc) make install # this can install the executable files under /usr/local ``` -# To build for Ubuntu 20.04: +## Ubuntu 18.04 -Following dependencies are needed for a clean install of Ubuntu 20.04: +Following dependencies are needed for a clean install of Ubuntu 18.04: ``` sudo apt-get install \ - apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ - dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ - libcurl4-openssl-dev libncurses-dev libreadline-dev libsnappy-dev \ - libssl-dev libtool libzip-dev libzmq3-dev locales lsb-release mc nano net-tools ntp \ - openssh-server pkg-config perl python3 python3-jinja2 sudo wget + apt-utils autoconf bash build-essential ca-certificates dnsutils doxygen \ + expect git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ + libreadline-dev libsnappy-dev libssl-dev libtool libzip-dev locales \ + lsb-release mc nano net-tools ntp openssh-server pkg-config perl \ + python3 python3-jinja2 sudo systemd-coredump wget ``` -# libzmq setup +Install Boost libraries from source ``` - wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ - unzip v4.3.4.zip && \ - cd libzmq-4.3.4 && \ - mkdir build && \ - cd build && \ - cmake .. && \ - make -j$(nproc) install -``` -# cppzmq setup - -``` - wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ - unzip v4.8.1.zip && \ - cd cppzmq-4.8.1 && \ - mkdir build && \ - cd build && \ - cmake .. && \ - make -j$(nproc) install +wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 +tar xjf boost_1_67_0.tar.bz2 +cd boost_1_67_0/ +./bootstrap.sh +sudo ./b2 install ``` -## Building Peerplays - +Install cmake +``` +wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh +chmod 755 ./cmake-3.23.1-linux-x86_64.sh +sudo ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license +``` + +Install libzmq from source: +``` +wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip +unzip v4.3.4.zip +cd libzmq-4.3.4 +mkdir build +cd build +cmake .. +make -j$(nproc) +sudo make install +sudo ldconfig +``` + +Install cppzmq from source: +``` +wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip +unzip v4.8.1.zip +cd cppzmq-4.8.1 +mkdir build +cd build +cmake .. +make -j$(nproc) +sudo make install +sudo ldconfig +``` + +Building Peerplays ``` -mkdir $HOME/src -cd $HOME/src git clone https://gitlab.com/PBSA/peerplays.git cd peerplays git submodule update --init --recursive @@ -164,10 +163,15 @@ sudo usermod -a -G docker $USER docker pull datasecuritynode/peerplays:latest ``` -### Building docker image manually +### Building docker images manually ``` -# Build docker image (from the project root, must be a docker group member) -docker build -t peerplays . +# Execute from the project root, must be a docker group member + +# Build docker image, using Ubuntu 20.04 base +docker build --no-cache -f Dockerfile -t peerplays . + +# Build docker image, using Ubuntu 18.04 base +docker build --no-cache -f Dockerfile.18.04 -t peerplays-18-04 . ``` ### Start docker image diff --git a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp index ae68a64f..2a741743 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp @@ -18,7 +18,7 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_wallet_deposit_object_type; time_point_sec timestamp; - uint32_t block_num; + uint32_t block_num = 0; sidechain_type sidechain = sidechain_type::unknown; std::string sidechain_uid; std::string sidechain_transaction_id; diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp index d65f5cab..2e4380ba 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp @@ -18,7 +18,7 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_wallet_withdraw_object_type; time_point_sec timestamp; - uint32_t block_num; + uint32_t block_num = 0; sidechain_type sidechain = sidechain_type::unknown; std::string peerplays_uid; std::string peerplays_transaction_id; From cb3302160bc6070d98a33c98c8fb26d55ed13821 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Mon, 9 May 2022 16:07:07 +0200 Subject: [PATCH 010/106] Fix P2P port/endpoint error message --- libraries/net/node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 3fab0db3..61ee3bef 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -4640,7 +4640,7 @@ namespace graphene { namespace net { namespace detail { error_message_stream << "Unable to listen for connections on port " << listen_endpoint.port() << ", retrying in a few seconds\n"; error_message_stream << "You can wait for it to become available, or restart this program using\n"; - error_message_stream << "the --p2p-port option to specify another port\n"; + error_message_stream << "the --p2p-endpoint option to specify another port\n"; first = false; } else From 27c77ba74b37227207dcdf59f8fcf1d3e1a62450 Mon Sep 17 00:00:00 2001 From: Pavel Baykov Date: Wed, 11 May 2022 03:48:25 -0300 Subject: [PATCH 011/106] fix asserts --- .../peerplays_sidechain/sidechain_net_handler_bitcoin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index bc7989f7..a9e59307 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1080,9 +1080,9 @@ std::vector zmq_listener::receive_multipart() { void zmq_listener::handle_zmq() { int linger = 0; auto rc = zmq_setsockopt(socket, ZMQ_SUBSCRIBE, "hashblock", 9); - FC_ASSERT(rc); + FC_ASSERT(0 == rc); rc = zmq_setsockopt(socket, ZMQ_LINGER, &linger, sizeof(linger)); - FC_ASSERT(rc); + FC_ASSERT(0 == rc); //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); From 8562a4c655c4b6ab94c192d1b35385618744c2c1 Mon Sep 17 00:00:00 2001 From: hirunda Date: Wed, 11 May 2022 22:34:02 +0200 Subject: [PATCH 012/106] Resolving the bug with disconnecting non compatible witness --- libraries/net/node.cpp | 43 +++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 61ee3bef..221804ff 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -1868,9 +1868,6 @@ namespace graphene { namespace net { namespace detail { user_data["last_known_block_time"] = _delegate->get_block_time(head_block_id); user_data["last_known_hardfork_time"] = _delegate->get_last_known_hardfork_time().sec_since_epoch(); - wlog("on generate hello message, hardfork: ${hardfork}", ("hardfork", _delegate->get_last_known_hardfork_time().sec_since_epoch())); - std::cout<<"on generate hello message :"<get_last_known_hardfork_time())<last_known_fork_block_number = user_data["last_known_fork_block_number"].as(1); if (user_data.contains("last_known_hardfork_time")){ originating_peer->last_known_hardfork_time = fc::time_point_sec(user_data["last_known_hardfork_time"].as(1)); - std::cout<<"on get helllo message: "<last_known_hardfork_time.to_iso_string()<get_last_known_hardfork_time() != originating_peer->last_known_hardfork_time) + if ( originating_peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time()) { - if (_delegate->get_block_time(_delegate->get_head_block_id()).sec_since_epoch() > originating_peer->last_known_hardfork_time.sec_since_epoch()) + if (_delegate->get_block_time(_delegate->get_head_block_id()).sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch()) { std::ostringstream rejection_message; rejection_message << "Your client is outdated -- you can only understand blocks up to #" << originating_peer->last_known_hardfork_time.to_iso_string() << ", but I'm already on block #" << _delegate->get_block_time(_delegate->get_head_block_id()).to_iso_string(); - std::cout<<"Reject connection due the hardforks on hello_message"<last_known_hardfork_time != _delegate->get_last_known_hardfork_time()) + if( peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time() ) { - if( block_message_to_send.block.timestamp.sec_since_epoch() > peer->last_known_hardfork_time.sec_since_epoch() ) + if( block_message_to_send.block.timestamp.sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch() ) { - std::cout<<"disconnect peer from resync method"<get_last_known_hardfork_time().sec_since_epoch() < originating_peer->last_known_hardfork_time.sec_since_epoch() + && block_message_to_process.block.timestamp.sec_since_epoch() >= originating_peer->last_known_hardfork_time.sec_since_epoch() ) + { + rejecting_block_due_hf = true; + } + // we can get into an intersting situation near the end of synchronization. We can be in // sync with one peer who is sending us the last block on the chain via a regular inventory // message, while at the same time still be synchronizing with a peer who is sending us the @@ -3454,7 +3456,7 @@ namespace graphene { namespace net { namespace detail { // message id, for the peer in the sync case we only known the block_id). fc::time_point message_validated_time; if (std::find(_most_recent_blocks_accepted.begin(), _most_recent_blocks_accepted.end(), - block_message_to_process.block_id) == _most_recent_blocks_accepted.end()) + block_message_to_process.block_id) == _most_recent_blocks_accepted.end() && !rejecting_block_due_hf) { std::vector contained_transaction_message_ids; _delegate->handle_block(block_message_to_process, false, contained_transaction_message_ids); @@ -3483,14 +3485,16 @@ namespace graphene { namespace net { namespace detail { if (new_transaction_discovered) trigger_advertise_inventory_loop(); } - else - dlog( "Already received and accepted this block (presumably through sync mechanism), treating it as accepted" ); + else { + dlog( "Already received and accepted this block (presumably through sync mechanism), treating it as accepted or non compatible node witness"); + } dlog( "client validated the block, advertising it to other peers" ); item_id block_message_item_id(core_message_type_enum::block_message_type, message_hash); uint32_t block_number = block_message_to_process.block.block_num(); fc::time_point_sec block_time = block_message_to_process.block.timestamp; + bool disconnect_this_peer = false; for (const peer_connection_ptr& peer : _active_connections) { @@ -3514,7 +3518,6 @@ namespace graphene { namespace net { namespace detail { for (const peer_connection_ptr& peer : _active_connections) { - bool disconnect_this_peer = false; if (is_hard_fork_block(block_number) ) { if (peer->last_known_fork_block_number != 0) @@ -3528,11 +3531,10 @@ namespace graphene { namespace net { namespace detail { } } - if(peer->last_known_hardfork_time != _delegate->get_last_known_hardfork_time()) + if(peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time()) { - if(block_message_to_process.block.timestamp.sec_since_epoch() > peer->last_known_hardfork_time.sec_since_epoch()) + if(block_message_to_process.block.timestamp.sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch()) { - std::cout<<"disconnect peer on processing block during normal operation"<shared_from_this()); + } + if (!peers_to_disconnect.empty()) { std::ostringstream disconnect_reason_stream; From 5b4a4d18d88e051fdbd398b51cfda24906b8744a Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 12 May 2022 01:31:13 +0200 Subject: [PATCH 013/106] Remove fc based RNG --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index 6171e973..e7369949 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 6171e973c7fcfc9e0a39eaee2f05da84416a90e6 +Subproject commit e7369949bea26f3201d8442ba78286a88df74762 From a2702cd1f4c0e383fc86c59349656e524f66a129 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 12 May 2022 13:54:52 +0200 Subject: [PATCH 014/106] Update README instructions for docker build --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index fc96f957..c626bf9d 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,18 @@ docker pull datasecuritynode/peerplays:latest ### Building docker images manually ``` +# Checkout the code +git clone https://gitlab.com/PBSA/peerplays.git +cd peerplays + +# Checkout the branch you want +# E.g. +# git checkout beatrice +# git checkout develop +git checkout master + +git submodule update --init --recursive + # Execute from the project root, must be a docker group member # Build docker image, using Ubuntu 20.04 base From 2e55b1818aa5e486391a1eb90d00a480deeb85ca Mon Sep 17 00:00:00 2001 From: serkixenos Date: Fri, 13 May 2022 03:26:30 +0200 Subject: [PATCH 015/106] Update README instructions for starting docker containers --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c626bf9d..12949dd5 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,11 @@ docker build --no-cache -f Dockerfile.18.04 -t peerplays-18-04 . ### Start docker image ``` -docker start peerplays +# Start docker image, using Ubuntu 20.04 base +docker run peerplays:latest + +# Start docker image, using Ubuntu 18.04 base +docker run peerplays-18-04:latest ``` Rest of the instructions on starting the chain remains same. From ca5dc441a71602bbc62ac0ffcc23bed040300045 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Mon, 16 May 2022 20:46:40 +0200 Subject: [PATCH 016/106] Update GitLab CI file, more manual build options --- .gitlab-ci.yml | 60 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 83fa21f4..44038b97 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,27 +29,17 @@ build: tags: - builder -build-testnet: - stage: build +test: + stage: test + dependencies: + - build script: - - rm -rf .git/modules/docs .git/modules/libraries/fc ./docs ./libraries/fc - - git submodule sync - - git submodule update --init --recursive - - rm -rf build - - mkdir build - - cd build - - cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1 .. - - make -j$(nproc) - artifacts: - untracked: true - paths: - - build/libraries/ - - build/programs/ - - build/tests/ + - ./build/libraries/fc/tests/all_tests + - ./build/tests/betting_test --log_level=message + - ./build/tests/chain_test --log_level=message + - ./build/tests/cli_test --log_level=message tags: - - builder-testnet - when: - manual + - builder dockerize: stage: build @@ -68,10 +58,34 @@ dockerize: timeout: 3h -test: +build-testnet: + stage: build + script: + - rm -rf .git/modules/docs .git/modules/libraries/fc ./docs ./libraries/fc + - git submodule sync + - git submodule update --init --recursive + - rm -rf build + - mkdir build + - cd build + - cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1 .. + - make -j$(nproc) + artifacts: + untracked: true + paths: + - build/libraries/ + - build/programs/ + - build/tests/ + tags: + - builder + when: + manual + timeout: + 3h + +test-testnet: stage: test dependencies: - - build + - build-testnet script: - ./build/libraries/fc/tests/all_tests - ./build/tests/betting_test --log_level=message @@ -79,3 +93,7 @@ test: - ./build/tests/cli_test --log_level=message tags: - builder + when: + manual + timeout: + 1h From f16aa73b3e82b133d8336a26b45670150fef0ae0 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Tue, 17 May 2022 00:32:00 +0200 Subject: [PATCH 017/106] Fix function name typos, disconnet -> disconnect --- libraries/net/node.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index a36cc856..2103c636 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -1338,7 +1338,7 @@ namespace graphene { namespace net { namespace detail { // reconnect with the rest of the network, or it might just futher isolate us. { // As usual, the first step is to walk through all our peers and figure out which - // peers need action (disconneting, sending keepalives, etc), then we walk through + // peers need action (disconnecting, sending keepalives, etc), then we walk through // those lists yielding at our leisure later. ASSERT_TASK_NOT_PREEMPTED(); @@ -1986,7 +1986,7 @@ namespace graphene { namespace net { namespace detail { return; } - auto disconnet_peer = [&](const std::ostringstream& rejection_message) { + auto disconnect_peer = [&](const std::ostringstream& rejection_message) { #ifdef ENABLE_DEBUG_ULOGS ulog("Rejecting connection from peer because their version is too old. Their version date: ${date}", ("date", originating_peer->graphene_git_revision_unix_timestamp)); #endif @@ -2017,7 +2017,7 @@ namespace graphene { namespace net { namespace detail { ("their_hard_fork", next_fork_block_number)("my_block_number", head_block_num)); std::ostringstream rejection_message; rejection_message << "Your client is outdated -- you can only understand blocks up to #" << next_fork_block_number << ", but I'm already on block #" << head_block_num; - disconnet_peer(rejection_message); + disconnect_peer(rejection_message); return; } } @@ -2032,7 +2032,7 @@ namespace graphene { namespace net { namespace detail { std::ostringstream rejection_message; rejection_message << "Your client is outdated -- you can only understand blocks up to #" << originating_peer->last_known_hardfork_time.to_iso_string() << ", but I'm already on block #" << _delegate->get_block_time(_delegate->get_head_block_id()).to_iso_string(); std::cout<<"Reject connection due the hardforks on hello_message"< Date: Thu, 19 May 2022 18:02:09 +0200 Subject: [PATCH 018/106] Update GitLab CI file, more manual build options --- .gitlab-ci.yml | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 44038b97..cda41654 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,8 +8,9 @@ include: stages: - build - test + - dockerize -build: +build-mainnet: stage: build script: - rm -rf .git/modules/docs .git/modules/libraries/fc ./docs ./libraries/fc @@ -29,10 +30,10 @@ build: tags: - builder -test: +test-mainnet: stage: test dependencies: - - build + - build-mainnet script: - ./build/libraries/fc/tests/all_tests - ./build/tests/betting_test --log_level=message @@ -41,16 +42,20 @@ test: tags: - builder -dockerize: - stage: build +dockerize-mainnet: + stage: dockerize + dependencies: + - test-mainnet variables: - IMAGE: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA + IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA before_script: - docker info - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - - docker build -t $IMAGE . + - docker build --no-cache -t $IMAGE . - docker push $IMAGE + after_script: + - docker rmi $IMAGE tags: - builder when: @@ -97,3 +102,24 @@ test-testnet: manual timeout: 1h + +dockerize-testnet: + stage: dockerize + dependencies: + - test-testnet + variables: + IMAGE: $CI_REGISTRY_IMAGE/testnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA + before_script: + - docker info + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + script: + - docker build --no-cache -t $IMAGE . + - docker push $IMAGE + after_script: + - docker rmi $IMAGE + tags: + - builder + when: + manual + timeout: + 3h From 0b04faec83edb50679b6c6328621497f14645779 Mon Sep 17 00:00:00 2001 From: hirunda Date: Tue, 24 May 2022 23:29:57 +0200 Subject: [PATCH 019/106] Disconnect witness which doesn't provide last hardfork time --- libraries/net/node.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index d0524d00..f70584ec 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -1900,6 +1900,11 @@ namespace graphene { namespace net { namespace detail { originating_peer->last_known_fork_block_number = user_data["last_known_fork_block_number"].as(1); if (user_data.contains("last_known_hardfork_time")){ originating_peer->last_known_hardfork_time = fc::time_point_sec(user_data["last_known_hardfork_time"].as(1)); + }else{ + // this state is invalid when node which wants to connect doesn't provide + // last hardfork time. We are setting to 0 which will disconnect the node + // on hello message + originating_peer->last_known_hardfork_time = fc::time_point_sec(0); } } @@ -2023,7 +2028,8 @@ namespace graphene { namespace net { namespace detail { // indetify if peer's are not compatible due the hardforks if ( originating_peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time()) { - if (_delegate->get_block_time(_delegate->get_head_block_id()).sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch()) + if ((_delegate->get_block_time(_delegate->get_head_block_id()).sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch()) + || originating_peer->last_known_hardfork_time.sec_since_epoch() == 0) { std::ostringstream rejection_message; rejection_message << "Your client is outdated -- you can only understand blocks up to #" << originating_peer->last_known_hardfork_time.to_iso_string() << ", but I'm already on block #" << _delegate->get_block_time(_delegate->get_head_block_id()).to_iso_string(); From aa2dea6ddf7243d25a63477cccc3660d90f0a5ed Mon Sep 17 00:00:00 2001 From: serkixenos Date: Wed, 25 May 2022 16:48:38 +0200 Subject: [PATCH 020/106] Remove unused libreadline-dev library --- Dockerfile | 1 - Dockerfile.18.04 | 1 - README.md | 13 +++++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index e90e5380..7a76c136 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,6 @@ RUN \ libbz2-dev \ libcurl4-openssl-dev \ libncurses-dev \ - libreadline-dev \ libsnappy-dev \ libssl-dev \ libtool \ diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index 5e928263..d3fa1d75 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -21,7 +21,6 @@ RUN \ libbz2-dev \ libcurl4-openssl-dev \ libncurses-dev \ - libreadline-dev \ libsnappy-dev \ libssl-dev \ libtool \ diff --git a/README.md b/README.md index 12949dd5..a8d98021 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Following dependencies are needed for a clean install of Ubuntu 20.04: sudo apt-get install \ apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ - libcurl4-openssl-dev libncurses-dev libreadline-dev libsnappy-dev \ + libcurl4-openssl-dev libncurses-dev libsnappy-dev \ libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \ openssh-server pkg-config perl python3 python3-jinja2 sudo \ systemd-coredump wget @@ -74,11 +74,12 @@ sudo make install # this can install the executable files under /usr/local Following dependencies are needed for a clean install of Ubuntu 18.04: ``` sudo apt-get install \ - apt-utils autoconf bash build-essential ca-certificates dnsutils doxygen \ - expect git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ - libreadline-dev libsnappy-dev libssl-dev libtool libzip-dev locales \ - lsb-release mc nano net-tools ntp openssh-server pkg-config perl \ - python3 python3-jinja2 sudo systemd-coredump wget + apt-utils autoconf bash build-essential ca-certificates clang-format \ + dnsutils doxygen expect git graphviz libbz2-dev \ + libcurl4-openssl-dev libncurses-dev libsnappy-dev \ + libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \ + openssh-server pkg-config perl python3 python3-jinja2 sudo \ + systemd-coredump wget ``` Install Boost libraries from source From 2c02591e247694597a0b7f36a74d348104d95435 Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Thu, 2 Jun 2022 16:18:15 +0000 Subject: [PATCH 021/106] Fix for scheduler wrong state --- .../chain/include/graphene/chain/global_property_object.hpp | 1 + libraries/net/node.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 0f95a6e3..53bdec08 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -131,6 +131,7 @@ namespace graphene { namespace chain { }} FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object), + (random) (head_block_number) (head_block_id) (time) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index f70584ec..0c95a419 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -92,6 +92,7 @@ #define DEFAULT_LOGGER "p2p" #define P2P_IN_DEDICATED_THREAD 1 +#define DISABLE_WITNESS_HF_CHECK 1 #define INVOCATION_COUNTER(name) \ static unsigned total_ ## name ## _counter = 0; \ @@ -1905,8 +1906,10 @@ namespace graphene { namespace net { namespace detail { // last hardfork time. We are setting to 0 which will disconnect the node // on hello message originating_peer->last_known_hardfork_time = fc::time_point_sec(0); + if(DISABLE_WITNESS_HF_CHECK) { + originating_peer->last_known_hardfork_time = _delegate->get_last_known_hardfork_time(); + } } - } void node_impl::on_hello_message( peer_connection* originating_peer, const hello_message& hello_message_received ) From e2d9741af85a1ee8dcf70769473d63b948b5ec1b Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Wed, 8 Jun 2022 22:02:40 +0000 Subject: [PATCH 022/106] Cancel the thread for sync blocks --- libraries/app/application.cpp | 9 +++++++++ libraries/net/node.cpp | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 56878eaf..d6324d7d 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -107,6 +108,7 @@ public: fc::optional _lock_file; bool _is_block_producer = false; bool _force_validate = false; + std::atomic_bool _running{true}; void reset_p2p_node(const fc::path &data_dir) { try { @@ -450,6 +452,12 @@ public: */ virtual bool handle_block(const graphene::net::block_message &blk_msg, bool sync_mode, std::vector &contained_transaction_message_ids) override { + + // check point for the threads which may be cancled on application shutdown + if(!_running.load()) { + return true; + } + try { auto latency = fc::time_point::now() - blk_msg.block.timestamp; FC_ASSERT((latency.count() / 1000) > -5000, "Rejecting block with timestamp in the future"); @@ -1012,6 +1020,7 @@ void application::shutdown_plugins() { return; } void application::shutdown() { + my->_running.store(false); if (my->_p2p_network) my->_p2p_network->close(); if (my->_chain_db) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 0c95a419..841705f0 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -895,7 +895,7 @@ namespace graphene { namespace net { namespace detail { void node_impl::p2p_network_connect_loop() { VERIFY_CORRECT_THREAD(); - while (!_p2p_network_connect_loop_done.canceled()) + while (!_p2p_network_connect_loop_done.canceled() && !_node_is_shutting_down) { try { @@ -4051,6 +4051,8 @@ namespace graphene { namespace net { namespace detail { { VERIFY_CORRECT_THREAD(); + _node_is_shutting_down = true; + try { _potential_peer_db.close(); From 2a373a70f7a5ecd546841e5c8bd0b9e3445eb9c6 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 9 Jun 2022 21:41:19 +0300 Subject: [PATCH 023/106] #386 - check valid() for optional --- libraries/chain/db_block.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 6c58e58b..c475813b 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -433,7 +433,12 @@ processed_transaction database::push_proposal(const proposal_object& proposal) { for( size_t i=old_applied_ops_size,n=_applied_ops.size(); iresult = result; else { From 9dd0747e5dcbed200393720e6e4f7e856ad092a2 Mon Sep 17 00:00:00 2001 From: Pavel Baykov Date: Mon, 13 Jun 2022 17:03:20 -0300 Subject: [PATCH 024/106] bug fix 388: add ZMQ_RCVTIMEO, graceful thread shutdown --- .../sidechain_net_handler_bitcoin.hpp | 6 ++ .../sidechain_net_handler_bitcoin.cpp | 58 +++++++++++++------ 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 9c067e0e..9b3ec841 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -90,7 +91,9 @@ private: class zmq_listener { public: zmq_listener(std::string _ip, uint32_t _zmq); + virtual ~zmq_listener(); + void start(); boost::signals2::signal event_received; private: @@ -102,6 +105,9 @@ private: zmq::context_t ctx; zmq::socket_t socket; + + std::atomic_bool stopped; + std::thread thr; }; // ============================================================================= diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index a9e59307..83e6c948 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include @@ -1060,8 +1059,31 @@ zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) : ip(_ip), zmq_port(_zmq), ctx(1), - socket(ctx, ZMQ_SUB) { - std::thread(&zmq_listener::handle_zmq, this).detach(); + socket(ctx, ZMQ_SUB), + stopped(false) { +} + +void zmq_listener::start() { + int linger = 0; + auto rc = zmq_setsockopt(socket, ZMQ_SUBSCRIBE, "hashblock", 9); + FC_ASSERT(0 == rc); + rc = zmq_setsockopt(socket, ZMQ_LINGER, &linger, sizeof(linger)); + FC_ASSERT(0 == rc); + int timeout = 100; //millisec + rc = zmq_setsockopt(socket, ZMQ_RCVTIMEO, &timeout, sizeof(timeout)); + //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); + socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); + + thr = std::thread(&zmq_listener::handle_zmq, this); + + ilog("zmq_listener thread started"); +} + +zmq_listener::~zmq_listener() { + stopped = true; + thr.join(); } std::vector zmq_listener::receive_multipart() { @@ -1078,26 +1100,25 @@ std::vector zmq_listener::receive_multipart() { } void zmq_listener::handle_zmq() { - int linger = 0; - auto rc = zmq_setsockopt(socket, ZMQ_SUBSCRIBE, "hashblock", 9); - FC_ASSERT(0 == rc); - rc = zmq_setsockopt(socket, ZMQ_LINGER, &linger, sizeof(linger)); - FC_ASSERT(0 == rc); - //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); - //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); - //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); - socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); - - while (true) { + while (false == stopped) { try { - auto msg = receive_multipart(); - const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); - const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); - event_received(block_hash); + std::vector msg; + auto res = zmq::recv_multipart(socket, std::back_inserter(msg)); + if (res.has_value()){ + if (3 != *res) { + elog("zmq::recv_multipart returned: ${res}", ("res", *res)); + throw zmq::error_t(); + } + const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); + const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); + event_received(block_hash); + } } catch (zmq::error_t &e) { elog("handle_zmq recv_multipart exception ${str}", ("str", e.what())); } } + + ilog("zmq_listener thread finished"); } // ============================================================================= @@ -1173,6 +1194,7 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain ilog("Bitcoin major version is: '${version}'", ("version", bitcoin_major_version)); listener = std::unique_ptr(new zmq_listener(ip, zmq_port)); + listener->start(); listener->event_received.connect([this](const std::string &event_data) { std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); }); From 02d898d4dc2a6cb61ac9dc6b4bb64506d019aa0f Mon Sep 17 00:00:00 2001 From: serkixenos Date: Wed, 15 Jun 2022 06:30:33 +0200 Subject: [PATCH 025/106] Silence SON logs when not needed --- libraries/chain/db_maint.cpp | 10 +++++----- libraries/net/node.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 56ac6fd0..0c6843d9 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -660,6 +660,10 @@ void database::update_active_committee_members() void database::update_active_sons() { try { + if (head_block_time() < HARDFORK_SON_TIME) { + return; + } + assert( _son_count_histogram_buffer.size() > 0 ); share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; @@ -759,11 +763,7 @@ void database::update_active_sons() } } - if (son_sets_equal) { - ilog( "Active SONs set NOT CHANGED" ); - } else { - ilog( "Active SONs set CHANGED" ); - + if (!son_sets_equal) { update_son_wallet(new_active_sons); update_son_statuses(cur_active_sons, new_active_sons); } diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 841705f0..c6bef00f 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -1339,7 +1339,7 @@ namespace graphene { namespace net { namespace detail { // reconnect with the rest of the network, or it might just futher isolate us. { // As usual, the first step is to walk through all our peers and figure out which - // peers need action (disconnecting, sending keepalives, etc), then we walk through + // peers need action (disconnecting, sending keepalives, etc), then we walk through // those lists yielding at our leisure later. ASSERT_TASK_NOT_PREEMPTED(); From 8ce0db6ec3764dff1e6b5e8f13505eeebe404de5 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 16 Jun 2022 02:04:58 +0200 Subject: [PATCH 026/106] Add hardcoded seed nodes to the config file --- libraries/app/application.cpp | 82 ++++++++++++---------------------- programs/witness_node/main.cpp | 6 --- 2 files changed, 28 insertions(+), 60 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index d6324d7d..6ff51c02 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -117,67 +117,29 @@ public: _p2p_network->load_configuration(data_dir / "p2p"); _p2p_network->set_node_delegate(this); + vector all_seeds; + if (_options->count("seed-node")) { auto seeds = _options->at("seed-node").as>(); - for (const string &endpoint_string : seeds) { - try { - std::vector endpoints = resolve_string_to_ip_endpoints(endpoint_string); - for (const fc::ip::endpoint &endpoint : endpoints) { - ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); - _p2p_network->add_node(endpoint); - _p2p_network->connect_to_endpoint(endpoint); - } - } catch (const fc::exception &e) { - wlog("caught exception ${e} while adding seed node ${endpoint}", - ("e", e.to_detail_string())("endpoint", endpoint_string)); - } - } + all_seeds.insert(all_seeds.end(), seeds.begin(), seeds.end()); } if (_options->count("seed-nodes")) { auto seeds_str = _options->at("seed-nodes").as(); auto seeds = fc::json::from_string(seeds_str).as>(2); - for (const string &endpoint_string : seeds) { - try { - std::vector endpoints = resolve_string_to_ip_endpoints(endpoint_string); - for (const fc::ip::endpoint &endpoint : endpoints) { - ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); - _p2p_network->add_node(endpoint); - } - } catch (const fc::exception &e) { - wlog("caught exception ${e} while adding seed node ${endpoint}", - ("e", e.to_detail_string())("endpoint", endpoint_string)); - } - } - } else { - // t.me/peerplays #seednodes - vector seeds = { -#ifdef BUILD_PEERPLAYS_TESTNET + all_seeds.insert(all_seeds.end(), seeds.begin(), seeds.end()); + } -#else - "51.222.110.110:9777", - "95.216.90.243:9777", - "96.46.48.98:19777", - "96.46.48.98:29777", - "96.46.48.98:39777", - "96.46.48.98:49777", - "96.46.48.98:59777", - "seed.i9networks.net.br:9777", - "witness.serverpit.com:9777" -#endif - }; - - for (const string &endpoint_string : seeds) { - try { - std::vector endpoints = resolve_string_to_ip_endpoints(endpoint_string); - for (const fc::ip::endpoint &endpoint : endpoints) { - ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); - _p2p_network->add_node(endpoint); - } - } catch (const fc::exception &e) { - wlog("caught exception ${e} while adding seed node ${endpoint}", - ("e", e.to_detail_string())("endpoint", endpoint_string)); + for (const string &endpoint_string : all_seeds) { + try { + std::vector endpoints = resolve_string_to_ip_endpoints(endpoint_string); + for (const fc::ip::endpoint &endpoint : endpoints) { + ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); + _p2p_network->add_node(endpoint); } + } catch (const fc::exception &e) { + wlog("caught exception ${e} while adding seed node ${endpoint}", + ("e", e.to_detail_string())("endpoint", endpoint_string)); } } @@ -454,7 +416,7 @@ public: std::vector &contained_transaction_message_ids) override { // check point for the threads which may be cancled on application shutdown - if(!_running.load()) { + if (!_running.load()) { return true; } @@ -872,9 +834,21 @@ application::~application() { void application::set_program_options(boost::program_options::options_description &cli, boost::program_options::options_description &cfg) const { + + std::vector seed_nodes = { +#ifdef BUILD_PEERPLAYS_TESTNET +#else + "51.222.110.110:9777", + "95.216.90.243:9777", + "seed.i9networks.net.br:9777", + "witness.serverpit.com:9777" +#endif + }; + std::string seed_nodes_str = fc::json::to_string(seed_nodes); + cfg.add_options()("p2p-endpoint", bpo::value()->default_value("0.0.0.0:9777"), "Endpoint for P2P node to listen on"); cfg.add_options()("seed-node,s", bpo::value>()->composing(), "P2P nodes to connect to on startup (may specify multiple times)"); - cfg.add_options()("seed-nodes", bpo::value()->composing(), "JSON array of P2P nodes to connect to on startup"); + cfg.add_options()("seed-nodes", bpo::value()->composing()->default_value(seed_nodes_str), "JSON array of P2P nodes to connect to on startup"); cfg.add_options()("checkpoint,c", bpo::value>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints."); cfg.add_options()("rpc-endpoint", bpo::value()->default_value("127.0.0.1:8090"), "Endpoint for websocket RPC to listen on"); cfg.add_options()("rpc-tls-endpoint", bpo::value()->implicit_value("127.0.0.1:8089"), "Endpoint for TLS websocket RPC to listen on"); diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index af5086b2..c5eea447 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -79,18 +79,12 @@ int main(int argc, char** argv) { node->set_program_options(cli, cfg); cfg_options.add(cfg); - cfg_options.add_options() - ("plugins", bpo::value()->default_value("witness account_history market_history accounts_list affiliate_stats bookie"), - "Space-separated list of plugins to activate"); - auto witness_plug = node->register_plugin(); auto debug_witness_plug = node->register_plugin(); auto history_plug = node->register_plugin(); auto elasticsearch_plug = node->register_plugin(); auto es_objects_plug = node->register_plugin(); auto market_history_plug = node->register_plugin(); - //auto generate_genesis_plug = node->register_plugin(); - //auto generate_uia_sharedrop_genesis_plug = node->register_plugin(); auto list_plug = node->register_plugin(); auto affiliate_stats_plug = node->register_plugin(); auto bookie_plug = node->register_plugin(); From 1ae9470daba1412589aeb27c72b85635e63f2b0a Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 16 Jun 2022 04:10:29 +0200 Subject: [PATCH 027/106] Code formatting --- .../sidechain_net_handler_bitcoin.hpp | 2 +- .../sidechain_net_handler_bitcoin.cpp | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 9b3ec841..6fd1fcfa 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -2,8 +2,8 @@ #include -#include #include +#include #include #include diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 83e6c948..6203ff79 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1102,17 +1102,17 @@ std::vector zmq_listener::receive_multipart() { void zmq_listener::handle_zmq() { while (false == stopped) { try { - std::vector msg; + std::vector msg; auto res = zmq::recv_multipart(socket, std::back_inserter(msg)); - if (res.has_value()){ - if (3 != *res) { - elog("zmq::recv_multipart returned: ${res}", ("res", *res)); - throw zmq::error_t(); - } - const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); - const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); - event_received(block_hash); - } + if (res.has_value()) { + if (3 != *res) { + elog("zmq::recv_multipart returned: ${res}", ("res", *res)); + throw zmq::error_t(); + } + const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); + const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); + event_received(block_hash); + } } catch (zmq::error_t &e) { elog("handle_zmq recv_multipart exception ${str}", ("str", e.what())); } From 11834c7f53d63b9144c4d31b0d5dcd1d98a0d3fb Mon Sep 17 00:00:00 2001 From: serkixenos Date: Fri, 17 Jun 2022 00:10:21 +0200 Subject: [PATCH 028/106] Add more mainnet seed nodes --- libraries/app/application.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 6ff51c02..652cc08d 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -840,6 +840,9 @@ void application::set_program_options(boost::program_options::options_descriptio #else "51.222.110.110:9777", "95.216.90.243:9777", + "ca.peerplays.info", + "de.peerplays.xyz", + "pl.peerplays.org", "seed.i9networks.net.br:9777", "witness.serverpit.com:9777" #endif From eb2894c3d39c8416e86da9ef961266958c71e692 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Fri, 17 Jun 2022 19:03:27 +0200 Subject: [PATCH 029/106] Allow querying witness_node version by API --- libraries/app/database_api.cpp | 25 +++++++++++++++++++ .../app/include/graphene/app/database_api.hpp | 15 +++++++++++ 2 files changed, 40 insertions(+) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index a9ea9774..1095bbeb 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -29,12 +29,15 @@ #include #include +#include + #include #include #include #include +#include #include #include #include @@ -90,6 +93,7 @@ public: processed_transaction get_transaction(uint32_t block_num, uint32_t trx_in_block) const; // Globals + version_info get_version_info() const; chain_property_object get_chain_properties() const; global_property_object get_global_properties() const; fc::variant_object get_config() const; @@ -563,6 +567,27 @@ processed_transaction database_api_impl::get_transaction(uint32_t block_num, uin // // ////////////////////////////////////////////////////////////////////// +version_info database_api::get_version_info() const { + return my->get_version_info(); +} + +version_info database_api_impl::get_version_info() const { + + std::string witness_version(graphene::utilities::git_revision_description); + const size_t pos = witness_version.find('/'); + if (pos != std::string::npos && witness_version.size() > pos) + witness_version = witness_version.substr(pos + 1); + + version_info vi; + vi.version = witness_version; + vi.git_revision = graphene::utilities::git_revision_sha; + vi.built = std::string(__DATE__) + " at " + std::string(__TIME__); + vi.openssl = OPENSSL_VERSION_TEXT; + vi.boost = boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", "."); + + return vi; +} + chain_property_object database_api::get_chain_properties() const { return my->get_chain_properties(); } diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 93a2b88c..679d8a4c 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -130,6 +130,14 @@ struct gpos_info { share_type account_vested_balance; }; +struct version_info { + string version; + string git_revision; + string built; + string openssl; + string boost; +}; + /** * @brief The database_api class implements the RPC API for the chain database. * @@ -218,6 +226,11 @@ public: // Globals // ///////////// + /** + * @brief Retrieve the @ref version_info associated with the witness node + */ + version_info get_version_info() const; + /** * @brief Retrieve the @ref chain_property_object associated with the chain */ @@ -1040,6 +1053,7 @@ FC_REFLECT(graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highe FC_REFLECT(graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume)); FC_REFLECT(graphene::app::market_trade, (date)(price)(amount)(value)); FC_REFLECT(graphene::app::gpos_info, (vesting_factor)(award)(total_amount)(current_subperiod)(last_voted_time)(allowed_withdraw_amount)(account_vested_balance)); +FC_REFLECT(graphene::app::version_info, (version)(git_revision)(built)(openssl)(boost)); FC_API(graphene::app::database_api, // Objects @@ -1060,6 +1074,7 @@ FC_API(graphene::app::database_api, (get_recent_transaction_by_id) // Globals + (get_version_info) (get_chain_properties) (get_global_properties) (get_config) From a7f5e1f603c9d5baab7f06400ca9c4d740734e0f Mon Sep 17 00:00:00 2001 From: serkixenos Date: Mon, 20 Jun 2022 14:07:17 +0200 Subject: [PATCH 030/106] Add port to sync node endpoints --- libraries/app/application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 652cc08d..afae81a7 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -840,9 +840,9 @@ void application::set_program_options(boost::program_options::options_descriptio #else "51.222.110.110:9777", "95.216.90.243:9777", - "ca.peerplays.info", - "de.peerplays.xyz", - "pl.peerplays.org", + "ca.peerplays.info:9777", + "de.peerplays.xyz:9777", + "pl.peerplays.org:9777", "seed.i9networks.net.br:9777", "witness.serverpit.com:9777" #endif From bc7b0e778894910619e427d0b649674346b501f3 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Fri, 24 Jun 2022 14:38:23 +0200 Subject: [PATCH 031/106] Set HARDFORK_SON3_TIME to 2022-07-16T00:00:00 --- libraries/chain/hardfork.d/SON3.hf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/hardfork.d/SON3.hf b/libraries/chain/hardfork.d/SON3.hf index 36347c09..d9556e30 100644 --- a/libraries/chain/hardfork.d/SON3.hf +++ b/libraries/chain/hardfork.d/SON3.hf @@ -1,7 +1,7 @@ #ifndef HARDFORK_SON3_TIME #ifdef BUILD_PEERPLAYS_TESTNET -#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-05-31T00:00:00")) +#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00")) #else -#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-05-31T00:00:00")) +#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00")) #endif #endif From 1a196bfcc21c6f568ab0492ee8a8a151ea4c17a1 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Mon, 27 Jun 2022 12:39:34 +0000 Subject: [PATCH 032/106] #387 Allow changing number of SONs by voting, similar to witnesses --- libraries/app/application.cpp | 2 +- libraries/chain/account_evaluator.cpp | 5 ++- libraries/chain/db_maint.cpp | 41 ++++++++++++++++--- .../graphene/chain/protocol/account.hpp | 10 ++++- libraries/chain/protocol/account.cpp | 2 +- libraries/wallet/wallet.cpp | 12 +++--- tests/cli/son.cpp | 8 ++-- tests/tests/block_tests.cpp | 9 ++-- 8 files changed, 64 insertions(+), 25 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index d6324d7d..841f6747 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -454,7 +454,7 @@ public: std::vector &contained_transaction_message_ids) override { // check point for the threads which may be cancled on application shutdown - if(!_running.load()) { + if (!_running.load()) { return true; } diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index aa199c84..3026c2f0 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -62,11 +62,14 @@ void verify_account_votes( const database& db, const account_options& options ) const auto& gpo = db.get_global_properties(); const auto& chain_params = gpo.parameters; + FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." ); + FC_ASSERT( options.num_witness <= chain_params.maximum_witness_count, "Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) ); FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); - FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." ); + FC_ASSERT( options.num_son() <= chain_params.maximum_son_count(), + "Voted for more sons than currently allowed (${c})", ("c", chain_params.maximum_son_count()) ); uint32_t max_vote_id = gpo.next_available_vote_id; bool has_worker_votes = false; diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 0c6843d9..c3f826d1 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -182,7 +182,26 @@ void database::pay_sons() const dynamic_global_property_object& dpo = get_dynamic_global_properties(); // Current requirement is that we have to pay every 24 hours, so the following check if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { - auto sons = sort_votable_objects(get_global_properties().parameters.maximum_son_count()); + assert( _son_count_histogram_buffer.size() > 0 ); + const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; + /// accounts that vote for 0 or 1 son do not get to express an opinion on + /// the number of sons to have (they abstain and are non-voting accounts) + share_type stake_tally = 0; + size_t son_count = 0; + if( stake_target > 0 ) + { + while( (son_count < _son_count_histogram_buffer.size() - 1) + && (stake_tally <= stake_target) ) + { + stake_tally += _son_count_histogram_buffer[++son_count]; + } + } + const vector> sons = [this, &son_count]{ + if(head_block_time() >= HARDFORK_SON3_TIME) + return sort_votable_objects(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)); + else + return sort_votable_objects(get_global_properties().parameters.maximum_son_count()); + }(); // After SON2 HF uint64_t total_votes = 0; for( const son_object& son : sons ) @@ -683,8 +702,12 @@ void database::update_active_sons() } const global_property_object& gpo = get_global_properties(); - const chain_parameters& cp = gpo.parameters; - auto sons = sort_votable_objects(cp.maximum_son_count()); + const vector> sons = [this, &son_count]{ + if(head_block_time() >= HARDFORK_SON3_TIME) + return sort_votable_objects(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)); + else + return sort_votable_objects(get_global_properties().parameters.maximum_son_count()); + }(); const auto& all_sons = get_index_type().indices(); @@ -2041,6 +2064,13 @@ void update_son_params(database& db) gpo.parameters.extensions.value.maximum_son_count = 7; }); } + else + { + const auto& gpo = db.get_global_properties(); + db.modify( gpo, []( global_property_object& gpo ) { + gpo.parameters.extensions.value.maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; + }); + } } void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) @@ -2167,9 +2197,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // same rationale as for witnesses d._committee_count_histogram_buffer[offset] += voting_stake; } - if( opinion_account.options.num_son <= props.parameters.maximum_son_count() ) + if( opinion_account.options.num_son() <= props.parameters.maximum_son_count() ) { - uint16_t offset = std::min(size_t(opinion_account.options.num_son/2), + uint16_t offset = std::min(size_t(opinion_account.options.num_son()/2), d._son_count_histogram_buffer.size() - 1); // votes for a number greater than maximum_son_count // are turned into votes for maximum_son_count. @@ -2271,6 +2301,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // the following parameters are not allowed to be changed. So take what is in global property p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; + p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count; p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset; p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account; p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start; diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 50ccb8ae..c6de2047 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -37,6 +37,11 @@ namespace graphene { namespace chain { /// These are the fields which can be updated by the active authority. struct account_options { + struct ext + { + optional< uint16_t > num_son = 0; + }; + /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- /// validated account activities. This field is here to prevent confusion if the active authority has zero or /// multiple keys in it. @@ -54,11 +59,11 @@ namespace graphene { namespace chain { uint16_t num_committee = 0; /// The number of active son members this account votes the blockchain should appoint /// Must not exceed the actual number of son members voted for in @ref votes - uint16_t num_son = 0; + uint16_t num_son() const { return extensions.value.num_son.valid() ? *extensions.value.num_son : 0; } /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this /// account's balance of core asset. flat_set votes; - extensions_type extensions; + extension< ext > extensions; /// Whether this account is voting inline bool is_voting() const @@ -289,6 +294,7 @@ namespace graphene { namespace chain { } } // graphene::chain +FC_REFLECT(graphene::chain::account_options::ext, (num_son) ) FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions)) // FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing) FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing, diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index 2405369a..b980998c 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -174,7 +174,7 @@ void account_options::validate() const { auto needed_witnesses = num_witness; auto needed_committee = num_committee; - auto needed_sons = num_son; + auto needed_sons = num_son(); for( vote_id_type id : votes ) if( id.type() == vote_id_type::witness && needed_witnesses ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index d2fac215..35b9ab1c 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2158,8 +2158,8 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account)(url)(block_signing_key)(broadcast) ) } - signed_transaction activate_deregistered_son(const string & owner_account, - bool broadcast /* = false */) + signed_transaction activate_deregistered_son(const string & owner_account, + bool broadcast /* = false */) { try { son_object son = get_son(owner_account); @@ -2408,7 +2408,7 @@ public: op.sidechain = sidechain; op.peerplays_uid = peerplays_uid; op.peerplays_transaction_id = peerplays_transaction_id; - op.peerplays_from = peerplays_from; + op.peerplays_from = peerplays_from; op.peerplays_asset = asset(asset_val.amount * asset_price.base.amount / asset_price.quote.amount); op.withdraw_sidechain = withdraw_sidechain; op.withdraw_address = withdraw_address; @@ -2875,7 +2875,7 @@ public: if (!votes_removed) FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); } - voting_account_object.options.num_son = desired_number_of_sons; + voting_account_object.options.extensions.value.num_son = desired_number_of_sons; account_update_operation account_update_op; account_update_op.account = voting_account_object.id; @@ -5410,7 +5410,7 @@ signed_transaction wallet_api::sidechain_withdrawal_transaction(const string &so const string &withdraw_amount) { return my->sidechain_withdrawal_transaction(son_name_or_id, - block_num, + block_num, sidechain, peerplays_uid, peerplays_transaction_id, @@ -5540,7 +5540,7 @@ signed_transaction wallet_api::sidechain_deposit_transaction( const string &son const string &peerplays_from_name_or_id, const string &peerplays_to_name_or_id) { - return my->sidechain_deposit_transaction(son_name_or_id, + return my->sidechain_deposit_transaction(son_name_or_id, sidechain, transaction_id, operation_index, diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 491ec8f9..1f09de9f 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -337,7 +337,8 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - unsigned int son_number = gpo.parameters.maximum_son_count(); + //! Set son number as 5 (as the begining son count) + unsigned int son_number = 5; flat_map sidechain_public_keys; @@ -400,7 +401,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); BOOST_CHECK(generate_maintenance_block()); - BOOST_CHECK(gpo.active_sons.size() == gpo.parameters.maximum_son_count()); + BOOST_CHECK(gpo.active_sons.size() == son_number); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -644,7 +645,8 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - unsigned int son_number = gpo.parameters.maximum_son_count(); + //! Set son number as 5 (as the begining son count) + unsigned int son_number = 5; flat_map sidechain_public_keys; diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index a802aac5..4fb3a24d 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -1040,16 +1040,13 @@ BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture ) generate_blocks(HARDFORK_SON3_TIME); // after this hardfork maximum son account should not reset the value - // on 7 after maintenance interval anymore. So change the global parameters - // and check the value after maintenance interval - db.modify(db.get_global_properties(), [](global_property_object& p) { - p.parameters.extensions.value.maximum_son_count = 13; - }); + // on 7 after maintenance interval anymore. It must be GRAPHENE_DEFAULT_MAX_SONS + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_block(); - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 13); + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 15); } FC_LOG_AND_RETHROW() } From 873dfd788bb387ff915ee8fbf91cbe01a12e7746 Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Tue, 5 Jul 2022 11:48:01 +0000 Subject: [PATCH 033/106] Clean exit from CTRL + C --- libraries/net/node.cpp | 23 +++++++++++++++-------- programs/witness_node/main.cpp | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index c6bef00f..8b797d4a 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include #include #include @@ -574,7 +575,7 @@ namespace graphene { namespace net { namespace detail { std::set _allowed_peers; #endif // ENABLE_P2P_DEBUGGING_API - bool _node_is_shutting_down; // set to true when we begin our destructor, used to prevent us from starting new tasks while we're shutting down + std::atomic_bool _node_is_shutting_down {false}; unsigned _maximum_number_of_blocks_to_handle_at_one_time; unsigned _maximum_number_of_sync_blocks_to_prefetch; @@ -825,7 +826,6 @@ namespace graphene { namespace net { namespace detail { _average_network_write_speed_hours(72), _average_network_usage_second_counter(0), _average_network_usage_minute_counter(0), - _node_is_shutting_down(false), _maximum_number_of_blocks_to_handle_at_one_time(MAXIMUM_NUMBER_OF_BLOCKS_TO_HANDLE_AT_ONE_TIME), _maximum_number_of_sync_blocks_to_prefetch(MAXIMUM_NUMBER_OF_BLOCKS_TO_PREFETCH), _maximum_blocks_per_peer_during_syncing(GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING) @@ -842,7 +842,7 @@ namespace graphene { namespace net { namespace detail { { VERIFY_CORRECT_THREAD(); ilog( "cleaning up node" ); - _node_is_shutting_down = true; + _node_is_shutting_down.store(true); for (const peer_connection_ptr& active_peer : _active_connections) { @@ -950,6 +950,11 @@ namespace graphene { namespace net { namespace detail { } display_current_connections(); + if(_node_is_shutting_down) + { + ilog("Breaking p2p_network_connect_loop loop because node is shutting down"); + break; + } // if we broke out of the while loop, that means either we have connected to enough nodes, or // we don't have any good candidates to connect to right now. @@ -1032,7 +1037,7 @@ namespace graphene { namespace net { namespace detail { void node_impl::fetch_sync_items_loop() { VERIFY_CORRECT_THREAD(); - while( !_fetch_sync_items_loop_done.canceled() ) + while( !_fetch_sync_items_loop_done.canceled() && !_node_is_shutting_down ) { _sync_items_to_fetch_updated = false; dlog( "beginning another iteration of the sync items loop" ); @@ -1676,13 +1681,15 @@ namespace graphene { namespace net { namespace detail { bool node_impl::is_accepting_new_connections() { VERIFY_CORRECT_THREAD(); - return !_p2p_network_connect_loop_done.canceled() && get_number_of_connections() <= _maximum_number_of_connections; + return !_node_is_shutting_down && (!_p2p_network_connect_loop_done.valid() || !_p2p_network_connect_loop_done.canceled()) && + get_number_of_connections() <= _maximum_number_of_connections; } bool node_impl::is_wanting_new_connections() { VERIFY_CORRECT_THREAD(); - return !_p2p_network_connect_loop_done.canceled() && get_number_of_connections() < _desired_number_of_connections; + return !_node_is_shutting_down && !_p2p_network_connect_loop_done.canceled() && + get_number_of_connections() < _desired_number_of_connections; } uint32_t node_impl::get_number_of_connections() @@ -3418,7 +3425,7 @@ namespace graphene { namespace net { namespace detail { dlog("leaving process_backlog_of_sync_blocks, ${count} processed", ("count", blocks_processed)); - if (!_suspend_fetching_sync_blocks) + if (!_suspend_fetching_sync_blocks && !_node_is_shutting_down) trigger_fetch_sync_items_loop(); } @@ -4051,7 +4058,7 @@ namespace graphene { namespace net { namespace detail { { VERIFY_CORRECT_THREAD(); - _node_is_shutting_down = true; + _node_is_shutting_down.store(true); try { diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index c5eea447..e1c6c12e 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -176,6 +176,7 @@ int main(int argc, char** argv) { node->shutdown_plugins(); node->shutdown(); delete node; + ilog("Witness node is closed and turned off"); return EXIT_SUCCESS; } catch( const fc::exception& e ) { // deleting the node can yield, so do this outside the exception handler From 09579fbab1b9150ee1291c0f5da276caed6e7219 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Wed, 6 Jul 2022 01:07:39 +0200 Subject: [PATCH 034/106] Fix SON cli tests --- tests/cli/son.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 1f09de9f..38a3799b 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -39,7 +39,7 @@ public: fixture_(fixture) { fixture_.init_nathan(); - fixture_.generate_blocks(HARDFORK_SON_FOR_HIVE_TIME); + fixture_.generate_blocks(HARDFORK_SON3_TIME); fixture_.generate_block(); } From 3b5e9280940c4b4bd573868be6d10f813883e917 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 7 Jul 2022 00:53:23 +0000 Subject: [PATCH 035/106] #357 secp256k1 lib from libbitcoin --- .gitmodules | 2 +- libraries/app/api.cpp | 57 -- libraries/app/application.cpp | 1 - libraries/app/database_api.cpp | 26 - libraries/app/include/graphene/app/api.hpp | 41 -- .../app/include/graphene/app/database_api.hpp | 12 - libraries/chain/confidential_evaluator.cpp | 104 ---- libraries/chain/db_notify.cpp | 25 +- .../include/graphene/chain/exceptions.hpp | 3 - .../graphene/chain/protocol/confidential.hpp | 51 +- .../graphene/chain/protocol/operations.hpp | 6 +- libraries/chain/protocol/confidential.cpp | 54 +- libraries/fc | 2 +- .../bitcoin/sign_bitcoin_transaction.cpp | 33 +- .../bitcoin/sign_bitcoin_transaction.hpp | 10 +- .../wallet/include/graphene/wallet/wallet.hpp | 159 ----- libraries/wallet/wallet.cpp | 575 ------------------ .../bitcoin_sign_tests.cpp | 4 +- tests/tests/confidential_tests.cpp | 134 ---- tests/tests/fee_tests.cpp | 206 ------- 20 files changed, 65 insertions(+), 1440 deletions(-) delete mode 100644 tests/tests/confidential_tests.cpp diff --git a/.gitmodules b/.gitmodules index e535465c..d9c387a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,5 +5,5 @@ [submodule "libraries/fc"] path = libraries/fc url = https://gitlab.com/PBSA/tools-libs/peerplays-fc.git - branch = latest-fc + branch = develop ignore = dirty diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index f6b084f1..8dd18915 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -45,7 +45,6 @@ template class fc::api; template class fc::api; template class fc::api; template class fc::api; -template class fc::api; template class fc::api; template class fc::api; template class fc::api; @@ -90,8 +89,6 @@ void login_api::enable_api(const std::string &api_name) { _history_api = std::make_shared(_app); } else if (api_name == "network_node_api") { _network_node_api = std::make_shared(std::ref(_app)); - } else if (api_name == "crypto_api") { - _crypto_api = std::make_shared(); } else if (api_name == "asset_api") { _asset_api = std::make_shared(_app); } else if (api_name == "debug_api") { @@ -289,11 +286,6 @@ fc::api login_api::history() const { return *_history_api; } -fc::api login_api::crypto() const { - FC_ASSERT(_crypto_api); - return *_crypto_api; -} - fc::api login_api::asset() const { FC_ASSERT(_asset_api); return *_asset_api; @@ -522,55 +514,6 @@ vector history_api::get_market_history(std::string asset_a, std:: FC_CAPTURE_AND_RETHROW((asset_a)(asset_b)(bucket_seconds)(start)(end)) } -crypto_api::crypto_api(){}; - -commitment_type crypto_api::blind(const blind_factor_type &blind, uint64_t value) { - return fc::ecc::blind(blind, value); -} - -blind_factor_type crypto_api::blind_sum(const std::vector &blinds_in, uint32_t non_neg) { - return fc::ecc::blind_sum(blinds_in, non_neg); -} - -bool crypto_api::verify_sum(const std::vector &commits_in, const std::vector &neg_commits_in, int64_t excess) { - return fc::ecc::verify_sum(commits_in, neg_commits_in, excess); -} - -verify_range_result crypto_api::verify_range(const commitment_type &commit, const std::vector &proof) { - verify_range_result result; - result.success = fc::ecc::verify_range(result.min_val, result.max_val, commit, proof); - return result; -} - -std::vector crypto_api::range_proof_sign(uint64_t min_value, - const commitment_type &commit, - const blind_factor_type &commit_blind, - const blind_factor_type &nonce, - int8_t base10_exp, - uint8_t min_bits, - uint64_t actual_value) { - return fc::ecc::range_proof_sign(min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value); -} - -verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind(const blind_factor_type &nonce, - const commitment_type &commit, - const std::vector &proof) { - verify_range_proof_rewind_result result; - result.success = fc::ecc::verify_range_proof_rewind(result.blind_out, - result.value_out, - result.message_out, - nonce, - result.min_val, - result.max_val, - const_cast(commit), - proof); - return result; -} - -range_proof_info crypto_api::range_get_info(const std::vector &proof) { - return fc::ecc::range_get_info(proof); -} - // asset_api asset_api::asset_api(graphene::app::application &app) : _app(app), diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index afae81a7..1a3d3a8a 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -362,7 +362,6 @@ public: wild_access.allowed_apis.push_back("database_api"); wild_access.allowed_apis.push_back("network_broadcast_api"); wild_access.allowed_apis.push_back("history_api"); - wild_access.allowed_apis.push_back("crypto_api"); wild_access.allowed_apis.push_back("bookie_api"); wild_access.allowed_apis.push_back("affiliate_stats_api"); wild_access.allowed_apis.push_back("sidechain_api"); diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 1095bbeb..250413ee 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -237,9 +237,6 @@ public: // Proposed transactions vector get_proposed_transactions(const std::string account_id_or_name) const; - // Blinded balances - vector get_blinded_balances(const flat_set &commitments) const; - // Tournaments vector get_tournaments_in_state(tournament_state state, uint32_t limit) const; vector get_tournaments(tournament_id_type stop, unsigned limit, tournament_id_type start); @@ -2650,29 +2647,6 @@ vector database_api_impl::get_proposed_transactions(const std:: return result; } -////////////////////////////////////////////////////////////////////// -// // -// Blinded balances // -// // -////////////////////////////////////////////////////////////////////// - -vector database_api::get_blinded_balances(const flat_set &commitments) const { - return my->get_blinded_balances(commitments); -} - -vector database_api_impl::get_blinded_balances(const flat_set &commitments) const { - vector result; - result.reserve(commitments.size()); - const auto &bal_idx = _db.get_index_type(); - const auto &by_commitment_idx = bal_idx.indices().get(); - for (const auto &c : commitments) { - auto itr = by_commitment_idx.find(c); - if (itr != by_commitment_idx.end()) - result.push_back(*itr); - } - return result; -} - ////////////////////////////////////////////////////////////////////// // // // Tournament methods // diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 98be16a6..68f2514a 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -289,33 +289,6 @@ private: std::function _on_pending_transaction; }; -class crypto_api { -public: - crypto_api(); - - fc::ecc::commitment_type blind(const fc::ecc::blind_factor_type &blind, uint64_t value); - - fc::ecc::blind_factor_type blind_sum(const std::vector &blinds_in, uint32_t non_neg); - - bool verify_sum(const std::vector &commits_in, const std::vector &neg_commits_in, int64_t excess); - - verify_range_result verify_range(const fc::ecc::commitment_type &commit, const std::vector &proof); - - std::vector range_proof_sign(uint64_t min_value, - const commitment_type &commit, - const blind_factor_type &commit_blind, - const blind_factor_type &nonce, - int8_t base10_exp, - uint8_t min_bits, - uint64_t actual_value); - - verify_range_proof_rewind_result verify_range_proof_rewind(const blind_factor_type &nonce, - const fc::ecc::commitment_type &commit, - const std::vector &proof); - - range_proof_info range_get_info(const std::vector &proof); -}; - /** * @brief */ @@ -359,7 +332,6 @@ extern template class fc::api; extern template class fc::api; extern template class fc::api; extern template class fc::api; -extern template class fc::api; extern template class fc::api; extern template class fc::api; @@ -394,8 +366,6 @@ public: fc::api history() const; /// @brief Retrieve the network node API fc::api network_node() const; - /// @brief Retrieve the cryptography API - fc::api crypto() const; /// @brief Retrieve the asset API fc::api asset() const; /// @brief Retrieve the debug API (if available) @@ -417,7 +387,6 @@ private: optional> _network_broadcast_api; optional> _network_node_api; optional> _history_api; - optional> _crypto_api; optional> _asset_api; optional> _debug_api; optional> _bookie_api; @@ -475,15 +444,6 @@ FC_API(graphene::app::network_node_api, (subscribe_to_pending_transactions) (unsubscribe_from_pending_transactions)) -FC_API(graphene::app::crypto_api, - (blind) - (blind_sum) - (verify_sum) - (verify_range) - (range_proof_sign) - (verify_range_proof_rewind) - (range_get_info)) - FC_API(graphene::app::asset_api, (get_asset_holders) (get_asset_holders_count) @@ -496,7 +456,6 @@ FC_API(graphene::app::login_api, (database) (history) (network_node) - (crypto) (asset) (debug) (bookie) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 679d8a4c..abe65a2b 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -877,15 +877,6 @@ public: */ vector get_proposed_transactions(const std::string account_id_or_name) const; - ////////////////////// - // Blinded balances // - ////////////////////// - - /** - * @return the set of blinded balance objects by commitment ID - */ - vector get_blinded_balances(const flat_set &commitments) const; - ///////////////// // Tournaments // ///////////////// @@ -1198,9 +1189,6 @@ FC_API(graphene::app::database_api, // Proposed transactions (get_proposed_transactions) - // Blinded balances - (get_blinded_balances) - // Tournaments (get_tournaments_in_state) (get_tournaments_by_state) diff --git a/libraries/chain/confidential_evaluator.cpp b/libraries/chain/confidential_evaluator.cpp index 9946b492..fa4ac515 100644 --- a/libraries/chain/confidential_evaluator.cpp +++ b/libraries/chain/confidential_evaluator.cpp @@ -33,149 +33,45 @@ namespace graphene { namespace chain { void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o ) { try { - const auto& d = db(); - - const auto& atype = o.amount.asset_id(db()); - FC_ASSERT( atype.allow_confidential() ); - FC_ASSERT( !atype.is_transfer_restricted() ); - FC_ASSERT( !(atype.options.flags & white_list) ); - - for( const auto& out : o.outputs ) - { - for( const auto& a : out.owner.account_auths ) - a.first(d); // verify all accounts exist and are valid - } return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o ) { try { - db().adjust_balance( o.from, -o.amount ); - - const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset - db().modify( add, [&]( asset_dynamic_data_object& obj ){ - obj.confidential_supply += o.amount.amount; - FC_ASSERT( obj.confidential_supply >= 0 ); - }); - for( const auto& out : o.outputs ) - { - db().create( [&]( blinded_balance_object& obj ){ - obj.asset_id = o.amount.asset_id; - obj.owner = out.owner; - obj.commitment = out.commitment; - }); - } return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void transfer_to_blind_evaluator::pay_fee() { - if( db().head_block_time() >= HARDFORK_563_TIME ) - pay_fba_fee( fba_accumulator_id_transfer_to_blind ); - else - generic_evaluator::pay_fee(); } void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o ) { try { - const auto& d = db(); - o.fee.asset_id(d); // verify fee is a legit asset - const auto& bbi = d.get_index_type(); - const auto& cidx = bbi.indices().get(); - for( const auto& in : o.inputs ) - { - auto itr = cidx.find( in.commitment ); - FC_ASSERT( itr != cidx.end() ); - FC_ASSERT( itr->asset_id == o.fee.asset_id ); - FC_ASSERT( itr->owner == in.owner ); - } return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o ) { try { - db().adjust_balance( o.fee_payer(), o.fee ); - db().adjust_balance( o.to, o.amount ); - const auto& bbi = db().get_index_type(); - const auto& cidx = bbi.indices().get(); - for( const auto& in : o.inputs ) - { - auto itr = cidx.find( in.commitment ); - FC_ASSERT( itr != cidx.end() ); - db().remove( *itr ); - } - const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset - db().modify( add, [&]( asset_dynamic_data_object& obj ){ - obj.confidential_supply -= o.amount.amount + o.fee.amount; - FC_ASSERT( obj.confidential_supply >= 0 ); - }); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void transfer_from_blind_evaluator::pay_fee() { - if( db().head_block_time() >= HARDFORK_563_TIME ) - pay_fba_fee( fba_accumulator_id_transfer_from_blind ); - else - generic_evaluator::pay_fee(); } void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o ) { try { - const auto& d = db(); - o.fee.asset_id(db()); // verify fee is a legit asset - const auto& bbi = db().get_index_type(); - const auto& cidx = bbi.indices().get(); - for( const auto& out : o.outputs ) - { - for( const auto& a : out.owner.account_auths ) - a.first(d); // verify all accounts exist and are valid - } - for( const auto& in : o.inputs ) - { - auto itr = cidx.find( in.commitment ); - GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) ); - FC_ASSERT( itr->asset_id == o.fee.asset_id ); - FC_ASSERT( itr->owner == in.owner ); - } return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o ) { try { - db().adjust_balance( o.fee_payer(), o.fee ); // deposit the fee to the temp account - const auto& bbi = db().get_index_type(); - const auto& cidx = bbi.indices().get(); - for( const auto& in : o.inputs ) - { - auto itr = cidx.find( in.commitment ); - GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) ); - db().remove( *itr ); - } - for( const auto& out : o.outputs ) - { - db().create( [&]( blinded_balance_object& obj ){ - obj.asset_id = o.fee.asset_id; - obj.owner = out.owner; - obj.commitment = out.commitment; - }); - } - const auto& add = o.fee.asset_id(db()).dynamic_asset_data_id(db()); - db().modify( add, [&]( asset_dynamic_data_object& obj ){ - obj.confidential_supply -= o.fee.amount; - FC_ASSERT( obj.confidential_supply >= 0 ); - }); - return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void blind_transfer_evaluator::pay_fee() { - if( db().head_block_time() >= HARDFORK_563_TIME ) - pay_fba_fee( fba_accumulator_id_blind_transfer ); - else - generic_evaluator::pay_fee(); } } } // graphene::chain diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 62d6d2bf..2360a318 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -203,27 +203,10 @@ struct get_impacted_account_visitor _impacted.insert( op.issuer ); } - void operator()( const transfer_to_blind_operation& op ) - { - _impacted.insert( op.from ); - for( const auto& out : op.outputs ) - add_authority_accounts( _impacted, out.owner ); - } - - void operator()( const blind_transfer_operation& op ) - { - for( const auto& in : op.inputs ) - add_authority_accounts( _impacted, in.owner ); - for( const auto& out : op.outputs ) - add_authority_accounts( _impacted, out.owner ); - } - - void operator()( const transfer_from_blind_operation& op ) - { - _impacted.insert( op.to ); - for( const auto& in : op.inputs ) - add_authority_accounts( _impacted, in.owner ); - } + //! We don't use this operations + void operator()( const transfer_to_blind_operation& op ){} + void operator()( const blind_transfer_operation& op ){} + void operator()( const transfer_from_blind_operation& op ){} void operator()( const asset_settle_cancel_operation& op ) { diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index 406a235e..92c7c5dd 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -182,9 +182,6 @@ namespace graphene { namespace chain { GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer ); GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" ) - GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" ); - /* FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" ) FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" ) diff --git a/libraries/chain/include/graphene/chain/protocol/confidential.hpp b/libraries/chain/include/graphene/chain/protocol/confidential.hpp index 697ef35b..d1a28da3 100644 --- a/libraries/chain/include/graphene/chain/protocol/confidential.hpp +++ b/libraries/chain/include/graphene/chain/protocol/confidential.hpp @@ -111,12 +111,12 @@ struct stealth_confirmation /** * Packs *this then encodes as base58 encoded string. */ - operator string()const; + //operator string()const; /** * Unpacks from a base58 string */ - stealth_confirmation( const std::string& base58 ); - stealth_confirmation(){} + //stealth_confirmation( const std::string& base58 ); + //stealth_confirmation(){} public_key_type one_time_key; optional to; @@ -152,16 +152,17 @@ struct transfer_to_blind_operation : public base_operation uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION; }; - asset fee; asset amount; account_id_type from; blind_factor_type blinding_factor; vector outputs; - account_id_type fee_payer()const { return from; } - void validate()const; - share_type calculate_fee(const fee_parameters_type& )const; + account_id_type fee_payer()const { return account_id_type{}; } + + //account_id_type fee_payer()const { return from; } + //void validate()const; + //share_type calculate_fee(const fee_parameters_type& )const; }; /** @@ -180,14 +181,15 @@ struct transfer_from_blind_operation : public base_operation blind_factor_type blinding_factor; vector inputs; - account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } - void validate()const; + account_id_type fee_payer()const { return account_id_type{}; } - void get_required_authorities( vector& a )const - { - for( const auto& in : inputs ) - a.push_back( in.owner ); - } + //account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } + //void validate()const; + //void get_required_authorities( vector& a )const + //{ + // for( const auto& in : inputs ) + // a.push_back( in.owner ); + //} }; /** @@ -243,17 +245,18 @@ struct blind_transfer_operation : public base_operation asset fee; vector inputs; vector outputs; - - /** graphene TEMP account */ - account_id_type fee_payer()const; - void validate()const; - share_type calculate_fee( const fee_parameters_type& k )const; - void get_required_authorities( vector& a )const - { - for( const auto& in : inputs ) - a.push_back( in.owner ); - } + account_id_type fee_payer()const { return account_id_type{}; } + + /** graphene TEMP account */ + //account_id_type fee_payer()const; + //void validate()const; + //share_type calculate_fee( const fee_parameters_type& k )const; + //void get_required_authorities( vector& a )const + //{ + // for( const auto& in : inputs ) + // a.push_back( in.owner ); + //} }; ///@} endgroup stealth diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 83d347ab..8cbde2ac 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -106,9 +106,9 @@ namespace graphene { namespace chain { assert_operation, balance_claim_operation, override_transfer_operation, - transfer_to_blind_operation, - blind_transfer_operation, - transfer_from_blind_operation, + transfer_to_blind_operation, //! We don't use this operation + blind_transfer_operation, //! We don't use this operation + transfer_from_blind_operation, //! We don't use this operation asset_settle_cancel_operation, // VIRTUAL asset_claim_fees_operation, fba_distribute_operation, // VIRTUAL diff --git a/libraries/chain/protocol/confidential.cpp b/libraries/chain/protocol/confidential.cpp index 2e8fbc68..4bca850d 100644 --- a/libraries/chain/protocol/confidential.cpp +++ b/libraries/chain/protocol/confidential.cpp @@ -22,12 +22,10 @@ * THE SOFTWARE. */ #include -#include -#include -#include #include +/* namespace graphene { namespace chain { void transfer_to_blind_operation::validate()const @@ -47,19 +45,6 @@ void transfer_to_blind_operation::validate()const FC_ASSERT( !outputs[i].owner.is_impossible() ); } FC_ASSERT( out.size(), "there must be at least one output" ); - - auto public_c = fc::ecc::blind(blinding_factor,net_public); - - FC_ASSERT( fc::ecc::verify_sum( {public_c}, out, 0 ), "", ("net_public",net_public) ); - - if( outputs.size() > 1 ) - { - for( auto out : outputs ) - { - auto info = fc::ecc::range_get_info( out.range_proof ); - FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY ); - } - } } share_type transfer_to_blind_operation::calculate_fee( const fee_parameters_type& k )const @@ -79,31 +64,15 @@ void transfer_from_blind_operation::validate()const vector in(inputs.size()); vector out; int64_t net_public = fee.amount.value + amount.amount.value; - out.push_back( fc::ecc::blind( blinding_factor, net_public ) ); - for( uint32_t i = 0; i < in.size(); ++i ) - { - in[i] = inputs[i].commitment; - /// by requiring all inputs to be sorted we also prevent duplicate commitments on the input - if( i > 0 ) FC_ASSERT( in[i-1] < in[i], "all inputs must be sorted by commitment id" ); - } - FC_ASSERT( in.size(), "there must be at least one input" ); - FC_ASSERT( fc::ecc::verify_sum( in, out, 0 ) ); } -/** - * If fee_payer = temp_account_id, then the fee is paid by the surplus balance of inputs-outputs and - * 100% of the fee goes to the network. - */ account_id_type blind_transfer_operation::fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } -/** - * This method can be computationally intensive because it verifies that input commitments - output commitments add up to 0 - */ void blind_transfer_operation::validate()const { try { vector in(inputs.size()); @@ -122,17 +91,6 @@ void blind_transfer_operation::validate()const FC_ASSERT( !outputs[i].owner.is_impossible() ); } FC_ASSERT( in.size(), "there must be at least one input" ); - FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), "", ("net_public", net_public) ); - - if( outputs.size() > 1 ) - { - for( auto out : outputs ) - { - auto info = fc::ecc::range_get_info( out.range_proof ); - FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY ); - } - } - FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), "", ("net_public", net_public) ); } FC_CAPTURE_AND_RETHROW( (*this) ) } share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k )const @@ -140,16 +98,12 @@ share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k return k.fee + outputs.size() * k.price_per_output; } -/** - * Packs *this then encodes as base58 encoded string. - */ + stealth_confirmation::operator string()const { return fc::to_base58( fc::raw::pack( *this ) ); } -/** - * Unpacks from a base58 string - */ + stealth_confirmation::stealth_confirmation( const std::string& base58 ) { *this = fc::raw::unpack( fc::from_base58( base58 ) ); @@ -157,6 +111,8 @@ stealth_confirmation::stealth_confirmation( const std::string& base58 ) } } // graphene::chain +*/ + GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_to_blind_operation::fee_parameters_type ) GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_from_blind_operation::fee_parameters_type ) GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blind_transfer_operation::fee_parameters_type ) diff --git a/libraries/fc b/libraries/fc index e7369949..156b0c4e 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit e7369949bea26f3201d8442ba78286a88df74762 +Subproject commit 156b0c4e41c9215eadb2af8009b05e0f38c16dda diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp index afad2093..e40ea329 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -6,8 +7,8 @@ namespace graphene { namespace peerplays_sidechain { namespace bitcoin { -const secp256k1_context_t *btc_context() { - static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); +const secp256k1_context *btc_context() { + static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); return ctx; } @@ -31,20 +32,14 @@ fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &script return fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size())); } -std::vector privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign) { +std::vector privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context *context_sign) { bytes sig; sig.resize(72); int sig_len = sig.size(); - FC_ASSERT(secp256k1_ecdsa_sign( - context_sign, - reinterpret_cast(hash.data()), - reinterpret_cast(sig.data()), - &sig_len, - reinterpret_cast(privkey.data()), - secp256k1_nonce_function_rfc6979, - nullptr)); // TODO: replace assert with exception - + secp256k1_ecdsa_signature sign; + FC_ASSERT(secp256k1_ecdsa_sign(context_sign, &sign, (const unsigned char *)hash.data(), (const unsigned char *)privkey.data(), secp256k1_nonce_function_rfc6979, nullptr)); + FC_ASSERT(secp256k1_ecdsa_signature_serialize_der(context_sign, (unsigned char *)sig.data(), (size_t *)&sig_len, &sign)); sig.resize(sig_len); return sig; @@ -52,7 +47,7 @@ std::vector privkey_sign(const bytes &privkey, const fc::sha256 &hash, con std::vector sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector &redeem_scripts, const std::vector &amounts, const bytes &privkey, - const secp256k1_context_t *context_sign, int hash_type) { + const secp256k1_context *context_sign, int hash_type) { FC_ASSERT(tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size()); FC_ASSERT(!privkey.empty()); @@ -77,17 +72,23 @@ void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vecto } } -bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context) { +bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context *context) { std::vector sig_temp(sig.begin(), sig.end()); std::vector pubkey_temp(pubkey.begin(), pubkey.end()); std::vector msg_temp(msg.begin(), msg.end()); - int result = secp256k1_ecdsa_verify(context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size()); + secp256k1_pubkey pub_key; + FC_ASSERT(secp256k1_ec_pubkey_parse(context, &pub_key, (const unsigned char *)pubkey_temp.data(), pubkey_temp.size())); + + secp256k1_ecdsa_signature sign; + FC_ASSERT(secp256k1_ecdsa_signature_parse_der(context, &sign, (const unsigned char *)sig_temp.data(), sig_temp.size())); + + int result = secp256k1_ecdsa_verify(context, &sign, (const unsigned char *)msg_temp.data(), &pub_key); return result == 1; } std::vector> sort_sigs(const bitcoin_transaction &tx, const std::vector &redeem_scripts, - const std::vector &amounts, const secp256k1_context_t *context) { + const std::vector &amounts, const secp256k1_context *context) { FC_ASSERT(redeem_scripts.size() == amounts.size()); using data = std::pair; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp index 41808562..34f8eb89 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp @@ -8,23 +8,23 @@ namespace graphene { namespace peerplays_sidechain { namespace bitcoin { class bitcoin_transaction; -const secp256k1_context_t *btc_context(); +const secp256k1_context *btc_context(); fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptPubKey, int64_t amount, size_t in_index, int hash_type, bool is_witness); -std::vector privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign = nullptr); +std::vector privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context *context_sign = nullptr); std::vector sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector &redeem_scripts, const std::vector &amounts, const bytes &privkey, - const secp256k1_context_t *context_sign = nullptr, int hash_type = 1); + const secp256k1_context *context_sign = nullptr, int hash_type = 1); void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector &redeem_scripts, bool use_mulisig_workaround = true); -bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context); +bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context *context); std::vector> sort_sigs(const bitcoin_transaction &tx, const std::vector &redeem_scripts, - const std::vector &amounts, const secp256k1_context_t *context); + const std::vector &amounts, const secp256k1_context *context); void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector> &signature_set); diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 02790e19..185db897 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -68,80 +68,12 @@ enum authority_type active }; -/** - * Contains the confirmation receipt the sender must give the receiver and - * the meta data about the receipt that helps the sender identify which receipt is - * for the receiver and which is for the change address. - */ -struct blind_confirmation -{ - struct output - { - string label; - public_key_type pub_key; - stealth_confirmation::memo_data decrypted_memo; - stealth_confirmation confirmation; - authority auth; - string confirmation_receipt; - }; - - signed_transaction trx; - vector outputs; -}; - -struct blind_balance -{ - asset amount; - public_key_type from; ///< the account this balance came from - public_key_type to; ///< the account this balance is logically associated with - public_key_type one_time_key; ///< used to derive the authority key and blinding factor - fc::sha256 blinding_factor; - fc::ecc::commitment_type commitment; - bool used = false; -}; - -struct blind_receipt -{ - std::pair from_date()const { return std::make_pair(from_key,date); } - std::pair to_date()const { return std::make_pair(to_key,date); } - std::tuple to_asset_used()const { return std::make_tuple(to_key,amount.asset_id,used); } - const commitment_type& commitment()const { return data.commitment; } - - fc::time_point date; - public_key_type from_key; - string from_label; - public_key_type to_key; - string to_label; - asset amount; - string memo; - authority control_authority; - stealth_confirmation::memo_data data; - bool used = false; - stealth_confirmation conf; -}; - -struct by_from; -struct by_to; -struct by_to_asset_used; -struct by_commitment; - -typedef multi_index_container< blind_receipt, - indexed_by< - ordered_unique< tag, const_mem_fun< blind_receipt, const commitment_type&, &blind_receipt::commitment > >, - ordered_unique< tag, const_mem_fun< blind_receipt, std::pair, &blind_receipt::to_date > >, - ordered_non_unique< tag, const_mem_fun< blind_receipt, std::tuple, &blind_receipt::to_asset_used > >, - ordered_unique< tag, const_mem_fun< blind_receipt, std::pair, &blind_receipt::from_date > > - > -> blind_receipt_index_type; - - struct key_label { string label; public_key_type key; }; - struct by_label; struct by_key; typedef multi_index_container< @@ -195,7 +127,6 @@ struct wallet_data map pending_witness_registrations; key_label_index_type labeled_keys; - blind_receipt_index_type blind_receipts; std::map committed_game_moves; @@ -872,74 +803,10 @@ class wallet_api bool set_key_label( public_key_type, string label ); string get_key_label( public_key_type )const; - /** - * Generates a new blind account for the given brain key and assigns it the given label. - */ - public_key_type create_blind_account( string label, string brain_key ); - - /** - * @return the total balance of all blinded commitments that can be claimed by the - * given account key or label - */ - vector get_blind_balances( string key_or_label ); - /** @return all blind accounts */ - map get_blind_accounts()const; - /** @return all blind accounts for which this wallet has the private key */ - map get_my_blind_accounts()const; /** @return the public key associated with the given label */ public_key_type get_public_key( string label )const; ///@} - /** - * @return all blind receipts to/form a particular account - */ - vector blind_history( string key_or_account ); - - /** - * Given a confirmation receipt, this method will parse it for a blinded balance and confirm - * that it exists in the blockchain. If it exists then it will report the amount received and - * who sent it. - * - * @param confirmation_receipt - a base58 encoded stealth confirmation - * @param opt_from - if not empty and the sender is a unknown public key, then the unknown public key will be given the label opt_from - * @param opt_memo - optional memo - */ - blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ); - - /** - * Transfers a public balance from from_account_id_or_name to one or more blinded balances using a - * stealth transfer. - * - * @param from_account_id_or_name account id or name - * @param asset_symbol asset symbol - * @param to_amounts map from key or label to amount - * @param broadcast true to broadcast the transaction on the network - * @returns blind confirmation structure - */ - blind_confirmation transfer_to_blind( string from_account_id_or_name, - string asset_symbol, - vector> to_amounts, - bool broadcast = false ); - - /** - * Transfers funds from a set of blinded balances to a public account balance. - */ - blind_confirmation transfer_from_blind( - string from_blind_account_key_or_label, - string to_account_id_or_name, - string amount, - string asset_symbol, - bool broadcast = false ); - - /** - * Used to transfer from one set of blinded balances to another - */ - blind_confirmation blind_transfer( string from_key_or_label, - string to_key_or_label, - string amount, - string symbol, - bool broadcast = false ); - /** Place a limit order attempting to sell one asset for another. * * Buying and selling are the same operation on Graphene; if you want to buy BTS @@ -2547,16 +2414,6 @@ class wallet_api void network_add_nodes( const vector& nodes ); vector< variant > network_get_connected_peers(); - /** - * Used to transfer from one set of blinded balances to another - */ - blind_confirmation blind_transfer_help( string from_key_or_label, - string to_key_or_label, - string amount, - string symbol, - bool broadcast = false, - bool to_temp = false ); - std::map> get_result_formatters() const; /** @@ -2607,9 +2464,6 @@ class wallet_api extern template class fc::api; FC_REFLECT( graphene::wallet::key_label, (label)(key) ) -FC_REFLECT( graphene::wallet::blind_balance, (amount)(from)(to)(one_time_key)(blinding_factor)(commitment)(used) ) -FC_REFLECT( graphene::wallet::blind_confirmation::output, (label)(pub_key)(decrypted_memo)(confirmation)(auth)(confirmation_receipt) ) -FC_REFLECT( graphene::wallet::blind_confirmation, (trx)(outputs) ) FC_REFLECT( graphene::wallet::plain_keys, (keys)(checksum) ) @@ -2620,7 +2474,6 @@ FC_REFLECT( graphene::wallet::wallet_data, (extra_keys) (pending_account_registrations)(pending_witness_registrations) (labeled_keys) - (blind_receipts) (committed_game_moves) (ws_server) (ws_user) @@ -2639,9 +2492,6 @@ FC_REFLECT( graphene::wallet::exported_account_keys, (account_name)(encrypted_pr FC_REFLECT( graphene::wallet::exported_keys, (password_checksum)(account_keys) ) -FC_REFLECT( graphene::wallet::blind_receipt, - (date)(from_key)(from_label)(to_key)(to_label)(amount)(memo)(control_authority)(data)(used)(conf) ) - FC_REFLECT( graphene::wallet::approval_delta, (active_approvals_to_add) (active_approvals_to_remove) @@ -2814,15 +2664,6 @@ FC_API( graphene::wallet::wallet_api, (set_key_label) (get_key_label) (get_public_key) - (get_blind_accounts) - (get_my_blind_accounts) - (get_blind_balances) - (create_blind_account) - (transfer_to_blind) - (transfer_from_blind) - (blind_transfer) - (blind_history) - (receive_blind_transfer) (list_sports) (list_event_groups) (list_betting_market_groups) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 35b9ab1c..09aa5bdc 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -134,8 +134,6 @@ public: std::string operator()(const T& op)const; std::string operator()(const transfer_operation& op)const; - std::string operator()(const transfer_from_blind_operation& op)const; - std::string operator()(const transfer_to_blind_operation& op)const; std::string operator()(const account_create_operation& op)const; std::string operator()(const account_update_operation& op)const; std::string operator()(const asset_create_operation& op)const; @@ -3472,70 +3470,6 @@ public: return ss.str(); }; - - m["get_blind_balances"] = [this](variant result, const fc::variants& a) - { - auto r = result.as>( GRAPHENE_MAX_NESTED_OBJECTS ); - vector asset_recs; - std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) { - return get_asset(a.asset_id); - }); - - std::stringstream ss; - for( unsigned i = 0; i < asset_recs.size(); ++i ) - ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n"; - - return ss.str(); - }; - m["transfer_to_blind"] = [this](variant result, const fc::variants& a) - { - auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); - std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); - ss << "\n"; - for( const auto& out : r.outputs ) - { - asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); - ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; - } - return ss.str(); - }; - m["blind_transfer"] = [this](variant result, const fc::variants& a) - { - auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); - std::stringstream ss; - r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) ); - ss << "\n"; - for( const auto& out : r.outputs ) - { - asset_object a = get_asset( out.decrypted_memo.amount.asset_id ); - ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n"; - } - return ss.str(); - }; - m["receive_blind_transfer"] = [this](variant result, const fc::variants& a) - { - auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); - std::stringstream ss; - asset_object as = get_asset( r.amount.asset_id ); - ss << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; - return ss.str(); - }; - m["blind_history"] = [this](variant result, const fc::variants& a) - { - auto records = result.as>( GRAPHENE_MAX_NESTED_OBJECTS ); - std::stringstream ss; - ss << "WHEN " - << " " << "AMOUNT" << " " << "FROM" << " => " << "TO" << " " << "MEMO" <<"\n"; - ss << "====================================================================================\n"; - for( auto& r : records ) - { - asset_object as = get_asset( r.amount.asset_id ); - ss << fc::get_approximate_relative_time_string( r.date ) - << " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; - } - return ss.str(); - }; m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a) { const vector tournaments = result.as >( GRAPHENE_MAX_NESTED_OBJECTS ); @@ -4396,26 +4330,6 @@ std::string operation_printer::operator()(const T& op)const out << " result: " << str_result; return ""; } -std::string operation_printer::operator()(const transfer_from_blind_operation& op)const -{ - auto a = wallet.get_asset( op.fee.asset_id ); - auto receiver = wallet.get_account( op.to ); - - out << receiver.name - << " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance"; - return ""; -} -std::string operation_printer::operator()(const transfer_to_blind_operation& op)const -{ - auto fa = wallet.get_asset( op.fee.asset_id ); - auto a = wallet.get_asset( op.amount.asset_id ); - auto sender = wallet.get_account( op.from ); - - out << sender.name - << " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"") - << " fee: " << fa.amount_to_pretty_string( op.fee ); - return ""; -} string operation_printer::operator()(const transfer_operation& op) const { out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) @@ -6186,495 +6100,6 @@ bool wallet_api::set_key_label( public_key_type key, string label } return false; } -map wallet_api::get_blind_accounts()const -{ - map result; - for( const auto& item : my->_wallet.labeled_keys ) - result[item.label] = item.key; - return result; -} -map wallet_api::get_my_blind_accounts()const -{ - FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); - map result; - for( const auto& item : my->_wallet.labeled_keys ) - { - if( my->_keys.find(item.key) != my->_keys.end() ) - result[item.label] = item.key; - } - return result; -} - -public_key_type wallet_api::create_blind_account( string label, string brain_key ) -{ - FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); - - auto label_itr = my->_wallet.labeled_keys.get().find(label); - if( label_itr != my->_wallet.labeled_keys.get().end() ) - FC_ASSERT( !"Key with label already exists" ); - brain_key = fc::trim_and_normalize_spaces( brain_key ); - auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() ); - auto priv_key = fc::ecc::private_key::regenerate( secret ); - public_key_type pub_key = priv_key.get_public_key(); - - FC_ASSERT( set_key_label( pub_key, label ) ); - - my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key ); - - save_wallet_file(); - return pub_key; -} - -vector wallet_api::get_blind_balances( string key_or_label ) -{ - vector result; - map balances; - - vector used; - - auto pub_key = get_public_key( key_or_label ); - auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); - auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false) ); - auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true) ); - while( start != end ) - { - if( !start->used ) - { - auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} ); - if( answer.size() ) - balances[start->amount.asset_id] += start->amount.amount; - else - used.push_back( start->commitment() ); - } - ++start; - } - for( const auto& u : used ) - { - auto itr = my->_wallet.blind_receipts.get().find( u ); - my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); - } - for( auto item : balances ) - result.push_back( asset( item.second, item.first ) ); - return result; -} - -blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label, - string to_account_id_or_name, - string amount_in, - string symbol, - bool broadcast ) -{ try { - transfer_from_blind_operation from_blind; - - - auto fees = my->_remote_db->get_global_properties().parameters.current_fees; - fc::optional asset_obj = get_asset(symbol); - FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); - auto amount = asset_obj->amount_from_string(amount_in); - - from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); - - auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount ); - - - auto conf = blind_transfer_help( from_blind_account_key_or_label, - from_blind_account_key_or_label, - blind_in, symbol, false, true/*to_temp*/ ); - FC_ASSERT( conf.outputs.size() > 0 ); - - auto to_account = my->get_account( to_account_id_or_name ); - from_blind.to = to_account.id; - from_blind.amount = amount; - from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor; - from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } ); - from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate ); - - idump( (from_blind) ); - conf.trx.operations.push_back(from_blind); - ilog( "about to validate" ); - conf.trx.validate(); - - if( broadcast && conf.outputs.size() == 2 ) { - - // Save the change - blind_confirmation::output conf_output; - blind_confirmation::output change_output = conf.outputs[0]; - - // The wallet must have a private key for confirmation.to, this is used to decrypt the memo - public_key_type from_key = get_public_key(from_blind_account_key_or_label); - conf_output.confirmation.to = from_key; - conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key; - conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo; - conf_output.confirmation_receipt = conf_output.confirmation; - //try { - receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name ); - //} catch ( ... ){} - } - - ilog( "about to broadcast" ); - conf.trx = sign_transaction( conf.trx, broadcast ); - - return conf; -} FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) } - -blind_confirmation wallet_api::blind_transfer( string from_key_or_label, - string to_key_or_label, - string amount_in, - string symbol, - bool broadcast ) -{ - return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false ); -} -blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, - string to_key_or_label, - string amount_in, - string symbol, - bool broadcast, - bool to_temp ) -{ - blind_confirmation confirm; - try { - - FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); - public_key_type from_key = get_public_key(from_key_or_label); - public_key_type to_key = get_public_key(to_key_or_label); - - fc::optional asset_obj = get_asset(symbol); - FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol)); - - blind_transfer_operation blind_tr; - blind_tr.outputs.resize(2); - - auto fees = my->_remote_db->get_global_properties().parameters.current_fees; - - auto amount = asset_obj->amount_from_string(amount_in); - - asset total_amount = asset_obj->amount(0); - - vector blinding_factors; - - //auto from_priv_key = my->get_private_key( from_key ); - - blind_tr.fee = fees->calculate_fee( blind_tr, asset_obj->options.core_exchange_rate ); - - vector used; - - auto& to_asset_used_idx = my->_wallet.blind_receipts.get(); - auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false) ); - auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true) ); - while( start != end ) - { - auto result = my->_remote_db->get_blinded_balances( {start->commitment() } ); - if( result.size() == 0 ) - { - used.push_back( start->commitment() ); - } - else - { - blind_tr.inputs.push_back({start->commitment(), start->control_authority}); - blinding_factors.push_back( start->data.blinding_factor ); - total_amount += start->amount; - - if( total_amount >= amount + blind_tr.fee ) - break; - } - ++start; - } - for( const auto& u : used ) - { - auto itr = my->_wallet.blind_receipts.get().find( u ); - my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); - } - - FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficent Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) ); - - auto one_time_key = fc::ecc::private_key::generate(); - auto secret = one_time_key.get_shared_secret( to_key ); - auto child = fc::sha256::hash( secret ); - auto nonce = fc::sha256::hash( one_time_key.get_secret() ); - auto blind_factor = fc::sha256::hash( child ); - - auto from_secret = one_time_key.get_shared_secret( from_key ); - auto from_child = fc::sha256::hash( from_secret ); - auto from_nonce = fc::sha256::hash( nonce ); - - auto change = total_amount - amount - blind_tr.fee; - fc::sha256 change_blind_factor; - fc::sha256 to_blind_factor; - if( change.amount > 0 ) - { - idump(("to_blind_factor")(blind_factor) ); - blinding_factors.push_back( blind_factor ); - change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 ); - wdump(("change_blind_factor")(change_blind_factor) ); - } - else // change == 0 - { - blind_tr.outputs.resize(1); - blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); - idump(("to_sum_blind_factor")(blind_factor) ); - blinding_factors.push_back( blind_factor ); - idump(("nochange to_blind_factor")(blind_factor) ); - } - fc::ecc::public_key from_pub_key = from_key; - fc::ecc::public_key to_pub_key = to_key; - - blind_output to_out; - to_out.owner = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); - to_out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); - idump(("to_out.blind")(blind_factor)(to_out.commitment) ); - - - if( blind_tr.outputs.size() > 1 ) - { - to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); - - blind_output change_out; - change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 ); - change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value ); - change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, 0, change.amount.value ); - blind_tr.outputs[1] = change_out; - - - blind_confirmation::output conf_output; - conf_output.label = from_key_or_label; - conf_output.pub_key = from_key; - conf_output.decrypted_memo.from = from_key; - conf_output.decrypted_memo.amount = change; - conf_output.decrypted_memo.blinding_factor = change_blind_factor; - conf_output.decrypted_memo.commitment = change_out.commitment; - conf_output.decrypted_memo.check = from_secret._hash[0]; - conf_output.confirmation.one_time_key = one_time_key.get_public_key(); - conf_output.confirmation.to = from_key; - conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) ); - conf_output.auth = change_out.owner; - conf_output.confirmation_receipt = conf_output.confirmation; - - confirm.outputs.push_back( conf_output ); - } - blind_tr.outputs[0] = to_out; - - blind_confirmation::output conf_output; - conf_output.label = to_key_or_label; - conf_output.pub_key = to_key; - conf_output.decrypted_memo.from = from_key; - conf_output.decrypted_memo.amount = amount; - conf_output.decrypted_memo.blinding_factor = blind_factor; - conf_output.decrypted_memo.commitment = to_out.commitment; - conf_output.decrypted_memo.check = secret._hash[0]; - conf_output.confirmation.one_time_key = one_time_key.get_public_key(); - conf_output.confirmation.to = to_key; - conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); - conf_output.auth = to_out.owner; - conf_output.confirmation_receipt = conf_output.confirmation; - - confirm.outputs.push_back( conf_output ); - - /** commitments must be in sorted order */ - std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(), - [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); - std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(), - [&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } ); - - confirm.trx.operations.emplace_back( std::move(blind_tr) ); - ilog( "validate before" ); - confirm.trx.validate(); - confirm.trx = sign_transaction(confirm.trx, broadcast); - - if( broadcast ) - { - for( const auto& out : confirm.outputs ) - { - try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){} - } - } - - return confirm; -} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) } - - - -/** - * Transfers a public balance from @from to one or more blinded balances using a - * stealth transfer. - */ -blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, - string asset_symbol, - /** map from key or label to amount */ - vector> to_amounts, - bool broadcast ) -{ try { - FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); - idump((to_amounts)); - - blind_confirmation confirm; - account_object from_account = my->get_account(from_account_id_or_name); - - fc::optional asset_obj = get_asset(asset_symbol); - FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); - - transfer_to_blind_operation bop; - bop.from = from_account.id; - - vector blinding_factors; - - asset total_amount = asset_obj->amount(0); - - for( auto item : to_amounts ) - { - auto one_time_key = fc::ecc::private_key::generate(); - auto to_key = get_public_key( item.first ); - auto secret = one_time_key.get_shared_secret( to_key ); - auto child = fc::sha256::hash( secret ); - auto nonce = fc::sha256::hash( one_time_key.get_secret() ); - auto blind_factor = fc::sha256::hash( child ); - - blinding_factors.push_back( blind_factor ); - - auto amount = asset_obj->amount_from_string(item.second); - total_amount += amount; - - - fc::ecc::public_key to_pub_key = to_key; - blind_output out; - out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); - out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); - if( to_amounts.size() > 1 ) - out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); - - - blind_confirmation::output conf_output; - conf_output.label = item.first; - conf_output.pub_key = to_key; - conf_output.decrypted_memo.amount = amount; - conf_output.decrypted_memo.blinding_factor = blind_factor; - conf_output.decrypted_memo.commitment = out.commitment; - conf_output.decrypted_memo.check = secret._hash[0]; - conf_output.confirmation.one_time_key = one_time_key.get_public_key(); - conf_output.confirmation.to = to_key; - conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); - conf_output.confirmation_receipt = conf_output.confirmation; - - confirm.outputs.push_back( conf_output ); - - bop.outputs.push_back(out); - } - bop.amount = total_amount; - bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); - - /** commitments must be in sorted order */ - std::sort( bop.outputs.begin(), bop.outputs.end(), - [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); - - confirm.trx.operations.push_back( bop ); - my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.current_fees); - confirm.trx.validate(); - confirm.trx = sign_transaction(confirm.trx, broadcast); - - if( broadcast ) - { - for( const auto& out : confirm.outputs ) - { - try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){} - } - } - - return confirm; -} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) } - -blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ) -{ - FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); - stealth_confirmation conf(confirmation_receipt); - FC_ASSERT( conf.to ); - - blind_receipt result; - result.conf = conf; - - auto to_priv_key_itr = my->_keys.find( *conf.to ); - FC_ASSERT( to_priv_key_itr != my->_keys.end(), "No private key for receiver", ("conf",conf) ); - - - auto to_priv_key = wif_to_key( to_priv_key_itr->second ); - FC_ASSERT( to_priv_key ); - - auto secret = to_priv_key->get_shared_secret( conf.one_time_key ); - auto child = fc::sha256::hash( secret ); - - auto child_priv_key = to_priv_key->child( child ); - //auto blind_factor = fc::sha256::hash( child ); - - auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo ); - auto memo = fc::raw::unpack( plain_memo ); - - result.to_key = *conf.to; - result.to_label = get_key_label( result.to_key ); - if( memo.from ) - { - result.from_key = *memo.from; - result.from_label = get_key_label( result.from_key ); - if( result.from_label == string() ) - { - result.from_label = opt_from; - set_key_label( result.from_key, result.from_label ); - } - } - else - { - result.from_label = opt_from; - } - result.amount = memo.amount; - result.memo = opt_memo; - - // confirm the amount matches the commitment (verify the blinding factor) - auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value ); - FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) ); - - blind_balance bal; - bal.amount = memo.amount; - bal.to = *conf.to; - if( memo.from ) bal.from = *memo.from; - bal.one_time_key = conf.one_time_key; - bal.blinding_factor = memo.blinding_factor; - bal.commitment = memo.commitment; - bal.used = false; - - auto child_pubkey = child_priv_key.get_public_key(); - auto owner = authority(1, public_key_type(child_pubkey), 1); - result.control_authority = owner; - result.data = memo; - - auto child_key_itr = owner.key_auths.find( child_pubkey ); - if( child_key_itr != owner.key_auths.end() ) - my->_keys[child_key_itr->first] = key_to_wif( child_priv_key ); - - // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal ); - - result.date = fc::time_point::now(); - my->_wallet.blind_receipts.insert( result ); - my->_keys[child_pubkey] = key_to_wif( child_priv_key ); - - save_wallet_file(); - - return result; -} - -vector wallet_api::blind_history( string key_or_account ) -{ - vector result; - auto pub_key = get_public_key( key_or_account ); - - if( pub_key == public_key_type() ) - return vector(); - - for( auto& r : my->_wallet.blind_receipts ) - { - if( r.from_key == pub_key || r.to_key == pub_key ) - result.push_back( r ); - } - std::sort( result.begin(), result.end(), [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } ); - return result; -} /////////////// // peerplays // diff --git a/tests/peerplays_sidechain/bitcoin_sign_tests.cpp b/tests/peerplays_sidechain/bitcoin_sign_tests.cpp index 82a121c9..c2b5908a 100644 --- a/tests/peerplays_sidechain/bitcoin_sign_tests.cpp +++ b/tests/peerplays_sidechain/bitcoin_sign_tests.cpp @@ -13,8 +13,8 @@ using namespace fc::ecc; BOOST_AUTO_TEST_SUITE(bitcoin_sign_tests) -const secp256k1_context_t *btc_context() { - static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); +const secp256k1_context *btc_context() { + static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); return ctx; } diff --git a/tests/tests/confidential_tests.cpp b/tests/tests/confidential_tests.cpp deleted file mode 100644 index 792a300e..00000000 --- a/tests/tests/confidential_tests.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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 - -#include -#include -#include -#include - -#include - -#include -#include "../common/database_fixture.hpp" - -using namespace graphene::chain; - -BOOST_FIXTURE_TEST_SUITE( confidential_tests, database_fixture ) -BOOST_AUTO_TEST_CASE( confidential_test ) -{ try { - ACTORS( (dan)(nathan) ) - const asset_object& core = asset_id_type()(db); - - transfer(account_id_type()(db), dan, core.amount(1000000)); - - transfer_to_blind_operation to_blind; - to_blind.amount = core.amount(1000); - to_blind.from = dan.id; - - auto owner1_key = fc::ecc::private_key::generate(); - auto owner1_pub = owner1_key.get_public_key(); - auto owner2_key = fc::ecc::private_key::generate(); - auto owner2_pub = owner2_key.get_public_key(); - - blind_output out1, out2; - out1.owner = authority( 1, public_key_type(owner1_pub), 1 ); - out2.owner = authority( 1, public_key_type(owner2_pub), 1 ); - - - auto InB1 = fc::sha256::hash("InB1"); - auto InB2 = fc::sha256::hash("InB2"); - auto nonce1 = fc::sha256::hash("nonce"); - auto nonce2 = fc::sha256::hash("nonce2"); - - out1.commitment = fc::ecc::blind(InB1,250); - out1.range_proof = fc::ecc::range_proof_sign( 0, out1.commitment, InB1, nonce1, 0, 0, 250 ); - - out2.commitment = fc::ecc::blind(InB2,750); - out2.range_proof = fc::ecc::range_proof_sign( 0, out2.commitment, InB1, nonce2, 0, 0, 750 ); - - to_blind.blinding_factor = fc::ecc::blind_sum( {InB1,InB2}, 2 ); - to_blind.outputs = {out2,out1}; - - trx.operations = {to_blind}; - sign( trx, dan_private_key ); - db.push_transaction(trx); - trx.clear_signatures(); - - BOOST_TEST_MESSAGE( "Transfering from blind to blind with change address" ); - auto Out3B = fc::sha256::hash("Out3B"); - auto Out4B = fc::ecc::blind_sum( {InB2,Out3B}, 1 ); // add InB2 - Out3b - blind_output out3, out4; - out3.commitment = fc::ecc::blind(Out3B,300); - out3.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 300 ); - out4.commitment = fc::ecc::blind(Out4B,750-300-10); - out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-10 ); - - - blind_transfer_operation blind_tr; - blind_tr.fee = core.amount(10); - blind_tr.inputs.push_back( {out2.commitment, out2.owner} ); - blind_tr.outputs = {out3,out4}; - blind_tr.validate(); - trx.operations = {blind_tr}; - sign( trx, owner2_key ); - db.push_transaction(trx); - - BOOST_TEST_MESSAGE( "Attempting to double spend the same commitments" ); - blind_tr.fee = core.amount(11); - - Out4B = fc::ecc::blind_sum( {InB2,Out3B}, 1 ); // add InB2 - Out3b - out4.commitment = fc::ecc::blind(Out4B,750-300-11); - auto out4_amount = 750-300-10; - out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-11 ); - blind_tr.outputs = {out4,out3}; - trx.operations = {blind_tr}; - BOOST_REQUIRE_THROW( db.push_transaction(trx, ~0), graphene::chain::blind_transfer_unknown_commitment ); - - - BOOST_TEST_MESSAGE( "Transfering from blind to nathan public" ); - out4.commitment = fc::ecc::blind(Out4B,750-300-10); - - transfer_from_blind_operation from_blind; - from_blind.fee = core.amount(10); - from_blind.to = nathan.id; - from_blind.amount = core.amount( out4_amount - 10 ); - from_blind.blinding_factor = Out4B; - from_blind.inputs.push_back( {out4.commitment, out4.owner} ); - trx.operations = {from_blind}; - trx.clear_signatures(); - db.push_transaction(trx); - - BOOST_REQUIRE_EQUAL( get_balance( nathan, core ), 750-300-10-10 ); - -} FC_LOG_AND_RETHROW() } - - - -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 86dd3d7f..5faf4038 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -741,212 +741,6 @@ BOOST_AUTO_TEST_CASE( account_create_fee_scaling ) // FC_LOG_AND_RETHROW() // } -BOOST_AUTO_TEST_CASE( stealth_fba_test ) -{ - try - { - ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin)(tom) ); - upgrade_to_lifetime_member(philbin_id); - - generate_blocks( HARDFORK_538_TIME ); - generate_blocks( HARDFORK_555_TIME ); - generate_blocks( HARDFORK_563_TIME ); - generate_blocks( HARDFORK_572_TIME ); - generate_blocks( HARDFORK_599_TIME ); - - // Philbin (registrar who registers Rex) - - // Izzy (initial issuer of stealth asset, will later transfer to Tom) - // Alice, Bob, Chloe, Dan (ABCD) - // Rex (recycler -- buyback account for stealth asset) - // Tom (owner of stealth asset who will be set as top_n authority) - - // Izzy creates STEALTH - asset_id_type stealth_id = create_user_issued_asset( "STEALTH", izzy_id(db), - disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee ).id; - - /* - // this is disabled because it doesn't work, our modify() is probably being overwritten by undo - - // - // Init blockchain with stealth ID's - // On a real chain, this would be done with #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET - // causing the designated_asset fields of these objects to be set at genesis, but for - // this test we modify the db directly. - // - auto set_fba_asset = [&]( uint64_t fba_acc_id, asset_id_type asset_id ) - { - db.modify( fba_accumulator_id_type(fba_acc_id)(db), [&]( fba_accumulator_object& fba ) - { - fba.designated_asset = asset_id; - } ); - }; - - set_fba_asset( fba_accumulator_id_transfer_to_blind , stealth_id ); - set_fba_asset( fba_accumulator_id_blind_transfer , stealth_id ); - set_fba_asset( fba_accumulator_id_transfer_from_blind, stealth_id ); - */ - - // Izzy kills some permission bits (this somehow happened to the real STEALTH in production) - { - asset_update_operation update_op; - update_op.issuer = izzy_id; - update_op.asset_to_update = stealth_id; - asset_options new_options; - new_options = stealth_id(db).options; - new_options.issuer_permissions = charge_market_fee; - new_options.flags = disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee; - // after fixing #579 you should be able to delete the following line - new_options.core_exchange_rate = price( asset( 1, stealth_id ), asset( 1, asset_id_type() ) ); - update_op.new_options = new_options; - signed_transaction tx; - tx.operations.push_back( update_op ); - set_expiration( db, tx ); - sign( tx, izzy_private_key ); - PUSH_TX( db, tx ); - } - - // Izzy transfers issuer duty to Tom - { - asset_update_operation update_op; - update_op.issuer = izzy_id; - update_op.asset_to_update = stealth_id; - update_op.new_issuer = tom_id; - // new_options should be optional, but isn't...the following line should be unnecessary #580 - update_op.new_options = stealth_id(db).options; - signed_transaction tx; - tx.operations.push_back( update_op ); - set_expiration( db, tx ); - sign( tx, izzy_private_key ); - PUSH_TX( db, tx ); - } - - // Tom re-enables the permission bits to clear the flags, then clears them again - // Allowed by #572 when current_supply == 0 - { - asset_update_operation update_op; - update_op.issuer = tom_id; - update_op.asset_to_update = stealth_id; - asset_options new_options; - new_options = stealth_id(db).options; - new_options.issuer_permissions = new_options.flags | charge_market_fee; - update_op.new_options = new_options; - signed_transaction tx; - // enable perms is one op - tx.operations.push_back( update_op ); - - new_options.issuer_permissions = charge_market_fee; - new_options.flags = charge_market_fee; - update_op.new_options = new_options; - // reset wrongly set flags and reset permissions can be done in a single op - tx.operations.push_back( update_op ); - - set_expiration( db, tx ); - sign( tx, tom_private_key ); - PUSH_TX( db, tx ); - } - - // Philbin registers Rex who will be the asset's buyback, including sig from the new issuer (Tom) - account_id_type rex_id; - { - buyback_account_options bbo; - bbo.asset_to_buy = stealth_id; - bbo.asset_to_buy_issuer = tom_id; - bbo.markets.emplace( asset_id_type() ); - account_create_operation create_op = make_account( "rex" ); - create_op.registrar = philbin_id; - create_op.extensions.value.buyback_options = bbo; - create_op.owner = authority::null_authority(); - create_op.active = authority::null_authority(); - - signed_transaction tx; - tx.operations.push_back( create_op ); - set_expiration( db, tx ); - sign( tx, philbin_private_key ); - sign( tx, tom_private_key ); - - processed_transaction ptx = PUSH_TX( db, tx ); - rex_id = ptx.operation_results.back().get< object_id_type >(); - } - - // Tom issues some asset to Alice and Bob - set_expiration( db, trx ); // #11 - issue_uia( alice_id, asset( 1000, stealth_id ) ); - issue_uia( bob_id, asset( 1000, stealth_id ) ); - - // Tom sets his authority to the top_n of the asset - { - top_holders_special_authority top2; - top2.num_top_holders = 2; - top2.asset = stealth_id; - - account_update_operation op; - op.account = tom_id; - op.extensions.value.active_special_authority = top2; - op.extensions.value.owner_special_authority = top2; - - signed_transaction tx; - tx.operations.push_back( op ); - - set_expiration( db, tx ); - sign( tx, tom_private_key ); - - PUSH_TX( db, tx ); - } - - // Wait until the next maintenance interval for top_n to take effect - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - // Do a blind op to add some fees to the pool. - fund( chloe_id(db), asset( 100000, asset_id_type() ) ); - - auto create_transfer_to_blind = [&]( account_id_type account, asset amount, const std::string& key ) -> transfer_to_blind_operation - { - fc::ecc::private_key blind_key = fc::ecc::private_key::regenerate( fc::sha256::hash( key+"-privkey" ) ); - public_key_type blind_pub = blind_key.get_public_key(); - - fc::sha256 secret = fc::sha256::hash( key+"-secret" ); - fc::sha256 nonce = fc::sha256::hash( key+"-nonce" ); - - transfer_to_blind_operation op; - blind_output blind_out; - blind_out.owner = authority( 1, blind_pub, 1 ); - blind_out.commitment = fc::ecc::blind( secret, amount.amount.value ); - blind_out.range_proof = fc::ecc::range_proof_sign( 0, blind_out.commitment, secret, nonce, 0, 0, amount.amount.value ); - - op.amount = amount; - op.from = account; - op.blinding_factor = fc::ecc::blind_sum( {secret}, 1 ); - op.outputs = {blind_out}; - - return op; - }; - - { - transfer_to_blind_operation op = create_transfer_to_blind( chloe_id, asset( 5000, asset_id_type() ), "chloe-key" ); - op.fee = asset( 1000, asset_id_type() ); - - signed_transaction tx; - tx.operations.push_back( op ); - set_expiration( db, tx ); - sign( tx, chloe_private_key ); - - PUSH_TX( db, tx ); - } - - // wait until next maint interval - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - - idump( ( get_operation_history( chloe_id ) ) ); - idump( ( get_operation_history( rex_id ) ) ); - idump( ( get_operation_history( tom_id ) ) ); - } - catch( const fc::exception& e ) - { - elog( "caught exception ${e}", ("e", e.to_detail_string()) ); - throw; - } -} // added test from bitshares for issues: // https://github.com/bitshares/bitshares-core/issues/429 // https://github.com/bitshares/bitshares-core/issues/433 From 99ed37e834a8eb86dc7d646790fed67a3de58bf1 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Fri, 15 Jul 2022 17:59:25 +0000 Subject: [PATCH 036/106] #400 - fix verify_sig function. --- .../bitcoin/sign_bitcoin_transaction.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp index e40ea329..893f82aa 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp @@ -73,7 +73,18 @@ void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vecto } bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context *context) { - std::vector sig_temp(sig.begin(), sig.end()); + //! Get sig_temp + FC_ASSERT(sig.size() > 70); + FC_ASSERT(sig[0] == 0x30); + FC_ASSERT(sig[1] == static_cast(sig.size()-3)); + FC_ASSERT(sig[2] == 0x02); + const uint r_size = sig[3]; + std::vector sig_temp(sig.begin()+4+(r_size-32), sig.begin()+4+r_size); + FC_ASSERT(sig[4+r_size] == 0x02); + const uint s_size = sig[5+r_size]; + FC_ASSERT(sig.size() == r_size+s_size+7); + sig_temp.insert(sig_temp.end(), sig.begin()+6+r_size, sig.end()); + std::vector pubkey_temp(pubkey.begin(), pubkey.end()); std::vector msg_temp(msg.begin(), msg.end()); @@ -81,7 +92,7 @@ bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const s FC_ASSERT(secp256k1_ec_pubkey_parse(context, &pub_key, (const unsigned char *)pubkey_temp.data(), pubkey_temp.size())); secp256k1_ecdsa_signature sign; - FC_ASSERT(secp256k1_ecdsa_signature_parse_der(context, &sign, (const unsigned char *)sig_temp.data(), sig_temp.size())); + FC_ASSERT(secp256k1_ecdsa_signature_parse_compact(context, &sign, (const unsigned char *)sig_temp.data())); int result = secp256k1_ecdsa_verify(context, &sign, (const unsigned char *)msg_temp.data(), &pub_key); return result == 1; From 662139ca22c0f683309b0449cd4ea1f57db3174a Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 21 Jul 2022 20:51:53 +0200 Subject: [PATCH 037/106] Fix invalid result of nft_get_total_supply (#399) --- libraries/app/database_api.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 250413ee..6ffee911 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2949,8 +2949,8 @@ uint64_t database_api::nft_get_total_supply(const nft_metadata_id_type nft_metad } uint64_t database_api_impl::nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const { - const auto &idx_nft_md = _db.get_index_type().indices().get(); - return idx_nft_md.size(); + const auto &idx_nft = _db.get_index_type().indices().get(); + return idx_nft.count(nft_metadata_id); } nft_object database_api::nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const { From 5bfd6856847350d6bb71655ef63cb945ba7ff1a3 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Tue, 26 Jul 2022 13:47:16 +0000 Subject: [PATCH 038/106] Boost Beast based RPC client --- .../peerplays_sidechain/common/rpc_client.cpp | 1091 +++------------- .../peerplays_sidechain/common/rpc_client.hpp | 129 +- .../sidechain_net_handler_bitcoin.hpp | 24 +- .../sidechain_net_handler_bitcoin.cpp | 1143 +++-------------- 4 files changed, 346 insertions(+), 2041 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index d08d337b..173019eb 100644 --- a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp @@ -1,883 +1,110 @@ #include +#include #include #include -//#include - -#include -#include -#include #include #include +#include + +#include +#include +#include +#include +#include + +#include #include #include -#include namespace graphene { namespace peerplays_sidechain { -constexpr auto http_port = 80; -constexpr auto https_port = 443; - -template -void make_trimmed(string *str) { - boost::algorithm::trim(*str); -} - -template -void make_lower(string *str) { - boost::algorithm::to_lower(*str); -} - -bool convert_hex_to_num_helper1(const std::string &str, uint32_t *value) { - try { - size_t idx; - auto v = stol(str, &idx, 16); - if (idx != str.size()) - return false; - if (value) - *value = v; - return true; - } catch (...) { - return false; - } -} - -bool convert_dec_to_num_helper1(const std::string &str, uint32_t *value) { - try { - size_t idx; - auto v = stol(str, &idx, 10); - if (idx != str.size()) - return false; - if (value) - *value = v; - return true; - } catch (...) { - return false; - } -} - -bool convert_dec_to_num_helper1(const std::string &str, uint16_t *value) { - try { - size_t idx; - auto v = stol(str, &idx, 10); - if (idx != str.size()) - return false; - if (v > std::numeric_limits::max()) - return false; - if (value) - *value = v; - return true; - } catch (...) { - return false; - } -} - -template -constexpr V ceilDiv(V value, D divider) { - return (value + divider - 1) / divider; -} - -template -constexpr V aligned(V value, A align) { - return ceilDiv(value, align) * align; -} - -template -void reserve( - Container *container, - typename Container::size_type freeSpaceRequired, - typename Container::size_type firstAlloc, - typename Container::size_type nextAlloc) { - //TSL_ASSERT(container); - auto &c = *container; - auto required = c.size() + freeSpaceRequired; - if (c.capacity() >= required) - return; - c.reserve((firstAlloc >= required) ? firstAlloc - : firstAlloc + aligned(required - firstAlloc, nextAlloc)); -} - -template -void reserve( - Container *container, - typename Container::size_type freeSpaceRequired, - typename Container::size_type alloc) { - //TSL_ASSERT(container); - auto &c = *container; - auto required = c.size() + freeSpaceRequired; - if (c.capacity() >= required) - return; - c.reserve(aligned(required, alloc)); -} - -bool is_valid(const boost::asio::ip::tcp::endpoint &ep) { - - if (ep.port() == 0) - return false; - - if (ep.address().is_unspecified()) - return false; - - return true; -} - -// utl - -url_schema_type identify_url_schema_type(const std::string &schema_name) { - // rework - auto temp = schema_name; - make_lower(&temp); - if (temp == "http") - return url_schema_type::http; - if (temp == "https") - return url_schema_type::https; - return url_schema_type::unknown; -} - -// url_data - -url_data::url_data(const std::string &url) { - if (!parse(url)) - FC_THROW("URL parse failed"); -} - -void url_data::clear() { - schema_type = url_schema_type::unknown; - schema = decltype(schema)(); - host = decltype(host)(); - port = 0; - path = decltype(path)(); -} - -bool url_data::parse(const std::string &url) { - - typedef std::string::size_type size_t; - constexpr auto npos = std::string::npos; - - size_t schema_end = url.find("://"); - size_t host_begin; - std::string temp_schema; - - if (schema_end == npos) - host_begin = 0; // no schema - else { - if (schema_end < 3) { // schema too short: less than 3 chars - return false; - } - if (schema_end > 5) { // schema too long: more than 5 chars - return false; - } - host_begin = schema_end + 3; - temp_schema = url.substr(0, schema_end); - } - - // ASSERT(url.size() >= host_begin); - - if (url.size() == host_begin) // host is empty - return false; - - size_t port_sep = url.find(':', host_begin); - - if (port_sep == host_begin) - return false; - - size_t path_sep = url.find('/', host_begin); - - if (path_sep == host_begin) - return false; - - if ((port_sep != npos) && (path_sep != npos) && (port_sep > path_sep)) - port_sep = npos; - - std::string temp_port; - - if (port_sep != npos) { - auto port_index = port_sep + 1; - if (path_sep == npos) - temp_port = url.substr(port_index); - else - temp_port = url.substr(port_index, path_sep - port_index); - } - - if (temp_port.empty()) - port = 0; - else { - if (!convert_dec_to_num_helper1(temp_port, &port)) - return false; - } - - std::string temp_path; - - if (path_sep != npos) - temp_path = url.substr(path_sep); - - std::string temp_host; - - if (port_sep != npos) { - temp_host = url.substr(host_begin, port_sep - host_begin); - } else { - if (path_sep != npos) - temp_host = url.substr(host_begin, path_sep - host_begin); - else - temp_host = url.substr(host_begin); - } - - schema = temp_schema; - host = temp_host; - path = temp_path; - schema_type = identify_url_schema_type(schema); - - return true; -} - -}} // namespace graphene::peerplays_sidechain - -namespace graphene { namespace peerplays_sidechain { - -using namespace boost::asio; -using error_code = boost::system::error_code; -using endpoint = ip::tcp::endpoint; - -namespace detail { - -// http_call_impl - -struct tcp_socket { - - typedef ip::tcp::socket underlying_type; - - underlying_type underlying; - - tcp_socket(http_call &call) : - underlying(call.m_service) { - } - - underlying_type &operator()() { - return underlying; - } - - void connect(const http_call &call, const endpoint &ep, error_code *ec) { - // TCP connect - underlying.connect(ep, *ec); - } - - void shutdown() { - error_code ec; - underlying.close(ec); - } -}; - -struct ssl_socket { - - typedef ssl::stream underlying_type; - - underlying_type underlying; - - ssl_socket(http_call &call) : - underlying(call.m_service, *call.m_context) { - } - - underlying_type &operator()() { - return underlying; - } - - void connect(const http_call &call, const endpoint &ep, error_code *ec) { - - auto &u = underlying; - - // TCP connect - u.lowest_layer().connect(ep, *ec); - - // SSL connect - if (!SSL_set_tlsext_host_name(u.native_handle(), call.m_host.c_str())) - FC_THROW("SSL_set_tlsext_host_name failed"); - - u.set_verify_mode(ssl::verify_peer, *ec); - u.handshake(ssl::stream_base::client, *ec); - } - - void shutdown() { - auto &u = underlying; - error_code ec; - u.shutdown(ec); - u.lowest_layer().close(ec); - } -}; - -template -class http_call_impl { -public: - http_call_impl(http_call &call, const void *body_data, size_t body_size, const std::string &content_type_, http_response &response); - void exec(); - -private: - http_call &call; - const void *body_data; - size_t body_size; - std::string content_type; - http_response &response; - - socket_type socket; - streambuf response_buf; - - int32_t content_length; - bool transfer_encoding_chunked; - -private: - void connect(); - void shutdown(); - void send_request(); - void on_header(std::string &name, std::string &value); - void on_header(); - void process_headers(); - void append_entity_body(std::istream *stream, size_t size); - void append_entity_body_2(std::istream *strm); - bool read_next_chunk(std::istream *strm); - void skip_footer(); - void read_body_chunked(); - void read_body_until_eof(); - void read_body_exact(); - void process_response(); -}; - -static const char cr = 0x0D; -static const char lf = 0x0A; -static const char *crlf = "\x0D\x0A"; -static const char *crlfcrlf = "\x0D\x0A\x0D\x0A"; -static const auto crlf_uint = (((uint16_t)lf) << 8) + cr; - -template -http_call_impl::http_call_impl(http_call &call_, const void *body_data_, size_t body_size_, const std::string &content_type_, http_response &response_) : - call(call_), - body_data(body_data_), - body_size(body_size_), - content_type(content_type_), - response(response_), - socket(call), - response_buf(http_call::response_size_limit_bytes) { -} - -template -void http_call_impl::exec() { - try { - connect(); - send_request(); - process_response(); - shutdown(); - } catch (...) { - shutdown(); - throw; - } -} - -template -void http_call_impl::connect() { - - { - error_code ec; - auto &ep = call.m_endpoint; - if (is_valid(ep)) { - socket.connect(call, ep, &ec); - if (!ec) - return; - } - } - - ip::tcp::resolver resolver(call.m_service); - - auto rng = resolver.resolve(call.m_host, std::string()); - - //ASSERT(rng.begin() != rng.end()); - - error_code ec; - - for (endpoint ep : rng) { - ep.port(call.m_port); - socket.connect(call, ep, &ec); - if (!ec) { - call.m_endpoint = ep; - return; // comment to test1 - } - } - // if (!ec) return; // uncomment to test1 - - //ASSERT(ec); - throw boost::system::system_error(ec); -} - -template -void http_call_impl::shutdown() { - socket.shutdown(); -} - -template -void http_call_impl::send_request() { - - streambuf request; - std::ostream stream(&request); - - // start string: HTTP/1.0 - - //ASSERT(!call.m_path.empty()); - - stream << call.m_method << " " << call.m_path << " HTTP/1.1" << crlf; - - // host - - stream << "Host: " << call.m_host << ":" << call.m_endpoint.port() << crlf; - - // content - - if (body_size) { - stream << "Content-Type: " << content_type << crlf; - stream << "Content-Length: " << body_size << crlf; - } - - // additional headers - - const auto &h = call.m_headers; - - if (!h.empty()) { - if (h.size() < 2) - FC_THROW("invalid headers data"); - stream << h; - // ensure headers finished correctly - if ((h.substr(h.size() - 2) != crlf)) - stream << crlf; - } - - // other - - // stream << "Accept: *\x2F*" << crlf; - stream << "Accept: text/html, application/json" << crlf; - stream << "Connection: close" << crlf; - - // end - - stream << crlf; - - // send headers - - write(socket(), request); - - // send body - - if (body_size) - write(socket(), buffer(body_data, body_size)); -} - -template -void http_call_impl::on_header(std::string &name, std::string &value) { - - if (name == "content-length") { - uint32_t u; - if (!convert_dec_to_num_helper1(value, &u)) - FC_THROW("invalid content-length header data"); - content_length = u; - return; - } - - if (name == "transfer-encoding") { - boost::algorithm::to_lower(value); - if (value == "chunked") - transfer_encoding_chunked = true; - return; - } -} - -template -void http_call_impl::process_headers() { - - std::istream stream(&response_buf); - - std::string http_version; - stream >> http_version; - stream >> response.status_code; - - make_trimmed(&http_version); - make_lower(&http_version); - - if (!stream || http_version.substr(0, 6) != "http/1") - FC_THROW("invalid response data"); - - // read/skip headers - - content_length = -1; - transfer_encoding_chunked = false; - - for (;;) { - std::string header; - if (!std::getline(stream, header, lf) || (header.size() == 1 && header[0] == cr)) - break; - auto pos = header.find(':'); - if (pos == std::string::npos) - continue; - auto name = header.substr(0, pos); - make_trimmed(&name); - boost::algorithm::to_lower(name); - auto value = header.substr(pos + 1); - make_trimmed(&value); - on_header(name, value); - } -} - -template -void http_call_impl::append_entity_body(std::istream *strm, size_t size) { - if (size == 0) - return; - auto &body = response.body; - reserve(&body, size, http_call::response_first_alloc_bytes, http_call::response_next_alloc_bytes); - auto cur = body.size(); - body.resize(cur + size); - auto p = &body[cur]; - if (!strm->read(p, size)) - FC_THROW("stream read failed"); -} - -template -void http_call_impl::append_entity_body_2(std::istream *strm) { - auto avail = response_buf.size(); - if (response.body.size() + avail > http_call::response_size_limit_bytes) - FC_THROW("response body size limit exceeded"); - append_entity_body(strm, avail); -} - -template -bool http_call_impl::read_next_chunk(std::istream *strm) { - - // content length info is used as pre-alloc hint only - // it is not used inside the reading logic - - auto &buf = response_buf; - auto &stream = *strm; - auto &body = response.body; - - read_until(socket(), buf, crlf); - - std::string chunk_header; - - if (!std::getline(stream, chunk_header, lf)) - FC_THROW("failed to read chunk size"); - - auto ext_index = chunk_header.find(':'); - - if (ext_index != std::string::npos) - chunk_header.resize(ext_index); - - make_trimmed(&chunk_header); - - uint32_t chink_size; - - if (!convert_hex_to_num_helper1(chunk_header, &chink_size)) - FC_THROW("invalid chunk size string"); - - if (body.size() + chink_size > http_call::response_size_limit_bytes) - FC_THROW("response body size limit exceeded"); - - auto avail = buf.size(); - if (avail < chink_size + 2) { - auto rest = chink_size + 2 - avail; - read(socket(), buf, transfer_at_least(rest)); - } - - append_entity_body(&stream, chink_size); - - uint16_t temp; - if (!stream.read((char *)(&temp), 2)) - FC_THROW("stream read failed"); - if (temp != crlf_uint) - FC_THROW("invalid chink end"); - - return chink_size != 0; -} - -template -void http_call_impl::skip_footer() { - // to be implemeted -} - -template -void http_call_impl::read_body_chunked() { - - std::istream stream(&response_buf); - - for (;;) { - if (!read_next_chunk(&stream)) - break; - } - - skip_footer(); -} - -template -void http_call_impl::read_body_until_eof() { - - auto &buf = response_buf; - std::istream stream(&buf); - - append_entity_body_2(&stream); - - error_code ec; - - for (;;) { - auto readed = read(socket(), buf, transfer_at_least(1), ec); - append_entity_body_2(&stream); - if (ec) - break; - if (!readed) { - //ASSERT(buf.size() == 0); - FC_THROW("logic error: read failed but no error conditon"); - } - } - if ((ec != error::eof) && - (ec != ssl::error::stream_truncated)) - throw boost::system::system_error(ec); -} - -template -void http_call_impl::read_body_exact() { - - auto &buf = response_buf; - auto &body = response.body; - - auto avail = buf.size(); - - if (avail > ((size_t)content_length)) - FC_THROW("invalid response body (content length mismatch)"); - - body.resize(content_length); - - if (avail) { - if (avail != ((size_t)buf.sgetn(&body[0], avail))) - FC_THROW("stream read failed"); - } - - auto rest = content_length - avail; - - if (rest > 0) { - auto readed = read(socket(), buffer(&body[avail], rest), transfer_exactly(rest)); - //ASSERT(readed <= rest); - if (readed < rest) - FC_THROW("logic error: read failed but no error conditon"); - } -} - -template -void http_call_impl::process_response() { - - auto &buf = response_buf; - auto &body = response.body; - - read_until(socket(), buf, crlfcrlf); - - process_headers(); - - // check content length - - if (content_length >= 0) { - if (content_length < 2) { // minimum content is "{}" - FC_THROW("invalid response body (too short)"); - } - if (content_length > http_call::response_size_limit_bytes) - FC_THROW("response body size limit exceeded"); - body.reserve(content_length); - } - - if (transfer_encoding_chunked) { - read_body_chunked(); - } else { - if (content_length < 0) - read_body_until_eof(); - else { - if (content_length > 0) - read_body_exact(); - } - } -} - -} // namespace detail - -// https_call - -http_call::http_call(const url_data &url, const std::string &method, const std::string &headers) : - m_host(url.host), - m_method(method), - m_headers(headers) { - - if (url.schema_type == url_schema_type::https) { - m_context = new boost::asio::ssl::context(ssl::context::tlsv12_client); - } else { - m_context = 0; - } - - if (url.port) - m_port_default = url.port; - else { - if (url.schema_type == url_schema_type::https) - m_port_default = https_port; - else - m_port_default = http_port; - } - - m_port = m_port_default; - - set_path(url.path); - - try { - ctor_priv(); - } catch (...) { - if (m_context) - delete m_context; - throw; - } -} - -http_call::~http_call() { - if (m_context) - delete m_context; -} - -bool http_call::is_ssl() const { - return m_context != 0; -} - -const std::string &http_call::path() const { - return m_path; -} - -void http_call::set_path(const std::string &path) { - if (path.empty()) - m_path = "/"; - else - m_path = path; -} - -void http_call::set_method(const std::string &method) { - m_method = method; -} - -void http_call::set_headers(const std::string &headers) { - m_headers = headers; -} - -const std::string &http_call::host() const { - return m_host; -} - -void http_call::set_host(const std::string &host) { - m_host = host; -} - -uint16_t http_call::port() const { - return m_port; -} - -void http_call::set_port(uint16_t port) { - if (port) - m_port = port; - else - m_port = m_port_default; -} - -bool http_call::exec(const http_request &request, http_response *response) { - - //ASSERT(response); - auto &resp = *response; - m_error_what = decltype(m_error_what)(); - resp.clear(); - - try { - try { - using namespace detail; - if (!m_context) - http_call_impl(*this, request.body.data(), request.body.size(), request.content_type, resp).exec(); - else - http_call_impl(*this, request.body.data(), request.body.size(), request.content_type, resp).exec(); - return true; - } catch (const std::exception &e) { - m_error_what = e.what(); - } - } catch (...) { - m_error_what = "unknown exception"; - } - - resp.clear(); - return false; -} - -const std::string &http_call::error_what() const { - return m_error_what; -} - -void http_call::ctor_priv() { - if (m_context) { - m_context->set_default_verify_paths(); - m_context->set_options(ssl::context::default_workarounds); - } -} - -}} // namespace graphene::peerplays_sidechain - -namespace graphene { namespace peerplays_sidechain { - -rpc_client::rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug) : - debug_rpc_calls(debug), +rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : + url(_url), + user(_user), + password(_password), + debug_rpc_calls(_debug_rpc_calls), request_id(0), - client(url) + resolver(ioc) { -{ + std::string reg_expr = "^((?Phttps|http):\\/\\/)?(?P[a-zA-Z0-9\\-\\.]+)(:(?P\\d{1,5}))?(?P\\/.+)?"; + boost::xpressive::sregex sr = boost::xpressive::sregex::compile(reg_expr); - client.set_method("POST"); - client.set_headers("Authorization : Basic" + fc::base64_encode(user_name + ":" + password)); + boost::xpressive::smatch sm; + + if (boost::xpressive::regex_search(url, sm, sr)) { + protocol = sm["Protocol"]; + if (protocol.empty()) { + protocol = "http"; + } + + host = sm["Host"]; + if (host.empty()) { + host + "localhost"; + } + + port = sm["Port"]; + if (port.empty()) { + port = "80"; + } + + target = sm["Target"]; + if (target.empty()) { + target = "/"; + } + + authorization = "Basic " + fc::base64_encode(user + ":" + password); + results = resolver.resolve(host, port); + + } else { + elog("Invalid URL: ${url}", ("url", url)); + } } std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx) { - if (reply_str.empty()) - return std::string(); std::stringstream ss(reply_str); boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); if (json.find("result") == json.not_found()) { - return std::string(); - } - auto json_result = json.get_child("result"); - if (json_result.find(array_path) == json_result.not_found()) { - return std::string(); + return ""; } - boost::property_tree::ptree array_ptree = json_result; - if (!array_path.empty()) { - array_ptree = json_result.get_child(array_path); - } - uint32_t array_el_idx = -1; - for (const auto &array_el : array_ptree) { - array_el_idx = array_el_idx + 1; - if (array_el_idx == idx) { - std::stringstream ss_res; - boost::property_tree::json_parser::write_json(ss_res, array_el.second); - return ss_res.str(); + auto json_result = json.get_child_optional("result"); + if (json_result) { + boost::property_tree::ptree array_ptree = json_result.get(); + if (!array_path.empty()) { + array_ptree = json_result.get().get_child(array_path); + } + uint32_t array_el_idx = -1; + for (const auto &array_el : array_ptree) { + array_el_idx = array_el_idx + 1; + if (array_el_idx == idx) { + std::stringstream ss_res; + boost::property_tree::json_parser::write_json(ss_res, array_el.second); + return ss_res.str(); + } } } - return std::string(); + return ""; } std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::string value_path) { - if (reply_str.empty()) - return std::string(); std::stringstream ss(reply_str); boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); if (json.find("result") == json.not_found()) { - return std::string(); + return ""; } - auto json_result = json.get_child("result"); - if (json_result.find(value_path) == json_result.not_found()) { - return std::string(); + + auto json_result = json.get_child_optional("result"); + if (json_result) { + return json_result.get().get(value_path); } - return json_result.get(value_path); + + return json.get("result"); } std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) { @@ -904,114 +131,98 @@ std::string rpc_client::send_post_request(std::string method, std::string params boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); - if (reply.status_code == 200) { - return ss.str(); - } - if (json.count("error") && !json.get_child("error").empty()) { wlog("RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body.str())("msg", ss.str())); } + + if (reply.status == 200) { + return ss.str(); + } + return ""; } -//fc::http::reply rpc_client::send_post_request(std::string body, bool show_log) { -// fc::http::connection conn; -// conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), port)); -// -// std::string url = "http://" + ip + ":" + std::to_string(port); -// -// //if (wallet.length() > 0) { -// // url = url + "/wallet/" + wallet; -// //} -// -// fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); -// -// if (show_log) { -// ilog("### Request URL: ${url}", ("url", url)); -// ilog("### Request: ${body}", ("body", body)); -// std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); -// ilog("### Response: ${ss}", ("ss", ss.str())); -// } -// -// return reply; -//} +rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { -//static size_t write_callback(char *ptr, size_t size, size_t nmemb, rpc_reply *reply) { -// size_t retval = 0; -// if (reply != nullptr) { -// reply->body.append(ptr, size * nmemb); -// retval = size * nmemb; -// } -// return retval; -//} + // These object is used as a context for ssl connection + boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client); -//rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { -// -// struct curl_slist *headers = nullptr; -// headers = curl_slist_append(headers, "Accept: application/json"); -// headers = curl_slist_append(headers, "Content-Type: application/json"); -// headers = curl_slist_append(headers, "charset: utf-8"); -// -// CURL *curl = curl_easy_init(); -// if (ip.find("https://", 0) != 0) { -// curl_easy_setopt(curl, CURLOPT_URL, ip.c_str()); -// curl_easy_setopt(curl, CURLOPT_PORT, port); -// } else { -// std::string full_address = ip + ":" + std::to_string(port); -// curl_easy_setopt(curl, CURLOPT_URL, full_address.c_str()); -// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); -// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); -// } -// if (!user.empty()) { -// curl_easy_setopt(curl, CURLOPT_USERNAME, user.c_str()); -// curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str()); -// } -// -// curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); -// curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str()); -// -// //curl_easy_setopt(curl, CURLOPT_VERBOSE, true); -// -// rpc_reply reply; -// -// curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); -// curl_easy_setopt(curl, CURLOPT_WRITEDATA, &reply); -// -// curl_easy_perform(curl); -// -// curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &reply.status); -// -// curl_easy_cleanup(curl); -// curl_slist_free_all(headers); -// -// if (show_log) { -// std::string url = ip + ":" + std::to_string(port); -// ilog("### Request URL: ${url}", ("url", url)); -// ilog("### Request: ${body}", ("body", body)); -// std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); -// ilog("### Response: ${ss}", ("ss", ss.str())); -// } -// -// return reply; -//} + boost::beast::net::ssl::stream ssl_tcp_stream(ioc, ctx); + boost::beast::tcp_stream tcp_stream(ioc); -http_response rpc_client::send_post_request(const std::string &body, bool show_log) { - - http_request request(body, "application/json"); - http_response response; - - client.exec(request, &response); - - if (show_log) { - std::string url = client.is_ssl() ? "https" : "http"; - url = url + "://" + client.host() + ":" + std::to_string(client.port()) + client.path(); - ilog("### Request URL: ${url}", ("url", url)); - ilog("### Request: ${body}", ("body", body)); - std::stringstream ss(std::string(response.body.begin(), response.body.end())); - ilog("### Response: ${ss}", ("ss", ss.str())); + // Set SNI Hostname (many hosts need this to handshake successfully) + if (protocol == "https") { + if (!SSL_set_tlsext_host_name(ssl_tcp_stream.native_handle(), host.c_str())) { + boost::beast::error_code ec{static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category()}; + throw boost::beast::system_error{ec}; + } + ctx.set_default_verify_paths(); + ctx.set_verify_mode(boost::asio::ssl::verify_peer); } - return response; + // Make the connection on the IP address we get from a lookup + if (protocol == "https") { + boost::beast::get_lowest_layer(ssl_tcp_stream).connect(results); + ssl_tcp_stream.handshake(boost::beast::net::ssl::stream_base::client); + } else { + tcp_stream.connect(results); + } + + // Set up an HTTP GET request message + boost::beast::http::request req{boost::beast::http::verb::post, target, 11}; + req.set(boost::beast::http::field::host, host + ":" + port); + req.set(boost::beast::http::field::accept, "application/json"); + req.set(boost::beast::http::field::authorization, authorization); + req.set(boost::beast::http::field::content_type, "application/json"); + req.set(boost::beast::http::field::content_encoding, "utf-8"); + req.set(boost::beast::http::field::content_length, body.length()); + req.body() = body; + + // Send the HTTP request to the remote host + if (protocol == "https") + boost::beast::http::write(ssl_tcp_stream, req); + else + boost::beast::http::write(tcp_stream, req); + + // This buffer is used for reading and must be persisted + boost::beast::flat_buffer buffer; + + // Declare a container to hold the response + boost::beast::http::response res; + + // Receive the HTTP response + if (protocol == "https") + boost::beast::http::read(ssl_tcp_stream, buffer, res); + else + boost::beast::http::read(tcp_stream, buffer, res); + + // Gracefully close the socket + boost::beast::error_code ec; + if (protocol == "https") { + boost::beast::get_lowest_layer(ssl_tcp_stream).close(); + } else { + tcp_stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + } + + // not_connected happens sometimes. Also on ssl level some servers are managing + // connecntion close, so closing here will sometimes end up with error stream truncated + // so don't bother reporting it. + if (ec && ec != boost::beast::errc::not_connected && ec != boost::asio::ssl::error::stream_truncated) + throw boost::beast::system_error{ec}; + + std::string rbody{boost::asio::buffers_begin(res.body().data()), + boost::asio::buffers_end(res.body().data())}; + rpc_reply reply; + reply.status = 200; + reply.body = rbody; + + if (show_log) { + ilog("### Request URL: ${url}", ("url", url)); + ilog("### Request: ${body}", ("body", body)); + ilog("### Response: ${rbody}", ("rbody", rbody)); + } + + return reply; } }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp index 63d218ee..1a797782 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp @@ -3,134 +3,43 @@ #include #include -#include -#include - -//#include +#include +#include namespace graphene { namespace peerplays_sidechain { -enum class url_schema_type { unknown, - http, - https, -}; - -// utl - -url_schema_type identify_url_schema_type(const std::string &schema_name); - -struct url_data { - - url_schema_type schema_type; - std::string schema; - std::string host; - uint16_t port; - std::string path; - - url_data() : - schema_type(url_schema_type::unknown), - port(0) { - } - - url_data(const std::string &url); - - void clear(); - - bool parse(const std::string &url); -}; - -struct http_request { - +struct rpc_reply { + uint16_t status; std::string body; - std::string content_type; - - http_request(const std::string &body_, const std::string &content_type_) : - body(body_), - content_type(content_type_) { - } }; -struct http_response { - - uint16_t status_code; - std::string body; - - void clear() { - status_code = 0; - body = decltype(body)(); - } -}; - -namespace detail { -template -class http_call_impl; -class tcp_socket; -class ssl_socket; -} // namespace detail - -class http_call { -public: - http_call(const url_data &url, const std::string &method = std::string(), const std::string &headers = std::string()); - ~http_call(); - - bool is_ssl() const; - - const std::string &path() const; - void set_path(const std::string &path); - void set_method(const std::string &method); - void set_headers(const std::string &headers); - const std::string &host() const; - void set_host(const std::string &host); - - uint16_t port() const; - void set_port(uint16_t port); - - bool exec(const http_request &request, http_response *response); - - const std::string &error_what() const; - -private: - template - friend class detail::http_call_impl; - friend detail::tcp_socket; - friend detail::ssl_socket; - static constexpr auto response_size_limit_bytes = 16 * 1024 * 1024; - static constexpr auto response_first_alloc_bytes = 32 * 1024; - static constexpr auto response_next_alloc_bytes = 256 * 1024; - std::string m_host; - uint16_t m_port_default; - uint16_t m_port; - std::string m_path; - std::string m_method; - std::string m_headers; - std::string m_error_what; - - boost::asio::io_service m_service; - boost::asio::ssl::context *m_context; - boost::asio::ip::tcp::endpoint m_endpoint; - - void ctor_priv(); -}; - -}} // namespace graphene::peerplays_sidechain - -namespace graphene { namespace peerplays_sidechain { - class rpc_client { public: - rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug); + rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); protected: std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx); std::string retrieve_value_from_reply(std::string reply_str, std::string value_path); std::string send_post_request(std::string method, std::string params, bool show_log); + std::string url; + std::string protocol; + std::string host; + std::string port; + std::string target; + std::string authorization; + + std::string user; + std::string password; bool debug_rpc_calls; uint32_t request_id; private: - http_call client; - http_response send_post_request(const std::string &body, bool show_log); + rpc_reply send_post_request(std::string body, bool show_log); + + boost::beast::net::io_context ioc; + boost::beast::net::ip::tcp::resolver resolver; + boost::asio::ip::basic_resolver_results results; }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 6fd1fcfa..55af0c4e 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -22,7 +23,7 @@ public: uint64_t amount_; }; -class bitcoin_rpc_client { +class bitcoin_rpc_client: public rpc_client { public: enum class multi_type { script, @@ -41,49 +42,30 @@ public: }; public: - bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password, bool _debug_rpc_calls); + bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); - std::string addmultisigaddress(const uint32_t nrequired, const std::vector public_keys); - std::string combinepsbt(const vector &psbts); - std::string createmultisig(const uint32_t nrequired, const std::vector public_keys); - std::string createpsbt(const std::vector &ins, const fc::flat_map outs); - std::string createrawtransaction(const std::vector &ins, const fc::flat_map outs); std::string createwallet(const std::string &wallet_name); - std::string decodepsbt(std::string const &tx_psbt); - std::string decoderawtransaction(std::string const &tx_hex); - std::string encryptwallet(const std::string &passphrase); uint64_t estimatesmartfee(uint16_t conf_target = 128); - std::string finalizepsbt(std::string const &tx_psbt); - std::string getaddressinfo(const std::string &address); std::string getblock(const std::string &block_hash, int32_t verbosity = 2); std::string getrawtransaction(const std::string &txid, const bool verbose = false); std::string getnetworkinfo(); - std::string gettransaction(const std::string &txid, const bool include_watch_only = false); std::string getblockchaininfo(); - void importaddress(const std::string &address_or_script, const std::string &label = "", const bool rescan = true, const bool p2sh = false); void importmulti(const std::vector &address_or_script_array, const bool rescan = true); std::vector listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999); std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999); std::string loadwallet(const std::string &filename); std::string sendrawtransaction(const std::string &tx_hex); - std::string signrawtransactionwithwallet(const std::string &tx_hash); - std::string unloadwallet(const std::string &filename); std::string walletlock(); - std::string walletprocesspsbt(std::string const &tx_psbt); bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); private: - fc::http::reply send_post_request(std::string body, bool show_log); - std::string ip; uint32_t rpc_port; std::string user; std::string password; std::string wallet; std::string wallet_password; - bool debug_rpc_calls; - fc::http::header authorization; }; // ============================================================================= diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 6203ff79..82511bed 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -25,1032 +25,230 @@ namespace graphene { namespace peerplays_sidechain { // ============================================================================= -bitcoin_rpc_client::bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password, bool _debug_rpc_calls) : - ip(_ip), - rpc_port(_rpc), - user(_user), - password(_password), - wallet(_wallet), - wallet_password(_wallet_password), - debug_rpc_calls(_debug_rpc_calls) { - authorization.key = "Authorization"; - authorization.val = "Basic " + fc::base64_encode(user + ":" + password); -} - -std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, const std::vector public_keys) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", " - "\"method\": \"addmultisigaddress\", \"params\": ["); - try { - std::string params = std::to_string(nrequired) + ", ["; - std::string pubkeys = ""; - for (std::string pubkey : public_keys) { - if (!pubkeys.empty()) { - pubkeys = pubkeys + ","; - } - pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); - } - params = params + pubkeys + std::string("]"); - body = body + params + std::string(", null, \"bech32\"] }"); - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::combinepsbt(const vector &psbts) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"combinepsbt\", \"method\": " - "\"combinepsbt\", \"params\": [["); - try { - std::string params = ""; - for (std::string psbt : psbts) { - if (!params.empty()) { - params = params + ","; - } - params = params + std::string("\"") + psbt + std::string("\""); - } - body = body + params + std::string("]] }"); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::createmultisig(const uint32_t nrequired, const std::vector public_keys) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createmultisig\", " - "\"method\": \"createmultisig\", \"params\": ["); - try { - std::string params = std::to_string(nrequired) + ", ["; - std::string pubkeys = ""; - for (std::string pubkey : public_keys) { - if (!pubkeys.empty()) { - pubkeys = pubkeys + ","; - } - pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); - } - params = params + pubkeys + std::string("]"); - body = body + params + std::string(", \"p2sh-segwit\" ] }"); - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::createpsbt(const std::vector &ins, const fc::flat_map outs) { - std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createpsbt\", " - "\"method\": \"createpsbt\", \"params\": ["); - try { - body += "["; - bool first = true; - for (const auto &entry : ins) { - if (!first) - body += ","; - body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; - first = false; - } - body += "],["; - first = true; - for (const auto &entry : outs) { - if (!first) - body += ","; - body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; - first = false; - } - body += std::string("]] }"); - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - if (json.find("result") != json.not_found()) { - return json.get("result"); - } - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::createrawtransaction(const std::vector &ins, const fc::flat_map outs) { - std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", " - "\"method\": \"createrawtransaction\", \"params\": ["); - try { - body += "["; - bool first = true; - for (const auto &entry : ins) { - if (!first) - body += ","; - body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; - first = false; - } - body += "],["; - first = true; - for (const auto &entry : outs) { - if (!first) - body += ","; - body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; - first = false; - } - body += std::string("]] }"); - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - if (json.find("result") != json.not_found()) { - return json.get("result"); - } - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } +bitcoin_rpc_client::bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : + rpc_client(_url, _user, _password, _debug_rpc_calls) { } std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createwallet\", \"method\": " - "\"createwallet\", \"params\": [\"" + - wallet_name + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::decodepsbt(std::string const &tx_psbt) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decodepsbt\", \"method\": " - "\"decodepsbt\", \"params\": [\"" + - tx_psbt + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::decoderawtransaction(std::string const &tx_hex) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decoderawtransaction\", \"method\": " - "\"decoderawtransaction\", \"params\": [\"" + - tx_hex + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::encryptwallet(const std::string &passphrase) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"encryptwallet\", \"method\": " - "\"encryptwallet\", \"params\": [\"" + - passphrase + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + std::string params = std::string("[\"") + wallet_name + std::string("\"]"); + std::string str = send_post_request("createwallet", params, debug_rpc_calls); + return str; } uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { - const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimatesmartfee\", " - "\"method\": \"estimatesmartfee\", \"params\": [" + - std::to_string(conf_target) + std::string("] }")); - try { - const auto reply = send_post_request(body, debug_rpc_calls); + std::string params = std::string("[") + std::to_string(conf_target) + std::string("]"); + std::string str = send_post_request("estimatesmartfee", params, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return 0; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - if (json.find("result") != json.not_found()) { - auto json_result = json.get_child("result"); - if (json_result.find("feerate") != json_result.not_found()) { - auto feerate_str = json_result.get("feerate"); - feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); - return std::stoll(feerate_str); - } - - if (json_result.find("errors") != json_result.not_found()) { - wlog("Bitcoin RPC call ${function} with body ${body} executed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - } - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return 20000; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return 20000; + if (str.length() == 0) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return 0; } -} -std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"finalizepsbt\", \"method\": " - "\"finalizepsbt\", \"params\": [\"" + - tx_psbt + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; + std::stringstream ss(str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.find("result") != json.not_found()) { + auto json_result = json.get_child("result"); + if (json_result.find("feerate") != json_result.not_found()) { + auto feerate_str = json_result.get("feerate"); + feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); + return std::stoll(feerate_str); } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; } -} -std::string bitcoin_rpc_client::getaddressinfo(const std::string &address) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getaddressinfo\", \"method\": " - "\"getaddressinfo\", \"params\": [\"" + - address + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + return 20000; } std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblock\", \"method\": " - "\"getblock\", \"params\": [\"" + - block_hash + "\", " + std::to_string(verbosity) + "] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + std::string params = std::string("[\"") + block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); + std::string str = send_post_request("getblock", params, debug_rpc_calls); + return str; } std::string bitcoin_rpc_client::getnetworkinfo() { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getnetworkinfo\", \"method\": " - "\"getnetworkinfo\", \"params\": [] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + std::string params = std::string("[]"); + std::string str = send_post_request("getnetworkinfo", params, debug_rpc_calls); + return str; } std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getrawtransaction\", \"method\": " - "\"getrawtransaction\", \"params\": ["); - try { - std::string params = "\"" + txid + "\", " + (verbose ? "true" : "false"); - body = body + params + "] }"; - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bool include_watch_only) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": " - "\"gettransaction\", \"params\": ["); - try { - std::string params = "\"" + txid + "\", " + (include_watch_only ? "true" : "false"); - body = body + params + "] }"; - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]"); + std::string str = send_post_request("getrawtransaction", params, debug_rpc_calls); + return str; } std::string bitcoin_rpc_client::getblockchaininfo() { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblockchaininfo\", \"method\": " - "\"getblockchaininfo\", \"params\": [] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); + std::string params = std::string("[]"); + std::string str = send_post_request("getblockchaininfo", params, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (str.length() > 0) { - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + std::stringstream ss(str); boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); } -} -void bitcoin_rpc_client::importaddress(const std::string &address_or_script, const std::string &label, const bool rescan, const bool p2sh) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", " - "\"method\": \"importaddress\", \"params\": ["); - try { - std::string params = "\"" + address_or_script + "\", " + - "\"" + label + "\", " + - (rescan ? "true" : "false") + ", " + - (p2sh ? "true" : "false"); - body = body + params + "] }"; - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return; - } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - } + return str; } void bitcoin_rpc_client::importmulti(const std::vector &address_or_script_array, const bool rescan) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importmulti\", " - "\"method\": \"importmulti\", \"params\": ["); - try { - std::string argument_1 = "["; - for (const auto ¶m : address_or_script_array) { - argument_1 += "{\"scriptPubKey\": "; - if (param.type == multi_type::address) { - argument_1 += "{\"address\": \"" + param.address_or_script + "\"}, \"label\": \"" + param.label + "\""; - } else if (param.type == multi_type::script) { - argument_1 += "\"" + param.address_or_script + "\", \"internal\": true"; - } else { - FC_THROW("Invalid multi_type."); - } - argument_1 += ", \"timestamp\": " + std::to_string(fc::time_point_sec::from_iso_string("2022-01-01T00:00:00").sec_since_epoch()) + "}"; - - //! Note - /* Creation time of the key expressed in UNIX epoch time, - or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest - key will determine how far back blockchain rescans need to begin for missing wallet transactions. - "now" can be specified to bypass scanning, for keys which are known to never have been used, and - 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key - creation time of all keys being imported by the importmulti call will be scanned.*/ - - if (¶m != &address_or_script_array.back()) { - argument_1 += ", "; - } + std::string params = std::string("["); + std::string argument_1 = "["; + for (const auto ¶m : address_or_script_array) { + argument_1 += "{\"scriptPubKey\": "; + if (param.type == multi_type::address) { + argument_1 += "{\"address\": \"" + param.address_or_script + "\"}, \"label\": \"" + param.label + "\""; + } else if (param.type == multi_type::script) { + argument_1 += "\"" + param.address_or_script + "\", \"internal\": true"; + } else { + FC_THROW("Invalid multi_type."); } - argument_1 += "]"; + argument_1 += ", \"timestamp\": " + std::to_string(fc::time_point_sec::from_iso_string("2022-01-01T00:00:00").sec_since_epoch()) + "}"; - std::string argument_2 = std::string{"{\"rescan\": "} + (rescan ? "true" : "false") + "}"; - body += argument_1 + ", " + argument_2 + "]}"; + //! Note + /* Creation time of the key expressed in UNIX epoch time, + or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest + key will determine how far back blockchain rescans need to begin for missing wallet transactions. + "now" can be specified to bypass scanning, for keys which are known to never have been used, and + 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key + creation time of all keys being imported by the importmulti call will be scanned.*/ - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return; + if (¶m != &address_or_script_array.back()) { + argument_1 += ", "; } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return; - } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); } + argument_1 += "]"; + + std::string argument_2 = std::string{"{\"rescan\": "} + (rescan ? "true" : "false") + "}"; + params += argument_1 + "," + argument_2 + "]"; + + send_post_request("importmulti", params, debug_rpc_calls); } std::vector bitcoin_rpc_client::listunspent(const uint32_t minconf, const uint32_t maxconf) { - const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": " - "\"listunspent\", \"params\": [" + - std::to_string(minconf) + "," + std::to_string(maxconf) + "] }"); - std::vector result; - try { - const auto reply = send_post_request(body, debug_rpc_calls); + std::string params = std::string("[") + std::to_string(minconf) + "," + std::to_string(maxconf) + std::string("]"); + std::string str = send_post_request("listunspent", params, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return result; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - if (json.count("result")) { - for (auto &entry : json.get_child("result")) { - btc_txout txo; - txo.txid_ = entry.second.get_child("txid").get_value(); - txo.out_num_ = entry.second.get_child("vout").get_value(); - string amount = entry.second.get_child("amount").get_value(); - amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); - txo.amount_ = std::stoll(amount); - result.push_back(txo); - } - } - } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return result; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + if (str.length() == 0) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); return result; } + + std::stringstream ss(str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + string amount = entry.second.get_child("amount").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + txo.amount_ = std::stoll(amount); + result.push_back(txo); + } + } + + return result; } std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount, const uint32_t minconf, const uint32_t maxconf) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": " - "\"listunspent\", \"params\": [" + - std::to_string(minconf) + "," + std::to_string(maxconf) + ","); - body += std::string("[\""); - body += address; - body += std::string("\"],true,{\"minimumAmount\":"); - body += std::to_string(minimum_amount); - body += std::string("} ] }"); + + std::string params = std::string("[") + std::to_string(minconf) + "," + std::to_string(maxconf) + ","; + params += std::string("[\""); + params += address; + params += std::string("\"],true,{\"minimumAmount\":"); + params += std::to_string(minimum_amount); + params += std::string("} ]"); std::vector result; + std::string str = send_post_request("listunspent", params, debug_rpc_calls); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return result; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - if (json.count("result")) { - for (auto &entry : json.get_child("result")) { - btc_txout txo; - txo.txid_ = entry.second.get_child("txid").get_value(); - txo.out_num_ = entry.second.get_child("vout").get_value(); - string amount = entry.second.get_child("amount").get_value(); - amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); - txo.amount_ = std::stoll(amount); - result.push_back(txo); - } - } - } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return result; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + if (str.length() == 0) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); return result; } + + std::stringstream ss(str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + string amount = entry.second.get_child("amount").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + txo.amount_ = std::stoll(amount); + result.push_back(txo); + } + } + + return result; } std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"loadwallet\", \"method\": " - "\"loadwallet\", \"params\": [\"" + - filename + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + std::string params = std::string("[\"") + filename + std::string("\"]"); + std::string str = send_post_request("loadwallet", params, debug_rpc_calls); + return str; } std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { - const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", " - "\"method\": \"sendrawtransaction\", \"params\": [") + - std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + std::string params = std::string("[\"") + tx_hex + std::string("\"]"); + std::string str = send_post_request("sendrawtransaction", params, debug_rpc_calls); - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + if (str.length() > 0) { + std::stringstream ss(str); boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return json.get("result"); - } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (!json.count("error.code")) { + if (json.get_child("error.code").get_value() == -27) { + return tx_hex; + } + } } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; + + return json.get("result"); } -} -std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"signrawtransactionwithwallet\", " - "\"method\": \"signrawtransactionwithwallet\", \"params\": ["); - std::string params = "\"" + tx_hash + "\""; - body = body + params + std::string("]}"); - - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } -} - -std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"unloadwallet\", \"method\": " - "\"unloadwallet\", \"params\": [\"" + - filename + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + return str; } std::string bitcoin_rpc_client::walletlock() { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": " - "\"walletlock\", \"params\": [] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); + std::string params = std::string("[]"); + std::string str = send_post_request("walletlock", params, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + if (str.length() == 0) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); return ""; } -} -std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": " - "\"walletprocesspsbt\", \"params\": [\"" + - tx_psbt + "\"] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); + std::stringstream ss(str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return ss.str(); - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } - return ""; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return ""; - } + ss.clear(); + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); } bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { - std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " - "\"walletpassphrase\", \"params\": [\"" + - passphrase + "\", " + std::to_string(timeout) + "] }"); - try { - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return false; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - if (reply.status == 200) { - return true; - } - - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); - } + std::string params = std::string("[\"") + passphrase + std::string("\",") + std::to_string(timeout) + std::string("]"); + std::string str = send_post_request("walletpassphrase", params, debug_rpc_calls); + if (str.length() == 0) return false; - } catch (const boost::exception &ex) { - wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); - return false; - } -} - -fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool show_log) { - fc::http::connection conn; - conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); - - std::string url = "http://" + ip + ":" + std::to_string(rpc_port); - - if (wallet.length() > 0) { - url = url + "/wallet/" + wallet; - } - - fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); - - if (show_log) { - ilog("### Request URL: ${url}", ("url", url)); - ilog("### Request: ${body}", ("body", body)); - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - ilog("### Response status: ${status}", ("status", reply.status)); - ilog("### Response: ${ss}", ("ss", ss.str())); - } - - return reply; + else + return true; } // ============================================================================= @@ -1069,11 +267,11 @@ void zmq_listener::start() { FC_ASSERT(0 == rc); rc = zmq_setsockopt(socket, ZMQ_LINGER, &linger, sizeof(linger)); FC_ASSERT(0 == rc); - int timeout = 100; //millisec + int timeout = 100; // millisec rc = zmq_setsockopt(socket, ZMQ_RCVTIMEO, &timeout, sizeof(timeout)); - //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); - //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); - //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); + // socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); + // socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); + // socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); thr = std::thread(&zmq_listener::handle_zmq, this); @@ -1157,15 +355,12 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } } - fc::http::connection conn; - try { - conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); - } catch (fc::exception &e) { - elog("No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", ip)("port", rpc_port)); - FC_ASSERT(false); + std::string url = ip + ":" + std::to_string(rpc_port); + if (wallet.length() > 0) { + url = url + "/wallet/" + wallet; } - bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(ip, rpc_port, rpc_user, rpc_password, wallet, wallet_password, debug_rpc_calls)); + bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); if (!wallet.empty()) { bitcoin_client->loadwallet(wallet); } @@ -1174,14 +369,18 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end())); boost::property_tree::ptree bci_json; boost::property_tree::read_json(bci_ss, bci_json); + using namespace bitcoin; network_type = bitcoin_address::network::mainnet; + if (bci_json.count("chain")) { std::string chain = bci_json.get("chain"); - if (chain == "test") { - network_type = bitcoin_address::network::testnet; - } else if (chain == "regtest") { - network_type = bitcoin_address::network::regtest; + if (chain.length() > 0) { + if (chain == "test") { + network_type = bitcoin_address::network::testnet; + } else if (chain == "regtest") { + network_type = bitcoin_address::network::regtest; + } } } @@ -1210,7 +409,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { on_changed_objects_task.cancel_and_wait(__FUNCTION__); } } catch (fc::canceled_exception &) { - //Expected exception. Move along. + // Expected exception. Move along. } catch (fc::exception &e) { edump((e.to_detail_string())); } @@ -1218,7 +417,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) { - //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + // ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); bool should_approve = false; @@ -1874,7 +1073,7 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_ if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { return ""; } - //Get redeem script for deposit address + // Get redeem script for deposit address std::string redeem_script = get_redeemscript_for_userdeposit(swdo.sidechain_from); std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; @@ -2026,8 +1225,8 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran uint32_t inputs_number = in_amounts.size(); vector dummy; dummy.resize(inputs_number); - //Organise weighted address signatures - //Add dummies for empty signatures + // Organise weighted address signatures + // Add dummies for empty signatures vector> signatures; for (unsigned idx = 0; idx < sto.signatures.size(); ++idx) { if (sto.signatures[idx].second.empty()) @@ -2035,13 +1234,13 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran else signatures.push_back(read_byte_arrays_from_string(sto.signatures[idx].second)); } - //Add empty sig for user signature for Deposit transaction + // Add empty sig for user signature for Deposit transaction if (sto.object_id.type() == son_wallet_deposit_object::type_id) { add_signatures_to_transaction_user_weighted_multisig(tx, signatures); } else { add_signatures_to_transaction_weighted_multisig(tx, signatures); } - //Add redeemscripts to vins and make tx ready for sending + // Add redeemscripts to vins and make tx ready for sending sign_witness_transaction_finalize(tx, redeem_scripts, false); std::string final_tx_hex = fc::to_hex(pack(tx)); std::string res = bitcoin_client->sendrawtransaction(final_tx_hex); @@ -2050,6 +1249,7 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran } void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { + std::string block = bitcoin_client->getblock(event_data); if (block.empty()) return; @@ -2119,12 +1319,14 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons std::vector sidechain_net_handler_bitcoin::extract_info_from_block(const std::string &_block) { std::stringstream ss(_block); - boost::property_tree::ptree block; - boost::property_tree::read_json(ss, block); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + auto json_result = json.get_child_optional("result"); std::vector result; - for (const auto &tx_child : block.get_child("tx")) { + for (const auto &tx_child : json_result.get().get_child("tx")) { const auto &tx = tx_child.second; for (const auto &o : tx.get_child("vout")) { @@ -2217,4 +1419,5 @@ void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector Date: Tue, 26 Jul 2022 23:17:42 +0000 Subject: [PATCH 039/106] SON for Hive voting --- .gitlab-ci.yml | 4 - Dockerfile | 2 +- Dockerfile.18.04 | 6 +- README.md | 6 +- libraries/app/database_api.cpp | 69 +- libraries/chain/account_evaluator.cpp | 10 +- libraries/chain/db_block.cpp | 17 +- libraries/chain/db_getter.cpp | 64 +- libraries/chain/db_init.cpp | 31 +- libraries/chain/db_maint.cpp | 662 ++++++++++-------- libraries/chain/db_witness_schedule.cpp | 151 ++-- .../chain/include/graphene/chain/database.hpp | 41 +- .../graphene/chain/global_property_object.hpp | 16 +- .../graphene/chain/protocol/account.hpp | 20 +- .../graphene/chain/protocol/son_wallet.hpp | 2 +- .../include/graphene/chain/protocol/vote.hpp | 5 +- .../chain/sidechain_address_object.hpp | 2 +- .../include/graphene/chain/sidechain_defs.hpp | 15 +- .../chain/include/graphene/chain/son_info.hpp | 19 +- .../include/graphene/chain/son_object.hpp | 42 +- .../graphene/chain/son_wallet_object.hpp | 2 +- .../include/graphene/chain/voters_info.hpp | 12 +- .../include/graphene/chain/votes_info.hpp | 12 +- .../chain/witness_schedule_object.hpp | 2 +- .../graphene/chain/witness_scheduler.hpp | 10 +- libraries/chain/protocol/account.cpp | 15 +- .../chain/sidechain_address_evaluator.cpp | 2 +- libraries/chain/son_evaluator.cpp | 192 +++-- .../chain/son_wallet_deposit_evaluator.cpp | 10 +- libraries/chain/son_wallet_evaluator.cpp | 12 +- .../chain/son_wallet_withdraw_evaluator.cpp | 12 +- .../peerplays_sidechain/CMakeLists.txt | 2 +- .../peerplays_sidechain_plugin.hpp | 16 +- .../sidechain_net_handler_factory.hpp | 23 + .../sidechain_net_manager.hpp | 38 - .../peerplays_sidechain_plugin.cpp | 318 +++++---- .../sidechain_net_handler.cpp | 50 +- .../sidechain_net_handler_bitcoin.cpp | 76 +- .../sidechain_net_handler_factory.cpp | 31 + .../sidechain_net_handler_hive.cpp | 34 +- .../sidechain_net_handler_peerplays.cpp | 14 +- .../sidechain_net_manager.cpp | 112 --- .../wallet/include/graphene/wallet/wallet.hpp | 5 + libraries/wallet/wallet.cpp | 186 +++-- tests/cli/son.cpp | 305 +++++--- tests/tests/block_tests.cpp | 7 +- tests/tests/sidechain_addresses_test.cpp | 142 +++- tests/tests/son_operations_tests.cpp | 85 ++- tests/tests/son_wallet_tests.cpp | 9 +- 49 files changed, 1783 insertions(+), 1135 deletions(-) create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp delete mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp delete mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cda41654..61afce6e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -44,8 +44,6 @@ test-mainnet: dockerize-mainnet: stage: dockerize - dependencies: - - test-mainnet variables: IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA before_script: @@ -105,8 +103,6 @@ test-testnet: dockerize-testnet: stage: dockerize - dependencies: - - test-testnet variables: IMAGE: $CI_REGISTRY_IMAGE/testnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA before_script: diff --git a/Dockerfile b/Dockerfile index 7a76c136..50c51c3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ RUN \ expect \ git \ graphviz \ - libboost1.67-all-dev \ + libboost-all-dev \ libbz2-dev \ libcurl4-openssl-dev \ libncurses-dev \ diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index d3fa1d75..49a06fa1 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -58,9 +58,9 @@ EXPOSE 22 WORKDIR /home/peerplays/ RUN \ - wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 && \ - tar xjf boost_1_67_0.tar.bz2 && \ - cd boost_1_67_0/ && \ + wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 && \ + tar xjf boost_1_71_0.tar.bz2 && \ + cd boost_1_71_0/ && \ ./bootstrap.sh && \ ./b2 install diff --git a/README.md b/README.md index a8d98021..f3d8c5b9 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,9 @@ sudo apt-get install \ Install Boost libraries from source ``` -wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 -tar xjf boost_1_67_0.tar.bz2 -cd boost_1_67_0/ +wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 +tar xjf boost_1_71_0.tar.bz2 +cd boost_1_71_0/ ./bootstrap.sh sudo ./b2 install ``` diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6ffee911..b10d6b99 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2083,7 +2083,8 @@ vector database_api_impl::lookup_vote_ids(const vector &v const auto &committee_idx = _db.get_index_type().indices().get(); const auto &for_worker_idx = _db.get_index_type().indices().get(); const auto &against_worker_idx = _db.get_index_type().indices().get(); - const auto &son_idx = _db.get_index_type().indices().get(); + const auto &son_bictoin_idx = _db.get_index_type().indices().get(); + const auto &son_hive_idx = _db.get_index_type().indices().get(); vector result; result.reserve(votes.size()); @@ -2119,15 +2120,22 @@ vector database_api_impl::lookup_vote_ids(const vector &v } break; } - case vote_id_type::son: { - auto itr = son_idx.find(id); - if (itr != son_idx.end()) + case vote_id_type::son_bitcoin: { + auto itr = son_bictoin_idx.find(id); + if (itr != son_bictoin_idx.end()) + result.emplace_back(variant(*itr, 5)); + else + result.emplace_back(variant()); + break; + } + case vote_id_type::son_hive: { + auto itr = son_hive_idx.find(id); + if (itr != son_hive_idx.end()) result.emplace_back(variant(*itr, 5)); else result.emplace_back(variant()); break; } - case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings default: @@ -2152,12 +2160,21 @@ vector database_api_impl::get_votes_ids(const string &account_name votes_info database_api_impl::get_votes(const string &account_name_or_id) const { votes_info result; - const auto &votes_ids = get_votes_ids(account_name_or_id); - const auto &committee_ids = get_votes_objects(votes_ids); - const auto &witness_ids = get_votes_objects(votes_ids); - const auto &for_worker_ids = get_votes_objects(votes_ids); - const auto &against_worker_ids = get_votes_objects(votes_ids); - const auto &son_ids = get_votes_objects(votes_ids, 5); + const auto votes_ids = get_votes_ids(account_name_or_id); + const auto committee_ids = get_votes_objects(votes_ids); + const auto witness_ids = get_votes_objects(votes_ids); + const auto for_worker_ids = get_votes_objects(votes_ids); + const auto against_worker_ids = get_votes_objects(votes_ids); + const auto son_ids = [this, &votes_ids]() { + flat_map> son_ids; + const auto son_bitcoin_ids = get_votes_objects(votes_ids, 5); + if (!son_bitcoin_ids.empty()) + son_ids[sidechain_type::bitcoin] = std::move(son_bitcoin_ids); + const auto son_hive_ids = get_votes_objects(votes_ids, 5); + if (!son_hive_ids.empty()) + son_ids[sidechain_type::hive] = std::move(son_hive_ids); + return son_ids; + }(); //! Fill votes info if (!committee_ids.empty()) { @@ -2201,11 +2218,15 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const } if (!son_ids.empty()) { - vector votes_for_sons; - votes_for_sons.reserve(son_ids.size()); - for (const auto &son : son_ids) { - const auto &son_obj = son.as(6); - votes_for_sons.emplace_back(votes_info_object{son_obj.vote_id, son_obj.id}); + flat_map> votes_for_sons; + for (const auto &son_sidechain_ids : son_ids) { + const auto &sidechain = son_sidechain_ids.first; + const auto &sidechain_ids = son_sidechain_ids.second; + votes_for_sons[sidechain].reserve(sidechain_ids.size()); + for (const auto &son : sidechain_ids) { + const auto &son_obj = son.as(6); + votes_for_sons[sidechain].emplace_back(votes_info_object{son_obj.get_sidechain_vote_id(sidechain), son_obj.id}); + } } result.votes_for_sons = std::move(votes_for_sons); } @@ -2379,12 +2400,16 @@ voters_info database_api_impl::get_voters(const string &account_name_or_id) cons //! Info for son voters if (son_object) { - const auto &son_voters = get_voters_by_id(son_object->vote_id); - voters_info_object voters_for_son; - voters_for_son.vote_id = son_object->vote_id; - voters_for_son.voters.reserve(son_voters.size()); - for (const auto &voter : son_voters) { - voters_for_son.voters.emplace_back(voter.get_id()); + flat_map voters_for_son; + for (const auto &vote_id : son_object->sidechain_vote_ids) { + const auto &son_voters = get_voters_by_id(vote_id.second); + voters_info_object voters_for_sidechain_son; + voters_for_sidechain_son.vote_id = vote_id.second; + voters_for_sidechain_son.voters.reserve(son_voters.size()); + for (const auto &voter : son_voters) { + voters_for_sidechain_son.voters.emplace_back(voter.get_id()); + } + voters_for_son[vote_id.first] = std::move(voters_for_sidechain_son); } result.voters_for_son = std::move(voters_for_son); } diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 3026c2f0..5f1a4416 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -68,8 +68,14 @@ void verify_account_votes( const database& db, const account_options& options ) "Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) ); FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); - FC_ASSERT( options.num_son() <= chain_params.maximum_son_count(), - "Voted for more sons than currently allowed (${c})", ("c", chain_params.maximum_son_count()) ); + FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" ); + FC_ASSERT( options.extensions.value.num_son.valid() , "Invalid son number" ); + for(const auto& num_sons : *options.extensions.value.num_son) + { + FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count, + "Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) ); + } + FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." ); uint32_t max_vote_id = gpo.next_available_vote_id; bool has_worker_votes = false; diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index c475813b..9606c59e 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -705,7 +705,12 @@ void database::_apply_block( const signed_block& next_block ) if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { update_witness_schedule(next_block); - if(global_props.active_sons.size() > 0) { + bool need_to_update_son_schedule = false; + for(const auto& active_sons : global_props.active_sons){ + if(!active_sons.second.empty()) + need_to_update_son_schedule = true; + } + if(need_to_update_son_schedule) { update_son_schedule(next_block); } } @@ -739,10 +744,18 @@ void database::_apply_block( const signed_block& next_block ) // TODO: figure out if we could collapse this function into // update_global_dynamic_data() as perhaps these methods only need // to be called for header validation? + update_maintenance_flag( maint_needed ); if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { update_witness_schedule(); - if(global_props.active_sons.size() > 0) { + + bool need_update_son_schedule = false; + for(const auto& active_sidechain_type : active_sidechain_types) { + if(global_props.active_sons.at(active_sidechain_type).size() > 0) { + need_update_son_schedule = true; + } + } + if(need_update_son_schedule) { update_son_schedule(); } } diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 78740c9b..0bb9b10b 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -222,17 +222,30 @@ std::set database::get_sons_to_be_deregistered() for( auto& son : son_idx ) { - if(son.status == son_status::in_maintenance) + bool need_to_be_deregistered = true; + for(const auto& status : son.statuses) { - auto stats = son.statistics(*this); - // TODO : We need to add a function that returns if we can deregister SON - // i.e. with introduction of PW code, we have to make a decision if the SON - // is needed for release of funds from the PW - if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())) + const auto& sidechain = status.first; + if(status.second != son_status::in_maintenance) + need_to_be_deregistered = false; + + if(need_to_be_deregistered) { - ret.insert(son.id); + auto stats = son.statistics(*this); + + // TODO : We need to add a function that returns if we can deregister SON + // i.e. with introduction of PW code, we have to make a decision if the SON + // is needed for release of funds from the PW + if (head_block_time() - stats.last_down_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { + need_to_be_deregistered = false; + } } } + + if(need_to_be_deregistered) + { + ret.insert(son.id); + } } return ret; } @@ -289,28 +302,51 @@ bool database::is_son_dereg_valid( son_id_type son_id ) return false; } - return (son->status == son_status::in_maintenance && - (head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))); + bool status_son_dereg_valid = true; + for(const auto& status : son->statuses) + { + const auto& sidechain = status.first; + if(status.second != son_status::in_maintenance) + status_son_dereg_valid = false; + + if(status_son_dereg_valid) + { + if(head_block_time() - son->statistics(*this).last_down_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) + { + status_son_dereg_valid = false; + } + } + } + + return status_son_dereg_valid; } -bool database::is_son_active( son_id_type son_id ) +bool database::is_son_active( sidechain_type type, son_id_type son_id ) { const auto& son_idx = get_index_type().indices().get< by_id >(); auto son = son_idx.find( son_id ); - if(son == son_idx.end()) - { + if(son == son_idx.end()) { return false; } const global_property_object& gpo = get_global_properties(); + if(!gpo.active_sons.contains(type)) { + return false; + } + + const auto& gpo_as = gpo.active_sons.at(type); vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + active_son_ids.reserve(gpo_as.size()); + std::transform(gpo_as.cbegin(), gpo_as.cend(), std::inserter(active_son_ids, active_son_ids.end()), [](const son_info& swi) { return swi.son_id; }); + if(active_son_ids.empty()) { + return false; + } + auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); return (it_son != active_son_ids.end()); } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index c4aabfa8..cc862618 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -1100,8 +1100,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() ); // Initialize witness schedule -#ifndef NDEBUG - const son_schedule_object& sso = + + #ifndef NDEBUG + const son_schedule_object& ssohive = #endif create([&](son_schedule_object& _sso) { @@ -1120,13 +1121,29 @@ void database::init_genesis(const genesis_state_type& genesis_state) _sso.recent_slots_filled = fc::uint128::max_value(); }); - assert( sso.id == son_schedule_id_type() ); + assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); - // Enable fees - modify(get_global_properties(), [&genesis_state](global_property_object& p) { - p.parameters.current_fees = genesis_state.initial_parameters.current_fees; +#ifndef NDEBUG + const son_schedule_object& ssobitcoin = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_witnesses = get_global_properties().active_witnesses; + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); + + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); }); - + assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) ); // Create FBA counters create([&]( fba_accumulator_object& acc ) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index c3f826d1..d18e0f26 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -78,26 +78,30 @@ vector> database::sort } template<> -vector> database::sort_votable_objects(size_t count) const +vector> database::sort_votable_objects(sidechain_type sidechain, size_t count) const { const auto& all_sons = get_index_type().indices().get< by_id >(); std::vector> refs; for( auto& son : all_sons ) { - if(son.has_valid_config(head_block_time()) && son.status != son_status::deregistered) + if(son.has_valid_config(head_block_time()) && son.statuses.at(sidechain) != son_status::deregistered) { refs.push_back(std::cref(son)); } } count = std::min(count, refs.size()); std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), - [this](const son_object& a, const son_object& b)->bool { - share_type oa_vote = _vote_tally_buffer[a.vote_id]; - share_type ob_vote = _vote_tally_buffer[b.vote_id]; + [this, sidechain](const son_object& a, const son_object& b)->bool { + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + + const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; + const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; + if( oa_vote != ob_vote ) return oa_vote > ob_vote; - return a.vote_id < b.vote_id; - }); + + return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain); +}); refs.resize(count, refs.front()); return refs; @@ -178,222 +182,233 @@ void database::update_worker_votes() void database::pay_sons() { - time_point_sec now = head_block_time(); + const time_point_sec now = head_block_time(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); // Current requirement is that we have to pay every 24 hours, so the following check - if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { - assert( _son_count_histogram_buffer.size() > 0 ); - const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; - /// accounts that vote for 0 or 1 son do not get to express an opinion on - /// the number of sons to have (they abstain and are non-voting accounts) - share_type stake_tally = 0; - size_t son_count = 0; - if( stake_target > 0 ) + if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) + { + for(const auto& active_sidechain_type : active_sidechain_types) { - while( (son_count < _son_count_histogram_buffer.size() - 1) - && (stake_tally <= stake_target) ) + assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 ); + const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2; + /// accounts that vote for 0 or 1 son do not get to express an opinion on + /// the number of sons to have (they abstain and are non-voting accounts) + share_type stake_tally = 0; + size_t son_count = 0; + if( stake_target > 0 ) { - stake_tally += _son_count_histogram_buffer[++son_count]; - } - } - const vector> sons = [this, &son_count]{ - if(head_block_time() >= HARDFORK_SON3_TIME) - return sort_votable_objects(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)); - else - return sort_votable_objects(get_global_properties().parameters.maximum_son_count()); - }(); - // After SON2 HF - uint64_t total_votes = 0; - for( const son_object& son : sons ) - { - total_votes += _vote_tally_buffer[son.vote_id]; - } - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { - uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); - return weight; - }; - // Before SON2 HF - auto get_weight_before_son2_hf = []( uint64_t son_votes ) { - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0); - uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); - return weight; - }; - uint64_t weighted_total_txs_signed = 0; - share_type son_budget = dpo.son_budget; - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf](const object& o) { - const son_statistics_object& s = static_cast(o); - const auto& idx = get_index_type().indices().get(); - auto son_obj = idx.find( s.owner ); - auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); - if( now < HARDFORK_SON2_TIME ) { - son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]); - } - uint64_t txs_signed = 0; - for (const auto &ts : s.txs_signed) { - txs_signed = txs_signed + ts.second; - } - weighted_total_txs_signed += (txs_signed * son_weight); - }); - - // Now pay off each SON proportional to the number of transactions signed. - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now](const object& o) { - const son_statistics_object& s = static_cast(o); - uint64_t txs_signed = 0; - for (const auto &ts : s.txs_signed) { - txs_signed = txs_signed + ts.second; + while( (son_count < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1) + && (stake_tally <= stake_target) ) + { + stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[++son_count]; + } } - if(txs_signed > 0){ + const auto sons = sort_votable_objects(active_sidechain_type, + (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)) + ); + + // After SON2 HF + uint64_t total_votes = 0; + for( const son_object& son : sons ) + { + total_votes += _vote_tally_buffer[son.sidechain_vote_ids.at(active_sidechain_type)]; + } + const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { + const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); + return weight; + }; + // Before SON2 HF + auto get_weight_before_son2_hf = []( uint64_t son_votes ) { + const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0); + const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); + return weight; + }; + uint64_t weighted_total_txs_signed = 0; + const share_type son_budget = dpo.son_budget; + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type](const object& o) { + const son_statistics_object& s = static_cast(o); const auto& idx = get_index_type().indices().get(); - auto son_obj = idx.find( s.owner ); - auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); - if( now < HARDFORK_SON2_TIME ) { - son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]); + const auto son_obj = idx.find( s.owner ); + uint16_t son_weight = 0; + if( now >= HARDFORK_SON2_TIME ) { + son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); } - share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; - modify( *son_obj, [&]( son_object& _son_obj) - { - _son_obj.pay_son_fee(pay, *this); - }); - //Remove the amount paid out to SON from global SON Budget - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.son_budget -= pay; - } ); - //Reset the tx counter in each son statistics object - modify( s, [&]( son_statistics_object& _s) - { - for (const auto &ts : s.txs_signed) { - _s.txs_signed.at(ts.first) = 0; + else { + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + } + const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; + weighted_total_txs_signed += (txs_signed * son_weight); + }); + + // Now pay off each SON proportional to the number of transactions signed. + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type](const object& o) { + const son_statistics_object& s = static_cast(o); + const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; + + if(txs_signed > 0){ + const auto& idx = get_index_type().indices().get(); + auto son_obj = idx.find( s.owner ); + uint16_t son_weight = 0; + if( now >= HARDFORK_SON2_TIME ) { + son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); } - }); - } - }); - //Note the last son pay out time - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.last_son_payout_time = now; - }); + else { + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + } + const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; + modify( *son_obj, [&]( son_object& _son_obj) + { + _son_obj.pay_son_fee(pay, *this); + }); + //Remove the amount paid out to SON from global SON Budget + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.son_budget -= pay; + } ); + //Reset the tx counter in each son statistics object + modify( s, [&]( son_statistics_object& _s) + { + if(_s.txs_signed.contains(active_sidechain_type)) + _s.txs_signed.at(active_sidechain_type) = 0; + }); + } + }); + //Note the last son pay out time + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.last_son_payout_time = now; + }); + } } } -void database::update_son_metrics(const vector& curr_active_sons) +void database::update_son_metrics(const flat_map >& curr_active_sons) { - vector current_sons; + for(const auto& curr_active_sidechain_sons : curr_active_sons) { + const auto& sidechain = curr_active_sidechain_sons.first; + const auto& _curr_active_sidechain_sons = curr_active_sidechain_sons.second; - current_sons.reserve(curr_active_sons.size()); - std::transform(curr_active_sons.begin(), curr_active_sons.end(), - std::inserter(current_sons, current_sons.end()), - [](const son_info &swi) { - return swi.son_id; - }); + vector current_sons; - const auto& son_idx = get_index_type().indices().get< by_id >(); - for( auto& son : son_idx ) - { - auto& stats = son.statistics(*this); - bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end()); - modify( stats, [&]( son_statistics_object& _stats ) - { - if(is_active_son) { - _stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval; - } - _stats.total_downtime += _stats.current_interval_downtime; - _stats.current_interval_downtime = 0; - for (const auto &str : _stats.sidechain_txs_reported) { - _stats.sidechain_txs_reported.at(str.first) = 0; - } - }); + current_sons.reserve(_curr_active_sidechain_sons.size()); + std::transform(_curr_active_sidechain_sons.cbegin(), _curr_active_sidechain_sons.cend(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + const auto &son_idx = get_index_type().indices().get(); + for (auto &son : son_idx) { + auto &stats = son.statistics(*this); + bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end()); + modify(stats, [&](son_statistics_object &_stats) { + if (is_active_son) { + _stats.total_voted_time[sidechain] = _stats.total_voted_time[sidechain] + get_global_properties().parameters.maintenance_interval; + } + _stats.total_downtime[sidechain] += _stats.current_interval_downtime[sidechain]; + _stats.current_interval_downtime[sidechain] = 0; + _stats.sidechain_txs_reported[sidechain] = 0; + }); + } } } -void database::update_son_statuses(const vector& curr_active_sons, const vector& new_active_sons) +void database::update_son_statuses( const flat_map >& curr_active_sons, + const flat_map >& new_active_sons ) { - vector current_sons, new_sons; - vector sons_to_remove, sons_to_add; - const auto& idx = get_index_type().indices().get(); + for(const auto& new_active_sidechain_sons : new_active_sons) { + const auto& sidechain = new_active_sidechain_sons.first; - current_sons.reserve(curr_active_sons.size()); - std::transform(curr_active_sons.begin(), curr_active_sons.end(), - std::inserter(current_sons, current_sons.end()), - [](const son_info &swi) { - return swi.son_id; - }); + vector current_sons, new_sons; + vector sons_to_remove, sons_to_add; + const auto &idx = get_index_type().indices().get(); - new_sons.reserve(new_active_sons.size()); - std::transform(new_active_sons.begin(), new_active_sons.end(), - std::inserter(new_sons, new_sons.end()), - [](const son_info &swi) { - return swi.son_id; - }); - - // find all cur_active_sons members that is not in new_active_sons - for_each(current_sons.begin(), current_sons.end(), - [&sons_to_remove, &new_sons](const son_id_type& si) - { - if(std::find(new_sons.begin(), new_sons.end(), si) == - new_sons.end()) - { - sons_to_remove.push_back(si); - } - } - ); - - for( const auto& sid : sons_to_remove ) - { - auto son = idx.find( sid ); - if(son == idx.end()) // SON is deleted already - continue; - // keep maintenance status for nodes becoming inactive - if(son->status == son_status::active) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::inactive; - }); + if(curr_active_sons.contains(sidechain)) { + current_sons.reserve(curr_active_sons.at(sidechain).size()); + std::transform(curr_active_sons.at(sidechain).cbegin(), curr_active_sons.at(sidechain).cend(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); } - } - // find all new_active_sons members that is not in cur_active_sons - for_each(new_sons.begin(), new_sons.end(), - [&sons_to_add, ¤t_sons](const son_id_type& si) - { - if(std::find(current_sons.begin(), current_sons.end(), si) == - current_sons.end()) - { - sons_to_add.push_back(si); - } - } - ); + new_sons.reserve(new_active_sons.at(sidechain).size()); + std::transform(new_active_sons.at(sidechain).cbegin(), new_active_sons.at(sidechain).cend(), + std::inserter(new_sons, new_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); - for( const auto& sid : sons_to_add ) - { - auto son = idx.find( sid ); - FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); - // keep maintenance status for new nodes - if(son->status == son_status::inactive) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::active; - }); - } - } + // find all cur_active_sons members that is not in new_active_sons + for_each(current_sons.begin(), current_sons.end(), + [&sons_to_remove, &new_sons](const son_id_type &si) { + if (std::find(new_sons.begin(), new_sons.end(), si) == + new_sons.end()) { + sons_to_remove.push_back(si); + } + }); - ilog("New SONS"); - for(size_t i = 0; i < new_sons.size(); i++) { - auto son = idx.find( new_sons[i] ); - if(son == idx.end()) // SON is deleted already + for (const auto &sid : sons_to_remove) { + auto son = idx.find(sid); + if (son == idx.end()) // SON is deleted already continue; - ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) ); + // keep maintenance status for nodes becoming inactive + if (son->statuses.at(sidechain) == son_status::active) { + modify(*son, [&](son_object &obj) { + obj.statuses.at(sidechain) = son_status::inactive; + }); + } + } + + // find all new_active_sons members that is not in cur_active_sons + for_each(new_sons.begin(), new_sons.end(), + [&sons_to_add, ¤t_sons](const son_id_type &si) { + if (std::find(current_sons.begin(), current_sons.end(), si) == + current_sons.end()) { + sons_to_add.push_back(si); + } + }); + + for (const auto &sid : sons_to_add) { + auto son = idx.find(sid); + FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); + // keep maintenance status for new nodes + if (son->statuses.at(sidechain) == son_status::inactive) { + modify(*son, [&](son_object &obj) { + obj.statuses.at(sidechain) = son_status::active; + }); + } + } + + ilog("New SONS for sidechain = ${sidechain}", ("sidechain", sidechain)); + for (size_t i = 0; i < new_sons.size(); i++) { + auto son = idx.find(new_sons[i]); + if (son == idx.end()) // SON is deleted already + continue; + ilog("${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->statuses.at(sidechain))("sv", son->total_votes)); + } } - if( sons_to_remove.size() > 0 ) - { + //! Remove inactive sons (when all sidechain inactive) + vector sons_to_remove; + const auto &idx = get_index_type().indices().get(); + for(const auto& son : idx) { + bool inactive_son = true; + for(const auto& status : son.statuses) { + if (status.second != son_status::inactive) + inactive_son = false; + } + if (inactive_son) + sons_to_remove.emplace_back(son.id); + } + if (sons_to_remove.size() > 0) { remove_inactive_son_proposals(sons_to_remove); } } -void database::update_son_wallet(const vector& new_active_sons) +void database::update_son_wallet(const flat_map >& new_active_sons) { bool should_recreate_pw = true; @@ -406,8 +421,16 @@ void database::update_son_wallet(const vector& new_active_sons) bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); if (wallet_son_sets_equal) { - for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { - wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); + for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) { + const auto& sidechain = cur_wallet_sidechain_sons.first; + const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second; + + wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_active_sons.at(sidechain).size()); + if (wallet_son_sets_equal) { + for (size_t i = 0; i < _cur_wallet_sidechain_sons.size(); i++) { + wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i)); + } + } } } @@ -420,14 +443,24 @@ void database::update_son_wallet(const vector& new_active_sons) } } - should_recreate_pw = should_recreate_pw && (new_active_sons.size() >= get_chain_properties().immutable_parameters.min_son_count); + bool should_recreate_pw_sidechain = false; + for(const auto& new_active_sidechain_sons : new_active_sons) { + if(new_active_sidechain_sons.second.size() >= get_chain_properties().immutable_parameters.min_son_count) + should_recreate_pw_sidechain = true; + } + should_recreate_pw = should_recreate_pw && should_recreate_pw_sidechain; if (should_recreate_pw) { // Create new son_wallet_object, to initiate wallet recreation create( [&]( son_wallet_object& obj ) { obj.valid_from = head_block_time(); obj.expires = time_point_sec::maximum(); - obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); + for(const auto& new_active_sidechain_sons : new_active_sons){ + const auto& sidechain = new_active_sidechain_sons.first; + const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second; + + obj.sons[sidechain].insert(obj.sons[sidechain].end(), _new_active_sidechain_sons.cbegin(), _new_active_sidechain_sons.cend()); + } }); } } @@ -684,48 +717,87 @@ void database::update_active_sons() } assert( _son_count_histogram_buffer.size() > 0 ); - share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; + for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ + assert( son_count_histogram_buffer.second.size() > 0 ); + } + + const flat_map stake_target = [this]{ + flat_map stake_target; + for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ + const auto sidechain = son_count_histogram_buffer.first; + stake_target[sidechain] = (_total_voting_stake-son_count_histogram_buffer.second[0]) / 2; + } + return stake_target; + }(); /// accounts that vote for 0 or 1 son do not get to express an opinion on /// the number of sons to have (they abstain and are non-voting accounts) - - share_type stake_tally = 0; - - size_t son_count = 0; - if( stake_target > 0 ) - { - while( (son_count < _son_count_histogram_buffer.size() - 1) - && (stake_tally <= stake_target) ) + flat_map stake_tally = []{ + flat_map stake_tally; + for(const auto& active_sidechain_type : active_sidechain_types){ + stake_tally[active_sidechain_type] = 0; + } + return stake_tally; + }(); + flat_map son_count = []{ + flat_map son_count; + for(const auto& active_sidechain_type : active_sidechain_types){ + son_count[active_sidechain_type] = 0; + } + return son_count; + }(); + for( const auto& stake_target_sidechain : stake_target ){ + const auto sidechain = stake_target_sidechain.first; + if( stake_target_sidechain.second > 0 ) { - stake_tally += _son_count_histogram_buffer[++son_count]; + while( (son_count[sidechain] < _son_count_histogram_buffer.at(sidechain).size() - 1) + && (stake_tally[sidechain] <= stake_target_sidechain.second) ) + { + stake_tally[sidechain] += _son_count_histogram_buffer.at(sidechain)[ ++son_count[sidechain] ]; + } } } const global_property_object& gpo = get_global_properties(); - const vector> sons = [this, &son_count]{ - if(head_block_time() >= HARDFORK_SON3_TIME) - return sort_votable_objects(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)); - else - return sort_votable_objects(get_global_properties().parameters.maximum_son_count()); - }(); - + const chain_property_object& cpo = get_chain_properties(); const auto& all_sons = get_index_type().indices(); + flat_map > > sons; + for(const auto& active_sidechain_type : active_sidechain_types) + { + if(head_block_time() >= HARDFORK_SON3_TIME) { + sons[active_sidechain_type] = sort_votable_objects(active_sidechain_type, + (std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count))); + } + else { + sons[active_sidechain_type] = sort_votable_objects(active_sidechain_type, get_global_properties().parameters.maximum_son_count()); + } + } auto& local_vote_buffer_ref = _vote_tally_buffer; for( const son_object& son : all_sons ) { - if(son.status == son_status::request_maintenance) + for(const auto& status: son.statuses) { - auto& stats = son.statistics(*this); - modify( stats, [&]( son_statistics_object& _s){ - _s.last_down_timestamp = head_block_time(); + const auto& sidechain = status.first; + if(status.second == son_status::in_maintenance) + { + auto &stats = son.statistics(*this); + modify(stats, [&](son_statistics_object &_s) { + _s.last_down_timestamp[sidechain] = head_block_time(); }); + } } + modify( son, [local_vote_buffer_ref]( son_object& obj ){ - obj.total_votes = local_vote_buffer_ref[obj.vote_id]; - if(obj.status == son_status::request_maintenance) - obj.status = son_status::in_maintenance; - }); + for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){ + obj.total_votes[sidechain_vote_id.first] = local_vote_buffer_ref[sidechain_vote_id.second]; + } + for(auto& status: obj.statuses) + { + if (status.second == son_status::request_maintenance) + status.second = son_status::in_maintenance; + } + }); } // Update SON authority @@ -733,21 +805,24 @@ void database::update_active_sons() { modify( get(gpo.parameters.son_account()), [&]( account_object& a ) { + set account_ids; + for(const auto& sidechain_sons : sons) + { + for( const son_object& son : sidechain_sons.second ) + { + account_ids.emplace(son.son_account); + } + } + if( head_block_time() < HARDFORK_533_TIME ) { - map weights; a.active.weight_threshold = 0; a.active.account_auths.clear(); - for( const son_object& son : sons ) - { - weights.emplace(son.son_account, uint64_t(1)); - } - - for( const auto& weight : weights ) + for( const auto& account_id : account_ids ) { // Ensure that everyone has at least one vote. Zero weights aren't allowed. - a.active.account_auths[weight.first] += 1; + a.active.account_auths[account_id] += 1; a.active.weight_threshold += 1; } @@ -758,8 +833,10 @@ void database::update_active_sons() else { vote_counter vc; - for( const son_object& son : sons ) - vc.add( son.son_account, UINT64_C(1) ); + for( const auto& account_id : account_ids ) + { + vc.add(account_id, UINT64_C(1)); + } vc.finish_2_3( a.active ); } } ); @@ -767,22 +844,36 @@ void database::update_active_sons() // Compare current and to-be lists of active sons - auto cur_active_sons = gpo.active_sons; - vector new_active_sons; + const auto cur_active_sons = gpo.active_sons; + flat_map > new_active_sons; const auto &acc = get(gpo.parameters.son_account()); - for( const son_object& son : sons ) { - son_info swi; - swi.son_id = son.id; - swi.weight = acc.active.account_auths.at(son.son_account); - swi.signing_key = son.signing_key; - swi.sidechain_public_keys = son.sidechain_public_keys; - new_active_sons.push_back(swi); + for( const auto& sidechain_sons : sons ){ + const auto& sidechain = sidechain_sons.first; + const auto& sons_array = sidechain_sons.second; + + new_active_sons[sidechain].reserve(sons_array.size()); + for( const son_object& son : sons_array ) { + son_info swi; + swi.son_id = son.id; + swi.weight = acc.active.account_auths.at(son.son_account); + swi.signing_key = son.signing_key; + swi.public_key = son.sidechain_public_keys.at(sidechain); + new_active_sons[sidechain].push_back(swi); + } } bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size()); if (son_sets_equal) { - for( size_t i = 0; i < cur_active_sons.size(); i++ ) { - son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i); + for( const auto& cur_active_sidechain_sons : cur_active_sons ){ + const auto& sidechain = cur_active_sidechain_sons.first; + const auto& _cur_active_sidechain_sons = cur_active_sidechain_sons.second; + + son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.size() == new_active_sons.at(sidechain).size()); + if (son_sets_equal) { + for (size_t i = 0; i < _cur_active_sidechain_sons.size(); i++) { + son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i)); + } + } } } @@ -797,28 +888,37 @@ void database::update_active_sons() modify(gpo, [&]( global_property_object& gp ){ gp.active_sons.clear(); gp.active_sons.reserve(new_active_sons.size()); - gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end()); - }); + for( const auto& new_active_sidechain_sons : new_active_sons ) { + const auto& sidechain = new_active_sidechain_sons.first; + const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second; - const son_schedule_object& sso = son_schedule_id_type()(*this); - modify(sso, [&](son_schedule_object& _sso) - { - flat_set active_sons; - active_sons.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(active_sons, active_sons.end()), - [](const son_info& swi) { - return swi.son_id; - }); - _sso.scheduler.update(active_sons); - // similar to witness, produce schedule for sons - if(cur_active_sons.size() == 0 && new_active_sons.size() > 0) - { - witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); - for( size_t i=0; i active_sons; + active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size()); + std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), + std::inserter(active_sons, active_sons.end()), + [](const son_info& swi) { + return swi.son_id; + }); + _sso.scheduler.update(active_sons); + // similar to witness, produce schedule for sons + if(cur_active_sons.at(active_sidechain_type).size() == 0 && new_active_sons.at(active_sidechain_type).size() > 0) + { + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + for( size_t i=0; i& target; }; + struct clear_canary_map { + clear_canary_map(flat_map >& target): target(target){} + ~clear_canary_map() { + for(auto& sidechain_target : target){ + sidechain_target.second.clear(); + } + } + private: + flat_map >& target; + }; clear_canary a(_witness_count_histogram_buffer), b(_committee_count_histogram_buffer), - d(_son_count_histogram_buffer), c(_vote_tally_buffer); + clear_canary_map d{_son_count_histogram_buffer}; perform_son_tasks(); update_top_n_authorities(*this); diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 084c8e1d..f4df5ac2 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -74,21 +74,31 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const return wid; } -son_id_type database::get_scheduled_son( uint32_t slot_num )const +unsigned_int database::get_son_schedule_id( sidechain_type type )const +{ + static const map schedule_map = { + { sidechain_type::hive, 0 }, + { sidechain_type::bitcoin, 1 } + }; + + return schedule_map.at(type); +} + +son_id_type database::get_scheduled_son( sidechain_type type, uint32_t slot_num )const { son_id_type sid; const global_property_object& gpo = get_global_properties(); if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const son_schedule_object& sso = son_schedule_id_type()(*this); + const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this); uint64_t current_aslot = dpo.current_aslot + slot_num; return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ]; } if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && slot_num != 0 ) { - const son_schedule_object& sso = son_schedule_id_type()(*this); + const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this); // ask the near scheduler who goes in the given slot bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid); if(! slot_is_near) @@ -191,36 +201,41 @@ void database::update_witness_schedule() void database::update_son_schedule() { - const son_schedule_object& sso = son_schedule_id_type()(*this); const global_property_object& gpo = get_global_properties(); - if( head_block_num() % gpo.active_sons.size() == 0 ) + for(const auto& active_sidechain_type : active_sidechain_types) { - modify( sso, [&]( son_schedule_object& _sso ) + const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); + if( head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0) { - _sso.current_shuffled_sons.clear(); - _sso.current_shuffled_sons.reserve( gpo.active_sons.size() ); - - for( const son_info& w : gpo.active_sons ) - _sso.current_shuffled_sons.push_back( w.son_id ); - - auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; - for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i ) + modify( sidechain_sso, [&]( son_schedule_object& _sso ) { - /// High performance random generator - /// http://xorshift.di.unimi.it/ - uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL; - k ^= (k >> 12); - k ^= (k << 25); - k ^= (k >> 27); - k *= 2685821657736338717ULL; + _sso.current_shuffled_sons.clear(); + _sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() ); - uint32_t jmax = _sso.current_shuffled_sons.size() - i; - uint32_t j = i + k%jmax; - std::swap( _sso.current_shuffled_sons[i], - _sso.current_shuffled_sons[j] ); - } - }); + for ( const son_info &w : gpo.active_sons.at(active_sidechain_type) ) { + _sso.current_shuffled_sons.push_back(w.son_id); + } + + auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; + + for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i) + { + /// High performance random generator + /// http://xorshift.di.unimi.it/ + uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL; + k ^= (k >> 12); + k ^= (k << 25); + k ^= (k >> 27); + k *= 2685821657736338717ULL; + + uint32_t jmax = _sso.current_shuffled_sons.size() - i; + uint32_t j = i + k % jmax; + std::swap(_sso.current_shuffled_sons[i], + _sso.current_shuffled_sons[j]); + } + }); + } } } @@ -309,7 +324,15 @@ void database::update_son_schedule(const signed_block& next_block) auto start = fc::time_point::now(); const global_property_object& gpo = get_global_properties(); const son_schedule_object& sso = get(son_schedule_id_type()); - uint32_t schedule_needs_filled = gpo.active_sons.size(); + const flat_map schedule_needs_filled = [&gpo]() + { + flat_map schedule_needs_filled; + for(const auto& sidechain_active_sons : gpo.active_sons) + { + schedule_needs_filled[sidechain_active_sons.first] = sidechain_active_sons.second.size(); + } + return schedule_needs_filled; + }(); uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); // We shouldn't be able to generate _pending_block with timestamp @@ -319,48 +342,52 @@ void database::update_son_schedule(const signed_block& next_block) assert( schedule_slot > 0 ); - son_id_type first_son; - bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son ); - - son_id_type son; - const dynamic_global_property_object& dpo = get_dynamic_global_properties(); assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); - modify(sso, [&](son_schedule_object& _sso) + for(const auto& active_sidechain_type : active_sidechain_types) { - _sso.slots_since_genesis += schedule_slot; - witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis); + const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); + son_id_type first_son; + bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son ); + son_id_type son_id; - _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1); + modify(sidechain_sso, [&](son_schedule_object& _sso) + { + _sso.slots_since_genesis += schedule_slot; + witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis); + + _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(active_sidechain_type).size()) / 2, 1); + + if( slot_is_near ) + { + uint32_t drain = schedule_slot; + while( drain > 0 ) + { + if( _sso.scheduler.size() == 0 ) + break; + _sso.scheduler.consume_schedule(); + --drain; + } + } + else + { + _sso.scheduler.reset_schedule( first_son ); + } + while( !_sso.scheduler.get_slot(schedule_needs_filled.at(active_sidechain_type), son_id) ) + { + if( _sso.scheduler.produce_schedule(rng) & emit_turn ) + memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); + } + _sso.last_scheduling_block = next_block.block_num(); + _sso.recent_slots_filled = ( + (_sso.recent_slots_filled << 1) + + 1) << (schedule_slot - 1); + }); + } - if( slot_is_near ) - { - uint32_t drain = schedule_slot; - while( drain > 0 ) - { - if( _sso.scheduler.size() == 0 ) - break; - _sso.scheduler.consume_schedule(); - --drain; - } - } - else - { - _sso.scheduler.reset_schedule( first_son ); - } - while( !_sso.scheduler.get_slot(schedule_needs_filled, son) ) - { - if( _sso.scheduler.produce_schedule(rng) & emit_turn ) - memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); - } - _sso.last_scheduling_block = next_block.block_num(); - _sso.recent_slots_filled = ( - (_sso.recent_slots_filled << 1) - + 1) << (schedule_slot - 1); - }); auto end = fc::time_point::now(); static uint64_t total_time = 0; static uint64_t calls = 0; diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f62df938..eeb25167 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -245,7 +245,16 @@ namespace graphene { namespace chain { witness_id_type get_scheduled_witness(uint32_t slot_num)const; /** - * @brief Get the son scheduled for block production in a slot. + * @brief Get son schedule id for the given sidechain_type. + * + * type sidechain_type we getting schedule. + * + * returns Id of the schedule object. + */ + unsigned_int get_son_schedule_id(sidechain_type type)const; + + /** + * @brief Get the bitcoin or hive son scheduled for block production in a slot. * * slot_num always corresponds to a time in the future. * @@ -258,7 +267,7 @@ namespace graphene { namespace chain { * * Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS */ - son_id_type get_scheduled_son(uint32_t slot_num)const; + son_id_type get_scheduled_son(sidechain_type type, uint32_t slot_num)const; /** * Get the time at which the given slot occurs. @@ -313,7 +322,7 @@ namespace graphene { namespace chain { fc::optional create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ); signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); bool is_son_dereg_valid( son_id_type son_id ); - bool is_son_active( son_id_type son_id ); + bool is_son_active( sidechain_type type, son_id_type son_id ); bool is_asset_creation_allowed(const string& symbol); time_point_sec head_block_time()const; @@ -517,6 +526,9 @@ namespace graphene { namespace chain { template vector> sort_votable_objects(size_t count)const; + template + vector> sort_votable_objects(sidechain_type sidechain, size_t count)const; + //////////////////// db_block.cpp //////////////////// public: @@ -571,13 +583,14 @@ namespace graphene { namespace chain { void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void update_active_witnesses(); void update_active_committee_members(); - void update_son_metrics( const vector& curr_active_sons ); + void update_son_metrics( const flat_map >& curr_active_sons ); void update_active_sons(); void remove_son_proposal( const proposal_object& proposal ); void remove_inactive_son_down_proposals( const vector& son_ids_to_remove ); void remove_inactive_son_proposals( const vector& son_ids_to_remove ); - void update_son_statuses( const vector& cur_active_sons, const vector& new_active_sons ); - void update_son_wallet( const vector& new_active_sons ); + void update_son_statuses( const flat_map >& curr_active_sons, + const flat_map >& new_active_sons ); + void update_son_wallet( const flat_map >& new_active_sons ); void update_worker_votes(); public: @@ -616,11 +629,17 @@ namespace graphene { namespace chain { uint16_t _current_op_in_trx = 0; uint32_t _current_virtual_op = 0; - vector _vote_tally_buffer; - vector _witness_count_histogram_buffer; - vector _committee_count_histogram_buffer; - vector _son_count_histogram_buffer; - uint64_t _total_voting_stake; + vector _vote_tally_buffer; + vector _witness_count_histogram_buffer; + vector _committee_count_histogram_buffer; + flat_map > _son_count_histogram_buffer = []{ + flat_map > son_count_histogram_buffer; + for(const auto& active_sidechain_type : active_sidechain_types){ + son_count_histogram_buffer[active_sidechain_type] = vector{}; + } + return son_count_histogram_buffer; + }(); + uint64_t _total_voting_stake; flat_map _checkpoints; diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 53bdec08..71aba1dd 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -49,10 +49,18 @@ namespace graphene { namespace chain { chain_parameters parameters; optional pending_parameters; - uint32_t next_available_vote_id = 0; - vector active_committee_members; // updated once per maintenance interval - flat_set active_witnesses; // updated once per maintenance interval - vector active_sons; // updated once per maintenance interval + uint32_t next_available_vote_id = 0; + vector active_committee_members; // updated once per maintenance interval + flat_set active_witnesses; // updated once per maintenance interval + flat_map > active_sons = []() // updated once per maintenance interval + { + flat_map > active_sons; + for(const auto& active_sidechain_type : active_sidechain_types) + { + active_sons[active_sidechain_type] = vector(); + } + return active_sons; + }(); // n.b. witness scheduling is done by witness_schedule object }; diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index c6de2047..9c4c16d5 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -39,7 +40,15 @@ namespace graphene { namespace chain { { struct ext { - optional< uint16_t > num_son = 0; + /// The number of active son members this account votes the blockchain should appoint + /// Must not exceed the actual number of son members voted for in @ref votes + optional< flat_map > num_son = []{ + flat_map num_son; + for(const auto& active_sidechain_type : active_sidechain_types){ + num_son[active_sidechain_type] = 0; + } + return num_son; + }(); }; /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- @@ -57,14 +66,11 @@ namespace graphene { namespace chain { /// The number of active committee members this account votes the blockchain should appoint /// Must not exceed the actual number of committee members voted for in @ref votes uint16_t num_committee = 0; - /// The number of active son members this account votes the blockchain should appoint - /// Must not exceed the actual number of son members voted for in @ref votes - uint16_t num_son() const { return extensions.value.num_son.valid() ? *extensions.value.num_son : 0; } /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this /// account's balance of core asset. flat_set votes; extension< ext > extensions; - + /// Whether this account is voting inline bool is_voting() const { @@ -249,7 +255,7 @@ namespace graphene { namespace chain { */ struct account_upgrade_operation : public base_operation { - struct fee_parameters_type { + struct fee_parameters_type { uint64_t membership_annual_fee = 2000 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t membership_lifetime_fee = 10000 * GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to upgrade to a lifetime member }; @@ -294,7 +300,7 @@ namespace graphene { namespace chain { } } // graphene::chain -FC_REFLECT(graphene::chain::account_options::ext, (num_son) ) +FC_REFLECT(graphene::chain::account_options::ext, (num_son)) FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions)) // FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing) FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing, diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp index 5194bed2..75c3db85 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp @@ -11,7 +11,7 @@ namespace graphene { namespace chain { asset fee; account_id_type payer; - vector sons; + flat_map > sons; account_id_type fee_payer()const { return payer; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 8a46954d..023bb511 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -59,7 +59,8 @@ struct vote_id_type committee, witness, worker, - son, + son_bitcoin, + son_hive, VOTE_TYPE_COUNT }; @@ -144,7 +145,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui FC_REFLECT_TYPENAME( fc::flat_set ) -FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) ) +FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp index b8aa07c6..73be44d5 100644 --- a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -31,7 +31,7 @@ namespace graphene { namespace chain { time_point_sec expires; sidechain_address_object() : - sidechain(sidechain_type::bitcoin), + sidechain(sidechain_type::bitcoin), //! FIXME - bitcoin ??? deposit_public_key(""), deposit_address(""), withdraw_public_key(""), diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp index 7f986f96..1097323b 100644 --- a/libraries/chain/include/graphene/chain/sidechain_defs.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include namespace graphene { namespace chain { @@ -13,12 +14,14 @@ enum class sidechain_type { hive }; +static const std::set active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::hive}; + } } FC_REFLECT_ENUM(graphene::chain::sidechain_type, - (unknown) - (bitcoin) - (ethereum) - (eos) - (hive) - (peerplays) ) + (unknown) + (bitcoin) + (ethereum) + (eos) + (hive) + (peerplays) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/son_info.hpp b/libraries/chain/include/graphene/chain/son_info.hpp index 2bfecac4..75e58319 100644 --- a/libraries/chain/include/graphene/chain/son_info.hpp +++ b/libraries/chain/include/graphene/chain/son_info.hpp @@ -14,26 +14,15 @@ namespace graphene { namespace chain { son_id_type son_id; weight_type weight = 0; public_key_type signing_key; - flat_map sidechain_public_keys; + string public_key; - bool operator==(const son_info& rhs) { + bool operator==(const son_info& rhs) const { bool son_sets_equal = (son_id == rhs.son_id) && (weight == rhs.weight) && (signing_key == rhs.signing_key) && - (sidechain_public_keys.size() == rhs.sidechain_public_keys.size()); + (public_key == rhs.public_key); - if (son_sets_equal) { - bool sidechain_public_keys_equal = true; - for (size_t i = 0; i < sidechain_public_keys.size(); i++) { - const auto lhs_scpk = sidechain_public_keys.nth(i); - const auto rhs_scpk = rhs.sidechain_public_keys.nth(i); - sidechain_public_keys_equal = sidechain_public_keys_equal && - (lhs_scpk->first == rhs_scpk->first) && - (lhs_scpk->second == rhs_scpk->second); - } - son_sets_equal = son_sets_equal && sidechain_public_keys_equal; - } return son_sets_equal; } }; @@ -44,4 +33,4 @@ FC_REFLECT( graphene::chain::son_info, (son_id) (weight) (signing_key) - (sidechain_public_keys) ) + (public_key) ) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index d0b74e79..c9089bfb 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -35,15 +35,15 @@ namespace graphene { namespace chain { // Transactions signed since the last son payouts flat_map txs_signed; // Total Voted Active time i.e. duration selected as part of voted active SONs - uint64_t total_voted_time = 0; + flat_map total_voted_time; // Total Downtime barring the current down time in seconds, used for stats to present to user - uint64_t total_downtime = 0; + flat_map total_downtime; // Current Interval Downtime since last maintenance - uint64_t current_interval_downtime = 0; + flat_map current_interval_downtime; // Down timestamp, if son status is in_maintenance use this - fc::time_point_sec last_down_timestamp; + flat_map last_down_timestamp; // Last Active heartbeat timestamp - fc::time_point_sec last_active_timestamp; + flat_map last_active_timestamp; // Deregistered Timestamp fc::time_point_sec deregistered_timestamp; // Total sidechain transactions reported by SON network while SON was active @@ -64,23 +64,36 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_object_type; account_id_type son_account; - vote_id_type vote_id; - uint64_t total_votes = 0; + flat_map sidechain_vote_ids; + flat_map total_votes; string url; vesting_balance_id_type deposit; public_key_type signing_key; vesting_balance_id_type pay_vb; son_statistics_id_type statistics; - son_status status = son_status::inactive; + flat_map statuses = []() + { + flat_map statuses; + for(const auto& active_sidechain_type : active_sidechain_types) + { + statuses[active_sidechain_type] = son_status::inactive; + } + return statuses; + }(); flat_map sidechain_public_keys; void pay_son_fee(share_type pay, database& db); bool has_valid_config()const; bool has_valid_config(time_point_sec head_block_time)const; + + inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); } + inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); } + inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); } }; struct by_account; - struct by_vote_id; + struct by_vote_id_bitcoin; + struct by_vote_id_hive; using son_multi_index_type = multi_index_container< son_object, indexed_by< @@ -90,8 +103,11 @@ namespace graphene { namespace chain { ordered_unique< tag, member >, - ordered_unique< tag, - member + ordered_unique< tag, + const_mem_fun + >, + ordered_unique< tag, + const_mem_fun > > >; @@ -117,14 +133,14 @@ FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintena FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), (son_account) - (vote_id) + (sidechain_vote_ids) (total_votes) (url) (deposit) (signing_key) (pay_vb) (statistics) - (status) + (statuses) (sidechain_public_keys) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp index 315def33..63b546ea 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -21,7 +21,7 @@ namespace graphene { namespace chain { time_point_sec expires; flat_map addresses; - vector sons; + flat_map > sons; }; struct by_valid_from; diff --git a/libraries/chain/include/graphene/chain/voters_info.hpp b/libraries/chain/include/graphene/chain/voters_info.hpp index 53b0e74a..86f3e9cc 100644 --- a/libraries/chain/include/graphene/chain/voters_info.hpp +++ b/libraries/chain/include/graphene/chain/voters_info.hpp @@ -19,11 +19,11 @@ namespace graphene { namespace chain { * @ingroup object */ struct voters_info { - optional voters_for_committee_member; - optional voters_for_witness; - optional > voters_for_workers; - optional > voters_against_workers; - optional voters_for_son; + optional voters_for_committee_member; + optional voters_for_witness; + optional > voters_for_workers; + optional > voters_against_workers; + optional > voters_for_son; }; } } // graphene::chain @@ -37,4 +37,4 @@ FC_REFLECT( graphene::chain::voters_info, (voters_for_witness) (voters_for_workers) (voters_against_workers) - (voters_for_son) ) \ No newline at end of file + (voters_for_son)) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/votes_info.hpp b/libraries/chain/include/graphene/chain/votes_info.hpp index 0a515589..f405d83a 100644 --- a/libraries/chain/include/graphene/chain/votes_info.hpp +++ b/libraries/chain/include/graphene/chain/votes_info.hpp @@ -19,11 +19,11 @@ namespace graphene { namespace chain { * @ingroup object */ struct votes_info { - optional< vector< votes_info_object > > votes_for_committee_members; - optional< vector< votes_info_object > > votes_for_witnesses; - optional< vector< votes_info_object > > votes_for_workers; - optional< vector< votes_info_object > > votes_against_workers; - optional< vector< votes_info_object > > votes_for_sons; + optional< vector< votes_info_object > > votes_for_committee_members; + optional< vector< votes_info_object > > votes_for_witnesses; + optional< vector< votes_info_object > > votes_for_workers; + optional< vector< votes_info_object > > votes_against_workers; + optional< flat_map > > votes_for_sons; }; } } // graphene::chain @@ -37,4 +37,4 @@ FC_REFLECT( graphene::chain::votes_info, (votes_for_witnesses) (votes_for_workers) (votes_against_workers) - (votes_for_sons) ) \ No newline at end of file + (votes_for_sons)) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index 2eff563f..945b218d 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -96,7 +96,7 @@ class son_schedule_object : public graphene::db::abstract_object current_shuffled_sons; + vector current_shuffled_sons; son_scheduler scheduler; uint32_t last_scheduling_block; diff --git a/libraries/chain/include/graphene/chain/witness_scheduler.hpp b/libraries/chain/include/graphene/chain/witness_scheduler.hpp index 42dfe149..40e0e1d2 100644 --- a/libraries/chain/include/graphene/chain/witness_scheduler.hpp +++ b/libraries/chain/include/graphene/chain/witness_scheduler.hpp @@ -162,8 +162,12 @@ class generic_witness_scheduler _schedule.pop_front(); auto it = _lame_duck.find( result ); - if( it != _lame_duck.end() ) - _lame_duck.erase( it ); + if( it != _lame_duck.end() ) { + set< WitnessID > removal_set; + removal_set.insert(*it); + remove_all( removal_set ); + _lame_duck.erase(it); + } if( debug ) check_invariant(); return result; } @@ -389,7 +393,7 @@ class generic_witness_scheduler // scheduled std::deque < WitnessID > _schedule; - // in _schedule, but not to be replaced + // in _schedule, but must be removed set< WitnessID > _lame_duck; }; diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index b980998c..bee6b1be 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -174,22 +174,27 @@ void account_options::validate() const { auto needed_witnesses = num_witness; auto needed_committee = num_committee; - auto needed_sons = num_son(); + FC_ASSERT( extensions.value.num_son.valid() , "Invalid son number" ); + flat_map needed_sons = *extensions.value.num_son; for( vote_id_type id : votes ) if( id.type() == vote_id_type::witness && needed_witnesses ) --needed_witnesses; else if ( id.type() == vote_id_type::committee && needed_committee ) --needed_committee; - else if ( id.type() == vote_id_type::son && needed_sons ) - --needed_sons; + else if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] ) + --needed_sons[sidechain_type::bitcoin]; + else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] ) + --needed_sons[sidechain_type::hive]; FC_ASSERT( needed_witnesses == 0, "May not specify fewer witnesses than the number voted for."); FC_ASSERT( needed_committee == 0, "May not specify fewer committee members than the number voted for."); - FC_ASSERT( needed_sons == 0, - "May not specify fewer SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0, + "May not specify fewer Bitcoin SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::hive] == 0, + "May not specify fewer Hive SONs than the number voted for."); } void affiliate_reward_distribution::validate() const diff --git a/libraries/chain/sidechain_address_evaluator.cpp b/libraries/chain/sidechain_address_evaluator.cpp index 625ef2f0..bd97fef5 100644 --- a/libraries/chain/sidechain_address_evaluator.cpp +++ b/libraries/chain/sidechain_address_evaluator.cpp @@ -47,7 +47,7 @@ void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_addr { try { const auto& sidx = db().get_index_type().indices().get(); const auto& son_obj = sidx.find(op.payer); - FC_ASSERT( son_obj != sidx.end() && db().is_son_active(son_obj->id), "Non active SON trying to update deposit address object" ); + FC_ASSERT( son_obj != sidx.end() && db().is_son_active(op.sidechain, son_obj->id), "Non active SON trying to update deposit address object" ); const auto& sdpke_idx = db().get_index_type().indices().get(); FC_ASSERT( op.deposit_address.valid() && op.deposit_public_key.valid() && op.deposit_address_data.valid(), "Update operation by SON is not valid"); FC_ASSERT( (*op.deposit_address).length() > 0 && (*op.deposit_public_key).length() > 0 && (*op.deposit_address_data).length() > 0, "SON should create a valid deposit address with valid deposit public key"); diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 776eb065..8d24bad6 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -38,14 +38,17 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op) object_id_type create_son_evaluator::do_apply(const son_create_operation& op) { try { - vote_id_type vote_id; - db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) { - vote_id = get_next_vote_id(p, vote_id_type::son); + vote_id_type vote_id_bitcoin; + vote_id_type vote_id_hive; + db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive](global_property_object& p) { + vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin); + vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive); }); const auto& new_son_object = db().create( [&]( son_object& obj ){ obj.son_account = op.owner_account; - obj.vote_id = vote_id; + obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin; + obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive; obj.url = op.url; obj.deposit = op.deposit; obj.signing_key = op.signing_key; @@ -94,7 +97,8 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op) if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key; if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys; if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb; - if(so.status == son_status::deregistered) so.status = son_status::inactive; + for(auto& status : so.statuses) + if(status.second == son_status::deregistered) status.second = son_status::inactive; }); } return op.son_id; @@ -127,7 +131,8 @@ void_result deregister_son_evaluator::do_apply(const son_deregister_operation& o }); db().modify(*son, [&op](son_object &so) { - so.status = son_status::deregistered; + for(auto& status : so.statuses) + status.second = son_status::deregistered; }); auto stats_obj = ss_idx.find(son->statistics); @@ -144,18 +149,28 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_id); + const auto itr = idx.find(op.son_id); FC_ASSERT( itr != idx.end() ); FC_ASSERT(itr->son_account == op.owner_account); auto stats = itr->statistics( db() ); // Inactive SONs need not send heartbeats - FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats"); + bool status_need_to_send_heartbeats = false; + for(const auto& status : itr->statuses) + { + if( (status.second == son_status::active) || (status.second == son_status::in_maintenance) || (status.second == son_status::request_maintenance) ) + status_need_to_send_heartbeats = true; + } + FC_ASSERT(status_need_to_send_heartbeats, "Inactive SONs need not send heartbeats"); // Account for network delays fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); // Account for server ntp sync difference fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval()); - FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time"); - FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp"); + for(const auto& active_sidechain_type : active_sidechain_types) { + if(stats.last_active_timestamp.contains(active_sidechain_type)) + FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type)); + if(stats.last_down_timestamp.contains(active_sidechain_type)) + FC_ASSERT(op.ts > stats.last_down_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} is invalid can't be <= last down timestamp", ("sidechain", active_sidechain_type)); + } FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold"); FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold"); return void_result(); @@ -164,44 +179,48 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op) { try { const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_id); + const auto itr = idx.find(op.son_id); if(itr != idx.end()) { const global_property_object& gpo = db().get_global_properties(); - vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id); - bool is_son_active = true; + for(const auto& active_sidechain_sons : gpo.active_sons) { + const auto& sidechain = active_sidechain_sons.first; + const auto& active_sons = active_sidechain_sons.second; - if(it_son == active_son_ids.end()) { - is_son_active = false; - } + vector active_son_ids; + active_son_ids.reserve(active_sons.size()); + std::transform(active_sons.cbegin(), active_sons.cend(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info &swi) { + return swi.son_id; + }); - if(itr->status == son_status::in_maintenance) { - db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) - { - sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch(); - sso.last_active_timestamp = op.ts; - } ); + const auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id); + bool is_son_active = true; - db().modify(*itr, [&is_son_active](son_object &so) { - if(is_son_active) { - so.status = son_status::active; - } else { - so.status = son_status::inactive; - } - }); - } else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { - db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) - { - sso.last_active_timestamp = op.ts; - } ); + if (it_son == active_son_ids.end()) { + is_son_active = false; + } + + if (itr->statuses.at(sidechain) == son_status::in_maintenance) { + db().modify(itr->statistics(db()), [&](son_statistics_object &sso) { + sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - sso.last_down_timestamp.at(sidechain).sec_since_epoch(); + sso.last_active_timestamp[sidechain] = op.ts; + }); + + db().modify(*itr, [&is_son_active, &sidechain](son_object &so) { + if (is_son_active) { + so.statuses[sidechain] = son_status::active; + } else { + so.statuses[sidechain] = son_status::inactive; + } + }); + } else if ((itr->statuses.at(sidechain) == son_status::active) || (itr->statuses.at(sidechain) == son_status::request_maintenance)) { + db().modify(itr->statistics(db()), [&](son_statistics_object &sso) { + sso.last_active_timestamp[sidechain] = op.ts; + }); + } } } return op.son_id; @@ -213,29 +232,40 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); - auto itr = idx.find(op.son_id); - auto stats = itr->statistics( db() ); - FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); - FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp"); + const auto itr = idx.find(op.son_id); + const auto stats = itr->statistics( db() ); + bool status_need_to_report_down = false; + for(const auto& status : itr->statuses) + { + if( (status.second == son_status::active) || (status.second == son_status::request_maintenance) ) + status_need_to_report_down = true; + } + FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); + for(const auto& active_sidechain_type : active_sidechain_types) { + FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type)); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op) { try { const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_id); + const auto itr = idx.find(op.son_id); if(itr != idx.end()) { - if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { - db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) - { - sso.last_down_timestamp = op.down_ts; - }); + for( const auto& status : itr->statuses ) { + const auto& sidechain = status.first; - db().modify(*itr, [&op](son_object &so) { - so.status = son_status::in_maintenance; - }); - } + if ((status.second == son_status::active) || (status.second == son_status::request_maintenance)) { + db().modify(*itr, [&sidechain](son_object &so) { + so.statuses[sidechain] = son_status::in_maintenance; + }); + + db().modify(itr->statistics(db()), [&](son_statistics_object &sso) { + sso.last_down_timestamp[sidechain] = op.down_ts; + }); + } + } } return op.son_id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -249,9 +279,19 @@ void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operati FC_ASSERT( itr != idx.end() ); // Inactive SONs can't go to maintenance, toggle between active and request_maintenance states if(op.request_type == son_maintenance_request_type::request_maintenance) { - FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance"); - } else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) { - FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request"); + bool status_active = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::active) ) + status_active = true; + } + FC_ASSERT(status_active, "Inactive SONs can't request for maintenance"); + } else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) { + bool status_request_maintenance = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::request_maintenance) ) + status_request_maintenance = true; + } + FC_ASSERT(status_request_maintenance, "Only maintenance requested SONs can cancel the request"); } else { FC_ASSERT(false, "Invalid maintenance operation"); } @@ -264,15 +304,33 @@ object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operati auto itr = idx.find(op.son_id); if(itr != idx.end()) { - if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) { - db().modify(*itr, [](son_object &so) { - so.status = son_status::request_maintenance; - }); - } else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) { - db().modify(*itr, [](son_object &so) { - so.status = son_status::active; - }); - } + bool status_active = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::active) ) + status_active = true; + } + if(status_active && op.request_type == son_maintenance_request_type::request_maintenance) { + db().modify(*itr, [](son_object &so) { + for(auto& status : so.statuses) { + status.second = son_status::request_maintenance; + } + }); + } + else + { + bool status_request_maintenance = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::request_maintenance) ) + status_request_maintenance = true; + } + if(status_request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) { + db().modify(*itr, [](son_object &so) { + for(auto& status : so.statuses) { + status.second = son_status::active; + } + }); + } + } } return op.son_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp index f9620282..8d0199d4 100644 --- a/libraries/chain/son_wallet_deposit_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -23,9 +23,9 @@ void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_de const auto &swdo_idx = db().get_index_type().indices().get(); const auto swdo = swdo_idx.find(op.sidechain_uid); if (swdo == swdo_idx.end()) { - auto &gpo = db().get_global_properties(); + const auto &gpo = db().get_global_properties(); bool expected = false; - for (auto &si : gpo.active_sons) { + for (auto &si : gpo.active_sons.at(op.sidechain)) { if (op.son_id == si.son_id) { expected = true; break; @@ -78,8 +78,8 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de swdo.peerplays_to = op.peerplays_to; swdo.peerplays_asset = op.peerplays_asset; - auto &gpo = db().get_global_properties(); - for (auto &si : gpo.active_sons) { + const auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons.at(op.sidechain)) { swdo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); auto stats_itr = db().get_index_type().indices().get().find(si.son_id); @@ -142,11 +142,11 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); - FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_deposit_id); FC_ASSERT(itr != idx.end(), "Son wallet deposit not found"); + FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); FC_ASSERT(!itr->processed, "Son wallet deposit is already processed"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 0baed1cb..9b1ff1b7 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -20,8 +20,16 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size()); if (son_sets_equal) { - for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { - son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i); + for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) { + const auto& sidechain = cur_wallet_sidechain_sons.first; + const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second; + + son_sets_equal = son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_wallet_sons.at(sidechain).size()); + if (son_sets_equal) { + for (size_t i = 0; i < cur_wallet_sons.size(); i++) { + son_sets_equal = son_sets_equal && _cur_wallet_sidechain_sons.at(i) == new_wallet_sons.at(sidechain).at(i); + } + } } } diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index 2110e49d..ae0a867b 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -23,15 +23,15 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w const auto &swwo_idx = db().get_index_type().indices().get(); const auto swwo = swwo_idx.find(op.peerplays_uid); if (swwo == swwo_idx.end()) { - auto &gpo = db().get_global_properties(); + const auto &gpo = db().get_global_properties(); bool expected = false; - for (auto &si : gpo.active_sons) { + for (auto &si : gpo.active_sons.at(op.sidechain)) { if (op.son_id == si.son_id) { expected = true; break; } } - FC_ASSERT(expected, "Only active SON can create deposit"); + FC_ASSERT(expected, "Only active SON can create withdraw"); } else { bool exactly_the_same = true; exactly_the_same = exactly_the_same && (swwo->sidechain == op.sidechain); @@ -76,8 +76,8 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w swwo.withdraw_currency = op.withdraw_currency; swwo.withdraw_amount = op.withdraw_amount; - auto &gpo = db().get_global_properties(); - for (auto &si : gpo.active_sons) { + const auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons.at(op.sidechain)) { swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); auto stats_itr = db().get_index_type().indices().get().find(si.son_id); @@ -140,11 +140,11 @@ void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_ { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); - FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_withdraw_id); FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); + FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index 9ea2ce34..d7337e42 100755 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -3,7 +3,7 @@ file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp") add_library( peerplays_sidechain peerplays_sidechain_plugin.cpp sidechain_api.cpp - sidechain_net_manager.cpp + sidechain_net_handler_factory.cpp sidechain_net_handler.cpp sidechain_net_handler_bitcoin.cpp sidechain_net_handler_hive.cpp diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 92591d0a..114f9bc7 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -13,16 +13,18 @@ class peerplays_sidechain_plugin_impl; } struct son_proposal_type { - son_proposal_type(int op, son_id_type son, object_id_type object) : + son_proposal_type(int op, sidechain_type sid, son_id_type son, object_id_type object) : op_type(op), + sidechain(sid), son_id(son), object_id(object) { } int op_type; + sidechain_type sidechain; son_id_type son_id; object_id_type object_id; bool operator<(const son_proposal_type &other) const { - return std::tie(op_type, son_id, object_id) < std::tie(other.op_type, other.son_id, other.object_id); + return std::tie(op_type, sidechain, son_id, object_id) < std::tie(other.op_type, other.sidechain, other.son_id, other.object_id); } }; @@ -42,15 +44,15 @@ public: std::unique_ptr my; std::set &get_sons(); - const son_id_type get_current_son_id(); - const son_object get_current_son_object(); + const son_id_type get_current_son_id(sidechain_type sidechain); + const son_object get_current_son_object(sidechain_type sidechain); const son_object get_son_object(son_id_type son_id); - bool is_active_son(son_id_type son_id); + bool is_active_son(sidechain_type sidechain, son_id_type son_id); bool is_son_deregistered(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(chain::public_key_type public_key); - void log_son_proposal_retry(int op_type, object_id_type object_id); - bool can_son_participate(int op_type, object_id_type object_id); + void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); + bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); }; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp new file mode 100644 index 00000000..3aa42894 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_handler_factory { +public: + sidechain_net_handler_factory(peerplays_sidechain_plugin &_plugin); + + std::unique_ptr create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) const; + +private: + peerplays_sidechain_plugin &plugin; +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp deleted file mode 100644 index 8bfda125..00000000 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include - -namespace graphene { namespace peerplays_sidechain { - -class sidechain_net_manager { -public: - sidechain_net_manager(peerplays_sidechain_plugin &_plugin); - virtual ~sidechain_net_manager(); - - bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options); - void process_proposals(); - void process_active_sons_change(); - void create_deposit_addresses(); - void process_deposits(); - void process_withdrawals(); - void process_sidechain_transactions(); - void send_sidechain_transactions(); - void settle_sidechain_transactions(); - - std::map> get_son_listener_log(); - -private: - peerplays_sidechain_plugin &plugin; - graphene::chain::database &database; - std::vector> net_handlers; - - void on_applied_block(const signed_block &b); -}; - -}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index ed80fbfc..461b1a92 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -11,7 +13,7 @@ #include #include #include -#include +#include #include namespace bpo = boost::program_options; @@ -33,36 +35,36 @@ public: void plugin_shutdown(); std::set &get_sons(); - const son_id_type get_current_son_id(); - const son_object get_current_son_object(); + const son_id_type get_current_son_id(sidechain_type sidechain); + const son_object get_current_son_object(sidechain_type sidechain); const son_object get_son_object(son_id_type son_id); - bool is_active_son(son_id_type son_id); + bool is_active_son(sidechain_type sidechain, son_id_type son_id); bool is_son_deregistered(son_id_type son_id); bool is_son_deregister_op_valid(const chain::operation &op); bool is_son_down_op_valid(const chain::operation &op); bool is_valid_son_proposal(const chain::proposal_object &proposal); fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(chain::public_key_type public_key); - void log_son_proposal_retry(int op_type, object_id_type object_id); - bool can_son_participate(int op_type, object_id_type object_id); + void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); + bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); void schedule_heartbeat_loop(); void heartbeat_loop(); void schedule_son_processing(); - void son_processing(); - void approve_proposals(); - void create_son_down_proposals(); - void create_son_deregister_proposals(); + void son_processing(sidechain_type sidechain); + void approve_proposals(sidechain_type sidechain); + void create_son_down_proposals(sidechain_type sidechain); + void create_son_deregister_proposals(sidechain_type sidechain); - void process_proposals(); - void process_active_sons_change(); - void create_deposit_addresses(); - void process_deposits(); - void process_withdrawals(); - void process_sidechain_transactions(); - void send_sidechain_transactions(); - void settle_sidechain_transactions(); + void process_proposals(sidechain_type sidechain); + void process_active_sons_change(sidechain_type sidechain); + void create_deposit_addresses(sidechain_type sidechain); + void process_deposits(sidechain_type sidechain); + void process_withdrawals(sidechain_type sidechain); + void process_sidechain_transactions(sidechain_type sidechain); + void send_sidechain_transactions(sidechain_type sidechain); + void settle_sidechain_transactions(sidechain_type sidechain); private: peerplays_sidechain_plugin &plugin; @@ -80,15 +82,19 @@ private: bool sidechain_enabled_hive; bool sidechain_enabled_peerplays; - son_id_type current_son_id; + std::map current_son_id; + std::mutex current_son_id_mutex; + std::mutex access_db_mutex; + std::mutex access_approve_prop_mutex; + std::mutex access_son_down_prop_mutex; - std::unique_ptr net_manager; + std::map> net_handlers; std::set sons; std::map private_keys; fc::future _heartbeat_task; - fc::future _son_processing_task; + std::map> _son_processing_task; std::map son_retry_count; - uint16_t retries_threshold; + uint16_t retries_threshold = 150; bool first_block_skipped; void on_applied_block(const signed_block &b); @@ -105,8 +111,20 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec sidechain_enabled_ethereum(false), sidechain_enabled_hive(false), sidechain_enabled_peerplays(false), - current_son_id(son_id_type(std::numeric_limits().max())), - net_manager(nullptr), + current_son_id([] { + std::map current_son_id; + for (const auto &active_sidechain_type : active_sidechain_types) { + current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits().max())); + } + return current_son_id; + }()), + net_handlers([] { + std::map> net_handlers; + for (const auto &active_sidechain_type : active_sidechain_types) { + net_handlers.emplace(active_sidechain_type, nullptr); + } + return net_handlers; + }()), first_block_skipped(false) { } @@ -121,8 +139,10 @@ peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { } try { - if (_son_processing_task.valid()) - _son_processing_task.cancel_and_wait(__FUNCTION__); + for (const auto &active_sidechain_type : active_sidechain_types) { + if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).valid()) + _son_processing_task.at(active_sidechain_type).wait(); + } } catch (fc::canceled_exception &) { //Expected exception. Move along. } catch (fc::exception &e) { @@ -262,10 +282,10 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { elog("No sons configured! Please add SON IDs and private keys to configuration."); } - net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); + sidechain_net_handler_factory net_handler_factory(plugin); if (sidechain_enabled_bitcoin && config_ready_bitcoin) { - net_manager->create_handler(sidechain_type::bitcoin, options); + net_handlers.at(sidechain_type::bitcoin) = net_handler_factory.create_handler(sidechain_type::bitcoin, options); ilog("Bitcoin sidechain handler running"); } @@ -275,12 +295,12 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { //} if (sidechain_enabled_hive && config_ready_hive) { - net_manager->create_handler(sidechain_type::hive, options); + net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options); ilog("Hive sidechain handler running"); } if (sidechain_enabled_peerplays && config_ready_peerplays) { - net_manager->create_handler(sidechain_type::peerplays, options); + net_handlers.at(sidechain_type::peerplays) = net_handler_factory.create_handler(sidechain_type::peerplays, options); ilog("Peerplays sidechain handler running"); } @@ -296,12 +316,13 @@ std::set &peerplays_sidechain_plugin_impl::get_sons() { return sons; } -const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() { - return current_son_id; +const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id(sidechain_type sidechain) { + const std::lock_guard lock(current_son_id_mutex); + return current_son_id.at(sidechain); } -const son_object peerplays_sidechain_plugin_impl::get_current_son_object() { - return get_son_object(current_son_id); +const son_object peerplays_sidechain_plugin_impl::get_current_son_object(sidechain_type sidechain) { + return get_son_object(get_current_son_id(sidechain)); } const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { @@ -312,16 +333,15 @@ const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son return *son_obj; } -bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) { +bool peerplays_sidechain_plugin_impl::is_active_son(sidechain_type sidechain, son_id_type son_id) { const auto &idx = plugin.database().get_index_type().indices().get(); auto son_obj = idx.find(son_id); if (son_obj == idx.end()) return false; const chain::global_property_object &gpo = plugin.database().get_global_properties(); - vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + set active_son_ids; + std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(), std::inserter(active_son_ids, active_son_ids.end()), [](const son_info &swi) { return swi.son_id; @@ -338,7 +358,13 @@ bool peerplays_sidechain_plugin_impl::is_son_deregistered(son_id_type son_id) { if (son_obj == idx.end()) return true; - if (son_obj->status == chain::son_status::deregistered) { + bool status_deregistered = true; + for (const auto &status : son_obj->statuses) { + if ((status.second != son_status::deregistered)) + status_deregistered = false; + } + + if (status_deregistered) { return true; } @@ -362,13 +388,23 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio } auto stats = son_obj->statistics(d); fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; - fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); int64_t down_threshold = gpo.parameters.son_down_time(); - if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && - ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { - return true; + + bool status_son_down_op_valid = true; + for (const auto &status : son_obj->statuses) { + if ((status.second != son_status::active) && (status.second != son_status::request_maintenance)) + status_son_down_op_valid = false; } - return false; + if (status_son_down_op_valid) { + for (const auto &active_sidechain_type : active_sidechain_types) { + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time); + if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) { + status_son_down_op_valid = false; + } + } + } + + return status_son_down_op_valid; } fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) { @@ -400,7 +436,23 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { chain::database &d = plugin.database(); for (son_id_type son_id : sons) { - if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { + const auto &son_obj = get_son_object(son_id); + + //! Check that son is in_maintenance + bool status_in_maintenance = false; + for (const auto &status : son_obj.statuses) { + if ((status.second == son_status::in_maintenance)) + status_in_maintenance = true; + } + + //! Check that son is active (at least for one sidechain_type) + bool is_son_active = false; + for (const auto &active_sidechain_type : active_sidechain_types) { + if (is_active_son(active_sidechain_type, son_id)) + is_son_active = true; + } + + if (is_son_active || status_in_maintenance) { ilog("Sending heartbeat for SON ${son}", ("son", son_id)); chain::son_heartbeat_operation op; @@ -426,19 +478,23 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { } void peerplays_sidechain_plugin_impl::schedule_son_processing() { - fc::time_point now = fc::time_point::now(); - int64_t time_to_next_son_processing = 500000; + const auto now = std::chrono::steady_clock::now(); + static const int64_t time_to_next_son_processing = 500000; - fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing)); + const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing); - _son_processing_task = fc::schedule([this] { - son_processing(); - }, - next_wakeup, "SON Processing"); + for (const auto &active_sidechain_type : active_sidechain_types) { + _son_processing_task[active_sidechain_type] = std::async(std::launch::async, [this, next_wakeup, active_sidechain_type] { + std::this_thread::sleep_until(next_wakeup); + son_processing(active_sidechain_type); + }); + } } -void peerplays_sidechain_plugin_impl::son_processing() { - if (plugin.database().get_global_properties().active_sons.size() <= 0) { +void peerplays_sidechain_plugin_impl::son_processing(sidechain_type sidechain) { + //! Check whether we have active SONs + if (plugin.database().get_global_properties().active_sons.count(sidechain) == 0 || + plugin.database().get_global_properties().active_sons.at(sidechain).empty()) { return; } @@ -448,50 +504,55 @@ void peerplays_sidechain_plugin_impl::son_processing() { // return; // Not synced //} - fc::time_point now_fine = fc::time_point::now(); - fc::time_point_sec now = now_fine - fc::milliseconds(3000); + const fc::time_point now_fine = fc::time_point::now(); + const fc::time_point_sec now = now_fine - fc::milliseconds(3000); if (plugin.database().head_block_time() < now) { return; // Not synced } - chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1); - ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ", - ("scheduled_son_id", scheduled_son_id)("now", now)); + //! Get scheduled_son_id according to sidechain_type + const chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(sidechain, 1); + ilog("Scheduled SON: ${scheduled_son_id} Sidechain: ${sidechain} Now: ${now}", + ("scheduled_son_id", scheduled_son_id)("sidechain", sidechain)("now", now)); for (son_id_type son_id : plugin.get_sons()) { if (plugin.is_son_deregistered(son_id)) { continue; } - current_son_id = son_id; + + { + const std::lock_guard lock(current_son_id_mutex); + current_son_id.at(sidechain) = son_id; + } // These tasks are executed by // - All active SONs, no matter if scheduled // - All previously active SONs - approve_proposals(); - process_proposals(); - process_sidechain_transactions(); + approve_proposals(sidechain); + process_proposals(sidechain); + process_sidechain_transactions(sidechain); - if (plugin.is_active_son(son_id)) { + if (plugin.is_active_son(sidechain, son_id)) { // Tasks that are executed by scheduled and active SON only - if (current_son_id == scheduled_son_id) { + if (get_current_son_id(sidechain) == scheduled_son_id) { - create_son_down_proposals(); + create_son_down_proposals(sidechain); - create_son_deregister_proposals(); + create_son_deregister_proposals(sidechain); - process_active_sons_change(); + process_active_sons_change(sidechain); - create_deposit_addresses(); + create_deposit_addresses(sidechain); - process_deposits(); + process_deposits(sidechain); - process_withdrawals(); + process_withdrawals(sidechain); - process_sidechain_transactions(); + process_sidechain_transactions(sidechain); - send_sidechain_transactions(); + send_sidechain_transactions(sidechain); - settle_sidechain_transactions(); + settle_sidechain_transactions(sidechain); } } } @@ -514,8 +575,8 @@ bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposa return false; } -void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object_id_type object_id) { - son_proposal_type prop_type(op_type, get_current_son_id(), object_id); +void peerplays_sidechain_plugin_impl::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) { + son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id); auto itr = son_retry_count.find(prop_type); if (itr != son_retry_count.end()) { itr->second++; @@ -524,18 +585,27 @@ void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object } } -bool peerplays_sidechain_plugin_impl::can_son_participate(int op_type, object_id_type object_id) { - son_proposal_type prop_type(op_type, get_current_son_id(), object_id); +bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) { + son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id); auto itr = son_retry_count.find(prop_type); return (itr == son_retry_count.end() || itr->second < retries_threshold); } std::map> peerplays_sidechain_plugin_impl::get_son_listener_log() { - return net_manager->get_son_listener_log(); + std::map> result; + for (const auto &active_sidechain_type : active_sidechain_types) { + result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log()); + } + return result; } -void peerplays_sidechain_plugin_impl::approve_proposals() { - +void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) { + // prevent approving duplicate proposals with lock for parallel execution. + // We can have the same propsals, but in the case of parallel execution we can run + // into problem of approving the same propsal since it might happens that previous + // approved proposal didn't have time or chance to populate the list of available + // active proposals which is consulted here in the code. + std::lock_guard lck(access_approve_prop_mutex); auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) { if (!is_valid_son_proposal(proposal)) { return; @@ -549,6 +619,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { fc::future fut = fc::async([&]() { try { trx.validate(); + std::lock_guard lck(access_db_mutex); plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -568,7 +639,6 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { } for (const auto proposal_id : proposals) { - const object *obj = plugin.database().find_object(proposal_id); const chain::proposal_object *proposal_ptr = dynamic_cast(obj); if (proposal_ptr == nullptr) { @@ -576,15 +646,16 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { } const proposal_object proposal = *proposal_ptr; - if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) { + if (proposal.available_active_approvals.find(get_current_son_object(sidechain).son_account) != proposal.available_active_approvals.end()) { continue; } - check_approve_proposal(get_current_son_id(), proposal); + check_approve_proposal(get_current_son_id(sidechain), proposal); } } -void peerplays_sidechain_plugin_impl::create_son_down_proposals() { +void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type sidechain) { + std::lock_guard lck(access_son_down_prop_mutex); auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { chain::database &d = plugin.database(); const chain::global_property_object &gpo = d.get_global_properties(); @@ -595,7 +666,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { son_down_op.down_ts = last_active_ts; proposal_create_operation proposal_op; - proposal_op.fee_paying_account = get_current_son_object().son_account; + proposal_op.fee_paying_account = get_current_son_object(sidechain).son_account; proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op)); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime); @@ -607,24 +678,32 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); const auto &idx = d.get_index_type().indices().get(); std::set sons_being_reported_down = d.get_sons_being_reported_down(); - chain::son_id_type my_son_id = get_current_son_id(); - for (auto son_inf : gpo.active_sons) { + chain::son_id_type my_son_id = get_current_son_id(sidechain); + + //! Fixme - check this part of the code + for (auto son_inf : gpo.active_sons.at(sidechain)) { if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) { continue; } auto son_obj = idx.find(son_inf.son_id); auto stats = son_obj->statistics(d); fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; - fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time); int64_t down_threshold = gpo.parameters.son_down_time(); - if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && - ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { + + bool status_son_down_valid = true; + for (const auto &status : son_obj->statuses) { + if ((status.second != son_status::active) && (status.second != son_status::request_maintenance)) + status_son_down_valid = false; + } + if ((status_son_down_valid) && ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id)))); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); fc::future fut = fc::async([&]() { try { trx.validate(); + std::lock_guard lck(access_db_mutex); d.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -639,10 +718,10 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { } } -void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { +void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) { chain::database &d = plugin.database(); std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); - chain::son_id_type my_son_id = get_current_son_id(); + chain::son_id_type my_son_id = get_current_son_id(sidechain); if (sons_to_be_dereg.size() > 0) { // We shouldn't raise proposals for the SONs for which a de-reg @@ -660,6 +739,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { fc::future fut = fc::async([&]() { try { trx.validate(); + std::lock_guard lck(access_db_mutex); d.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -676,36 +756,36 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { } } -void peerplays_sidechain_plugin_impl::process_proposals() { - net_manager->process_proposals(); +void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_proposals(); } -void peerplays_sidechain_plugin_impl::process_active_sons_change() { - net_manager->process_active_sons_change(); +void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_active_sons_change(); } -void peerplays_sidechain_plugin_impl::create_deposit_addresses() { - net_manager->create_deposit_addresses(); +void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) { + net_handlers.at(sidechain)->create_deposit_addresses(); } -void peerplays_sidechain_plugin_impl::process_deposits() { - net_manager->process_deposits(); +void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_deposits(); } -void peerplays_sidechain_plugin_impl::process_withdrawals() { - net_manager->process_withdrawals(); +void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_withdrawals(); } -void peerplays_sidechain_plugin_impl::process_sidechain_transactions() { - net_manager->process_sidechain_transactions(); +void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_sidechain_transactions(); } -void peerplays_sidechain_plugin_impl::send_sidechain_transactions() { - net_manager->send_sidechain_transactions(); +void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) { + net_handlers.at(sidechain)->send_sidechain_transactions(); } -void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() { - net_manager->settle_sidechain_transactions(); +void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) { + net_handlers.at(sidechain)->settle_sidechain_transactions(); } void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { @@ -758,20 +838,20 @@ std::set &peerplays_sidechain_plugin::get_sons() { return my->get_sons(); } -const son_id_type peerplays_sidechain_plugin::get_current_son_id() { - return my->get_current_son_id(); +const son_id_type peerplays_sidechain_plugin::get_current_son_id(sidechain_type sidechain) { + return my->get_current_son_id(sidechain); } -const son_object peerplays_sidechain_plugin::get_current_son_object() { - return my->get_current_son_object(); +const son_object peerplays_sidechain_plugin::get_current_son_object(sidechain_type sidechain) { + return my->get_current_son_object(sidechain); } const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { return my->get_son_object(son_id); } -bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) { - return my->is_active_son(son_id); +bool peerplays_sidechain_plugin::is_active_son(sidechain_type sidechain, son_id_type son_id) { + return my->is_active_son(sidechain, son_id); } bool peerplays_sidechain_plugin::is_son_deregistered(son_id_type son_id) { @@ -786,12 +866,12 @@ fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_k return my->get_private_key(public_key); } -void peerplays_sidechain_plugin::log_son_proposal_retry(int op_type, object_id_type object_id) { - my->log_son_proposal_retry(op_type, object_id); +void peerplays_sidechain_plugin::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) { + my->log_son_proposal_retry(sidechain, op_type, object_id); } -bool peerplays_sidechain_plugin::can_son_participate(int op_type, object_id_type object_id) { - return my->can_son_participate(op_type, object_id); +bool peerplays_sidechain_plugin::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) { + return my->can_son_participate(sidechain, op_type, object_id); } std::map> peerplays_sidechain_plugin::get_son_listener_log() { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 7c87a9ef..224144a0 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -182,7 +182,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || enable_peerplays_asset_deposits); - bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain_type::peerplays) && + bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays ((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))); @@ -191,7 +191,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ if (deposit_condition) { for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { + if (plugin.is_active_son(sidechain, son_id)) { son_wallet_deposit_create_operation op; op.payer = plugin.get_son_object(son_id).son_account; @@ -253,7 +253,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ } for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { + if (plugin.is_active_son(sidechain, son_id)) { son_wallet_withdraw_create_operation op; op.payer = plugin.get_son_object(son_id).son_account; @@ -297,7 +297,7 @@ void sidechain_net_handler::process_proposals() { const auto po = idx.find(proposal_id); if (po != idx.end()) { - if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) { + if (po->available_active_approvals.find(plugin.get_current_son_object(sidechain).son_account) != po->available_active_approvals.end()) { continue; } @@ -380,12 +380,12 @@ void sidechain_net_handler::process_proposals() { elog("=================================================="); } - if (should_process && (op_idx_0 == chain::operation::tag::value || plugin.can_son_participate(op_idx_0, object_id))) { + if (should_process && (op_idx_0 == chain::operation::tag::value || plugin.can_son_participate(sidechain, op_idx_0, object_id))) { bool should_approve = process_proposal(*po); if (should_approve) { - if (approve_proposal(po->id, plugin.get_current_son_id())) { + if (approve_proposal(po->id, plugin.get_current_son_id(sidechain))) { if (op_idx_0 != chain::operation::tag::value) { - plugin.log_son_proposal_retry(op_idx_0, object_id); + plugin.log_son_proposal_retry(sidechain, op_idx_0, object_id); } } } @@ -399,14 +399,14 @@ void sidechain_net_handler::process_active_sons_change() { } void sidechain_net_handler::create_deposit_addresses() { - if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) { return; } process_sidechain_addresses(); } void sidechain_net_handler::process_deposits() { - if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) { return; } @@ -414,7 +414,7 @@ void sidechain_net_handler::process_deposits() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) { - if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag::value, swdo.id)) { + if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag::value, swdo.id)) { return; } //Ignore the deposits which are not valid anymore, considered refunds. @@ -436,12 +436,12 @@ void sidechain_net_handler::process_deposits() { wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); return; } - plugin.log_son_proposal_retry(chain::operation::tag::value, swdo.id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, swdo.id); }); } void sidechain_net_handler::process_withdrawals() { - if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) { return; } @@ -449,7 +449,7 @@ void sidechain_net_handler::process_withdrawals() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) { - if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag::value, swwo.id)) { + if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag::value, swwo.id)) { return; } @@ -461,7 +461,7 @@ void sidechain_net_handler::process_withdrawals() { wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); return; } - plugin.log_son_proposal_retry(chain::operation::tag::value, swwo.id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, swwo.id); }); } @@ -470,7 +470,7 @@ void sidechain_net_handler::process_sidechain_transactions() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid)); std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { - if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id())) { + if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id(sidechain))) { return; } @@ -485,13 +485,13 @@ void sidechain_net_handler::process_sidechain_transactions() { const chain::global_property_object &gpo = database.get_global_properties(); sidechain_transaction_sign_operation sts_op; - sts_op.signer = plugin.get_current_son_id(); + sts_op.signer = plugin.get_current_son_id(sidechain); sts_op.payer = gpo.parameters.son_account(); sts_op.sidechain_transaction_id = sto.id; sts_op.signature = processed_sidechain_tx; proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); proposal_op.proposed_ops.emplace_back(sts_op); @@ -500,7 +500,7 @@ void sidechain_net_handler::process_sidechain_transactions() { return; } - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -531,11 +531,11 @@ void sidechain_net_handler::send_sidechain_transactions() { } sidechain_transaction_send_operation sts_op; - sts_op.payer = plugin.get_current_son_object().son_account; + sts_op.payer = plugin.get_current_son_object(sidechain).son_account; sts_op.sidechain_transaction_id = sto.id; sts_op.sidechain_transaction = sidechain_transaction; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), sts_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -560,7 +560,7 @@ void sidechain_net_handler::settle_sidechain_transactions() { return; } - if (!plugin.can_son_participate(chain::operation::tag::value, sto.object_id)) { + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, sto.object_id)) { return; } @@ -577,7 +577,7 @@ void sidechain_net_handler::settle_sidechain_transactions() { const chain::global_property_object &gpo = database.get_global_properties(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -605,13 +605,13 @@ void sidechain_net_handler::settle_sidechain_transactions() { } } - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - plugin.log_son_proposal_retry(chain::operation::tag::value, sto.object_id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, sto.object_id); } catch (fc::exception &e) { elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what())); } @@ -673,7 +673,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { sidechain_event_data sed; sed.timestamp = database.head_block_time(); sed.block_num = database.head_block_num(); - sed.sidechain = sidechain_type::peerplays; + sed.sidechain = sidechain; //! Fixme -> sidechain_type::peerplays sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = trx.id().str(); sed.sidechain_from = sidechain_from; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 82511bed..b48ccbb6 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -417,7 +417,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) { - // ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -451,8 +451,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) const auto swo = idx.find(swo_id); if (swo != idx.end()) { - auto active_sons = gpo.active_sons; - vector wallet_sons = swo->sons; + const auto &active_sons = gpo.active_sons.at(sidechain); + vector wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -463,10 +463,10 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) } if (son_sets_equal) { - auto active_sons = gpo.active_sons; + const auto &active_sons = gpo.active_sons.at(sidechain); vector son_pubkeys_bitcoin; for (const son_info &si : active_sons) { - son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); + son_pubkeys_bitcoin.push_back(si.public_key); } string reply_str = create_primary_wallet_address(active_sons); @@ -676,7 +676,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) read_transaction_data(sto->transaction, tx_hex, in_amounts, redeem_script); bitcoin_transaction tx = unpack(parse_hex(tx_hex)); - bitcoin::bytes pubkey = parse_hex(son->sidechain_public_keys.at(sidechain_type::bitcoin)); + bitcoin::bytes pubkey = parse_hex(son->sidechain_public_keys.at(sidechain)); vector sigs = read_byte_arrays_from_string(signature); for (size_t i = 0; i < tx.vin.size(); i++) { const auto &sighash_str = get_signature_hash(tx, parse_hex(redeem_script), static_cast(in_amounts[i]), i, 1, true).str(); @@ -706,8 +706,8 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { const auto &active_sw = swi.rbegin(); if (active_sw != swi.rend()) { - if ((active_sw->addresses.find(sidechain_type::bitcoin) == active_sw->addresses.end()) || - (active_sw->addresses.at(sidechain_type::bitcoin).empty())) { + if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || + (active_sw->addresses.at(sidechain).empty())) { if (proposal_exists(chain::operation::tag::value, active_sw->id)) { return; @@ -715,19 +715,19 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { const chain::global_property_object &gpo = database.get_global_properties(); - auto active_sons = gpo.active_sons; + const auto &active_sons = gpo.active_sons.at(sidechain); string reply_str = create_primary_wallet_address(active_sons); std::stringstream active_pw_ss(reply_str); boost::property_tree::ptree active_pw_pt; boost::property_tree::read_json(active_pw_ss, active_pw_pt); if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { - if (!plugin.can_son_participate(chain::operation::tag::value, active_sw->id)) { + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, active_sw->id)) { return; } proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -737,7 +737,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { son_wallet_update_operation swu_op; swu_op.payer = gpo.parameters.son_account(); swu_op.son_wallet_id = active_sw->id; - swu_op.sidechain = sidechain_type::bitcoin; + swu_op.sidechain = sidechain; swu_op.address = res.str(); proposal_op.proposed_ops.emplace_back(swu_op); @@ -752,18 +752,18 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { stc_op.object_id = prev_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = prev_sw->sons; + stc_op.signers = prev_sw->sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); } } - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - plugin.log_son_proposal_retry(chain::operation::tag::value, active_sw->id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, active_sw->id); } catch (fc::exception &e) { elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; @@ -778,9 +778,8 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { const chain::global_property_object &gpo = database.get_global_properties(); std::vector> pubkeys; - for (auto &son : gpo.active_sons) { - std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + for (auto &son : gpo.active_sons.at(sidechain)) { + auto pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(son.public_key))); pubkeys.push_back(std::make_pair(pubkey, son.weight)); } @@ -800,7 +799,7 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { if (addr.get_address() != sao.deposit_address) { sidechain_address_update_operation op; - op.payer = plugin.get_current_son_object().son_account; + op.payer = plugin.get_current_son_object(sidechain).son_account; op.sidechain_address_id = sao.id; op.sidechain_address_account = sao.sidechain_address_account; op.sidechain = sao.sidechain; @@ -810,7 +809,7 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { op.withdraw_public_key = sao.withdraw_public_key; op.withdraw_address = sao.withdraw_address; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -842,7 +841,7 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj const chain::global_property_object &gpo = database.get_global_properties(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -856,10 +855,10 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj stc_op.object_id = swdo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -886,7 +885,7 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw const chain::global_property_object &gpo = database.get_global_properties(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -900,10 +899,10 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw stc_op.object_id = swwo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -951,8 +950,7 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain using namespace bitcoin; std::vector> pubkey_weights; for (auto si : sto.signers) { - std::string pub_key_str = si.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); } btc_weighted_multisig_address addr(pubkey_weights, network_type); @@ -1001,8 +999,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s std::vector> pubkey_weights; for (auto &son : son_pubkeys) { - std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(son.public_key))); pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } @@ -1019,7 +1016,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address) { - const auto &address_data = prev_swo.addresses.find(sidechain_type::bitcoin); + const auto &address_data = prev_swo.addresses.find(sidechain); if (address_data == prev_swo.addresses.end()) { return ""; } @@ -1070,12 +1067,12 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_wallet_deposit_object &swdo) { const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); - if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + if (obj == idx.rend() || obj->addresses.find(sidechain) == obj->addresses.end()) { return ""; } // Get redeem script for deposit address std::string redeem_script = get_redeemscript_for_userdeposit(swdo.sidechain_from); - std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + std::string pw_address_json = obj->addresses.find(sidechain)->second; std::stringstream ss(pw_address_json); boost::property_tree::ptree json; @@ -1117,11 +1114,11 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); - if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + if (obj == idx.rend() || obj->addresses.find(sidechain) == obj->addresses.end()) { return ""; } - std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + std::string pw_address_json = obj->addresses.find(sidechain)->second; std::stringstream ss(pw_address_json); boost::property_tree::ptree json; @@ -1186,7 +1183,7 @@ std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector< std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_transaction_object &sto) { using namespace bitcoin; - std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain); + std::string pubkey = plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain); std::string prvkey = get_private_key(pubkey); std::vector in_amounts; std::string tx_hex; @@ -1302,14 +1299,13 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); - if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + if (obj == idx.rend() || obj->addresses.find(sidechain) == obj->addresses.end()) { return ""; } std::vector> pubkey_weights; - for (auto &son : obj->sons) { - std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + for (auto &son : obj->sons.at(sidechain)) { + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(son.public_key))); pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key))); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp new file mode 100644 index 00000000..1c6632d8 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp @@ -0,0 +1,31 @@ +#include + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_handler_factory::sidechain_net_handler_factory(peerplays_sidechain_plugin &_plugin) : + plugin(_plugin) { +} + +std::unique_ptr sidechain_net_handler_factory::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) const { + switch (sidechain) { + case sidechain_type::bitcoin: { + return std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); + } + case sidechain_type::hive: { + return std::unique_ptr(new sidechain_net_handler_hive(plugin, options)); + } + case sidechain_type::peerplays: { + return std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); + } + default: + assert(false); + } + + return nullptr; +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index d63e6743..c62911ec 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -180,7 +180,7 @@ sidechain_net_handler_hive::~sidechain_net_handler_hive() { } bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { - //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -213,8 +213,8 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { const auto swo = idx.find(swo_id); if (swo != idx.end()) { - auto active_sons = gpo.active_sons; - vector wallet_sons = swo->sons; + auto active_sons = gpo.active_sons.at(sidechain); + vector wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -251,7 +251,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { uint32_t total_weight = 0; for (const auto &wallet_son : wallet_sons) { total_weight = total_weight + wallet_son.weight; - account_auths[wallet_son.sidechain_public_keys.at(sidechain)] = wallet_son.weight; + account_auths[wallet_son.public_key] = wallet_son.weight; } std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); @@ -487,12 +487,12 @@ void sidechain_net_handler_hive::process_primary_wallet() { const chain::global_property_object &gpo = database.get_global_properties(); - auto active_sons = gpo.active_sons; + const auto &active_sons = gpo.active_sons.at(sidechain); fc::flat_map account_auths; uint32_t total_weight = 0; for (const auto &active_son : active_sons) { total_weight = total_weight + active_son.weight; - account_auths[active_son.sidechain_public_keys.at(sidechain)] = active_son.weight; + account_auths[active_son.public_key] = active_son.weight; } std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); @@ -530,7 +530,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { } proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -547,11 +547,11 @@ void sidechain_net_handler_hive::process_primary_wallet() { stc_op.object_id = active_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -575,7 +575,7 @@ void sidechain_net_handler_hive::process_sidechain_addresses() { if (sao.expires == time_point_sec::maximum()) { if (sao.deposit_address == "") { sidechain_address_update_operation op; - op.payer = plugin.get_current_son_object().son_account; + op.payer = plugin.get_current_son_object(sidechain).son_account; op.sidechain_address_id = sao.id; op.sidechain_address_account = sao.sidechain_address_account; op.sidechain = sao.sidechain; @@ -585,7 +585,7 @@ void sidechain_net_handler_hive::process_sidechain_addresses() { op.withdraw_public_key = sao.withdraw_public_key; op.withdraw_address = sao.withdraw_address; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -617,7 +617,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object } proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -633,7 +633,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object ai_op.issue_to_account = swdo.peerplays_from; proposal_op.proposed_ops.emplace_back(ai_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -690,7 +690,7 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob //===== proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -704,10 +704,10 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob stc_op.object_id = swwo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -730,7 +730,7 @@ std::string sidechain_net_handler_hive::process_sidechain_transaction(const side std::string chain_id_str = node_rpc_client->get_chain_id(); const hive::chain_id_type chain_id(chain_id_str); - fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))); + fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain))); signature_type st = htrx.sign(*privkey, chain_id); std::stringstream ss_st; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index d5a9ec32..b2945251 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -53,7 +53,7 @@ sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { bool sidechain_net_handler_peerplays::process_proposal(const proposal_object &po) { - ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -140,7 +140,7 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() { if (sao.expires == time_point_sec::maximum()) { if (sao.deposit_address == "") { sidechain_address_update_operation op; - op.payer = plugin.get_current_son_object().son_account; + op.payer = plugin.get_current_son_object(sidechain).son_account; op.sidechain_address_id = sao.id; op.sidechain_address_account = sao.sidechain_address_account; op.sidechain = sao.sidechain; @@ -150,7 +150,7 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() { op.withdraw_public_key = sao.withdraw_public_key; op.withdraw_address = sao.withdraw_address; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -197,15 +197,15 @@ bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_o stc_op.object_id = swdo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; proposal_op.proposed_ops.emplace_back(stc_op); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -230,7 +230,7 @@ std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const signed_transaction trx; fc::raw::unpack(ss_trx, trx, 1000); - fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))); + fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain))); signature_type st = trx.sign(*privkey, database.get_chain_id()); std::stringstream ss_st; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp deleted file mode 100644 index e2cb1608..00000000 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -namespace graphene { namespace peerplays_sidechain { - -sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin) : - plugin(_plugin), - database(_plugin.database()) { - - //database.applied_block.connect([&](const signed_block &b) { - // on_applied_block(b); - //}); -} - -sidechain_net_manager::~sidechain_net_manager() { -} - -bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) { - - bool ret_val = false; - - switch (sidechain) { - case sidechain_type::bitcoin: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - case sidechain_type::hive: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_hive(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - case sidechain_type::peerplays: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - default: - assert(false); - } - - return ret_val; -} - -void sidechain_net_manager::process_proposals() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_proposals(); - } -} - -void sidechain_net_manager::process_active_sons_change() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_active_sons_change(); - } -} - -void sidechain_net_manager::create_deposit_addresses() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->create_deposit_addresses(); - } -} - -void sidechain_net_manager::process_deposits() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_deposits(); - } -} - -void sidechain_net_manager::process_withdrawals() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_withdrawals(); - } -} - -void sidechain_net_manager::process_sidechain_transactions() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_sidechain_transactions(); - } -} - -void sidechain_net_manager::send_sidechain_transactions() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->send_sidechain_transactions(); - } -} - -void sidechain_net_manager::settle_sidechain_transactions() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->settle_sidechain_transactions(); - } -} - -std::map> sidechain_net_manager::get_son_listener_log() { - std::map> result; - for (size_t i = 0; i < net_handlers.size(); i++) { - result[net_handlers.at(i)->get_sidechain()] = net_handlers.at(i)->get_son_listener_log(); - } - return result; -} - -void sidechain_net_manager::on_applied_block(const signed_block &b) { -} - -}} // namespace graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 185db897..0086c654 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1610,6 +1610,7 @@ class wallet_api * * @param voting_account the name or id of the account who is voting with their shares * @param son the name or id of the SONs' owner account + * @param sidechain the name of the sidechain * @param approve true if you wish to vote in favor of that SON, false to * remove your vote in favor of that SON * @param broadcast true if you wish to broadcast the transaction @@ -1617,6 +1618,7 @@ class wallet_api */ signed_transaction vote_for_son(string voting_account, string son, + sidechain_type sidechain, bool approve, bool broadcast = false); @@ -1640,6 +1642,8 @@ class wallet_api * @param sons_to_reject the names or ids of the SONs owner accounts you wish * to reject (these will be removed from the list of SONs * you currently approve). This list can be empty. + * @param sidechain the name of the sidechain + * * @param desired_number_of_sons the number of SONs you believe the network * should have. You must vote for at least this many * SONs. You can set this to 0 to abstain from @@ -1650,6 +1654,7 @@ class wallet_api signed_transaction update_son_votes(string voting_account, std::vector sons_to_approve, std::vector sons_to_reject, + sidechain_type sidechain, uint16_t desired_number_of_sons, bool broadcast = false); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 09aa5bdc..43ca49fd 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2230,69 +2230,99 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account) ) } + //! Fixme - do we need to specify sidechain_type as params here? map list_active_sons() - { try { - global_property_object gpo = get_global_properties(); - vector son_ids; - son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(son_ids, son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - std::vector> son_objects = _remote_db->get_sons(son_ids); - vector owners; - for(auto obj: son_objects) + { + try { - std::string acc_id = account_id_to_string(obj->son_account); - owners.push_back(acc_id); - } - vector< optional< account_object> > accs = _remote_db->get_accounts(owners); - std::remove_if(son_objects.begin(), son_objects.end(), - [](const fc::optional& obj) -> bool { return obj.valid(); }); - map result; - std::transform(accs.begin(), accs.end(), son_objects.begin(), - std::inserter(result, result.end()), - [](fc::optional& acct, fc::optional son) { - FC_ASSERT(acct, "Invalid active SONs list in global properties."); - return std::make_pair(string(acct->name), std::move(son->id)); - }); - return result; - } FC_CAPTURE_AND_RETHROW() } - - map get_son_network_status() - { try { - global_property_object gpo = get_global_properties(); - vector son_ids; - son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(son_ids, son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - - map result; - std::vector> son_objects = _remote_db->get_sons(son_ids); - for(auto son_obj: son_objects) { - string status; - if (son_obj) { - son_statistics_object sso = get_object(son_obj->statistics); - if (sso.last_active_timestamp + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { - status = "OK, regular SON heartbeat"; - } else { - if (sso.last_active_timestamp + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { - status = "OK, irregular SON heartbeat, but not triggering SON down proposal"; - } else { - status = "NOT OK, irregular SON heartbeat, triggering SON down proposal"; - } - } - } else { - status = "NOT OK, invalid SON id"; + const global_property_object& gpo = get_global_properties(); + set son_ids_set; + for(const auto& active_sidechain_type : active_sidechain_types) + { + std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), + std::inserter(son_ids_set, son_ids_set.end()), + [](const son_info &swi) { + return swi.son_id; + }); } - result[son_obj->id] = status; + vector son_ids; + son_ids.reserve(son_ids_set.size()); + for(const auto& son_id : son_ids_set) + { + son_ids.emplace_back(son_id); + } + + std::vector> son_objects = _remote_db->get_sons(son_ids); + vector owners; + for(auto obj: son_objects) + { + std::string acc_id = account_id_to_string(obj->son_account); + owners.push_back(acc_id); + } + vector< optional< account_object> > accs = _remote_db->get_accounts(owners); + std::remove_if(son_objects.begin(), son_objects.end(), + [](const fc::optional& obj) -> bool { return obj.valid(); }); + map result; + std::transform(accs.begin(), accs.end(), son_objects.begin(), + std::inserter(result, result.end()), + [](fc::optional& acct, fc::optional son) { + FC_ASSERT(acct, "Invalid active SONs list in global properties."); + return std::make_pair(string(acct->name), std::move(son->id)); + }); + return result; } - return result; - } FC_CAPTURE_AND_RETHROW() } + FC_CAPTURE_AND_RETHROW() + } + + //! Fixme - do we need to specify sidechain_type as params here? + map get_son_network_status() + { + try + { + const global_property_object& gpo = get_global_properties(); + + set son_ids_set; + for(const auto& active_sidechain_type : active_sidechain_types) { + std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), + std::inserter(son_ids_set, son_ids_set.end()), + [](const son_info &swi) { + return swi.son_id; + }); + } + vector son_ids; + son_ids.reserve(son_ids_set.size()); + std::transform(son_ids_set.cbegin(), son_ids_set.cend(), + std::inserter(son_ids, son_ids.end()), + [](const son_id_type& sit) { + return sit; + }); + + map result; + std::vector> son_objects = _remote_db->get_sons(son_ids); + for(auto son_obj: son_objects) { + string status; + if (son_obj) { + son_statistics_object sso = get_object(son_obj->statistics); + for(const auto& active_sidechain_type : active_sidechain_types) { + if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { + status = "[OK, regular SON heartbeat for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; + } else { + if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { + status = "[OK, irregular SON heartbeat, but not triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; + } else { + status = "[NOT OK, irregular SON heartbeat, triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; + } + } + } + } else { + status = "NOT OK, invalid SON id"; + } + result[son_obj->id] = status; + } + return result; + } + FC_CAPTURE_AND_RETHROW() + } optional get_active_son_wallet() { try { @@ -2800,6 +2830,7 @@ public: signed_transaction vote_for_son(string voting_account, string son, + sidechain_type sidechain, bool approve, bool broadcast /* = false */) { try { @@ -2813,19 +2844,20 @@ public: account_object voting_account_object = get_account(voting_account); account_id_type son_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_account_id); - if (!son_obj) - FC_THROW("Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + if (approve) { - auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); + auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) - FC_THROW("Account ${account} was already voting for son ${son}", ("account", voting_account)("son", son)); + FC_THROW("Account ${account} has already voted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); } else { - unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); + unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) - FC_THROW("Account ${account} is already not voting for son ${son}", ("account", voting_account)("son", son)); + FC_THROW("Account ${account} has already unvoted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); } account_update_operation account_update_op; account_update_op.account = voting_account_object.id; @@ -2842,6 +2874,7 @@ public: signed_transaction update_son_votes(string voting_account, std::vector sons_to_approve, std::vector sons_to_reject, + sidechain_type sidechain, uint16_t desired_number_of_sons, bool broadcast /* = false */) { try { @@ -2857,9 +2890,10 @@ public: { account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); - if (!son_obj) - FC_THROW("Account ${son} is not registered as a SON", ("son", son)); - auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); + FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + + auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); } @@ -2867,13 +2901,15 @@ public: { account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); - if (!son_obj) - FC_THROW("Account ${son} is not registered as a SON", ("son", son)); - unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); + FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + + unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); } - voting_account_object.options.extensions.value.num_son = desired_number_of_sons; + FC_ASSERT( voting_account_object.options.extensions.value.num_son.valid() , "Invalid son number" ); + (*voting_account_object.options.extensions.value.num_son)[sidechain] = desired_number_of_sons; account_update_operation account_update_op; account_update_op.account = voting_account_object.id; @@ -5428,19 +5464,21 @@ signed_transaction wallet_api::vote_for_committee_member(string voting_account, signed_transaction wallet_api::vote_for_son(string voting_account, string son, + sidechain_type sidechain, bool approve, bool broadcast /* = false */) { - return my->vote_for_son(voting_account, son, approve, broadcast); + return my->vote_for_son(voting_account, son, sidechain, approve, broadcast); } signed_transaction wallet_api::update_son_votes(string voting_account, std::vector sons_to_approve, std::vector sons_to_reject, + sidechain_type sidechain, uint16_t desired_number_of_sons, bool broadcast /* = false */) { - return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, desired_number_of_sons, broadcast); + return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, sidechain, desired_number_of_sons, broadcast); } signed_transaction wallet_api::sidechain_deposit_transaction( const string &son_name_or_id, diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 38a3799b..87257735 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -138,10 +138,18 @@ BOOST_AUTO_TEST_CASE( create_sons ) auto son1_obj = con.wallet_api_ptr->get_son("son1account"); BOOST_CHECK(son1_obj.son_account == con.wallet_api_ptr->get_account_id("son1account")); BOOST_CHECK_EQUAL(son1_obj.url, "http://son1"); + BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); + BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); auto son2_obj = con.wallet_api_ptr->get_son("son2account"); BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account")); BOOST_CHECK_EQUAL(son2_obj.url, "http://son2"); + BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); + BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2"); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 24); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 25); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -172,6 +180,10 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) auto son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember"); BOOST_CHECK(son_data.son_account == sonmember_acct.get_id()); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); // update SON sidechain_public_keys.clear(); @@ -181,6 +193,8 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", sidechain_public_keys, true); son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember_updated"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 2"); // update SON signing key sidechain_public_keys.clear(); @@ -221,8 +235,11 @@ BOOST_AUTO_TEST_CASE( son_voting ) son_object son2_obj; signed_transaction vote_son1_tx; signed_transaction vote_son2_tx; - uint64_t son1_start_votes, son1_end_votes; - uint64_t son2_start_votes, son2_end_votes; + flat_map son1_start_votes, son1_end_votes; + flat_map son2_start_votes, son2_end_votes; + + //! Get nathan account + const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_start_votes = son1_obj.total_votes; @@ -232,85 +249,101 @@ BOOST_AUTO_TEST_CASE( son_voting ) con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); // Vote for a son1account BOOST_TEST_MESSAGE("Voting for son1account"); - vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, true, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes > son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] > son1_start_votes[sidechain_type::hive]); // Vote for a son2account BOOST_TEST_MESSAGE("Voting for son2account"); - vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", true, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, true, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes > son2_start_votes); - - //! Get nathan account - const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); //! Check son1account voters - auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account"); - BOOST_REQUIRE(voters_for_son1account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account.voters_for_son->voters[0].instance, nathan_account_object.id.instance()); + auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); //! Check son2account voters - auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account"); - BOOST_REQUIRE(voters_for_son2account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account.voters_for_son->voters[0].instance, nathan_account_object.id.instance()); + auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); //! Check votes of nathan - auto nathan_votes = con.wallet_api_ptr->get_votes("nathan"); - BOOST_REQUIRE(nathan_votes.votes_for_sons); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 2); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son1_obj.id.instance()); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(1).id.instance(), son2_obj.id.instance()); + auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son1account BOOST_TEST_MESSAGE("Withdraw vote for a son1account"); - vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", false, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, false, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); //! Check son1account voters - voters_for_son1account = con.wallet_api_ptr->get_voters("son1account"); - BOOST_REQUIRE(voters_for_son1account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 0); + voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); //! Check votes of nathan - nathan_votes = con.wallet_api_ptr->get_votes("nathan"); - BOOST_REQUIRE(nathan_votes.votes_for_sons); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 1); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son2_obj.id.instance()); + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son2account BOOST_TEST_MESSAGE("Withdraw vote for a son2account"); - vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", false, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, false, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); //! Check son2account voters - voters_for_son2account = con.wallet_api_ptr->get_voters("son2account"); - BOOST_REQUIRE(voters_for_son2account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 0); + voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); //! Check votes of nathan - nathan_votes = con.wallet_api_ptr->get_votes("nathan"); - BOOST_CHECK(!nathan_votes.votes_for_sons.valid()); + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_CHECK(!nathan_votes_for_son); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -363,7 +396,8 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); std::string name = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -371,37 +405,46 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) { std::string name1 = "sonaccount" + fc::to_pretty_string(i); std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); for(unsigned int i = 0; i < son_number - 1; i++) { std::string name1 = "sonaccount" + fc::to_pretty_string(i + 2); std::string name2 = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); for(unsigned int i = 0; i < son_number - 2; i++) { std::string name1 = "sonaccount" + fc::to_pretty_string(i + 3); std::string name2 = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); BOOST_CHECK(generate_maintenance_block()); - BOOST_CHECK(gpo.active_sons.size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -466,8 +509,11 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) son_object son1_obj; son_object son2_obj; - uint64_t son1_start_votes, son1_end_votes; - uint64_t son2_start_votes, son2_end_votes; + flat_map son1_start_votes, son1_end_votes; + flat_map son2_start_votes, son2_end_votes; + + //! Get nathan account + const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); // Get votes at start son1_obj = con.wallet_api_ptr->get_son("son1account"); @@ -485,114 +531,189 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) accepted.push_back("son1account"); accepted.push_back("son2account"); con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 2, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 2, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 2, true); generate_block(); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes > son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes > son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check son1account voters + auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + + //! Check son2account voters + auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + + //! Check votes of nathan + auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for SON 1 accepted.clear(); rejected.clear(); rejected.push_back("son1account"); con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes < son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] < son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); // voice distribution changed, SON2 now has all voices son2_end_votes = son2_obj.total_votes; - BOOST_CHECK((son2_end_votes > son2_start_votes)); // nathan spent funds for vb, it has different voting power + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); // nathan spent funds for vb, it has different voting power son2_start_votes = son2_end_votes; + //! Check son1account voters + voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); + + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + // Try to reject incorrect SON accepted.clear(); rejected.clear(); rejected.push_back("son1accnt"); - BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); generate_block(); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + // Reject SON2 accepted.clear(); rejected.clear(); rejected.push_back("son2account"); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 0, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 0, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 0, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes < son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] < son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check son2account voters + voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); + + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); + // Try to accept and reject the same SON accepted.clear(); rejected.clear(); rejected.push_back("son1accnt"); accepted.push_back("son1accnt"); - BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); + // Try to accept and reject empty lists accepted.clear(); rejected.clear(); - BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); + } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); edump((e.to_detail_string())); @@ -607,7 +728,8 @@ BOOST_AUTO_TEST_CASE( related_functions ) try { global_property_object gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_CHECK(gpo.active_sons.size() == 0); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 0); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 0); flat_map sidechain_public_keys; @@ -624,7 +746,8 @@ BOOST_AUTO_TEST_CASE( related_functions ) sth.create_son("son2account", "http://son2", sidechain_public_keys); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_CHECK(gpo.active_sons.size() == 2); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 2); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 2); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -670,7 +793,8 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) for(unsigned int i = 1; i < son_number + 1; i++) { std::string name = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -678,13 +802,16 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) { std::string name1 = "sonaccount" + fc::to_pretty_string(i); std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); - BOOST_CHECK(gpo.active_sons.size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); map active_sons = con.wallet_api_ptr->list_active_sons(); BOOST_CHECK(active_sons.size() == son_number); @@ -737,12 +864,14 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) con.wallet_api_ptr->transfer( "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); - con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::bitcoin, true, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); son_object son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -750,7 +879,8 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) // check SON is in request_maintenance son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); // restore SON activity con.wallet_api_ptr->cancel_request_son_maintenance(name, true); @@ -758,7 +888,8 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) // check SON is active son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -766,14 +897,16 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) // check SON is in request_maintenance son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); // process maintenance BOOST_CHECK(generate_maintenance_block()); // check SON is in maintenance son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::in_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::in_maintenance); } catch( fc::exception& e ) { diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 4fb3a24d..807c495a 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -1040,14 +1040,9 @@ BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture ) generate_blocks(HARDFORK_SON3_TIME); // after this hardfork maximum son account should not reset the value - // on 7 after maintenance interval anymore. It must be GRAPHENE_DEFAULT_MAX_SONS + // on 7 after maintenance interval anymore. It must be HARDFORK_SON2_TIME BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 15); - } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index 755ad37f..72f50b9d 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace graphene::chain; using namespace graphene::chain::test; @@ -63,9 +64,134 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { INVOKE(sidechain_address_add_test); - GET_ACTOR(alice); + generate_block(); + + //! ----- BEGIN CREATE SON bob ----- + ACTORS((bob)); + upgrade_to_lifetime_member(bob); + + transfer( committee_account, bob_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + + set_expiration(db, trx); + std::string test_url = "https://create_son_test"; + + // create deposit vesting + vesting_balance_id_type deposit; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(10*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.clear(); + trx.operations.push_back(op); + + // amount in the son balance need to be at least 50 + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception ); + + op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); + trx.clear(); + + trx.operations.push_back(op); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + deposit = ptx.operation_results[0].get(); + + auto deposit_vesting = db.get(ptx.operation_results[0].get()); + + BOOST_CHECK_EQUAL(deposit(db).balance.amount.value, 50*GRAPHENE_BLOCKCHAIN_PRECISION); + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit(db).is_withdraw_allowed(now, asset(50*GRAPHENE_BLOCKCHAIN_PRECISION)), false); // cant withdraw + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment ; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + op.policy = linear_vesting_policy_initializer {}; + op.validate(); + + trx.clear(); + trx.operations.push_back(op); + trx.validate(); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment = ptx.operation_results[0].get(); + } + + generate_block(); + set_expiration(db, trx); + + // bob became son + { + flat_map sidechain_public_keys; + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + sidechain_public_keys[sidechain_type::hive] = "hive address"; + + son_create_operation op; + op.owner_account = bob_id; + op.url = test_url; + op.deposit = deposit; + op.pay_vb = payment; + op.signing_key = bob_public_key; + op.sidechain_public_keys = sidechain_public_keys; + + trx.clear(); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + + { + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 1); + auto obj = idx.find(bob_id); + BOOST_REQUIRE(obj != idx.end()); + BOOST_CHECK(obj->url == test_url); + BOOST_CHECK(obj->signing_key == bob_public_key); + BOOST_CHECK(obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address"); + BOOST_CHECK(obj->deposit.instance == deposit.instance.value); + BOOST_CHECK(obj->pay_vb.instance == payment.instance.value); + } + + // Note payment time just to generate enough blocks to make budget + const auto block_interval = db.get_global_properties().parameters.block_interval; + auto pay_fee_time = db.head_block_time().sec_since_epoch(); + generate_block(); + // Do maintenance from the upcoming block + auto schedule_maint = [&]() + { + db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo ) + { + _dpo.next_maintenance_time = db.head_block_time() + 1; + } ); + }; + + // Generate enough blocks to make budget + while( db.head_block_time().sec_since_epoch() - pay_fee_time < 100 * block_interval ) + { + generate_block(); + } + + // Enough blocks generated schedule maintenance now + schedule_maint(); + // This block triggers maintenance + generate_block(); + + //! ----- END CREATE SON bob ----- + + GET_ACTOR(alice); + const auto& idx = db.get_index_type().indices().get(); BOOST_REQUIRE( idx.size() == 1 ); auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); @@ -77,19 +203,7 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { std::string new_withdraw_address = "withdraw_address"; generate_block(); - auto& son = db.create( [&]( son_object& sobj ) - { - sobj.son_account = bob_id; - sobj.statistics = db.create([&](son_statistics_object& s){s.owner = sobj.id;}).id; - }); - generate_block(); - db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) - { - son_info sinfo; - sinfo.son_id = son.id; - _gpo.active_sons.push_back(sinfo); - }); - generate_block(); + set_expiration(db, trx); { BOOST_TEST_MESSAGE("Send sidechain_address_update_operation"); trx.clear(); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 1e3bb7e4..5bb003aa 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -193,12 +193,14 @@ try { // Modify SON's status to active db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::in_maintenance; + _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; + _s.statuses[sidechain_type::hive] = son_status::in_maintenance; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { - _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); }); auto deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -218,7 +220,8 @@ try { generate_block(); BOOST_REQUIRE( idx.size() == 1 ); - BOOST_REQUIRE( obj->status == son_status::deregistered ); + BOOST_REQUIRE( obj->statuses.at(sidechain_type::bitcoin) == son_status::deregistered ); + BOOST_REQUIRE( obj->statuses.at(sidechain_type::hive) == son_status::deregistered ); BOOST_REQUIRE( son_stats_obj->deregistered_timestamp == now ); deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -604,12 +607,14 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { // Modify SON's status to active db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::active; + _s.statuses[sidechain_type::bitcoin] = son_status::active; + _s.statuses[sidechain_type::hive] = son_status::active; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { - _s.last_down_timestamp = fc::time_point_sec(db.head_block_time()); + _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time()); + _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time()); }); { @@ -626,7 +631,8 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_CHECK( obj->status == son_status::request_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance); } { @@ -643,16 +649,20 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); } // Modify SON's status to in_maintenance db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::in_maintenance; + _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; + _s.statuses[sidechain_type::hive] = son_status::in_maintenance; }); - uint64_t downtime = 0; + flat_map downtime; + downtime[sidechain_type::bitcoin] = 0; + downtime[sidechain_type::hive] = 0; { generate_block(); @@ -668,16 +678,21 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); - downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); - BOOST_CHECK( obj->status == son_status::inactive); - BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); + downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); } // Modify SON's status to in_maintenance db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::in_maintenance; + _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; + _s.statuses[sidechain_type::hive] = son_status::in_maintenance; }); // SON is selected as one of the active SONs @@ -685,7 +700,8 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { son_info son_inf; son_inf.son_id = son_id_type(0); - _gpo.active_sons.push_back(son_inf); + _gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf); + _gpo.active_sons[sidechain_type::hive].push_back(son_inf); }); { @@ -702,10 +718,15 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); - downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); - BOOST_CHECK( obj->status == son_status::active); - BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); + downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); } { @@ -722,9 +743,12 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime); - BOOST_CHECK( obj->status == son_status::active); - BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin)); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive)); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); } } FC_LOG_AND_RETHROW() } @@ -749,7 +773,8 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { auto son_stats_obj = sidx.find( obj->statistics ); BOOST_REQUIRE( son_stats_obj != sidx.end() ); - BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); { // Check that transaction fails if down_ts < last_active_timestamp @@ -758,7 +783,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); - op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1)); + op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) - fc::seconds(1)); trx.operations.push_back(op); set_expiration(db, trx); @@ -775,7 +800,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = alice_id; op.son_id = son_id_type(0); - op.down_ts = son_stats_obj->last_active_timestamp; + op.down_ts = son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin); trx.operations.push_back(op); set_expiration(db, trx); @@ -792,7 +817,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); - op.down_ts = son_stats_obj->last_active_timestamp; + op.down_ts = son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin); trx.operations.push_back(op); set_expiration(db, trx); @@ -801,8 +826,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { generate_block(); trx.clear(); - BOOST_CHECK( obj->status == son_status::in_maintenance); - BOOST_CHECK( son_stats_obj->last_down_timestamp == op.down_ts); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance); + BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts); + BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts); } { @@ -812,7 +839,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); - op.down_ts = son_stats_obj->last_active_timestamp; + op.down_ts = son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin); trx.operations.push_back(op); set_expiration(db, trx); diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp index cef29b54..4b1e4fe2 100644 --- a/tests/tests/son_wallet_tests.cpp +++ b/tests/tests/son_wallet_tests.cpp @@ -154,13 +154,14 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { op.payer = db.get_global_properties().parameters.son_account(); + //! Fixme - add hive tests { son_info si; si.son_id = son_id_type(0); si.weight = 1000; si.signing_key = alice_public_key; - si.sidechain_public_keys[sidechain_type::bitcoin] = ""; - op.sons.push_back(si); + si.public_key = ""; + op.sons[sidechain_type::bitcoin].push_back(si); } { @@ -168,8 +169,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { si.son_id = son_id_type(1); si.weight = 1000; si.signing_key = bob_public_key; - si.sidechain_public_keys[sidechain_type::bitcoin] = ""; - op.sons.push_back(si); + si.public_key = ""; + op.sons[sidechain_type::bitcoin].push_back(si); } trx.operations.push_back(op); From f127495c0e48aded2a05f024e0ec71d7782e637e Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 25 Aug 2022 12:21:22 +0200 Subject: [PATCH 040/106] Fix unit test failing on develop branch, #418 --- tests/cli/son.cpp | 388 +++++++++++++++++++++++----------------------- 1 file changed, 196 insertions(+), 192 deletions(-) diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 87257735..67c5019a 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -229,6 +229,8 @@ BOOST_AUTO_TEST_CASE( son_voting ) sidechain_public_keys[sidechain_type::hive] = "hive account 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); + BOOST_CHECK(generate_maintenance_block()); + BOOST_TEST_MESSAGE("Voting for SONs"); son_object son1_obj; @@ -488,9 +490,9 @@ BOOST_AUTO_TEST_CASE( list_son ) BOOST_AUTO_TEST_CASE( update_son_votes_test ) { - BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests begin"); - try - { + BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests begin"); + try + { flat_map sidechain_public_keys; son_test_helper sth(*this); @@ -505,221 +507,223 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_public_keys[sidechain_type::hive] = "hive account 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); - BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes"); + BOOST_CHECK(generate_maintenance_block()); - son_object son1_obj; - son_object son2_obj; - flat_map son1_start_votes, son1_end_votes; - flat_map son2_start_votes, son2_end_votes; + BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes"); - //! Get nathan account - const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); + son_object son1_obj; + son_object son2_obj; + flat_map son1_start_votes, son1_end_votes; + flat_map son2_start_votes, son2_end_votes; - // Get votes at start - son1_obj = con.wallet_api_ptr->get_son("son1account"); - son1_start_votes = son1_obj.total_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - son2_start_votes = son2_obj.total_votes; + //! Get nathan account + const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); - std::vector accepted; - std::vector rejected; - signed_transaction update_votes_tx; + // Get votes at start + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_start_votes = son1_obj.total_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_start_votes = son2_obj.total_votes; - // Vote for both SONs - accepted.clear(); - rejected.clear(); - accepted.push_back("son1account"); - accepted.push_back("son2account"); - con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::bitcoin, 2, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::hive, 2, true); - generate_block(); - BOOST_CHECK(generate_maintenance_block()); + std::vector accepted; + std::vector rejected; + signed_transaction update_votes_tx; - // Verify the votes - son1_obj = con.wallet_api_ptr->get_son("son1account"); - son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); - son1_start_votes = son1_end_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); - son2_start_votes = son2_end_votes; + // Vote for both SONs + accepted.clear(); + rejected.clear(); + accepted.push_back("son1account"); + accepted.push_back("son2account"); + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 2, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 2, true); + generate_block(); + BOOST_CHECK(generate_maintenance_block()); - //! Check son1account voters - auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; - BOOST_REQUIRE(voters_for_son1account); - BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); - BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); + son2_start_votes = son2_end_votes; - //! Check son2account voters - auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; - BOOST_REQUIRE(voters_for_son2account); - BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); - BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + //! Check son1account voters + auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); - //! Check votes of nathan - auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; - BOOST_REQUIRE(nathan_votes_for_son); - BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 2); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son1_obj.id.instance()); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(1).id.instance(), son2_obj.id.instance()); - BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); + //! Check son2account voters + auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); - // Withdraw vote for SON 1 - accepted.clear(); - rejected.clear(); - rejected.push_back("son1account"); - con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::bitcoin, 1, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::hive, 1, true); - BOOST_CHECK(generate_maintenance_block()); + //! Check votes of nathan + auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); - // Verify the votes - son1_obj = con.wallet_api_ptr->get_son("son1account"); - son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] < son1_start_votes[sidechain_type::bitcoin]); - son1_start_votes = son1_end_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - // voice distribution changed, SON2 now has all voices - son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); // nathan spent funds for vb, it has different voting power - son2_start_votes = son2_end_votes; + // Withdraw vote for SON 1 + accepted.clear(); + rejected.clear(); + rejected.push_back("son1account"); + con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true); + BOOST_CHECK(generate_maintenance_block()); - //! Check son1account voters - voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; - BOOST_REQUIRE(voters_for_son1account); - BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); - BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] < son1_start_votes[sidechain_type::bitcoin]); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + // voice distribution changed, SON2 now has all voices + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); // nathan spent funds for vb, it has different voting power + son2_start_votes = son2_end_votes; - //! Check votes of nathan - nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; - BOOST_REQUIRE(nathan_votes_for_son); - BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); - BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + //! Check son1account voters + voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); - // Try to reject incorrect SON - accepted.clear(); - rejected.clear(); - rejected.push_back("son1accnt"); - BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::bitcoin, 1, true), fc::exception); - BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::hive, 1, true), fc::exception); - generate_block(); + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); - // Verify the votes - son1_obj = con.wallet_api_ptr->get_son("son1account"); - son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); - son1_start_votes = son1_end_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); - son2_start_votes = son2_end_votes; + // Try to reject incorrect SON + accepted.clear(); + rejected.clear(); + rejected.push_back("son1accnt"); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); + generate_block(); - //! Check votes of nathan - nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; - BOOST_REQUIRE(nathan_votes_for_son); - BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); - BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); - BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + son2_start_votes = son2_end_votes; - // Reject SON2 - accepted.clear(); - rejected.clear(); - rejected.push_back("son2account"); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::bitcoin, 0, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::hive, 0, true); - BOOST_CHECK(generate_maintenance_block()); + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); - // Verify the votes - son1_obj = con.wallet_api_ptr->get_son("son1account"); - son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); - son1_start_votes = son1_end_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] < son2_start_votes[sidechain_type::bitcoin]); - son2_start_votes = son2_end_votes; + // Reject SON2 + accepted.clear(); + rejected.clear(); + rejected.push_back("son2account"); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 0, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 0, true); + BOOST_CHECK(generate_maintenance_block()); - //! Check son2account voters - voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; - BOOST_REQUIRE(voters_for_son2account); - BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); - BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] < son2_start_votes[sidechain_type::bitcoin]); + son2_start_votes = son2_end_votes; - //! Check votes of nathan - nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; - BOOST_REQUIRE(!nathan_votes_for_son); + //! Check son2account voters + voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); - // Try to accept and reject the same SON - accepted.clear(); - rejected.clear(); - rejected.push_back("son1accnt"); - accepted.push_back("son1accnt"); - BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::bitcoin, 1, true), fc::exception); - BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::hive, 1, true), fc::exception); - BOOST_CHECK(generate_maintenance_block()); + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); - // Verify the votes - son1_obj = con.wallet_api_ptr->get_son("son1account"); - son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); - son1_start_votes = son1_end_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); - son2_start_votes = son2_end_votes; + // Try to accept and reject the same SON + accepted.clear(); + rejected.clear(); + rejected.push_back("son1accnt"); + accepted.push_back("son1accnt"); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); + BOOST_CHECK(generate_maintenance_block()); - //! Check votes of nathan - nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; - BOOST_REQUIRE(!nathan_votes_for_son); + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + son2_start_votes = son2_end_votes; - // Try to accept and reject empty lists - accepted.clear(); - rejected.clear(); - BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, - sidechain_type::bitcoin, 1, true), fc::exception); - BOOST_CHECK(generate_maintenance_block()); + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); - // Verify the votes - son1_obj = con.wallet_api_ptr->get_son("son1account"); - son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); - son1_start_votes = son1_end_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); - son2_start_votes = son2_end_votes; + // Try to accept and reject empty lists + accepted.clear(); + rejected.clear(); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_CHECK(generate_maintenance_block()); - //! Check votes of nathan - nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; - BOOST_REQUIRE(!nathan_votes_for_son); + // Verify the votes + son1_obj = con.wallet_api_ptr->get_son("son1account"); + son1_end_votes = son1_obj.total_votes; + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + son1_start_votes = son1_end_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_end_votes = son2_obj.total_votes; + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + son2_start_votes = son2_end_votes; - } catch( fc::exception& e ) { - BOOST_TEST_MESSAGE("SON cli wallet tests exception"); - edump((e.to_detail_string())); - throw; - } - BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests end"); + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON update_son_votes cli wallet tests end"); } BOOST_AUTO_TEST_CASE( related_functions ) @@ -774,7 +778,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) flat_map sidechain_public_keys; // create son accounts - for(unsigned int i = 0; i < son_number + 1; i++) + for(unsigned int i = 1; i < son_number + 1; i++) { sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); From 9268c31ac4fce3539d27c2a947f4fb65b07fff35 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 25 Aug 2022 17:41:44 +0200 Subject: [PATCH 041/106] Code formatting --- .../bitcoin/sign_bitcoin_transaction.cpp | 12 ++++++------ .../sidechain_net_handler_bitcoin.hpp | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp index 893f82aa..033cafe0 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.cpp @@ -76,14 +76,14 @@ bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const s //! Get sig_temp FC_ASSERT(sig.size() > 70); FC_ASSERT(sig[0] == 0x30); - FC_ASSERT(sig[1] == static_cast(sig.size()-3)); + FC_ASSERT(sig[1] == static_cast(sig.size() - 3)); FC_ASSERT(sig[2] == 0x02); const uint r_size = sig[3]; - std::vector sig_temp(sig.begin()+4+(r_size-32), sig.begin()+4+r_size); - FC_ASSERT(sig[4+r_size] == 0x02); - const uint s_size = sig[5+r_size]; - FC_ASSERT(sig.size() == r_size+s_size+7); - sig_temp.insert(sig_temp.end(), sig.begin()+6+r_size, sig.end()); + std::vector sig_temp(sig.begin() + 4 + (r_size - 32), sig.begin() + 4 + r_size); + FC_ASSERT(sig[4 + r_size] == 0x02); + const uint s_size = sig[5 + r_size]; + FC_ASSERT(sig.size() == r_size + s_size + 7); + sig_temp.insert(sig_temp.end(), sig.begin() + 6 + r_size, sig.end()); std::vector pubkey_temp(pubkey.begin(), pubkey.end()); std::vector msg_temp(msg.begin(), msg.end()); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 55af0c4e..9a858cb7 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include @@ -23,7 +23,7 @@ public: uint64_t amount_; }; -class bitcoin_rpc_client: public rpc_client { +class bitcoin_rpc_client : public rpc_client { public: enum class multi_type { script, @@ -65,7 +65,6 @@ private: std::string password; std::string wallet; std::string wallet_password; - }; // ============================================================================= From 5c416e3a5b39316eb034f3170bc007036e419fe2 Mon Sep 17 00:00:00 2001 From: Meheboob Khan Date: Wed, 7 Sep 2022 13:57:00 +0000 Subject: [PATCH 042/106] Port net library --- libraries/net/core_messages.cpp | 1 - libraries/net/include/graphene/net/config.hpp | 5 + libraries/net/include/graphene/net/node.hpp | 29 ++++- .../include/graphene/net/peer_connection.hpp | 3 +- .../include/graphene/net/peer_database.hpp | 2 +- libraries/net/node.cpp | 117 ++++++++++++++++++ libraries/net/peer_database.cpp | 3 +- 7 files changed, 154 insertions(+), 6 deletions(-) diff --git a/libraries/net/core_messages.cpp b/libraries/net/core_messages.cpp index efff812d..3a4b842c 100644 --- a/libraries/net/core_messages.cpp +++ b/libraries/net/core_messages.cpp @@ -47,4 +47,3 @@ namespace graphene { namespace net { const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type; } } // graphene::net - diff --git a/libraries/net/include/graphene/net/config.hpp b/libraries/net/include/graphene/net/config.hpp index 9edca51c..894850d6 100644 --- a/libraries/net/include/graphene/net/config.hpp +++ b/libraries/net/include/graphene/net/config.hpp @@ -23,6 +23,8 @@ */ #pragma once +#include + #define GRAPHENE_NET_PROTOCOL_VERSION 106 /** @@ -110,3 +112,6 @@ #define GRAPHENE_NET_MAX_NESTED_OBJECTS (250) #define MAXIMUM_PEERDB_SIZE 1000 + +constexpr size_t MAX_BLOCKS_TO_HANDLE_AT_ONCE = 200; +constexpr size_t MAX_SYNC_BLOCKS_TO_PREFETCH = 10 * MAX_BLOCKS_TO_HANDLE_AT_ONCE; diff --git a/libraries/net/include/graphene/net/node.hpp b/libraries/net/include/graphene/net/node.hpp index adbaf262..cbf5c594 100644 --- a/libraries/net/include/graphene/net/node.hpp +++ b/libraries/net/include/graphene/net/node.hpp @@ -61,7 +61,7 @@ namespace graphene { namespace net { class node_delegate { public: - virtual ~node_delegate(){} + virtual ~node_delegate() = default; /** * If delegate has the item, the network has no need to fetch it. @@ -71,7 +71,9 @@ namespace graphene { namespace net { /** * @brief Called when a new block comes in from the network * + * @param blk_msg the message which contains the block * @param sync_mode true if the message was fetched through the sync process, false during normal operation + * @param contained_transaction_msg_ids container for the transactions to write back into * @returns true if this message caused the blockchain to switch forks, false if it did not * * @throws exception if error validating the item, otherwise the item is @@ -195,7 +197,7 @@ namespace graphene { namespace net { { public: node(const std::string& user_agent); - ~node(); + virtual ~node(); void close(); @@ -213,11 +215,34 @@ namespace graphene { namespace net { */ void add_node( const fc::ip::endpoint& ep ); + /***** + * @brief add a list of nodes to seed the p2p network + * @param seeds a vector of url strings + */ + void add_seed_nodes( std::vector seeds ); + + /**** + * @brief add a node to seed the p2p network + * @param in the url as a string + */ + void add_seed_node( const std::string& in); + /** * Attempt to connect to the specified endpoint immediately. */ virtual void connect_to_endpoint( const fc::ip::endpoint& ep ); + /** + * @brief Helper to convert a string to a collection of endpoints + * + * This converts a string (i.e. "bitshares.eu:665535" to a collection of endpoints. + * NOTE: Throws an exception if not in correct format or was unable to resolve URL. + * + * @param in the incoming string + * @returns a vector of endpoints + */ + static std::vector resolve_string_to_ip_endpoints( const std::string& in ); + /** * Specifies the network interface and port upon which incoming * connections should be accepted. diff --git a/libraries/net/include/graphene/net/peer_connection.hpp b/libraries/net/include/graphene/net/peer_connection.hpp index 0cd0288f..58c467a6 100644 --- a/libraries/net/include/graphene/net/peer_connection.hpp +++ b/libraries/net/include/graphene/net/peer_connection.hpp @@ -62,6 +62,7 @@ namespace graphene { namespace net class peer_connection_delegate { public: + virtual ~peer_connection_delegate() = default; virtual void on_message(peer_connection* originating_peer, const message& received_message) = 0; virtual void on_connection_closed(peer_connection* originating_peer) = 0; @@ -125,7 +126,7 @@ namespace graphene { namespace net * it is sitting on the queue */ virtual size_t get_size_in_queue() = 0; - virtual ~queued_message() {} + virtual ~queued_message() = default; }; /* when you queue up a 'real_queued_message', a full copy of the message is diff --git a/libraries/net/include/graphene/net/peer_database.hpp b/libraries/net/include/graphene/net/peer_database.hpp index ff7f4036..3aadf599 100644 --- a/libraries/net/include/graphene/net/peer_database.hpp +++ b/libraries/net/include/graphene/net/peer_database.hpp @@ -97,7 +97,7 @@ namespace graphene { namespace net { { public: peer_database(); - ~peer_database(); + virtual ~peer_database(); void open(const fc::path& databaseFilename); void close(); diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 8b797d4a..85e8c676 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -72,6 +72,7 @@ #include #include #include +#include #include #include @@ -555,6 +556,10 @@ namespace graphene { namespace net { namespace detail { fc::future _bandwidth_monitor_loop_done; fc::future _dump_node_status_task_done; + /// Used by the task that checks whether addresses of seed nodes have been updated + /// @{ + boost::container::flat_set _seed_nodes; + fc::future _update_seed_nodes_loop_done; /* We have two alternate paths through the schedule_peer_for_deletion code -- one that * uses a mutex to prevent one fiber from adding items to the queue while another is deleting @@ -728,6 +733,11 @@ namespace graphene { namespace net { namespace detail { void listen_to_p2p_network(); void connect_to_p2p_network(); void add_node( const fc::ip::endpoint& ep ); + void add_seed_node( const std::string& in); + void add_seed_nodes( std::vector seeds ); + void resolve_seed_node_and_add( const std::string& seed_string ); + void update_seed_nodes_task(); + void schedule_next_update_seed_nodes_task(); void initiate_connect_to(const peer_connection_ptr& peer); void connect_to_endpoint(const fc::ip::endpoint& ep); void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available); @@ -4757,7 +4767,69 @@ namespace graphene { namespace net { namespace detail { _potential_peer_db.update_entry(updated_peer_record); trigger_p2p_network_connect_loop(); } + void node_impl::add_seed_node(const std::string& endpoint_string) + { + VERIFY_CORRECT_THREAD(); + _seed_nodes.insert( endpoint_string ); + resolve_seed_node_and_add( endpoint_string ); + } + void node_impl::resolve_seed_node_and_add(const std::string& endpoint_string) + { + VERIFY_CORRECT_THREAD(); + std::vector endpoints; + ilog("Resolving seed node ${endpoint}", ("endpoint", endpoint_string)); + try + { + endpoints = graphene::net::node::resolve_string_to_ip_endpoints(endpoint_string); + } + catch(...) + { + wlog( "Unable to resolve endpoint during attempt to add seed node ${ep}", ("ep", endpoint_string) ); + } + for (const fc::ip::endpoint& endpoint : endpoints) + { + ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); + add_node(endpoint); + } + } + void node_impl::update_seed_nodes_task() + { + VERIFY_CORRECT_THREAD(); + try + { + dlog("Starting an iteration of update_seed_nodes loop."); + for( const std::string& endpoint_string : _seed_nodes ) + { + resolve_seed_node_and_add( endpoint_string ); + } + dlog("Done an iteration of update_seed_nodes loop."); + } + catch (const fc::canceled_exception&) + { + ilog( "update_seed_nodes_task canceled" ); + throw; + } + FC_CAPTURE_AND_LOG( (_seed_nodes) ) + + schedule_next_update_seed_nodes_task(); + } + + void node_impl::schedule_next_update_seed_nodes_task() + { + VERIFY_CORRECT_THREAD(); + + if( _node_is_shutting_down ) + return; + + if( _update_seed_nodes_loop_done.valid() && _update_seed_nodes_loop_done.canceled() ) + return; + + _update_seed_nodes_loop_done = fc::schedule( [this]() { update_seed_nodes_task(); }, + fc::time_point::now() + fc::hours(3), + "update_seed_nodes_loop" ); + } + void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer) { new_peer->get_socket().open(); @@ -5296,6 +5368,11 @@ namespace graphene { namespace net { namespace detail { INVOKE_IN_IMPL(add_node, ep); } + void node::add_seed_node(const std::string& in) + { + INVOKE_IN_IMPL(add_seed_node, in); + } + void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint ) { INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint); @@ -5677,5 +5754,45 @@ namespace graphene { namespace net { namespace detail { #undef INVOKE_AND_COLLECT_STATISTICS } // end namespace detail + std::vector node::resolve_string_to_ip_endpoints(const std::string& in) + { + try + { + std::string::size_type colon_pos = in.find(':'); + if (colon_pos == std::string::npos) + FC_THROW("Missing required port number in endpoint string \"${endpoint_string}\"", + ("endpoint_string", in)); + std::string port_string = in.substr(colon_pos + 1); + try + { + uint16_t port = boost::lexical_cast(port_string); + + std::string hostname = in.substr(0, colon_pos); + std::vector endpoints = fc::resolve(hostname, port); + if (endpoints.empty()) + FC_THROW_EXCEPTION( fc::unknown_host_exception, + "The host name can not be resolved: ${hostname}", + ("hostname", hostname) ); + return endpoints; + } + catch (const boost::bad_lexical_cast&) + { + FC_THROW("Bad port: ${port}", ("port", port_string)); + } + } + FC_CAPTURE_AND_RETHROW((in)) + } + void node::add_seed_nodes(std::vector seeds) + { + for(const std::string& endpoint_string : seeds ) + { + try { + add_seed_node(endpoint_string); + } catch( const fc::exception& e ) { + wlog( "caught exception ${e} while adding seed node ${endpoint}", + ("e", e.to_detail_string())("endpoint", endpoint_string) ); + } + } + } } } // end namespace graphene::net diff --git a/libraries/net/peer_database.cpp b/libraries/net/peer_database.cpp index 76ae9c8c..20bc5da9 100644 --- a/libraries/net/peer_database.cpp +++ b/libraries/net/peer_database.cpp @@ -50,7 +50,8 @@ namespace graphene { namespace net { indexed_by, member >, + &potential_peer_record::last_seen_time>, + std::greater >, hashed_unique, member Date: Wed, 7 Sep 2022 13:57:17 +0000 Subject: [PATCH 043/106] Update delayed node feature --- libraries/app/application.cpp | 5 +- .../delayed_node/delayed_node_plugin.cpp | 39 ++- programs/CMakeLists.txt | 1 - programs/delayed_node/CMakeLists.txt | 21 -- programs/delayed_node/main.cpp | 305 ------------------ programs/witness_node/CMakeLists.txt | 2 +- programs/witness_node/main.cpp | 2 + 7 files changed, 30 insertions(+), 345 deletions(-) delete mode 100644 programs/delayed_node/CMakeLists.txt delete mode 100644 programs/delayed_node/main.cpp diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 1a3d3a8a..b29cd87b 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -917,7 +917,8 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti wanted.insert("accounts_list"); wanted.insert("affiliate_stats"); } - wanted.insert("witness"); + if (!wanted.count("delayed_node") && !wanted.count("witness")) // explicitly requested delayed_node functionality suppresses witness functions + wanted.insert("witness"); wanted.insert("bookie"); int es_ah_conflict_counter = 0; @@ -949,7 +950,7 @@ void application::startup() { } std::shared_ptr application::get_plugin(const string &name) const { - return my->_active_plugins[name]; + return is_plugin_enabled(name) ? my->_active_plugins[name] : nullptr; } bool application::is_plugin_enabled(const string &name) const { diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index 71de7db5..99b02378 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -63,8 +63,24 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c void delayed_node_plugin::connect() { - my->client_connection = std::make_shared(my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); + fc::http::websocket_connection_ptr con; + try + { + con = my->client.connect(my->remote_endpoint); + } + catch( const fc::exception& e ) + { + wlog("Error while connecting: ${e}", ("e", e.to_detail_string())); + connection_failed(); + return; + } + my->client_connection = std::make_shared( + con, GRAPHENE_NET_MAX_NESTED_OBJECTS ); my->database_api = my->client_connection->get_remote_api(0); + my->database_api->set_block_applied_callback([this]( const fc::variant& block_id ) + { + fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS ); + } ); my->client_connection_closed = my->client_connection->closed.connect([this] { connection_failed(); }); @@ -73,7 +89,9 @@ void delayed_node_plugin::connect() void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options) { FC_ASSERT(options.count("trusted-node") > 0); + ilog("delayed_node_plugin: plugin_initialize() begin"); my->remote_endpoint = "ws://" + options.at("trusted-node").as(); + ilog("delayed_node_plugin: plugin_initialize() end"); } void delayed_node_plugin::sync_with_trusted_node() @@ -100,8 +118,11 @@ void delayed_node_plugin::sync_with_trusted_node() while( remote_dpo.last_irreversible_block_num > db.head_block_num() ) { fc::optional block = my->database_api->get_block( db.head_block_num()+1 ); + // TODO: during sync, decouple requesting blocks from preprocessing + applying them FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have."); ilog("Pushing block #${n}", ("n", block->block_num())); + // timur: failed to merge from bitshares, API n/a in peerplays + // db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait(); db.push_block(*block); synced_blocks++; } @@ -136,24 +157,12 @@ void delayed_node_plugin::plugin_startup() mainloop(); }); - try - { - connect(); - my->database_api->set_block_applied_callback([this]( const fc::variant& block_id ) - { - fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS ); - } ); - return; - } - catch (const fc::exception& e) - { - elog("Error during connection: ${e}", ("e", e.to_detail_string())); - } - fc::async([this]{connection_failed();}); + connect(); } void delayed_node_plugin::connection_failed() { + my->last_received_remote_head = my->last_processed_remote_head; elog("Connection to trusted node failed; retrying in 5 seconds..."); fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5)); } diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index d9c82346..7b9b9918 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -4,7 +4,6 @@ if( BUILD_PEERPLAYS_PROGRAMS ) add_subdirectory( genesis_util ) add_subdirectory( witness_node ) add_subdirectory( debug_node ) - add_subdirectory( delayed_node ) add_subdirectory( js_operation_serializer ) add_subdirectory( size_checker ) endif( BUILD_PEERPLAYS_PROGRAMS ) diff --git a/programs/delayed_node/CMakeLists.txt b/programs/delayed_node/CMakeLists.txt deleted file mode 100644 index 7e610ace..00000000 --- a/programs/delayed_node/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -add_executable( delayed_node main.cpp ) -if( UNIX AND NOT APPLE ) - set(rt_library rt ) -endif() - -find_package( Gperftools QUIET ) -if( GPERFTOOLS_FOUND ) - message( STATUS "Found gperftools; compiling delayed_node with TCMalloc") - list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -endif() - -target_link_libraries( delayed_node - PRIVATE graphene_app graphene_egenesis_full graphene_delayed_node ${PLATFORM_SPECIFIC_LIBS} ) - -install( TARGETS - delayed_node - - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib -) diff --git a/programs/delayed_node/main.cpp b/programs/delayed_node/main.cpp deleted file mode 100644 index 3e058b64..00000000 --- a/programs/delayed_node/main.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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 - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#ifdef WIN32 -# include -#else -# include -#endif - -using namespace graphene; -namespace bpo = boost::program_options; - -void write_default_logging_config_to_stream(std::ostream& out); -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename); - -int main(int argc, char** argv) { - try { - app::application node; - bpo::options_description app_options("Graphene Delayed Node"); - bpo::options_description cfg_options("Graphene Delayed Node"); - app_options.add_options() - ("help,h", "Print this help message and exit.") - ("data-dir,d", bpo::value()->default_value("delayed_node_data_dir"), "Directory containing databases, configuration file, etc.") - ; - - bpo::variables_map options; - - bpo::options_description cli, cfg; - node.set_program_options(cli, cfg); - cfg_options.add(cfg); - - cfg_options.add_options() - ("plugins", bpo::value()->default_value("delayed_node account_history market_history"), - "Space-separated list of plugins to activate"); - - auto delayed_plug = node.register_plugin(); - auto history_plug = node.register_plugin(); - auto market_history_plug = node.register_plugin(); - - // add plugin options to config - try - { - bpo::options_description cli, cfg; - node.set_program_options(cli, cfg); - app_options.add(cli); - cfg_options.add(cfg); - bpo::store(bpo::parse_command_line(argc, argv, app_options), options); - } - catch (const boost::program_options::error& e) - { - std::cerr << "Error parsing command line: " << e.what() << "\n"; - return 1; - } - - if( options.count("help") ) - { - std::cout << app_options << "\n"; - return 0; - } - - fc::path data_dir; - if( options.count("data-dir") ) - { - data_dir = options["data-dir"].as(); - if( data_dir.is_relative() ) - data_dir = fc::current_path() / data_dir; - } - - fc::path config_ini_path = data_dir / "config.ini"; - // Create config file if not already present - if( !fc::exists(config_ini_path) ) - { - ilog("Writing new config file at ${path}", ("path", config_ini_path)); - if( !fc::exists(data_dir) ) - fc::create_directories(data_dir); - - std::ofstream out_cfg(config_ini_path.preferred_string()); - for( const boost::shared_ptr od : cfg_options.options() ) - { - if( !od->description().empty() ) - out_cfg << "# " << od->description() << "\n"; - boost::any store; - if( !od->semantic()->apply_default(store) ) - out_cfg << "# " << od->long_name() << " = \n"; - else { - auto example = od->format_parameter(); - if( example.empty() ) - // This is a boolean switch - out_cfg << od->long_name() << " = " << "false\n"; - else { - // The string is formatted "arg (=)" - example.erase(0, 6); - example.erase(example.length()-1); - out_cfg << od->long_name() << " = " << example << "\n"; - } - } - out_cfg << "\n"; - } - write_default_logging_config_to_stream(out_cfg); - out_cfg.close(); - // read the default logging config we just wrote out to the file and start using it - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } - - // Parse configuration file - try { - bpo::store(bpo::parse_config_file(config_ini_path.preferred_string().c_str(), cfg_options, true), options); - // try to get logging options from the config file. - try - { - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } - catch (const fc::exception&) - { - wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string())); - } - - bpo::notify(options); - } catch( const boost::program_options::error& e ) { - elog("Error parsing configuration file: ${e}", ("e", e.what())); - return 1; - } - - if( !options.count("plugins") ) - options.insert( std::make_pair( "plugins", bpo::variable_value(std::string("delayed_node account_history market_history"), true) ) ); - - node.initialize(data_dir, options); - node.initialize_plugins( options ); - - node.startup(); - node.startup_plugins(); - - fc::promise::ptr exit_promise = new fc::promise("UNIX Signal Handler"); - fc::set_signal_handler([&exit_promise](int signal) { - exit_promise->set_value(signal); - }, SIGINT); - - ilog("Started delayed node on a chain with ${h} blocks.", ("h", node.chain_database()->head_block_num())); - ilog("Chain ID is ${id}", ("id", node.chain_database()->get_chain_id()) ); - - int signal = exit_promise->wait(); - ilog("Exiting from signal ${n}", ("n", signal)); - node.shutdown_plugins(); - return 0; - } catch( const fc::exception& e ) { - elog("Exiting with error:\n${e}", ("e", e.to_detail_string())); - return 1; - } -} - -// logging config is too complicated to be parsed by boost::program_options, -// so we do it by hand -// -// Currently, you can only specify the filenames and logging levels, which -// are all most users would want to change. At a later time, options can -// be added to control rotation intervals, compression, and other seldom- -// used features -void write_default_logging_config_to_stream(std::ostream& out) -{ - out << "# declare an appender named \"stderr\" that writes messages to the console\n" - "[log.console_appender.stderr]\n" - "stream=std_error\n\n" - "# declare an appender named \"p2p\" that writes messages to p2p.log\n" - "[log.file_appender.p2p]\n" - "filename=logs/p2p/p2p.log\n" - "# filename can be absolute or relative to this config file\n\n" - "# route any messages logged to the default logger to the \"stderr\" logger we\n" - "# declared above, if they are info level are higher\n" - "[logger.default]\n" - "level=info\n" - "appenders=stderr\n\n" - "# route messages sent to the \"p2p\" logger to the p2p appender declared above\n" - "[logger.p2p]\n" - "level=info\n" - "appenders=p2p\n\n"; -} - -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename) -{ - try - { - fc::logging_config logging_config; - bool found_logging_config = false; - - boost::property_tree::ptree config_ini_tree; - boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree); - for (const auto& section : config_ini_tree) - { - const std::string& section_name = section.first; - const boost::property_tree::ptree& section_tree = section.second; - - const std::string console_appender_section_prefix = "log.console_appender."; - const std::string file_appender_section_prefix = "log.file_appender."; - const std::string logger_section_prefix = "logger."; - - if (boost::starts_with(section_name, console_appender_section_prefix)) - { - std::string console_appender_name = section_name.substr(console_appender_section_prefix.length()); - std::string stream_name = section_tree.get("stream"); - - // construct a default console appender config here - // stdout/stderr will be taken from ini file, everything else hard-coded here - fc::console_appender::config console_appender_config; - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::debug, - fc::console_appender::color::green)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::warn, - fc::console_appender::color::brown)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::error, - fc::console_appender::color::cyan)); - console_appender_config.stream = fc::variant(stream_name, 1).as(1); - logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config, GRAPHENE_MAX_NESTED_OBJECTS))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, file_appender_section_prefix)) - { - std::string file_appender_name = section_name.substr(file_appender_section_prefix.length()); - fc::path file_name = section_tree.get("filename"); - if (file_name.is_relative()) - file_name = fc::absolute(config_ini_filename).parent_path() / file_name; - - - // construct a default file appender config here - // filename will be taken from ini file, everything else hard-coded here - fc::file_appender::config file_appender_config; - file_appender_config.filename = file_name; - file_appender_config.flush = true; - file_appender_config.rotate = true; - file_appender_config.rotation_interval = fc::hours(1); - file_appender_config.rotation_limit = fc::days(1); - logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config, GRAPHENE_MAX_NESTED_OBJECTS))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, logger_section_prefix)) - { - std::string logger_name = section_name.substr(logger_section_prefix.length()); - std::string level_string = section_tree.get("level"); - std::string appenders_string = section_tree.get("appenders"); - fc::logger_config logger_config(logger_name); - logger_config.level = fc::variant(level_string, 1).as(1); - boost::split(logger_config.appenders, appenders_string, - boost::is_any_of(" ,"), - boost::token_compress_on); - logging_config.loggers.push_back(logger_config); - found_logging_config = true; - } - } - if (found_logging_config) - return logging_config; - else - return fc::optional(); - } - FC_RETHROW_EXCEPTIONS(warn, "") -} diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index 806330d6..d2235034 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -11,7 +11,7 @@ endif() # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 target_link_libraries( witness_node - PRIVATE graphene_app graphene_egenesis_full graphene_snapshot graphene_witness peerplays_sidechain ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_egenesis_full graphene_snapshot graphene_delayed_node graphene_witness peerplays_sidechain ${PLATFORM_SPECIFIC_LIBS} ) # also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins install( TARGETS diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index e1c6c12e..b3c0aa37 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,7 @@ int main(int argc, char** argv) { auto bookie_plug = node->register_plugin(); auto peerplays_sidechain = node->register_plugin(); auto snapshot_plug = node->register_plugin(); + auto delayed_plug = node->register_plugin(); // add plugin options to config try From b895b52b7bcbfea3fc7e4f33edf372053242aba3 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 8 Sep 2022 16:58:08 +0200 Subject: [PATCH 044/106] Cherrypick important fixes/cosmetics from feature/son-for-ethereum --- libraries/app/application.cpp | 4 +- .../peerplays_sidechain/common/rpc_client.cpp | 16 ++- .../peerplays_sidechain/common/utils.cpp | 42 ++++++++ .../peerplays_sidechain/common/rpc_client.hpp | 7 +- .../peerplays_sidechain/common/utils.hpp | 7 ++ .../sidechain_net_handler_bitcoin.hpp | 7 +- .../sidechain_net_handler_hive.hpp | 15 +-- .../peerplays_sidechain_plugin.cpp | 22 ++--- .../sidechain_net_handler_bitcoin.cpp | 14 +-- .../sidechain_net_handler_hive.cpp | 98 +++++++++---------- 10 files changed, 138 insertions(+), 94 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index b29cd87b..db9ef028 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -917,8 +917,8 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti wanted.insert("accounts_list"); wanted.insert("affiliate_stats"); } - if (!wanted.count("delayed_node") && !wanted.count("witness")) // explicitly requested delayed_node functionality suppresses witness functions - wanted.insert("witness"); + if (!wanted.count("delayed_node") && !wanted.count("witness")) // explicitly requested delayed_node functionality suppresses witness functions + wanted.insert("witness"); wanted.insert("bookie"); int es_ah_conflict_counter = 0; diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index 173019eb..4c1365f3 100644 --- a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp @@ -2,23 +2,20 @@ #include #include -#include - -#include -#include -#include #include #include #include #include #include +#include +#include +#include -#include - -#include #include +#include + namespace graphene { namespace peerplays_sidechain { rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : @@ -55,7 +52,8 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor target = "/"; } - authorization = "Basic " + fc::base64_encode(user + ":" + password); + authorization = "Basic " + base64_encode(user + ":" + password); + results = resolver.resolve(host, port); } else { diff --git a/libraries/plugins/peerplays_sidechain/common/utils.cpp b/libraries/plugins/peerplays_sidechain/common/utils.cpp index 4491487f..5bd1dfd7 100644 --- a/libraries/plugins/peerplays_sidechain/common/utils.cpp +++ b/libraries/plugins/peerplays_sidechain/common/utils.cpp @@ -1,8 +1,50 @@ #include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +const std::string base64_padding[] = {"", "==", "="}; + +std::string base64_encode(const std::string &s) { + using namespace boost::archive::iterators; + + typedef base64_from_binary> base64_enc; + + std::stringstream os; + std::copy(base64_enc(s.c_str()), base64_enc(s.c_str() + s.size()), std::ostream_iterator(os)); + os << base64_padding[s.size() % 3]; + + return os.str(); +} + +std::string base64_decode(const std::string &s) { + using namespace boost::archive::iterators; + + typedef transform_width, 8, 6> base64_dec; + + std::stringstream os; + unsigned int size = s.size(); + if (size && s[size - 1] == '=') { + --size; + if (size && s[size - 1] == '=') + --size; + } + if (size == 0) + return std::string(); + + std::copy(base64_dec(s.data()), base64_dec(s.data() + size), std::ostream_iterator(os)); + + return os.str(); +} + std::string object_id_to_string(graphene::chain::object_id_type id) { std::string object_id = fc::to_string(id.space()) + "." + fc::to_string(id.type()) + "." + fc::to_string(id.instance()); return object_id; } + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp index 1a797782..eb8eac0c 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp @@ -23,15 +23,16 @@ protected: std::string send_post_request(std::string method, std::string params, bool show_log); std::string url; + std::string user; + std::string password; + bool debug_rpc_calls; + std::string protocol; std::string host; std::string port; std::string target; std::string authorization; - std::string user; - std::string password; - bool debug_rpc_calls; uint32_t request_id; private: diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp index 99c59019..066a36fe 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp @@ -2,4 +2,11 @@ #include +namespace graphene { namespace peerplays_sidechain { + +std::string base64_encode(const std::string &s); +std::string base64_decode(const std::string &s); + std::string object_id_to_string(graphene::chain::object_id_type id); + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 9a858cb7..62eeeab6 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -63,7 +63,7 @@ private: uint32_t rpc_port; std::string user; std::string password; - std::string wallet; + std::string wallet_name; std::string wallet_password; }; @@ -111,17 +111,18 @@ private: std::string ip; uint32_t zmq_port; uint32_t rpc_port; - uint32_t bitcoin_major_version; std::string rpc_user; std::string rpc_password; - std::string wallet; + std::string wallet_name; std::string wallet_password; std::unique_ptr bitcoin_client; std::unique_ptr listener; fc::future on_changed_objects_task; + bitcoin::bitcoin_address::network network_type; + uint32_t bitcoin_major_version; std::mutex event_handler_mutex; typedef std::lock_guard scoped_lock; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp index 47e6bf90..72539db2 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp @@ -6,15 +6,14 @@ #include -#include #include #include namespace graphene { namespace peerplays_sidechain { -class hive_node_rpc_client : public rpc_client { +class hive_rpc_client : public rpc_client { public: - hive_node_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); std::string account_history_api_get_transaction(std::string transaction_id); std::string block_api_get_block(uint32_t block_number); @@ -48,10 +47,12 @@ public: bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); private: - std::string node_rpc_url; - std::string node_rpc_user; - std::string node_rpc_password; - hive_node_rpc_client *node_rpc_client; + std::string rpc_url; + std::string rpc_user; + std::string rpc_password; + std::string wallet_account_name; + + hive_rpc_client *rpc_client; hive::chain_id_type chain_id; hive::network network_type; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 461b1a92..12de3dfd 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -172,7 +172,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); cli.add_options()("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password"); - cli.add_options()("bitcoin-wallet", bpo::value(), "Bitcoin wallet"); + cli.add_options()("bitcoin-wallet-name", bpo::value(), "Bitcoin wallet name"); cli.add_options()("bitcoin-wallet-password", bpo::value(), "Bitcoin wallet password"); cli.add_options()("bitcoin-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); @@ -181,6 +181,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("hive-node-rpc-url", bpo::value()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-user", bpo::value(), "Hive node RPC user"); cli.add_options()("hive-node-rpc-password", bpo::value(), "Hive node RPC password"); + cli.add_options()("hive-wallet-account-name", bpo::value(), "Hive wallet account name"); cli.add_options()("hive-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4", "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n")), "Tuple of [Hive public key, Hive private key] (may specify multiple times)"); @@ -231,9 +232,9 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt config_ready_bitcoin = options.count("bitcoin-node-ip") && options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") && - /*options.count("bitcoin-wallet") && options.count("bitcoin-wallet-password") &&*/ + options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") && options.count("bitcoin-private-key"); - if (!config_ready_bitcoin) { + if (sidechain_enabled_bitcoin && !config_ready_bitcoin) { wlog("Haven't set up Bitcoin sidechain parameters"); } @@ -248,28 +249,21 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt sidechain_enabled_hive = options.at("hive-sidechain-enabled").as(); config_ready_hive = options.count("hive-node-rpc-url") && /*options.count("hive-node-rpc-user") && options.count("hive-node-rpc-password") &&*/ + options.count("hive-wallet-account-name") && options.count("hive-private-key"); - if (!config_ready_hive) { + if (sidechain_enabled_hive && !config_ready_hive) { wlog("Haven't set up Hive sidechain parameters"); } #ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS - sidechain_enabled_peerplays = true; //options.at("peerplays-sidechain-enabled").as(); + sidechain_enabled_peerplays = true; #else sidechain_enabled_peerplays = false; #endif config_ready_peerplays = true; - if (!config_ready_peerplays) { + if (sidechain_enabled_peerplays && !config_ready_peerplays) { wlog("Haven't set up Peerplays sidechain parameters"); } - - if (!(config_ready_bitcoin && - /*config_ready_ethereum &&*/ - config_ready_hive && - config_ready_peerplays)) { - wlog("Haven't set up any sidechain parameters"); - throw; - } } void peerplays_sidechain_plugin_impl::plugin_startup() { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index b48ccbb6..cace6fb3 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -334,9 +334,9 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain rpc_port = options.at("bitcoin-node-rpc-port").as(); rpc_user = options.at("bitcoin-node-rpc-user").as(); rpc_password = options.at("bitcoin-node-rpc-password").as(); - wallet = ""; - if (options.count("bitcoin-wallet")) { - wallet = options.at("bitcoin-wallet").as(); + wallet_name = ""; + if (options.count("bitcoin-wallet-name")) { + wallet_name = options.at("bitcoin-wallet-name").as(); } wallet_password = ""; if (options.count("bitcoin-wallet-password")) { @@ -356,13 +356,13 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } std::string url = ip + ":" + std::to_string(rpc_port); - if (wallet.length() > 0) { - url = url + "/wallet/" + wallet; + if (!wallet_name.empty()) { + url = url + "/wallet/" + wallet_name; } bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); - if (!wallet.empty()) { - bitcoin_client->loadwallet(wallet); + if (!wallet_name.empty()) { + bitcoin_client->loadwallet(wallet_name); } std::string blockchain_info = bitcoin_client->getblockchaininfo(); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index c62911ec..7d3c4de9 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -28,25 +28,23 @@ #include #include -#include - namespace graphene { namespace peerplays_sidechain { -hive_node_rpc_client::hive_node_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : +hive_rpc_client::hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : rpc_client(url, user_name, password, debug_rpc_calls) { } -std::string hive_node_rpc_client::account_history_api_get_transaction(std::string transaction_id) { +std::string hive_rpc_client::account_history_api_get_transaction(std::string transaction_id) { std::string params = "{ \"id\": \"" + transaction_id + "\" }"; return send_post_request("account_history_api.get_transaction", params, debug_rpc_calls); } -std::string hive_node_rpc_client::block_api_get_block(uint32_t block_number) { +std::string hive_rpc_client::block_api_get_block(uint32_t block_number) { std::string params = "{ \"block_num\": " + std::to_string(block_number) + " }"; return send_post_request("block_api.get_block", params, debug_rpc_calls); } -std::string hive_node_rpc_client::condenser_api_get_accounts(std::vector accounts) { +std::string hive_rpc_client::condenser_api_get_accounts(std::vector accounts) { std::string params = ""; for (auto account : accounts) { if (!params.empty()) { @@ -58,58 +56,58 @@ std::string hive_node_rpc_client::condenser_api_get_accounts(std::vector accounts; accounts.push_back(account); std::string reply_str = condenser_api_get_accounts(accounts); return retrieve_array_value_from_reply(reply_str, "", 0); } -std::string hive_node_rpc_client::get_account_memo_key(std::string account) { +std::string hive_rpc_client::get_account_memo_key(std::string account) { std::string reply_str = get_account(account); reply_str = "{\"result\":" + reply_str + "}"; return retrieve_value_from_reply(reply_str, "memo_key"); } -std::string hive_node_rpc_client::get_chain_id() { +std::string hive_rpc_client::get_chain_id() { std::string reply_str = database_api_get_version(); return retrieve_value_from_reply(reply_str, "chain_id"); } -std::string hive_node_rpc_client::get_head_block_id() { +std::string hive_rpc_client::get_head_block_id() { std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "head_block_id"); } -std::string hive_node_rpc_client::get_head_block_time() { +std::string hive_rpc_client::get_head_block_time() { std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "time"); } -std::string hive_node_rpc_client::get_is_test_net() { +std::string hive_rpc_client::get_is_test_net() { std::string reply_str = condenser_api_get_config(); return retrieve_value_from_reply(reply_str, "IS_TEST_NET"); } -std::string hive_node_rpc_client::get_last_irreversible_block_num() { +std::string hive_rpc_client::get_last_irreversible_block_num() { std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "last_irreversible_block_num"); } @@ -122,18 +120,20 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi debug_rpc_calls = options.at("debug-rpc-calls").as(); } - node_rpc_url = options.at("hive-node-rpc-url").as(); - if (options.count("hive-node-rpc-user")) { - node_rpc_user = options.at("hive-node-rpc-user").as(); + rpc_url = options.at("hive-node-rpc-url").as(); + if (options.count("hive-rpc-user")) { + rpc_user = options.at("hive-rpc-user").as(); } else { - node_rpc_user = ""; + rpc_user = ""; } - if (options.count("hive-node-rpc-password")) { - node_rpc_password = options.at("hive-node-rpc-password").as(); + if (options.count("hive-rpc-password")) { + rpc_password = options.at("hive-rpc-password").as(); } else { - node_rpc_password = ""; + rpc_password = ""; } + wallet_account_name = options.at("hive-wallet-account-name").as(); + if (options.count("hive-private-key")) { const std::vector pub_priv_keys = options["hive-private-key"].as>(); for (const std::string &itr_key_pair : pub_priv_keys) { @@ -146,16 +146,16 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi } } - node_rpc_client = new hive_node_rpc_client(node_rpc_url, node_rpc_user, node_rpc_password, debug_rpc_calls); + rpc_client = new hive_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); - std::string chain_id_str = node_rpc_client->get_chain_id(); + std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { - elog("No Hive node running at ${url}", ("url", node_rpc_url)); + elog("No Hive node running at ${url}", ("url", rpc_url)); FC_ASSERT(false); } chain_id = chain_id_type(chain_id_str); - std::string is_test_net = node_rpc_client->get_is_test_net(); + std::string is_test_net = rpc_client->get_is_test_net(); network_type = is_test_net.compare("true") == 0 ? hive::network::testnet : hive::network::mainnet; if (network_type == hive::network::mainnet) { ilog("Running on Hive mainnet, chain id ${chain_id_str}", ("chain_id_str", chain_id_str)); @@ -225,7 +225,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { } if (son_sets_equal) { - address_ok = (op_obj_idx_0.get().address == "son-account"); + address_ok = (op_obj_idx_0.get().address == wallet_account_name); } if (po.proposed_transaction.operations.size() >= 2) { @@ -254,14 +254,14 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { account_auths[wallet_son.public_key] = wallet_son.weight; } - std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); + std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); hive::authority active; active.weight_threshold = total_weight * 2 / 3 + 1; active.account_auths = account_auths; hive::account_update_operation auo; - auo.account = "son-account"; + auo.account = wallet_account_name; auo.active = active; auo.memo_key = op_trx.operations[0].get().memo_key; @@ -303,7 +303,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value; uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-"))); - std::string tx_str = node_rpc_client->account_history_api_get_transaction(swdo_txid); + std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid); if (tx_str != "") { std::stringstream ss_tx(tx_str); @@ -408,7 +408,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { } hive::transfer_operation t_op; - t_op.from = "son-account"; + t_op.from = wallet_account_name; t_op.to = swwo->withdraw_address; t_op.amount.amount = swwo->withdraw_amount; t_op.amount.symbol = symbol; @@ -495,7 +495,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { account_auths[active_son.public_key] = active_son.weight; } - std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); + std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); if (memo_key.empty()) { return; @@ -506,14 +506,14 @@ void sidechain_net_handler_hive::process_primary_wallet() { active.account_auths = account_auths; hive::account_update_operation auo; - auo.account = "son-account"; + auo.account = wallet_account_name; auo.active = active; auo.memo_key = hive::public_key_type(memo_key); - std::string block_id_str = node_rpc_client->get_head_block_id(); + std::string block_id_str = rpc_client->get_head_block_id(); hive::block_id_type head_block_id(block_id_str); - std::string head_block_time_str = node_rpc_client->get_head_block_time(); + std::string head_block_time_str = rpc_client->get_head_block_time(); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str); hive::signed_transaction htrx; @@ -538,7 +538,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { swu_op.payer = gpo.parameters.son_account(); swu_op.son_wallet_id = active_sw->id; swu_op.sidechain = sidechain; - swu_op.address = "son-account"; + swu_op.address = wallet_account_name; proposal_op.proposed_ops.emplace_back(swu_op); @@ -662,16 +662,16 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob } hive::transfer_operation t_op; - t_op.from = "son-account"; + t_op.from = wallet_account_name; t_op.to = swwo.withdraw_address; t_op.amount.amount = swwo.withdraw_amount; t_op.amount.symbol = symbol; t_op.memo = ""; - std::string block_id_str = node_rpc_client->get_head_block_id(); + std::string block_id_str = rpc_client->get_head_block_id(); hive::block_id_type head_block_id(block_id_str); - std::string head_block_time_str = node_rpc_client->get_head_block_time(); + std::string head_block_time_str = rpc_client->get_head_block_time(); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str); hive::signed_transaction htrx; @@ -727,7 +727,7 @@ std::string sidechain_net_handler_hive::process_sidechain_transaction(const side hive::signed_transaction htrx; fc::raw::unpack(ss_trx, htrx, 1000); - std::string chain_id_str = node_rpc_client->get_chain_id(); + std::string chain_id_str = rpc_client->get_chain_id(); const hive::chain_id_type chain_id(chain_id_str); fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain))); @@ -755,7 +755,7 @@ std::string sidechain_net_handler_hive::send_sidechain_transaction(const sidecha } std::string params = fc::json::to_string(htrx); - node_rpc_client->network_broadcast_api_broadcast_transaction(params); + rpc_client->network_broadcast_api_broadcast_transaction(params); return htrx.id().str(); } @@ -770,7 +770,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr return false; } - std::string tx_str = node_rpc_client->account_history_api_get_transaction(sto.sidechain_transaction); + std::string tx_str = rpc_client->account_history_api_get_transaction(sto.sidechain_transaction); if (tx_str != "") { std::stringstream ss_tx(tx_str); @@ -781,7 +781,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr std::string tx_txid = tx_json.get("result.transaction_id"); uint32_t tx_block_num = tx_json.get("result.block_num"); - uint32_t last_irreversible_block = std::stoul(node_rpc_client->get_last_irreversible_block_num()); + uint32_t last_irreversible_block = std::stoul(rpc_client->get_last_irreversible_block_num()); //std::string tx_address = addr.get_address(); //int64_t tx_amount = -1; @@ -817,7 +817,7 @@ void sidechain_net_handler_hive::schedule_hive_listener() { void sidechain_net_handler_hive::hive_listener_loop() { schedule_hive_listener(); - std::string reply = node_rpc_client->database_api_get_dynamic_global_properties(); + std::string reply = rpc_client->database_api_get_dynamic_global_properties(); if (!reply.empty()) { std::stringstream ss(reply); boost::property_tree::ptree json; @@ -832,7 +832,7 @@ void sidechain_net_handler_hive::hive_listener_loop() { } } - //std::string reply = node_rpc_client->get_last_irreversible_block_num(); + //std::string reply = rpc_client->get_last_irreversible_block_num(); //if (!reply.empty()) { // uint64_t last_irreversible_block = std::stoul(reply); // if (last_irreversible_block != last_block_received) { @@ -844,7 +844,7 @@ void sidechain_net_handler_hive::hive_listener_loop() { } void sidechain_net_handler_hive::handle_event(const std::string &event_data) { - std::string block = node_rpc_client->block_api_get_block(std::atoll(event_data.c_str())); + std::string block = rpc_client->block_api_get_block(std::atoll(event_data.c_str())); if (block != "") { add_to_son_listener_log("BLOCK : " + event_data); std::stringstream ss(block); @@ -869,7 +869,7 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) { std::string from = op_value.get("from"); std::string to = op_value.get("to"); - if (to == "son-account") { + if (to == wallet_account_name) { const auto &amount_child = op_value.get_child("amount"); From 0f64947f4a86c17c3ec4931716f0b447b2c3e76c Mon Sep 17 00:00:00 2001 From: Meheboob Khan Date: Wed, 14 Sep 2022 18:03:40 +0000 Subject: [PATCH 045/106] Improved get_active_sons and get_son_network_status API/CLI [issue 430] --- libraries/app/database_api.cpp | 78 ++++++++++++++++++ .../app/include/graphene/app/database_api.hpp | 30 +++++++ .../wallet/include/graphene/wallet/wallet.hpp | 29 ++++++- libraries/wallet/wallet.cpp | 81 ++++++++----------- 4 files changed, 167 insertions(+), 51 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b10d6b99..3158ba49 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -184,6 +184,10 @@ public: fc::optional get_son_by_account(const std::string account_id_or_name) const; map lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const; uint64_t get_son_count() const; + flat_map> get_active_sons(); + vector get_active_sons_by_sidechain(sidechain_type sidechain); + map> get_son_network_status(); + map get_son_network_status_by_sidechain(sidechain_type sidechain); // SON wallets optional get_active_son_wallet(); @@ -1848,6 +1852,80 @@ uint64_t database_api_impl::get_son_count() const { return _db.get_index_type().indices().size(); } +flat_map> database_api::get_active_sons() { + return my->get_active_sons(); +} + +flat_map> database_api_impl::get_active_sons() { + return get_global_properties().active_sons; +} + +vector database_api::get_active_sons_by_sidechain(sidechain_type sidechain) { + return my->get_active_sons_by_sidechain(sidechain); +} + +vector database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) { + const global_property_object &gpo = get_global_properties(); + + vector result; + + if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) { + result = gpo.active_sons.at(sidechain); + } + + return result; +} + +map> database_api::get_son_network_status() { + return my->get_son_network_status(); +} + +map> database_api_impl::get_son_network_status() { + map> result; + + for (auto active_sidechain_type : active_sidechain_types) { + result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type); + } + + return result; +} + +map database_api::get_son_network_status_by_sidechain(sidechain_type sidechain) { + return my->get_son_network_status_by_sidechain(sidechain); +} + +map database_api_impl::get_son_network_status_by_sidechain(sidechain_type sidechain) { + const global_property_object &gpo = get_global_properties(); + + map result; + + if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) { + for (const auto si : gpo.active_sons.at(sidechain)) { + const auto son_obj = si.son_id(_db); + const auto sso = son_obj.statistics(_db); + string status; + + if (sso.last_active_timestamp.find(sidechain) != sso.last_active_timestamp.end()) { + if (sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { + status = "OK, regular SON heartbeat"; + } else { + if (sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { + status = "OK, irregular SON heartbeat, but not triggering SON down proposal"; + } else { + status = "NOT OK, irregular SON heartbeat, triggering SON down proposal]"; + } + } + } else { + status = "No heartbeats sent"; + } + + result[si.son_id] = status; + } + } + + return result; +} + ////////////////////////////////////////////////////////////////////// // // // SON Wallets // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index abe65a2b..b53c8eeb 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -675,6 +675,32 @@ public: */ uint64_t get_son_count() const; + /** + * @brief Get list of active sons + * @return List of active SONs + */ + flat_map> get_active_sons(); + + /** + * @brief Get list of active sons + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return List of active SONs + */ + vector get_active_sons_by_sidechain(sidechain_type sidechain); + + /** + * @brief Get SON network status + * @return SON network status description for a given sidechain type + */ + map> get_son_network_status(); + + /** + * @brief Get SON network status + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return SON network status description for a given sidechain type + */ + map get_son_network_status_by_sidechain(sidechain_type sidechain); + ///////////////////////// // SON Wallets // ///////////////////////// @@ -1149,6 +1175,10 @@ FC_API(graphene::app::database_api, (get_son_by_account) (lookup_son_accounts) (get_son_count) + (get_active_sons) + (get_active_sons_by_sidechain) + (get_son_network_status) + (get_son_network_status_by_sidechain) // SON wallets (get_active_son_wallet) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 0086c654..9850a72b 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1377,10 +1377,30 @@ class wallet_api map list_active_sons(); /** - * @brief Get SON network status - * @return SON network status description + * @brief Get list of active sons + * @return List of active SONs */ - map get_son_network_status(); + flat_map> get_active_sons(); + + /** + * @brief Get list of active sons + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return List of active SONs + */ + vector get_active_sons_by_sidechain(sidechain_type sidechain); + + /** + * @brief Get SON network status + * @return SON network status description for a given sidechain type + */ + map> get_son_network_status(); + + /** + * @brief Get SON network status + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return SON network status description for a given sidechain type + */ + map get_son_network_status_by_sidechain(sidechain_type sidechain); /** * @brief Get active SON wallet @@ -2599,7 +2619,10 @@ FC_API( graphene::wallet::wallet_api, (activate_deregistered_son) (list_sons) (list_active_sons) + (get_active_sons) + (get_active_sons_by_sidechain) (get_son_network_status) + (get_son_network_status_by_sidechain) (request_son_maintenance) (cancel_request_son_maintenance) (get_active_son_wallet) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 43ca49fd..49825790 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2274,55 +2274,25 @@ public: FC_CAPTURE_AND_RETHROW() } - //! Fixme - do we need to specify sidechain_type as params here? - map get_son_network_status() - { - try - { - const global_property_object& gpo = get_global_properties(); + flat_map> get_active_sons() + { try { + return _remote_db->get_active_sons(); + } FC_CAPTURE_AND_RETHROW() } - set son_ids_set; - for(const auto& active_sidechain_type : active_sidechain_types) { - std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), - std::inserter(son_ids_set, son_ids_set.end()), - [](const son_info &swi) { - return swi.son_id; - }); - } - vector son_ids; - son_ids.reserve(son_ids_set.size()); - std::transform(son_ids_set.cbegin(), son_ids_set.cend(), - std::inserter(son_ids, son_ids.end()), - [](const son_id_type& sit) { - return sit; - }); + vector get_active_sons_by_sidechain(sidechain_type sidechain) + { try { + return _remote_db->get_active_sons_by_sidechain(sidechain); + } FC_CAPTURE_AND_RETHROW() } - map result; - std::vector> son_objects = _remote_db->get_sons(son_ids); - for(auto son_obj: son_objects) { - string status; - if (son_obj) { - son_statistics_object sso = get_object(son_obj->statistics); - for(const auto& active_sidechain_type : active_sidechain_types) { - if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { - status = "[OK, regular SON heartbeat for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; - } else { - if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { - status = "[OK, irregular SON heartbeat, but not triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; - } else { - status = "[NOT OK, irregular SON heartbeat, triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; - } - } - } - } else { - status = "NOT OK, invalid SON id"; - } - result[son_obj->id] = status; - } - return result; - } - FC_CAPTURE_AND_RETHROW() - } + map> get_son_network_status() + { try { + return _remote_db->get_son_network_status(); + } FC_CAPTURE_AND_RETHROW() } + + map get_son_network_status_by_sidechain(sidechain_type sidechain) + { try { + return _remote_db->get_son_network_status_by_sidechain(sidechain); + } FC_CAPTURE_AND_RETHROW() } optional get_active_son_wallet() { try { @@ -5310,11 +5280,26 @@ map wallet_api::list_active_sons() return my->list_active_sons(); } -map wallet_api::get_son_network_status() +flat_map> wallet_api::get_active_sons() +{ + return my->get_active_sons(); +} + +vector wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain) +{ + return my->get_active_sons_by_sidechain(sidechain); +} + +map> wallet_api::get_son_network_status() { return my->get_son_network_status(); } +map wallet_api::get_son_network_status_by_sidechain(sidechain_type sidechain) +{ + return my->get_son_network_status_by_sidechain(sidechain); +} + optional wallet_api::get_active_son_wallet() { return my->get_active_son_wallet(); From a9267544def377f8d277dd3a142d7d4560434487 Mon Sep 17 00:00:00 2001 From: timur <12267899-timur.5@users.noreply.gitlab.com> Date: Fri, 16 Sep 2022 02:42:31 +0000 Subject: [PATCH 046/106] Bug/fix wallet api doc generation --- .../wallet/generate_api_documentation.pl | 115 ++++++++++++------ .../wallet/include/graphene/wallet/wallet.hpp | 56 +++++++-- 2 files changed, 128 insertions(+), 43 deletions(-) diff --git a/libraries/wallet/generate_api_documentation.pl b/libraries/wallet/generate_api_documentation.pl index a3f333db..3e1b2e06 100755 --- a/libraries/wallet/generate_api_documentation.pl +++ b/libraries/wallet/generate_api_documentation.pl @@ -44,13 +44,37 @@ for my $class (@{$doxydocs->{classes}}) if ($member->{kind} eq 'function') { my @params = map { join(' ', cleanupDoxygenType($_->{type}), $_->{declaration_name}) } @{$member->{parameters}}; - my $briefDescription = sprintf("%40s %s(%s)\n", cleanupDoxygenType($member->{type}), $member->{name}, join(', ', @params)); - my $escapedBriefDescription = "\"" . escapeStringForC($briefDescription) . "\""; - my %paramInfo = map { $_->{declaration_name} => { type => $_->{type}} } @{$member->{parameters}}; + my $callDescription = sprintf("%40s %s(%s)\n", cleanupDoxygenType($member->{type}), $member->{name}, join(', ', @params)); + my $escapedBriefDescription = "\"" . escapeStringForC($callDescription) . "\""; + my %paramInfo = map { $_->{declaration_name} => { type => explainCType(cleanupDoxygenType($_->{type})) } } @{$member->{parameters}}; my $escapedDetailedDescription = "\"\"\n"; - if ($member->{detailed}->{doc}) + my $doc = $member->{detailed}->{doc}; + if ($doc) { - my $docString = formatDocComment($member->{detailed}->{doc}, \%paramInfo); + my $briefDescr = formatDocComment($member->{brief}->{doc}); # get from the proper place + unless ($briefDescr =~ /\w/) # if not provided (API author forgot to add '@brief' comment), + { + for (my $i = 0; $i < @{$doc}; ++$i) # then look inside 'detailed' section + { + my $docElement = $doc->[$i]; + if ($docElement->{type} eq 'text' and $docElement->{content} =~ /\w+/) # use first meaningful line as brief description + { + $briefDescr = $docElement->{content}; + $briefDescr =~ s/^\s+|\s+$//g; + splice @{$doc}, $i, 1; # this section shouldn't be used twice + last; + } + } + } + + my $cmdSyntax = $member->{name}; + my $cmdArgs = join(' ', map { $_->{declaration_name} } @{$member->{parameters}}); + $cmdSyntax .= " $cmdArgs" if $cmdArgs; + + my $docString; + $docString .= $briefDescr; + $docString .= "\n\n" . formatDocComment($doc, \%paramInfo, $cmdSyntax); + for my $line (split(/\n/, $docString)) { $escapedDetailedDescription .= " \"" . escapeStringForC($line . "\n") . "\"\n"; @@ -96,62 +120,85 @@ sub cleanupDoxygenType return $type; } +sub explainCType +{ + my($type) = @_; + $type =~ s/\b\w+:://g; # remove namespaces + $type =~ s/^(?:optional|api)<(.+)>$/$1/; # disregard optional<> and some other templates + $type =~ s/^const\s+(.+)/$1/; # strip const modifier + $type =~ s/^(.+)&/$1/; # strip references + $type =~ s/\s+$/$1/; + $type =~ s/\b(u?int(8|16|32|64)_t|int|unsigned)\b/integer/; # spare the user from width and signedness + $type =~ s/\bbool\b/boolean/; # they're not C++ people + $type =~ s/^(?:vector|set|flat_set)<(.+)>$/[$1, ...]/; # represent as JSon-like array notation + $type =~ s/^(?:map|flat_map)<(.+)\s*,\s*(.+)>$/{$1 => $2, ...}/; # same for map + $type =~ s/^time_point_sec$/time, e.g. 2021-12-25T14:30:05/; + return $type; +} + sub formatDocComment { - my($doc, $paramInfo) = @_; + my($doc, $paramInfo, $cmdSyntax) = @_; my $bodyDocs = ''; + my $notes = ''; + my $see = ''; my $paramDocs = ''; my $returnDocs = ''; for (my $i = 0; $i < @{$doc}; ++$i) { - if ($doc->[$i] eq 'params') + my $docElement = $doc->[$i]; + + if ($docElement->{params}) { $paramDocs .= "Parameters:\n"; - @parametersList = @{$doc->[$i + 1]}; - for my $parameter (@parametersList) + for my $parameter (@{$docElement->{params}}) { my $declname = $parameter->{parameters}->[0]->{name}; my $decltype = cleanupDoxygenType($paramInfo->{$declname}->{type}); - $paramDocs .= Text::Wrap::fill(' ', ' ', "$declname: " . formatDocComment($parameter->{doc}) . " (type: $decltype)") . "\n"; + $paramDocs .= Text::Wrap::fill(' ', ' ', "$declname ($decltype): " . formatDocComment($parameter->{doc})) . "\n"; } - ++$i; } - elsif ($doc->[$i]->{return}) + elsif ($docElement->{return}) { - $returnDocs .= "Returns\n"; - $returnDocs .= Text::Wrap::fill(' ',' ', formatDocComment($doc->[$i]->{return})) . "\n"; + $returnDocs .= "Returns:\n"; + $returnDocs .= Text::Wrap::fill(' ',' ', formatDocComment($docElement->{return})) . "\n"; } - else + elsif ($docElement->{note}) { - my $docElement = $doc->[$i]; - if ($docElement->{type} eq 'text' or $docElement->{type} eq 'url') - { - $bodyDocs .= $docElement->{content}; - } - elsif ($docElement->{type} eq 'parbreak') - { - $bodyDocs .= "\n\n"; - } - elsif ($docElement->{type} eq 'style' and $docElement->{style} eq 'code') - { - $bodyDocs .= "'"; - } + $notes .= Text::Wrap::fill(' ',' ', "Note: ".formatDocComment($docElement->{note})) . "\n"; + } + elsif ($docElement->{see}) + { + $see .= Text::Wrap::fill(' ',' ', "See: ".formatDocComment($docElement->{see})) . "\n"; + } + elsif ($docElement->{type} eq 'text' or $docElement->{type} eq 'url') + { + $bodyDocs .= $docElement->{content}; + } + elsif ($docElement->{type} eq 'parbreak') + { + $bodyDocs .= "\n\n"; + } + elsif ($docElement->{type} eq 'style' and $docElement->{style} eq 'code') + { + $bodyDocs .= "'"; } } $bodyDocs =~ s/^\s+|\s+$//g; $bodyDocs = Text::Wrap::fill('', '', $bodyDocs); - $paramDocs =~ s/^\s+|\s+$//g; + $notes =~ s/^\s+|\s+$//g; + $see =~ s/^\s+|\s+$//g; + $paramDocs =~ s/^\s+|\s+$//g; $returnDocs =~ s/^\s+|\s+$//g; - my $result = Text::Wrap::fill('', '', $bodyDocs); - $result .= "\n\n" . $paramDocs if $paramDocs; - $result .= "\n\n" . $returnDocs if $returnDocs; - - return $result; + my $cmdDocs; + $cmdDocs = "Command:\n" . Text::Wrap::fill(' ',' ', $cmdSyntax) if $cmdSyntax; + + return join "\n\n", grep {$_} ($bodyDocs, $notes, $see, $cmdDocs, $paramDocs, $returnDocs); } sub escapeCharForCString diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 9850a72b..305526c7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -84,6 +84,32 @@ typedef multi_index_container< > > key_label_index_type; +/* How to write doxygen docs + * + * Good + * / ** Returns the block chain's rapidly-changing properties. + * * + * * The returned object contains information that changes every block interval + * * such as the head block number, the next witness, etc. + * * / + * + * Bad, no empty line + * / ** Returns the block chain's rapidly-changing properties. + * * The returned object contains information that changes every block interval + * * such as the head block number, the next witness, etc. + * * / + * + * Better, using @brief tag + * / ** + * * @brief Returns the block chain's rapidly-changing properties. + * * Long description text 1 + * * Long description text 2 + * * @param param1 param1 description + * * @param param2 param2 description + * * @returns return value description + * * / + * string get_rapidly_changing_properties(int32_t interval, string chain_id) + */ struct wallet_data { @@ -249,6 +275,7 @@ class wallet_api */ uint64_t get_account_count()const; /** Lists all accounts controlled by this wallet. + * * This returns a list of the full account objects for all accounts whose private keys * we possess. * @returns a list of account objects @@ -330,6 +357,7 @@ class wallet_api vector get_settle_orders(string a, uint32_t limit)const; /** Returns the block chain's slowly-changing settings. + * * This object contains all of the properties of the blockchain that are fixed * or that change only once per maintenance interval (daily) such as the * current list of witnesses, committee_members, block interval, etc. @@ -339,6 +367,7 @@ class wallet_api global_property_object get_global_properties() const; /** Returns the block chain's rapidly-changing properties. + * * The returned object contains information that changes every block interval * such as the head block number, the next witness, etc. * @see \c get_global_properties() for less-frequently changing properties @@ -354,6 +383,7 @@ class wallet_api account_object get_account(string account_name_or_id) const; /** Returns information about the given asset. + * * @param asset_name_or_id the symbol or id of the asset in question * @returns the information about the asset stored in the block chain */ @@ -368,6 +398,7 @@ class wallet_api asset_bitasset_data_object get_bitasset_data(string asset_name_or_id)const; /** Lookup the id of a named account. + * * @param account_name_or_id the name of the account to look up * @returns the id of the named account */ @@ -375,6 +406,7 @@ class wallet_api /** * Lookup the id of a named asset. + * * @param asset_name_or_id the symbol of an asset to look up * @returns the id of the given asset */ @@ -577,7 +609,7 @@ class wallet_api /** * Derive any number of *possible* owner keys from a given brain key. * - * NOTE: These keys may or may not match with the owner keys of any account. + * @note These keys may or may not match with the owner keys of any account. * This function is merely intended to assist with account or key recovery. * * @see suggest_brain_key() @@ -591,7 +623,8 @@ class wallet_api /** * Determine whether a textual representation of a public key * (in Base-58 format) is *currently* linked - * to any *registered* (i.e. non-stealth) account on the blockchain + * to any *registered* (i.e. non-stealth) account on the blockchain. + * * @param public_key Public key * @return Whether a public key is known */ @@ -682,7 +715,7 @@ class wallet_api uint32_t referrer_percent, bool broadcast = false); - /** Updates account public keys + /** Updates account public keys. * * @param name the name of the existing account * @param old_owner the owner key for the named account to be replaced @@ -700,7 +733,8 @@ class wallet_api bool broadcast = false); /** - * This method updates the key of an authority for an exisiting account. + * Updates the key of an authority for an exisiting account. + * Warning: You can create impossible authorities using this method. The method * will fail if you create an impossible owner authority, but will allow impossible * active and posting authorities. @@ -719,6 +753,7 @@ class wallet_api /** * Upgrades an account to prime status. + * * This makes the account holder a 'lifetime member'. * * @todo there is no option for annual membership @@ -786,7 +821,7 @@ class wallet_api /** - * This method is used to convert a JSON transaction to its transactin ID. + * Convert a JSON transaction to its transactin ID. */ transaction_id_type get_transaction_id( const signed_transaction& trx )const { return trx.id(); } @@ -794,7 +829,7 @@ class wallet_api /** These methods are used for stealth transfers */ ///@{ /** - * This method can be used to set the label for a public key + * Set the label for a public key * * @note No two keys can have the same label. * @@ -918,7 +953,7 @@ class wallet_api signed_transaction borrow_asset(string borrower_name, string amount_to_borrow, string asset_symbol, string amount_of_collateral, bool broadcast = false); - /** Cancel an existing order + /** Cancel an existing order. * * @param order_id the id of order to be cancelled * @param broadcast true to broadcast the transaction on the network @@ -978,6 +1013,7 @@ class wallet_api bool broadcast = false); /** Update the core options on an asset. + * * There are a number of options which all assets in the network use. These options are * enumerated in the asset_object::asset_options struct. This command is used to update * these options for an existing asset. @@ -1184,6 +1220,7 @@ class wallet_api bool broadcast = false); /** Lists all witnesses registered in the blockchain. + * * This returns a list of all account names that own witnesses, and the associated witness id, * sorted by name. This lists witnesses whether they are currently voted in or not. * @@ -1214,6 +1251,7 @@ class wallet_api map list_committee_members(const string& lowerbound, uint32_t limit); /** Lists all workers in the blockchain. + * * This returns a list of all account names that own worker, and the associated worker id, * sorted by name. This lists workers whether they are currently voted in or not. * @@ -1355,6 +1393,7 @@ class wallet_api bool broadcast = false); /** Lists all SONs in the blockchain. + * * This returns a list of all account names that own SON, and the associated SON id, * sorted by name. This lists SONs whether they are currently voted in or not. * @@ -1582,8 +1621,7 @@ class wallet_api string asset_symbol, bool broadcast = false); - /** - * Withdraw a GPOS vesting balance. + /** Withdraw a GPOS vesting balance. * * @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type. * @param amount The amount to withdraw. From 5f97eb7662f1dd8cdb48272abedeba4af1bf7d16 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Mon, 19 Sep 2022 19:23:39 +0000 Subject: [PATCH 047/106] SON for Ethereum --- libraries/CMakeLists.txt | 1 + libraries/app/database_api.cpp | 12 + libraries/chain/db_init.cpp | 69 +- libraries/chain/db_maint.cpp | 64 +- libraries/chain/db_witness_schedule.cpp | 9 +- .../chain/hardfork.d/SON_FOR_ETHEREUM.hf | 7 + .../chain/protocol/chain_parameters.hpp | 5 + .../include/graphene/chain/protocol/vote.hpp | 3 +- .../include/graphene/chain/sidechain_defs.hpp | 4 +- .../include/graphene/chain/son_object.hpp | 5 + libraries/chain/protocol/account.cpp | 4 + libraries/chain/son_evaluator.cpp | 5 +- libraries/chain/son_object.cpp | 4 + .../peerplays_sidechain/CMakeLists.txt | 8 +- .../peerplays_sidechain/ethereum/decoders.cpp | 224 ++++++ .../peerplays_sidechain/ethereum/encoders.cpp | 102 +++ .../ethereum/transaction.cpp | 229 ++++++ .../peerplays_sidechain/ethereum/types.cpp | 5 + .../peerplays_sidechain/ethereum/utils.cpp | 52 ++ .../peerplays_sidechain/ethereum/decoders.hpp | 28 + .../peerplays_sidechain/ethereum/encoders.hpp | 73 ++ .../ethereum/transaction.hpp | 163 ++++ .../peerplays_sidechain/ethereum/types.hpp | 12 + .../peerplays_sidechain/ethereum/utils.hpp | 37 + .../sidechain_net_handler_ethereum.hpp | 75 ++ .../peerplays_sidechain_plugin.cpp | 31 +- .../sidechain_net_handler.cpp | 8 + .../sidechain_net_handler_ethereum.cpp | 761 ++++++++++++++++++ .../sidechain_net_handler_factory.cpp | 4 + libraries/sha3/CMakeLists.txt | 16 + libraries/sha3/include/sha3/memzero.h | 16 + libraries/sha3/include/sha3/sha3.h | 88 ++ libraries/sha3/memzero.c | 75 ++ libraries/sha3/sha3.c | 397 +++++++++ libraries/wallet/wallet.cpp | 7 +- tests/cli/son.cpp | 119 ++- .../ethereum_transaction_tests.cpp | 147 ++++ tests/tests/sidechain_addresses_test.cpp | 1 + tests/tests/son_operations_tests.cpp | 53 +- 39 files changed, 2860 insertions(+), 63 deletions(-) create mode 100644 libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/types.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/utils.cpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp create mode 100644 libraries/sha3/CMakeLists.txt create mode 100644 libraries/sha3/include/sha3/memzero.h create mode 100644 libraries/sha3/include/sha3/sha3.h create mode 100644 libraries/sha3/memzero.c create mode 100644 libraries/sha3/sha3.c create mode 100644 tests/peerplays_sidechain/ethereum_transaction_tests.cpp diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index cf2355f1..f28a6cee 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory( egenesis ) add_subdirectory( fc ) add_subdirectory( net ) add_subdirectory( plugins ) +add_subdirectory( sha3 ) add_subdirectory( time ) add_subdirectory( utilities ) add_subdirectory( wallet ) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b10d6b99..0a96bc26 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2085,6 +2085,7 @@ vector database_api_impl::lookup_vote_ids(const vector &v const auto &against_worker_idx = _db.get_index_type().indices().get(); const auto &son_bictoin_idx = _db.get_index_type().indices().get(); const auto &son_hive_idx = _db.get_index_type().indices().get(); + const auto &son_ethereum_idx = _db.get_index_type().indices().get(); vector result; result.reserve(votes.size()); @@ -2136,6 +2137,14 @@ vector database_api_impl::lookup_vote_ids(const vector &v result.emplace_back(variant()); break; } + case vote_id_type::son_ethereum: { + auto itr = son_ethereum_idx.find(id); + if (itr != son_ethereum_idx.end()) + result.emplace_back(variant(*itr, 5)); + else + result.emplace_back(variant()); + break; + } case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings default: @@ -2173,6 +2182,9 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const const auto son_hive_ids = get_votes_objects(votes_ids, 5); if (!son_hive_ids.empty()) son_ids[sidechain_type::hive] = std::move(son_hive_ids); + const auto son_ethereum_ids = get_votes_objects(votes_ids, 5); + if (!son_ethereum_ids.empty()) + son_ids[sidechain_type::ethereum] = std::move(son_ethereum_ids); return son_ids; }(); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index cc862618..e9f3b9f5 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -1101,28 +1101,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Initialize witness schedule - #ifndef NDEBUG - const son_schedule_object& ssohive = -#endif - create([&](son_schedule_object& _sso) - { - // for scheduled - memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); - - witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); - - auto init_witnesses = get_global_properties().active_witnesses; - - _sso.scheduler = son_scheduler(); - _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); - - - _sso.last_scheduling_block = 0; - - _sso.recent_slots_filled = fc::uint128::max_value(); - }); - assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); - #ifndef NDEBUG const son_schedule_object& ssobitcoin = #endif @@ -1133,11 +1111,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); - auto init_witnesses = get_global_properties().active_witnesses; + auto init_bitcoin_sons = get_global_properties().active_sons.at(sidechain_type::bitcoin); _sso.scheduler = son_scheduler(); - _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); - + _sso.scheduler._min_token_count = std::max(int(init_bitcoin_sons.size()) / 2, 1); _sso.last_scheduling_block = 0; @@ -1145,6 +1122,48 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) ); +#ifndef NDEBUG + const son_schedule_object& ssoethereum = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_ethereum_sons = get_global_properties().active_sons.at(sidechain_type::ethereum); + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_ethereum_sons.size()) / 2, 1); + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); + }); + assert( ssoethereum.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::ethereum)) ); + +#ifndef NDEBUG + const son_schedule_object& ssohive = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_hive_sons = get_global_properties().active_sons.at(sidechain_type::hive); + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_hive_sons.size()) / 2, 1); + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); + }); + assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); + // Create FBA counters create([&]( fba_accumulator_object& acc ) { diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index d18e0f26..7474edac 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -92,7 +92,10 @@ vector> database::sort_votable_objects< count = std::min(count, refs.size()); std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), [this, sidechain](const son_object& a, const son_object& b)->bool { - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || + sidechain == sidechain_type::ethereum || + sidechain == sidechain_type::hive, + "Unexpected sidechain type"); const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; @@ -718,7 +721,9 @@ void database::update_active_sons() assert( _son_count_histogram_buffer.size() > 0 ); for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ +#ifndef NDEBUG assert( son_count_histogram_buffer.second.size() > 0 ); +#endif } const flat_map stake_target = [this]{ @@ -2044,7 +2049,7 @@ void database::perform_son_tasks() }); } // create BTC asset here because son_account is the issuer of the BTC - if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME) + if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME) { const asset_dynamic_data_object& dyn_asset = create([](asset_dynamic_data_object& a) { @@ -2063,7 +2068,7 @@ void database::perform_son_tasks() asset_issuer_permission_flags::override_authority; a.options.core_exchange_rate.base.amount = 100000; a.options.core_exchange_rate.base.asset_id = asset_id_type(0); - a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value + a.options.core_exchange_rate.quote.amount = 2500; a.options.core_exchange_rate.quote.asset_id = a.id; a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set @@ -2077,8 +2082,42 @@ void database::perform_son_tasks() gpo.pending_parameters->extensions.value.btc_asset = btc_asset.get_id(); }); } + // create ETH asset here because son_account is the issuer of the ETH + if (gpo.parameters.eth_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) + { + const asset_dynamic_data_object& dyn_asset = + create([](asset_dynamic_data_object& a) { + a.current_supply = 0; + }); + + const asset_object& eth_asset = + create( [&gpo, &dyn_asset]( asset_object& a ) { + a.symbol = "ETH"; + a.precision = 8; + a.issuer = gpo.parameters.son_account(); + a.options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + a.options.market_fee_percent = 500; // 5% + a.options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK; + a.options.flags = asset_issuer_permission_flags::charge_market_fee | + asset_issuer_permission_flags::override_authority; + a.options.core_exchange_rate.base.amount = 100000; + a.options.core_exchange_rate.base.asset_id = asset_id_type(0); + a.options.core_exchange_rate.quote.amount = 2500; + a.options.core_exchange_rate.quote.asset_id = a.id; + a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty + a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set + a.options.whitelist_markets.clear(); // might be traded with + a.options.blacklist_markets.clear(); // might not be traded with + a.dynamic_asset_data_id = dyn_asset.id; + }); + modify( gpo, [ð_asset]( global_property_object& gpo ) { + gpo.parameters.extensions.value.eth_asset = eth_asset.get_id(); + if( gpo.pending_parameters ) + gpo.pending_parameters->extensions.value.eth_asset = eth_asset.get_id(); + }); + } // create HBD asset here because son_account is the issuer of the HBD - if (gpo.parameters.hbd_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME) + if (gpo.parameters.hbd_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME) { const asset_dynamic_data_object& dyn_asset = create([](asset_dynamic_data_object& a) { @@ -2097,7 +2136,7 @@ void database::perform_son_tasks() asset_issuer_permission_flags::override_authority; a.options.core_exchange_rate.base.amount = 100000; a.options.core_exchange_rate.base.asset_id = asset_id_type(0); - a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value + a.options.core_exchange_rate.quote.amount = 2500; a.options.core_exchange_rate.quote.asset_id = a.id; a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set @@ -2131,7 +2170,7 @@ void database::perform_son_tasks() asset_issuer_permission_flags::override_authority; a.options.core_exchange_rate.base.amount = 100000; a.options.core_exchange_rate.base.asset_id = asset_id_type(0); - a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value + a.options.core_exchange_rate.quote.amount = 2500; a.options.core_exchange_rate.quote.asset_id = a.id; a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set @@ -2413,14 +2452,17 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; if( !p.pending_parameters->extensions.value.hive_asset.valid() ) p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; + if( !p.pending_parameters->extensions.value.eth_asset.valid() ) + p.pending_parameters->extensions.value.eth_asset = p.parameters.extensions.value.eth_asset; // the following parameters are not allowed to be changed. So take what is in global property - p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; - p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; - p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count; - p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset; - p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account; p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start; + p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account; + p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset; + p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count; + p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; + p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; + p.pending_parameters->extensions.value.eth_asset = p.parameters.extensions.value.eth_asset; p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index f4df5ac2..b4c4bb6a 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -77,8 +77,9 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const unsigned_int database::get_son_schedule_id( sidechain_type type )const { static const map schedule_map = { - { sidechain_type::hive, 0 }, - { sidechain_type::bitcoin, 1 } + { sidechain_type::bitcoin, 0 }, + { sidechain_type::ethereum, 1 }, + { sidechain_type::hive, 2 } }; return schedule_map.at(type); @@ -322,8 +323,10 @@ void database::update_witness_schedule(const signed_block& next_block) void database::update_son_schedule(const signed_block& next_block) { auto start = fc::time_point::now(); - const global_property_object& gpo = get_global_properties(); +#ifndef NDEBUG const son_schedule_object& sso = get(son_schedule_id_type()); +#endif + const global_property_object& gpo = get_global_properties(); const flat_map schedule_needs_filled = [&gpo]() { flat_map schedule_needs_filled; diff --git a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf new file mode 100644 index 00000000..72e929fe --- /dev/null +++ b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf @@ -0,0 +1,7 @@ +#ifndef HARDFORK_SON_FOR_ETHEREUM_TIME +#ifdef BUILD_PEERPLAYS_TESTNET +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) +#else +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) +#endif +#endif diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 3a11e99f..94493f30 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -70,6 +70,7 @@ namespace graphene { namespace chain { optional < uint16_t > maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS optional < asset_id_type > hbd_asset = asset_id_type(); optional < asset_id_type > hive_asset = asset_id_type(); + optional < asset_id_type > eth_asset = asset_id_type(); }; struct chain_parameters @@ -220,6 +221,9 @@ namespace graphene { namespace chain { inline asset_id_type hive_asset() const { return extensions.value.hive_asset.valid() ? *extensions.value.hive_asset : asset_id_type(); } + inline asset_id_type eth_asset() const { + return extensions.value.eth_asset.valid() ? *extensions.value.eth_asset : asset_id_type(); + } private: static void safe_copy(chain_parameters& to, const chain_parameters& from); }; @@ -257,6 +261,7 @@ FC_REFLECT( graphene::chain::parameter_extension, (maximum_son_count) (hbd_asset) (hive_asset) + (eth_asset) ) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 023bb511..913f6c5b 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -61,6 +61,7 @@ struct vote_id_type worker, son_bitcoin, son_hive, + son_ethereum, VOTE_TYPE_COUNT }; @@ -145,7 +146,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui FC_REFLECT_TYPENAME( fc::flat_set ) -FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(VOTE_TYPE_COUNT) ) +FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(son_ethereum)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp index 1097323b..a717f778 100644 --- a/libraries/chain/include/graphene/chain/sidechain_defs.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -14,7 +14,7 @@ enum class sidechain_type { hive }; -static const std::set active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::hive}; +static const std::set active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive}; } } @@ -24,4 +24,4 @@ FC_REFLECT_ENUM(graphene::chain::sidechain_type, (ethereum) (eos) (hive) - (peerplays) ) \ No newline at end of file + (peerplays) ) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index c9089bfb..afe7230f 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -89,11 +89,13 @@ namespace graphene { namespace chain { inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); } inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); } inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); } + inline vote_id_type get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); } }; struct by_account; struct by_vote_id_bitcoin; struct by_vote_id_hive; + struct by_vote_id_ethereum; using son_multi_index_type = multi_index_container< son_object, indexed_by< @@ -108,6 +110,9 @@ namespace graphene { namespace chain { >, ordered_unique< tag, const_mem_fun + >, + ordered_unique< tag, + const_mem_fun > > >; diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index bee6b1be..a1b7994e 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -186,6 +186,8 @@ void account_options::validate() const --needed_sons[sidechain_type::bitcoin]; else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] ) --needed_sons[sidechain_type::hive]; + else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] ) + --needed_sons[sidechain_type::ethereum]; FC_ASSERT( needed_witnesses == 0, "May not specify fewer witnesses than the number voted for."); @@ -195,6 +197,8 @@ void account_options::validate() const "May not specify fewer Bitcoin SONs than the number voted for."); FC_ASSERT( needed_sons[sidechain_type::hive] == 0, "May not specify fewer Hive SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0, + "May not specify fewer Ethereum SONs than the number voted for."); } void affiliate_reward_distribution::validate() const diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 8d24bad6..5016ea0f 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -40,15 +40,18 @@ object_id_type create_son_evaluator::do_apply(const son_create_operation& op) { try { vote_id_type vote_id_bitcoin; vote_id_type vote_id_hive; - db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive](global_property_object& p) { + vote_id_type vote_id_ethereum; + db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive, &vote_id_ethereum](global_property_object& p) { vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin); vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive); + vote_id_ethereum = get_next_vote_id(p, vote_id_type::son_ethereum); }); const auto& new_son_object = db().create( [&]( son_object& obj ){ obj.son_account = op.owner_account; obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin; obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive; + obj.sidechain_vote_ids[sidechain_type::ethereum] = vote_id_ethereum; obj.url = op.url; obj.deposit = op.deposit; obj.signing_key = op.signing_key; diff --git a/libraries/chain/son_object.cpp b/libraries/chain/son_object.cpp index c94a6250..e607c103 100644 --- a/libraries/chain/son_object.cpp +++ b/libraries/chain/son_object.cpp @@ -20,6 +20,10 @@ namespace graphene { namespace chain { retval = retval && (sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) && (sidechain_public_keys.at(sidechain_type::hive).length() > 0); + + retval = retval && + (sidechain_public_keys.find( sidechain_type::ethereum ) != sidechain_public_keys.end()) && + (sidechain_public_keys.at(sidechain_type::ethereum).length() > 0); } return retval; diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index d7337e42..4f724602 100755 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -6,6 +6,7 @@ add_library( peerplays_sidechain sidechain_net_handler_factory.cpp sidechain_net_handler.cpp sidechain_net_handler_bitcoin.cpp + sidechain_net_handler_ethereum.cpp sidechain_net_handler_hive.cpp sidechain_net_handler_peerplays.cpp bitcoin/bech32.cpp @@ -17,6 +18,11 @@ add_library( peerplays_sidechain bitcoin/sign_bitcoin_transaction.cpp common/rpc_client.cpp common/utils.cpp + ethereum/encoders.cpp + ethereum/decoders.cpp + ethereum/transaction.cpp + ethereum/types.cpp + ethereum/utils.cpp hive/asset.cpp hive/operations.cpp hive/transaction.cpp @@ -36,7 +42,7 @@ endif() unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) -target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin zmq ) +target_link_libraries( peerplays_sidechain PRIVATE graphene_plugin sha3 zmq ) target_include_directories( peerplays_sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp new file mode 100644 index 00000000..02f334f5 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp @@ -0,0 +1,224 @@ +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +//! rlp_decoder + +namespace { +const signed char p_util_hexdigit[256] = + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +} + +std::vector rlp_decoder::decode(const std::string &str) { + size_t consumed = 0; + const auto raw_vec = parse_hex(str); + const std::vector rlp_array = decode_rlp(raw_vec.data(), raw_vec.size(), consumed); + std::vector result_array; + for (const auto &rlp : decode_rlp(raw_vec.data(), raw_vec.size(), consumed)) { + result_array.emplace_back(bytes2hex(rlp)); + } + return result_array; +} + +std::vector rlp_decoder::decode_rlp(const unsigned char *raw, size_t len, size_t &consumed) { + std::vector rlp_result; + + consumed = 0; + + const unsigned char *end = raw + len; + const size_t prefixlen = 1; + unsigned char ch = *raw; + + if (len < 1) { + return rlp_result; + } + + // Case 1: [prefix is 1-byte data buffer] + if (ch <= 0x7f) { + const unsigned char *tok_start = raw; + const unsigned char *tok_end = tok_start + prefixlen; + FC_ASSERT(tok_end <= end); + + // parsing done; assign data buffer value. + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back(buf.cbegin(), buf.cend()); + + consumed = buf.size(); + } + // Case 2: [prefix, including buffer length][data] + else if ((ch >= 0x80) && (ch <= 0xb7)) { + const size_t blen = ch - 0x80; + const size_t expected = prefixlen + blen; + + if (len < expected) + return std::vector{}; + + const unsigned char *tok_start = raw + 1; + const unsigned char *tok_end = tok_start + blen; + FC_ASSERT(tok_end <= end); + + // require minimal encoding + if ((blen == 1) && (tok_start[0] <= 0x7f)) + return std::vector{}; + + // parsing done; assign data buffer value. + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back(buf.cbegin(), buf.cend()); + + consumed = expected; + } + // Case 3: [prefix][buffer length][data] + else if ((ch >= 0xb8) && (ch <= 0xbf)) { + const size_t uintlen = ch - 0xb7; + size_t expected = prefixlen + uintlen; + + if (len < expected) + return std::vector{}; + + FC_ASSERT(uintlen > 0 && uintlen <= RLP_maxUintLen); + + const unsigned char *tok_start = raw + prefixlen; + if ((uintlen > 1) && (tok_start[0] == 0)) // no leading zeroes + return std::vector{}; + + // read buffer length + const uint64_t slen = to_int(tok_start, uintlen); + + // validate buffer length, including possible addition overflows. + expected = prefixlen + uintlen + slen; + if ((slen < (RLP_listStart - RLP_bufferLenStart - RLP_maxUintLen)) || (expected > len) || (slen > len)) + return std::vector{}; + + // parsing done; assign data buffer value. + tok_start = raw + prefixlen + uintlen; + const unsigned char *tok_end = tok_start + slen; + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back(buf.cbegin(), buf.cend()); + + consumed = expected; + } + // Case 4: [prefix][list] + else if ((ch >= 0xc0) && (ch <= 0xf7)) { + const size_t payloadlen = ch - 0xc0; + const size_t expected = prefixlen + payloadlen; + + // read list payload + const auto array = decode_array(raw, len, 0, payloadlen); + rlp_result.insert(rlp_result.end(), array.cbegin(), array.cend()); + + consumed = expected; + } + // Case 5: [prefix][list length][list] + else { + FC_ASSERT((ch >= 0xf8) && (ch <= 0xff)); + + const size_t uintlen = ch - 0xf7; + const size_t expected = prefixlen + uintlen; + + if (len < expected) + return std::vector{}; + + FC_ASSERT(uintlen > 0 && uintlen <= RLP_maxUintLen); + + const unsigned char *tok_start = raw + prefixlen; + if ((uintlen > 1) && (tok_start[0] == 0)) // no leading zeroes + return std::vector{}; + + // read list length + const size_t payloadlen = to_int(tok_start, uintlen); + + // special requirement for non-immediate length + if (payloadlen < (0x100 - RLP_listStart - RLP_maxUintLen)) + return std::vector{}; + + // read list payload + const auto array = decode_array(raw, len, uintlen, payloadlen); + rlp_result.insert(rlp_result.end(), array.cbegin(), array.cend()); + + consumed = prefixlen + uintlen + payloadlen; + } + + return rlp_result; +} + +std::vector rlp_decoder::decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen) { + std::vector rlp_result; + const size_t prefixlen = 1; + + // validate list length, including possible addition overflows. + const size_t expected = prefixlen + uintlen + payloadlen; + if ((expected > len) || (payloadlen > len)) + return std::vector{}; + + size_t child_len = payloadlen; + const unsigned char *list_ent = raw + prefixlen + uintlen; + + // recursively read until payloadlen bytes parsed, or error + while (child_len > 0) { + size_t child_consumed = 0; + + const auto val = decode_rlp(list_ent, child_len, child_consumed); + rlp_result.insert(rlp_result.end(), val.cbegin(), val.cend()); + + list_ent += child_consumed; + child_len -= child_consumed; + } + + return rlp_result; +} + +uint64_t rlp_decoder::to_int(const unsigned char *raw, size_t len) { + if (len == 0) + return 0; + else if (len == 1) + return *raw; + else + return (raw[len - 1]) + (to_int(raw, len - 1) * 256); +} + +std::vector rlp_decoder::parse_hex(const std::string &str) { + return parse_hex(str.c_str()); +} + +std::vector rlp_decoder::parse_hex(const char *psz) { + // convert hex dump to vector + std::vector vch; + while (true) { + while (isspace(*psz)) + psz++; + signed char c = hex_digit(*psz++); + if (c == (signed char)-1) + break; + unsigned char n = (c << 4); + c = hex_digit(*psz++); + if (c == (signed char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +signed char rlp_decoder::hex_digit(char c) { + return p_util_hexdigit[(unsigned char)c]; +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp new file mode 100644 index 00000000..ad42088b --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -0,0 +1,102 @@ +#include + +#include +#include +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +//! base_encoder +std::string base_encoder::encode_uint256(boost::multiprecision::uint256_t value) { + return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str(); +} + +std::string base_encoder::encode_address(const std::string &value) { + return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str(); +} + +std::string base_encoder::encode_string(const std::string &value) { + std::string data = (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value.size())).str(); + data += boost::algorithm::hex(value) + std::string((64 - value.size() * 2 % 64), '0'); + return data; +} + +//! update_owners_encoder +std::string update_owners_encoder::encode(const std::vector> &owners_weights, const std::string &object_id) const { + std::string data = "0x" + function_signature; + data += base_encoder::encode_uint256(64); + data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32); + data += base_encoder::encode_uint256(owners_weights.size()); + for (const auto &owner : owners_weights) { + data += base_encoder::encode_address(owner.first); + data += base_encoder::encode_uint256(owner.second); + } + data += base_encoder::encode_string(object_id); + + return data; +} + +//! withdrawal_encoder +std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) const { + std::string data = "0x" + function_signature; + data += base_encoder::encode_address(to); + data += base_encoder::encode_uint256(amount); + data += base_encoder::encode_uint256(32 * 3); + data += base_encoder::encode_string(object_id); + + return data; +} + +//! rlp_encoder +std::string rlp_encoder::encode(const std::string &s) { + return encode_rlp(hex2bytes(s)); +} + +std::string rlp_encoder::encode_length(int len, int offset) { + if (len < 56) { + std::string temp; + temp = (char)(len + offset); + return temp; + } else { + const std::string hexLength = to_hex(len); + const int lLength = hexLength.size() / 2; + const std::string fByte = to_hex(offset + 55 + lLength); + return hex2bytes(fByte + hexLength); + } +} + +std::string rlp_encoder::hex2bytes(const std::string &s) { + std::string dest; + dest.resize(s.size() / 2); + hex2bin(s.c_str(), &dest[0]); + return dest; +} + +std::string rlp_encoder::encode_rlp(const std::string &s) { + if (s.size() == 1 && (unsigned char)s[0] < 128) + return s; + else + return encode_length(s.size(), 128) + s; +} + +int rlp_encoder::char2int(char input) { + if (input >= '0' && input <= '9') + return input - '0'; + if (input >= 'A' && input <= 'F') + return input - 'A' + 10; + if (input >= 'a' && input <= 'f') + return input - 'a' + 10; + + return -1; +} + +void rlp_encoder::hex2bin(const char *src, char *target) { + while (*src && src[1]) { + *(target++) = char2int(*src) * 16 + char2int(src[1]); + src += 2; + } +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp new file mode 100644 index 00000000..a75327c3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp @@ -0,0 +1,229 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +const secp256k1_context *eth_context() { + static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + return ctx; +} + +//! transaction + +base_transaction::base_transaction(const std::string &raw_tx) { +} + +//! transaction + +transaction::transaction(const std::string &raw_tx) : + base_transaction{raw_tx} { + deserialize(raw_tx); +} + +const transaction &transaction::sign(const std::string &private_key) const { + return *this; +} + +std::string transaction::serialize() const { + boost::property_tree::ptree pt; + pt.put("from", from); + pt.put("to", to); + pt.put("data", data); + + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, pt); + return ss.str(); +} + +void transaction::deserialize(const std::string &raw_tx) { + std::stringstream ss_tx(raw_tx); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(ss_tx, tx_json); + + if (tx_json.count("from")) + from = tx_json.get("from"); + if (tx_json.count("to")) + to = tx_json.get("to"); + if (tx_json.count("data")) + data = tx_json.get("data"); +} + +//! raw_transaction + +raw_transaction::raw_transaction(const std::string &raw_tx) : + base_transaction{raw_tx} { + deserialize(raw_tx); +} + +bytes raw_transaction::hash() const { + bytes hash; + hash.resize(32); + const auto transaction_string = boost::algorithm::unhex(remove_0x(serialize())); + keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data()); + + return hash; +} + +signed_transaction raw_transaction::sign(const std::string &private_key) const { + //! Prepare signed transaction + signed_transaction tr; + tr.nonce = nonce; + tr.gas_price = gas_price; + tr.gas_limit = gas_limit; + tr.to = to; + tr.value = value; + tr.data = data; + + const bytes priv_key = parse_hex(private_key); + + int recid = 0; + secp256k1_ecdsa_recoverable_signature sig; + FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash().data(), (const unsigned char *)priv_key.data(), NULL, NULL)); + fc::ecc::compact_signature result; + FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig)); + + bytes r; + for (int i = 1; i < 33; i++) + r.emplace_back((char)result.at(i)); + + bytes v = bytes{char(recid + from_hex(chain_id) * 2 + 35)}; + + bytes s; + for (int i = 33; i < 65; i++) + s.emplace_back((char)result.at(i)); + + tr.r = fc::to_hex((char *)&r[0], r.size()); + tr.v = fc::to_hex((char *)&v[0], v.size()); + tr.s = fc::to_hex((char *)&s[0], s.size()); + + return tr; +} + +std::string raw_transaction::serialize() const { + const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) + + rlp_encoder::encode(remove_0x(gas_price)) + + rlp_encoder::encode(remove_0x(gas_limit)) + + rlp_encoder::encode(remove_0x(to)) + + rlp_encoder::encode(remove_0x(value)) + + rlp_encoder::encode(remove_0x(data)) + + rlp_encoder::encode(remove_0x(chain_id)) + + rlp_encoder::encode("") + + rlp_encoder::encode(""); + + return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized)); +} + +void raw_transaction::deserialize(const std::string &raw_tx) { + const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx)); + FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format"); + + nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0"); + boost::algorithm::to_lower(nonce); + gas_price = add_0x(rlp_array.at(1)); + boost::algorithm::to_lower(gas_price); + gas_limit = add_0x(rlp_array.at(2)); + boost::algorithm::to_lower(gas_limit); + to = add_0x(rlp_array.at(3)); + boost::algorithm::to_lower(to); + value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0"); + boost::algorithm::to_lower(value); + data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : ""; + boost::algorithm::to_lower(data); + chain_id = add_0x(rlp_array.at(6)); + boost::algorithm::to_lower(chain_id); +} + +//! signed_transaction + +signed_transaction::signed_transaction(const std::string &raw_tx) : + base_transaction{raw_tx} { + deserialize(raw_tx); +} + +std::string signed_transaction::recover(const std::string &chain_id) const { + fc::ecc::compact_signature input64; + fc::from_hex(r, (char *)&input64.at(1), 32); + fc::from_hex(v, (char *)&input64.at(0), 1); + int recid = input64.at(0) - from_hex(chain_id) * 2 - 35; + fc::from_hex(s, (char *)&input64.at(33), 32); + + secp256k1_ecdsa_recoverable_signature sig; + FC_ASSERT(secp256k1_ecdsa_recoverable_signature_parse_compact(eth_context(), &sig, (const unsigned char *)&input64.data[1], recid)); + + raw_transaction tr; + tr.nonce = nonce; + tr.gas_price = gas_price; + tr.gas_limit = gas_limit; + tr.to = to; + tr.value = value; + tr.data = data; + tr.chain_id = chain_id; + + secp256k1_pubkey rawPubkey; + FC_ASSERT(secp256k1_ecdsa_recover(eth_context(), &rawPubkey, &sig, (const unsigned char *)tr.hash().data())); + + std::array pubkey; + size_t biglen = 65; + FC_ASSERT(secp256k1_ec_pubkey_serialize(eth_context(), pubkey.data(), &biglen, &rawPubkey, SECP256K1_EC_UNCOMPRESSED)); + + const std::string out = std::string(pubkey.begin(), pubkey.end()).substr(1); + bytes hash; + hash.resize(32); + keccak_256((const unsigned char *)out.data(), out.size(), (unsigned char *)hash.data()); + + return add_0x(fc::to_hex((char *)&hash[0], hash.size()).substr(24)); +} + +std::string signed_transaction::serialize() const { + const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) + + rlp_encoder::encode(remove_0x(gas_price)) + + rlp_encoder::encode(remove_0x(gas_limit)) + + rlp_encoder::encode(remove_0x(to)) + + rlp_encoder::encode(remove_0x(value)) + + rlp_encoder::encode(remove_0x(data)) + + rlp_encoder::encode(remove_0x(v)) + + rlp_encoder::encode(remove_0x(r)) + + rlp_encoder::encode(remove_0x(s)); + + return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized)); +} + +void signed_transaction::deserialize(const std::string &raw_tx) { + const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx)); + FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format"); + + nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0"); + boost::algorithm::to_lower(nonce); + gas_price = add_0x(rlp_array.at(1)); + boost::algorithm::to_lower(gas_price); + gas_limit = add_0x(rlp_array.at(2)); + boost::algorithm::to_lower(gas_limit); + to = add_0x(rlp_array.at(3)); + boost::algorithm::to_lower(to); + value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0"); + boost::algorithm::to_lower(value); + data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : ""; + boost::algorithm::to_lower(data); + v = add_0x(rlp_array.at(6)); + boost::algorithm::to_lower(v); + r = add_0x(rlp_array.at(7)); + boost::algorithm::to_lower(r); + s = add_0x(rlp_array.at(8)); + boost::algorithm::to_lower(s); +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/types.cpp b/libraries/plugins/peerplays_sidechain/ethereum/types.cpp new file mode 100644 index 00000000..f85d0e61 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/types.cpp @@ -0,0 +1,5 @@ +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp new file mode 100644 index 00000000..ce64e1ae --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp @@ -0,0 +1,52 @@ +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +bytes parse_hex(const std::string &str) { + bytes vec(str.size() / 2); + fc::from_hex(str, vec.data(), vec.size()); + return vec; +} + +std::string bytes2hex(const std::string &s) { + std::string dest; + for (const auto &i : s) + dest += uchar2Hex((unsigned char)i); + + return dest; +} + +std::string uchar2Hex(unsigned char n) { + std::string dest; + dest.resize(2); + sprintf(&dest[0], "%X", n); + + if (n < (unsigned char)16) { + dest[1] = dest[0]; + dest[0] = '0'; + } + + return dest; +} + +std::string add_0x(const std::string &s) { + if (s.size() > 1) { + if (s.substr(0, 2) == "0x") + return s; + } + + return "0x" + s; +} + +std::string remove_0x(const std::string &s) { + if (s.size() > 1) { + if (s.substr(0, 2) == "0x") + return s.substr(2); + } + + return s; +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp new file mode 100644 index 00000000..9edb6ae3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +class rlp_decoder { +private: + enum RLP_constants { + RLP_maxUintLen = 8, + RLP_bufferLenStart = 0x80, + RLP_listStart = 0xc0, + }; + +public: + static std::vector decode(const std::string &str); + +private: + static std::vector decode_rlp(const unsigned char *raw, size_t len, size_t &consumed); + static std::vector decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen); + static uint64_t to_int(const unsigned char *raw, size_t len); + static std::vector parse_hex(const std::string &str); + static std::vector parse_hex(const char *psz); + static signed char hex_digit(char c); +}; + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp new file mode 100644 index 00000000..1ff97978 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +class base_encoder { +public: + static std::string encode_uint256(boost::multiprecision::uint256_t value); + static std::string encode_address(const std::string &value); + static std::string encode_string(const std::string &value); +}; + +class update_owners_encoder { +public: + const std::string function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string) + + std::string encode(const std::vector> &owners_weights, const std::string &object_id) const; +}; + +class withdrawal_encoder { +public: + const std::string function_signature = "e088747b"; //! withdraw(address,uint256,string) + + std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) const; +}; + +class rlp_encoder { +public: + static std::string encode(const std::string &s); + static std::string encode_length(int len, int offset); + static std::string hex2bytes(const std::string &s); + +private: + static std::string encode_rlp(const std::string &s); + static int char2int(char input); + static void hex2bin(const char *src, char *target); +}; + +/*class ethereum_function_call_encoder { +public: + enum operation_t { + OPERATION_CALL, + OPERATION_DELEGATE_CALL + }; + + static constexpr const char *const default_prev_addr = "0000000000000000000000000000000000000001"; + + std::string encode_function_signature(const std::string &function_signature); + std::string encode_address(const std::string &addr); + std::string encode_uint256(const std::string &value); + std::string encode_uint8(uint8_t value); + std::string encode_bytes(const std::string &values); +}; + +class safe_transaction_encoder { +public: + static constexpr const char *const default_safe_tx_gas = "0"; + static constexpr const char *const default_data_gas = "0"; + static constexpr const char *const default_gas_price = "0"; + static constexpr const char *const default_gas_token = "0000000000000000000000000000000000000000"; + static constexpr const char *const default_refund_receiver = "0000000000000000000000000000000000000000"; + + std::string create_safe_address(const std::vector &owner_addresses, uint32_t threshold); + std::string build_transaction(const std::string &safe_account_addr, const std::string &value, const std::string &data, uint8_t operation, const std::string &safeTxGas, const std::string &dataGas, const std::string &gasPrice, const std::string &gasToken, const std::string &refundReceiver); + +private: + ethereum_function_call_encoder m_ethereum_function_call_encoder; +};*/ + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp new file mode 100644 index 00000000..693c7284 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +class base_transaction { +public: + base_transaction() = default; + base_transaction(const std::string &raw_tx); + + virtual std::string serialize() const = 0; + virtual void deserialize(const std::string &raw_tx) = 0; +}; + +class transaction : base_transaction { +public: + std::string from; + std::string to; + std::string data; + + transaction() = default; + transaction(const std::string &raw_tx); + + const transaction &sign(const std::string &private_key) const; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string &raw_tx) override; +}; + +class signed_transaction; +class raw_transaction : base_transaction { +public: + std::string nonce; + std::string gas_price; + std::string gas_limit; + std::string to; + std::string value; + std::string data; + std::string chain_id; + + raw_transaction() = default; + raw_transaction(const std::string &raw_tx); + + bytes hash() const; + signed_transaction sign(const std::string &private_key) const; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string &raw_tx) override; +}; + +class signed_transaction : base_transaction { +public: + std::string nonce; + std::string gas_price; + std::string gas_limit; + std::string to; + std::string value; + std::string data; + std::string v; + std::string r; + std::string s; + + signed_transaction() = default; + signed_transaction(const std::string &raw_tx); + + std::string recover(const std::string &chain_id) const; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string &raw_tx) override; +}; + +}}} // namespace graphene::peerplays_sidechain::ethereum + +// Example 1 +//{ +// "blockHash": "0x64a6706ecaf5a97b7f3e047abb20ff223ce82c6994d80e68fdb1fdfb38d0209c", +// "blockNumber": "0xe5827c", +// "from": "0x8614c67e085f2334010f2a28e806c6f1cc176d12", +// "gas": "0x38822", +// "gasPrice": "0xce42cba69", +// "maxFeePerGas": "0xddb4d8d16", +// "maxPriorityFeePerGas": "0x3b9aca00", +// "hash": "0xeac92ea09fa8eb3ca2fb0d156cceb38ae69d4345869d41e8e49d5ecbcbb622dc", +// "input": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000", +// "nonce": "0x32", +// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", +// "transactionIndex": "0xb6", +// "value": "0x2514d9d7d7d8000", +// "type": "0x2", +// "accessList": [], +// "chainId": "0x1", +// "v": "0x1", +// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c", +// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" +//} +// +//"0xf9021332850ce42cba69830388229468b3465833fb72a70ecdf485e0e4c7bd8665fc458802514d9d7d7d8000b901a45ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde0312150000000000000000000000000000000000000000000000000000000001a02f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28ca0782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" +// +//{ +// "nonce": 50, +// "gasPrice": { +// "_hex": "0x0ce42cba69" +// }, +// "gasLimit": { +// "_hex": "0x038822" +// }, +// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", +// "value": { +// "_hex": "0x02514d9d7d7d8000" +// }, +// "data": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000", +// "v": 1, +// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c", +// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" +//} + +// Example 2 +//{ +// "blockHash": "0xe2ae3afd86dc7343c7fb753441447a0a51bb19499325ad6e278256f0cd1b5894", +// "blockNumber": "0xe58271", +// "from": "0xb895ade6d337fbb8cb97f2ea7da43106c7f5cc26", +// "gas": "0x5208", +// "gasPrice": "0xe6f3b322e", +// "maxFeePerGas": "0x1322455fd3", +// "maxPriorityFeePerGas": "0x53724e00", +// "hash": "0xed29b56e52ad2d452e25b8ec70c37f59d935cd6d0f8fe8e83b256f3ffdfd3fce", +// "input": "0x", +// "nonce": "0x37", +// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373", +// "transactionIndex": "0x8a", +// "value": "0x4563918244f40000", +// "type": "0x2", +// "accessList": [], +// "chainId": "0x1", +// "v": "0x0", +// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c", +// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" +//} +// +//"0xf86c37850e6f3b322e82520894176386b6ffc469ac049f9ec1f6cc0efd1d09b373884563918244f400008000a0dcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4ca028c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" +// +//{ +// "nonce": 55, +// "gasPrice": { +// "_hex": "0x0e6f3b322e" +// }, +// "gasLimit": { +// "_hex": "0x5208" +// }, +// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373", +// "value": { +// "_hex": "0x4563918244f40000" +// }, +// "data": "0x", +// "v": 0, +// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c", +// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" +//} diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp new file mode 100644 index 00000000..963244fa --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +typedef uint64_t chain_id_type; +typedef uint64_t network_id_type; + +using bytes = std::vector; + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp new file mode 100644 index 00000000..b4461713 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +bytes parse_hex(const std::string &str); + +std::string bytes2hex(const std::string &s); + +std::string uchar2Hex(unsigned char n); + +std::string add_0x(const std::string &s); + +std::string remove_0x(const std::string &s); + +template +std::string to_hex(const T &val) { + std::stringstream stream; + stream << std::hex << val; + std::string result(stream.str()); + if (result.size() % 2) + result = "0" + result; + return result; +} + +template +T from_hex(const std::string &s) { + T val; + std::stringstream stream; + stream << std::hex << s; + stream >> val; + + return val; +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp new file mode 100644 index 00000000..3944d0a2 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include + +#include + +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +class ethereum_rpc_client : public rpc_client { +public: + ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + + std::string admin_node_info(); + std::string eth_get_block_by_number(std::string block_number, bool full_block); + std::string eth_get_logs(std::string wallet_contract_address); + std::string net_version(); + std::string eth_get_transaction_count(const std::string ¶ms); + std::string eth_gas_price(); + + std::string get_chain_id(); + std::string get_network_id(); + std::string get_nonce(const std::string &address); + std::string get_gas_price(); + std::string get_gas_limit(); + + std::string eth_send_transaction(const std::string ¶ms); + std::string eth_send_raw_transaction(const std::string ¶ms); + std::string eth_get_transaction_receipt(const std::string ¶ms); +}; + +class sidechain_net_handler_ethereum : public sidechain_net_handler { +public: + sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + virtual ~sidechain_net_handler_ethereum(); + + bool process_proposal(const proposal_object &po); + void process_primary_wallet(); + void process_sidechain_addresses(); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto); + std::string send_sidechain_transaction(const sidechain_transaction_object &sto); + bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + +private: + std::string rpc_url; + std::string rpc_user; + std::string rpc_password; + std::string wallet_contract_address; + + ethereum_rpc_client *rpc_client; + + ethereum::chain_id_type chain_id; + ethereum::network_id_type network_id; + + std::string create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id); + std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); + std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); + + std::string sign_transaction(const sidechain_transaction_object &sto); + + uint64_t last_block_received; + fc::future _listener_task; + boost::signals2::signal event_received; + void schedule_ethereum_listener(); + void ethereum_listener_loop(); + void handle_event(const std::string &event_data); +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 12de3dfd..e2a8d9b8 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -177,6 +177,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("bitcoin-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); + cli.add_options()("ethereum-sidechain-enabled", bpo::value()->default_value(false), "Ethereum sidechain handler enabled"); + cli.add_options()("ethereum-node-rpc-url", bpo::value()->default_value("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]"); + cli.add_options()("ethereum-node-rpc-user", bpo::value(), "Ethereum RPC user"); + cli.add_options()("ethereum-node-rpc-password", bpo::value(), "Ethereum RPC password"); + cli.add_options()("ethereum-wallet-contract-address", bpo::value(), "Ethereum wallet contract address"); + cli.add_options()("ethereum-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")), + "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); + cli.add_options()("hive-sidechain-enabled", bpo::value()->default_value(false), "Hive sidechain handler enabled"); cli.add_options()("hive-node-rpc-url", bpo::value()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-user", bpo::value(), "Hive node RPC user"); @@ -238,13 +246,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt wlog("Haven't set up Bitcoin sidechain parameters"); } - //sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as(); - //config_ready_ethereum = options.count("ethereum-node-ip") && - // options.count("ethereum-address") && - // options.count("ethereum-public-key") && options.count("ethereum-private-key"); - //if (!config_ready_ethereum) { - // wlog("Haven't set up Ethereum sidechain parameters"); - //} + sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as(); + config_ready_ethereum = options.count("ethereum-node-rpc-url") && + /*options.count("ethereum-node-rpc-user") && options.count("ethereum-node-rpc-password") &&*/ + options.count("ethereum-wallet-contract-address") && + options.count("ethereum-private-key"); + if (sidechain_enabled_ethereum && !config_ready_ethereum) { + wlog("Haven't set up Ethereum sidechain parameters"); + } sidechain_enabled_hive = options.at("hive-sidechain-enabled").as(); config_ready_hive = options.count("hive-node-rpc-url") && @@ -283,10 +292,10 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { ilog("Bitcoin sidechain handler running"); } - //if (sidechain_enabled_ethereum && config_ready_ethereum) { - // net_manager->create_handler(sidechain_type::ethereum, options); - // ilog("Ethereum sidechain handler running"); - //} + if (sidechain_enabled_ethereum && config_ready_ethereum) { + net_handlers.at(sidechain_type::ethereum) = net_handler_factory.create_handler(sidechain_type::ethereum, options); + ilog("Ethereum sidechain handler running"); + } if (sidechain_enabled_hive && config_ready_hive) { net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 224144a0..971df7c7 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -172,18 +172,21 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ #ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS //enable_peerplays_asset_deposits = (sed.sidechain == sidechain_type::peerplays) && // (sed.sidechain_currency.compare("BTC") != 0) && + // (sed.sidechain_currency.compare("ETH") != 0) && // (sed.sidechain_currency.compare("HBD") != 0) && // (sed.sidechain_currency.compare("HIVE") != 0); #endif bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) || + ((sed.sidechain == sidechain_type::ethereum) && (sed.sidechain_currency.compare("ETH") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || enable_peerplays_asset_deposits); bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays ((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || + (sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))); @@ -240,6 +243,10 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ withdraw_currency = "BTC"; withdraw_currency_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; } + if (sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) { + withdraw_currency = "ETH"; + withdraw_currency_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; + } if (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) { withdraw_currency = "HBD"; withdraw_currency_price = database.get(database.get_global_properties().parameters.hbd_asset()).options.core_exchange_rate; @@ -648,6 +655,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { bool is_tracked_asset = ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || + ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp new file mode 100644 index 00000000..066c9dd8 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -0,0 +1,761 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SEND_RAW_TRANSACTION 1 + +namespace graphene { namespace peerplays_sidechain { + +ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : + rpc_client(url, user_name, password, debug_rpc_calls) { +} + +std::string ethereum_rpc_client::admin_node_info() { + return send_post_request("admin_nodeInfo", "", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_block_by_number(std::string block_number, bool full_block) { + std::string params = "[ \"" + block_number + "\", " + (full_block ? "true" : "false") + "]"; + return send_post_request("eth_getBlockByNumber", params, debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_logs(std::string wallet_contract_address) { + std::string params = "[{\"address\": \"" + wallet_contract_address + "\"}]"; + std::string reply_str = send_post_request("eth_getLogs", params, debug_rpc_calls); + return retrieve_value_from_reply(reply_str, ""); +} + +std::string ethereum_rpc_client::net_version() { + return send_post_request("net_version", "", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_transaction_count(const std::string ¶ms) { + return send_post_request("eth_getTransactionCount", params, debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_gas_price() { + return send_post_request("eth_gasPrice", "", debug_rpc_calls); +} + +std::string ethereum_rpc_client::get_chain_id() { + std::string reply_str = net_version(); + return retrieve_value_from_reply(reply_str, ""); +} + +std::string ethereum_rpc_client::get_network_id() { + std::string reply_str = admin_node_info(); + return retrieve_value_from_reply(reply_str, "protocols.eth.network"); +} + +std::string ethereum_rpc_client::get_nonce(const std::string &address) { + std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]"); + const auto nonce_val = ethereum::from_hex(retrieve_value_from_reply(reply_str, "")); + return nonce_val == 0 ? ethereum::add_0x("0") : ethereum::add_0x(ethereum::to_hex(nonce_val)); +} + +std::string ethereum_rpc_client::get_gas_price() { + std::string reply_str = eth_gas_price(); + return retrieve_value_from_reply(reply_str, ""); +} + +std::string ethereum_rpc_client::get_gas_limit() { + std::string reply_str = eth_get_block_by_number("latest", false); + if (!reply_str.empty()) { + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.count("result")) { + std::string gas_limit_s = json.get("result.gasLimit"); + return gas_limit_s; + } + } + return std::string{}; +} + +std::string ethereum_rpc_client::eth_send_transaction(const std::string ¶ms) { + return send_post_request("eth_sendTransaction", "[" + params + "]", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_send_raw_transaction(const std::string ¶ms) { + return send_post_request("eth_sendRawTransaction", "[ \"" + params + "\" ]", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_transaction_receipt(const std::string ¶ms) { + return send_post_request("eth_getTransactionReceipt", "[\"" + params + "\"]", debug_rpc_calls); +} + +sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + sidechain_net_handler(_plugin, options) { + sidechain = sidechain_type::ethereum; + + if (options.count("debug-rpc-calls")) { + debug_rpc_calls = options.at("debug-rpc-calls").as(); + } + + rpc_url = options.at("ethereum-node-rpc-url").as(); + if (options.count("ethereum-node-rpc-user")) { + rpc_user = options.at("ethereum-node-rpc-user").as(); + } else { + rpc_user = ""; + } + if (options.count("ethereum-node-rpc-password")) { + rpc_password = options.at("ethereum-node-rpc-password").as(); + } else { + rpc_password = ""; + } + + wallet_contract_address = options.at("ethereum-wallet-contract-address").as(); + + if (options.count("ethereum-private-key")) { + const std::vector pub_priv_keys = options["ethereum-private-key"].as>(); + for (const std::string &itr_key_pair : pub_priv_keys) { + auto key_pair = graphene::app::dejsonify>(itr_key_pair, 5); + ilog("Ethereum Public Key: ${public}", ("public", key_pair.first)); + if (!key_pair.first.length() || !key_pair.second.length()) { + FC_THROW("Invalid public private key pair."); + } + private_keys[key_pair.first] = key_pair.second; + } + } + + rpc_client = new ethereum_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); + + std::string chain_id_str = rpc_client->get_chain_id(); + if (chain_id_str.empty()) { + elog("No Ethereum node running at ${url}", ("url", rpc_url)); + FC_ASSERT(false); + } + chain_id = std::stoll(chain_id_str); + std::string network_id_str = rpc_client->get_network_id(); + network_id = std::stoll(network_id_str); + + ilog("Running on Ethereum network, chain id ${chain_id_str}, network id ${network_id_str}", ("chain_id_str", chain_id_str)("network_id_str", network_id_str)); + + last_block_received = 0; + schedule_ethereum_listener(); + event_received.connect([this](const std::string &event_data) { + std::thread(&sidechain_net_handler_ethereum::handle_event, this, event_data).detach(); + }); +} + +sidechain_net_handler_ethereum::~sidechain_net_handler_ethereum() { +} + +bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) { + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); + + bool should_approve = false; + + const chain::global_property_object &gpo = database.get_global_properties(); + + int32_t op_idx_0 = -1; + chain::operation op_obj_idx_0; + + if (po.proposed_transaction.operations.size() >= 1) { + op_idx_0 = po.proposed_transaction.operations[0].which(); + op_obj_idx_0 = po.proposed_transaction.operations[0]; + } + + int32_t op_idx_1 = -1; + chain::operation op_obj_idx_1; + (void)op_idx_1; + + if (po.proposed_transaction.operations.size() >= 2) { + op_idx_1 = po.proposed_transaction.operations[1].which(); + op_obj_idx_1 = po.proposed_transaction.operations[1]; + } + + switch (op_idx_0) { + + case chain::operation::tag::value: { + bool address_ok = false; + bool transaction_ok = false; + son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swo = idx.find(swo_id); + if (swo != idx.end()) { + + auto active_sons = gpo.active_sons.at(sidechain); + vector wallet_sons = swo->sons.at(sidechain); + + bool son_sets_equal = (active_sons.size() == wallet_sons.size()); + + if (son_sets_equal) { + for (size_t i = 0; i < active_sons.size(); i++) { + son_sets_equal = son_sets_equal && active_sons.at(i) == wallet_sons.at(i); + } + } + + if (son_sets_equal) { + address_ok = (op_obj_idx_0.get().address == wallet_contract_address); + } + + if (po.proposed_transaction.operations.size() >= 2) { + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swo = idx.find(object_id); + if (swo != idx.end()) { + tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), object_id.operator std::string()); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } else { + transaction_ok = true; + } + } + + should_approve = address_ok && + transaction_ok; + break; + } + + case chain::operation::tag::value: { + bool process_ok = false; + son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swdo = idx.find(swdo_id); + if (swdo != idx.end()) { + + //std::string swdo_txid = swdo->sidechain_transaction_id; + //std::string swdo_sidechain_from = swdo->sidechain_from; + //std::string swdo_sidechain_currency = swdo->sidechain_currency; + //uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value; + //uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-"))); + // + //std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid); + //if (tx_str != "") { + // + // std::stringstream ss_tx(tx_str); + // boost::property_tree::ptree tx; + // boost::property_tree::read_json(ss_tx, tx); + // + // uint64_t op_idx = -1; + // for (const auto &ops : tx.get_child("result.operations")) { + // const auto &op = ops.second; + // op_idx = op_idx + 1; + // if (op_idx == swdo_op_idx) { + // std::string operation_type = op.get("type"); + // + // if (operation_type == "transfer_operation") { + // const auto &op_value = op.get_child("value"); + // + // std::string sidechain_from = op_value.get("from"); + // + // const auto &amount_child = op_value.get_child("amount"); + // + // uint64_t amount = amount_child.get("amount"); + // std::string nai = amount_child.get("nai"); + // std::string sidechain_currency = ""; + // if ((nai == "@@000000013" /*?? HBD*/) || (nai == "@@000000013" /*TBD*/)) { + // sidechain_currency = "HBD"; + // } + // if ((nai == "@@000000021") /*?? HIVE*/ || (nai == "@@000000021" /*TESTS*/)) { + // sidechain_currency = "HIVE"; + // } + // + // std::string memo = op_value.get("memo"); + // boost::trim(memo); + // if (!memo.empty()) { + // sidechain_from = memo; + // } + // + // process_ok = (swdo_sidechain_from == sidechain_from) && + // (swdo_sidechain_currency == sidechain_currency) && + // (swdo_sidechain_amount == amount); + // } + // } + // } + //} + } + + process_ok = true; + + should_approve = process_ok; + break; + } + + case chain::operation::tag::value: { + bool process_ok = false; + bool transaction_ok = false; + son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(swwo_id); + if (swwo != idx.end()) { + uint32_t swwo_block_num = swwo->block_num; + std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id; + uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1)); + + const auto &block = database.fetch_block_by_number(swwo_block_num); + + for (const auto &tx : block->transactions) { + if (tx.id().str() == swwo_peerplays_transaction_id) { + operation op = tx.operations[swwo_op_idx]; + transfer_operation t_op = op.get(); + + price asset_price = database.get(t_op.amount.asset_id).options.core_exchange_rate; + asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); + + process_ok = (t_op.to == gpo.parameters.son_account()) && + (swwo->peerplays_from == t_op.from) && + (swwo->peerplays_asset == peerplays_asset); + break; + } + } + + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(object_id); + if (swwo != idx.end()) { + tx_str = create_withdrawal_transaction(*swwo); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } + + should_approve = process_ok && + transaction_ok; + break; + } + + case chain::operation::tag::value: { + should_approve = true; + son_id_type signer = op_obj_idx_0.get().signer; + std::string signature = op_obj_idx_0.get().signature; + sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get().sidechain_transaction_id; + const auto &st_idx = database.get_index_type().indices().get(); + const auto sto = st_idx.find(sidechain_transaction_id); + if (sto == st_idx.end()) { + should_approve = false; + break; + } + + const auto &s_idx = database.get_index_type().indices().get(); + const auto son = s_idx.find(signer); + if (son == s_idx.end()) { + should_approve = false; + break; + } + + break; + } + + case chain::operation::tag::value: { + should_approve = true; + break; + } + + default: + should_approve = false; + elog("=================================================="); + elog("Proposal not considered for approval ${po}", ("po", po)); + elog("=================================================="); + } + + return should_approve; +} + +void sidechain_net_handler_ethereum::process_primary_wallet() { + const auto &swi = database.get_index_type().indices().get(); + const auto &active_sw = swi.rbegin(); + if (active_sw != swi.rend()) { + + if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || + (active_sw->addresses.at(sidechain).empty())) { + + if (proposal_exists(chain::operation::tag::value, active_sw->id)) { + return; + } + + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, active_sw->id)) { + return; + } + + const chain::global_property_object &gpo = database.get_global_properties(); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_update_operation swu_op; + swu_op.payer = gpo.parameters.son_account(); + swu_op.son_wallet_id = active_sw->id; + swu_op.sidechain = sidechain; + swu_op.address = wallet_contract_address; + proposal_op.proposed_ops.emplace_back(swu_op); + + std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), active_sw->id.operator std::string()); + if (!tx_str.empty()) { + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = active_sw->id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons.at(sidechain); + proposal_op.proposed_ops.emplace_back(stc_op); + } + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, active_sw->id); + } catch (fc::exception &e) { + elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); + return; + } + } + } +} + +void sidechain_net_handler_ethereum::process_sidechain_addresses() { +} + +bool sidechain_net_handler_ethereum::process_deposit(const son_wallet_deposit_object &swdo) { + const chain::global_property_object &gpo = database.get_global_properties(); + + price asset_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; + asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, database.get_global_properties().parameters.eth_asset()); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_deposit_process_operation swdp_op; + swdp_op.payer = gpo.parameters.son_account(); + swdp_op.son_wallet_deposit_id = swdo.id; + proposal_op.proposed_ops.emplace_back(swdp_op); + + asset_issue_operation ai_op; + ai_op.fee = database.current_fee_schedule().calculate_fee(ai_op); + ai_op.issuer = gpo.parameters.son_account(); + ai_op.asset_to_issue = asset_to_issue; + ai_op.issue_to_account = swdo.peerplays_from; + proposal_op.proposed_ops.emplace_back(ai_op); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception &e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + + return false; +} + +bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdraw_object &swwo) { + + if (proposal_exists(chain::operation::tag::value, swwo.id)) { + return false; + } + + std::string tx_str = create_withdrawal_transaction(swwo); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_withdraw_process_operation swwp_op; + swwp_op.payer = gpo.parameters.son_account(); + swwp_op.son_wallet_withdraw_id = swwo.id; + proposal_op.proposed_ops.emplace_back(swwp_op); + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swwo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons.at(sidechain); + proposal_op.proposed_ops.emplace_back(stc_op); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception &e) { + elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; +} + +std::string sidechain_net_handler_ethereum::process_sidechain_transaction(const sidechain_transaction_object &sto) { + return sign_transaction(sto); +} + +std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sidechain_transaction_object &sto) { + boost::property_tree::ptree pt; + boost::property_tree::ptree pt_array; + for (const auto &signature : sto.signatures) { + const auto &transaction = signature.second; + + //! Check if we have this signed transaction, if not, don't send it + if (transaction.empty()) + continue; + +#ifdef SEND_RAW_TRANSACTION + const std::string sidechain_transaction = rpc_client->eth_send_raw_transaction(transaction); +#else + const std::string sidechain_transaction = rpc_client->eth_send_transaction(transaction); +#endif + + std::stringstream ss_tx(sidechain_transaction); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(ss_tx, tx_json); + if (tx_json.count("result") && !tx_json.count("error")) { + boost::property_tree::ptree node; + node.put("transaction", transaction); + node.put("transaction_receipt", tx_json.get("result")); + pt_array.push_back(std::make_pair("", node)); + } else { + //! Fixme + //! How should we proceed with error in eth_send_transaction + elog("Error in eth_send_transaction for transaction ${id}, transaction ${transaction}", ("id", sto.id)("transaction", transaction)); + return std::string{}; //! Return empty string, as we have error in sending + } + } + pt.add_child("result_array", pt_array); + + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, pt); + return ss.str(); +} + +bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount) { + std::stringstream ss(sto.sidechain_transaction); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (!json.count("result_array")) { + return false; + } + + size_t count = 0; + for (const auto &entry : json.get_child("result_array")) { + const std::string receipt = rpc_client->eth_get_transaction_receipt(entry.second.get("transaction_receipt")); + + std::stringstream ss_receipt(receipt); + boost::property_tree::ptree json_receipt; + boost::property_tree::read_json(ss_receipt, json_receipt); + + if (json_receipt.get("result") == "null") { + wlog("Block is not minted yet for transaction ${id}", ("id", sto.id)); + return false; + } + + if ("0x1" == json_receipt.get("result.status")) { + count += 1; + //! Fixme - compare data somehow? + //if( sto.transaction == entry_receipt.second.get("data") ) { + //} + } + } + + //! Check that we have all transactions + if (count != json.get_child("result_array").size()) { + wlog("Not all receipts received for transaction ${id}", ("id", sto.id)); + return false; + } else + return true; + + return false; +} + +std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id) { + std::vector> owners_weights; + for (auto &son : son_pubkeys) { + const std::string pub_key_str = son.public_key; + owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight)); + } + + const ethereum::update_owners_encoder encoder; + return encoder.encode(owners_weights, object_id); +} + +std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) { + return "Deposit-Transaction"; +} + +std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { + const ethereum::withdrawal_encoder encoder; + return encoder.encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); +} + +std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) { + const auto ¤t_son = plugin.get_current_son_object(sidechain); + FC_ASSERT(current_son.sidechain_public_keys.contains(sidechain), "No public keys for current son: ${account_id}", ("account_id", current_son.son_account)); + + const auto &public_key = current_son.sidechain_public_keys.at(sidechain); + +#ifdef SEND_RAW_TRANSACTION + ethereum::raw_transaction raw_tr; + raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key)); + raw_tr.gas_price = rpc_client->get_gas_price(); + raw_tr.gas_limit = rpc_client->get_gas_limit(); + raw_tr.to = wallet_contract_address; + raw_tr.value = ""; + raw_tr.data = sto.transaction; + raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id)); + + const auto sign_tr = raw_tr.sign(get_private_key(public_key)); + return sign_tr.serialize(); +#else + ethereum::transaction sign_transaction; + sign_transaction.data = sto.transaction; + sign_transaction.to = wallet_contract_address; + sign_transaction.from = "0x" + public_key; + return sign_transaction.sign(get_private_key(public_key)).serialize(); +#endif +} + +void sidechain_net_handler_ethereum::schedule_ethereum_listener() { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next = 5000; + + fc::time_point next_wakeup(now + fc::milliseconds(time_to_next)); + + _listener_task = fc::schedule([this] { + ethereum_listener_loop(); + }, + next_wakeup, "SON Ethereum listener task"); +} + +void sidechain_net_handler_ethereum::ethereum_listener_loop() { + schedule_ethereum_listener(); + + std::string reply = rpc_client->eth_get_block_by_number("latest", false); + //std::string reply = rpc_client->eth_get_logs(wallet_contract_address); + if (!reply.empty()) { + std::stringstream ss(reply); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.count("result")) { + std::string head_block_number_s = json.get("result.number"); + uint64_t head_block_number = std::strtoul(head_block_number_s.c_str(), nullptr, 16); + if (head_block_number != last_block_received) { + std::string event_data = std::to_string(head_block_number); + handle_event(event_data); + last_block_received = head_block_number; + } + } + } +} + +void sidechain_net_handler_ethereum::handle_event(const std::string &event_data) { + std::string block = rpc_client->eth_get_block_by_number("latest", true); + if (block != "") { + add_to_son_listener_log("BLOCK : " + event_data); + std::stringstream ss(block); + boost::property_tree::ptree block_json; + boost::property_tree::read_json(ss, block_json); + + size_t tx_idx = -1; + for (const auto &tx_child : block_json.get_child("result.transactions")) { + boost::property_tree::ptree tx = tx_child.second; + tx_idx = tx_idx + 1; + + std::string from = tx.get("from"); + std::string to = tx.get("to"); + + std::string cmp_to = to; + std::transform(cmp_to.begin(), cmp_to.end(), cmp_to.begin(), ::toupper); + std::string cmp_wallet_contract_address = wallet_contract_address; + std::transform(cmp_wallet_contract_address.begin(), cmp_wallet_contract_address.end(), cmp_wallet_contract_address.begin(), ::toupper); + + if (cmp_to == cmp_wallet_contract_address) { + + std::string value_s = tx.get("value"); + boost::multiprecision::uint256_t amount(value_s); + amount = amount / 100000; + amount = amount / 100000; + + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, from, time_point_sec::maximum())); + if (addr_itr == sidechain_addresses_idx.end()) { + continue; + } + + std::stringstream ss; + ss << "ethereum" + << "-" << tx.get("hash") << "-" << tx_idx; + std::string sidechain_uid = ss.str(); + + sidechain_event_data sed; + sed.timestamp = database.head_block_time(); + sed.block_num = database.head_block_num(); + sed.sidechain = sidechain; + sed.sidechain_uid = sidechain_uid; + sed.sidechain_transaction_id = tx.get("hash"); + sed.sidechain_from = from; + sed.sidechain_to = to; + sed.sidechain_currency = "ETH"; + sed.sidechain_amount = amount; + sed.peerplays_from = addr_itr->sidechain_address_account; + sed.peerplays_to = database.get_global_properties().parameters.son_account(); + price eth_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; + sed.peerplays_asset = asset(sed.sidechain_amount * eth_price.base.amount / eth_price.quote.amount); + + add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id); + + sidechain_event_data_received(sed); + } + } + } +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp index 1c6632d8..cd6b50be 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -18,6 +19,9 @@ std::unique_ptr sidechain_net_handler_factory::create_han case sidechain_type::hive: { return std::unique_ptr(new sidechain_net_handler_hive(plugin, options)); } + case sidechain_type::ethereum: { + return std::unique_ptr(new sidechain_net_handler_ethereum(plugin, options)); + } case sidechain_type::peerplays: { return std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); } diff --git a/libraries/sha3/CMakeLists.txt b/libraries/sha3/CMakeLists.txt new file mode 100644 index 00000000..c790323e --- /dev/null +++ b/libraries/sha3/CMakeLists.txt @@ -0,0 +1,16 @@ +file(GLOB HEADERS "include/sha3/*.h") + +add_library( sha3 + memzero.c + sha3.c +) + +target_include_directories( sha3 PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include ) +target_compile_definitions( sha3 PUBLIC USE_KECCAK=1 ) + +install( TARGETS + sha3 + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/libraries/sha3/include/sha3/memzero.h b/libraries/sha3/include/sha3/memzero.h new file mode 100644 index 00000000..1e744c12 --- /dev/null +++ b/libraries/sha3/include/sha3/memzero.h @@ -0,0 +1,16 @@ +#ifndef __MEMZERO_H__ +#define __MEMZERO_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif +void memzero(void* const pnt, const size_t len); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif diff --git a/libraries/sha3/include/sha3/sha3.h b/libraries/sha3/include/sha3/sha3.h new file mode 100644 index 00000000..881e806c --- /dev/null +++ b/libraries/sha3/include/sha3/sha3.h @@ -0,0 +1,88 @@ +/* sha3.h - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#ifndef __SHA3_H__ +#define __SHA3_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define sha3_224_hash_size 28 +#define sha3_256_hash_size 32 +#define sha3_384_hash_size 48 +#define sha3_512_hash_size 64 +#define sha3_max_permutation_size 25 +#define sha3_max_rate_in_qwords 24 + +#define SHA3_224_BLOCK_LENGTH 144 +#define SHA3_256_BLOCK_LENGTH 136 +#define SHA3_384_BLOCK_LENGTH 104 +#define SHA3_512_BLOCK_LENGTH 72 + +#define SHA3_224_DIGEST_LENGTH sha3_224_hash_size +#define SHA3_256_DIGEST_LENGTH sha3_256_hash_size +#define SHA3_384_DIGEST_LENGTH sha3_384_hash_size +#define SHA3_512_DIGEST_LENGTH sha3_512_hash_size + +/** + * SHA3 Algorithm context. + */ +typedef struct SHA3_CTX +{ + /* 1600 bits algorithm hashing state */ + uint64_t hash[sha3_max_permutation_size]; + /* 1536-bit buffer for leftovers */ + uint64_t message[sha3_max_rate_in_qwords]; + /* count of bytes in the message[] buffer */ + unsigned rest; + /* size of a message block processed at once */ + unsigned block_size; +} SHA3_CTX; + +/* methods for calculating the hash function */ + +void sha3_224_Init(SHA3_CTX *ctx); +void sha3_256_Init(SHA3_CTX *ctx); +void sha3_384_Init(SHA3_CTX *ctx); +void sha3_512_Init(SHA3_CTX *ctx); +void sha3_Update(SHA3_CTX *ctx, const unsigned char* msg, size_t size); +void sha3_Final(SHA3_CTX *ctx, unsigned char* result); + +#if USE_KECCAK +#define keccak_224_Init sha3_224_Init +#define keccak_256_Init sha3_256_Init +#define keccak_384_Init sha3_384_Init +#define keccak_512_Init sha3_512_Init +#define keccak_Update sha3_Update +void keccak_Final(SHA3_CTX *ctx, unsigned char* result); +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest); +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest); +#endif + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest); +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __SHA3_H__ */ diff --git a/libraries/sha3/memzero.c b/libraries/sha3/memzero.c new file mode 100644 index 00000000..32aa140f --- /dev/null +++ b/libraries/sha3/memzero.c @@ -0,0 +1,75 @@ +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 // C11's bounds-checking interface. +#endif +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef __unix__ +#include +#include +#endif + +// C11's bounds-checking interface. +#if defined(__STDC_LIB_EXT1__) +#define HAVE_MEMSET_S 1 +#endif + +// GNU C Library version 2.25 or later. +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// Newlib +#if defined(__NEWLIB__) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// FreeBSD version 11.0 or later. +#if defined(__FreeBSD__) && __FreeBSD_version >= 1100037 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// OpenBSD version 5.5 or later. +#if defined(__OpenBSD__) && OpenBSD >= 201405 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// NetBSD version 7.2 or later. +#if defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 +#define HAVE_EXPLICIT_MEMSET 1 +#endif + +// Adapted from +// https://github.com/jedisct1/libsodium/blob/1647f0d53ae0e370378a9195477e3df0a792408f/src/libsodium/sodium/utils.c#L102-L130 + +void memzero(void *const pnt, const size_t len) { +#ifdef _WIN32 + SecureZeroMemory(pnt, len); +#elif defined(HAVE_MEMSET_S) + memset_s(pnt, (rsize_t)len, 0, (rsize_t)len); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero(pnt, len); +#elif defined(HAVE_EXPLICIT_MEMSET) + explicit_memset(pnt, 0, len); +#else + volatile unsigned char *volatile pnt_ = (volatile unsigned char *volatile)pnt; + size_t i = (size_t)0U; + + while (i < len) { + pnt_[i++] = 0U; + } +#endif + + // explicitly mark the memory as overwritten for the Clang MemorySanitizer + // this is only included at compile time if MemorySanitizer is enabled and + // should not come with any downsides during regular builds +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + memset(pnt, 0, len); +#endif +#endif +} diff --git a/libraries/sha3/sha3.c b/libraries/sha3/sha3.c new file mode 100644 index 00000000..172728eb --- /dev/null +++ b/libraries/sha3/sha3.c @@ -0,0 +1,397 @@ +/* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#include +#include + +#include +#include + +#define I64(x) x##LL +#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) +#define le2me_64(x) (x) +#define IS_ALIGNED_64(p) (0 == (7 & ((long)(p)))) // [wallet-core] pointer/numerical type, for MacOS SDK 12.3 +# define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) + +/* constants */ +#define NumberOfRounds 24 + +/* SHA3 (Keccak) constants for 24 rounds */ +uint64_t keccak_round_constants[NumberOfRounds] = { + I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), I64(0x8000000080008000), + I64(0x000000000000808B), I64(0x0000000080000001), I64(0x8000000080008081), I64(0x8000000000008009), + I64(0x000000000000008A), I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A), + I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), I64(0x8000000000008003), + I64(0x8000000000008002), I64(0x8000000000000080), I64(0x000000000000800A), I64(0x800000008000000A), + I64(0x8000000080008081), I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008) +}; + +/* Initializing a sha3 context for given number of output bits */ +static void keccak_Init(SHA3_CTX *ctx, unsigned bits) +{ + /* NB: The Keccak capacity parameter = bits * 2 */ + unsigned rate = 1600 - bits * 2; + + memzero(ctx, sizeof(SHA3_CTX)); + ctx->block_size = rate / 8; + assert(rate <= 1600 && (rate % 64) == 0); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_224_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 224); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_256_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 256); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_384_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 384); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_512_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 512); +} + +/* Keccak theta() transformation */ +static void keccak_theta(uint64_t *A) +{ + unsigned int x = 0; + uint64_t C[5] = {0}, D[5] = {0}; + + for (x = 0; x < 5; x++) { + C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; + } + D[0] = ROTL64(C[1], 1) ^ C[4]; + D[1] = ROTL64(C[2], 1) ^ C[0]; + D[2] = ROTL64(C[3], 1) ^ C[1]; + D[3] = ROTL64(C[4], 1) ^ C[2]; + D[4] = ROTL64(C[0], 1) ^ C[3]; + + for (x = 0; x < 5; x++) { + A[x] ^= D[x]; + A[x + 5] ^= D[x]; + A[x + 10] ^= D[x]; + A[x + 15] ^= D[x]; + A[x + 20] ^= D[x]; + } +} + +/* Keccak pi() transformation */ +static void keccak_pi(uint64_t *A) +{ + uint64_t A1 = 0; + A1 = A[1]; + A[ 1] = A[ 6]; + A[ 6] = A[ 9]; + A[ 9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[ 2]; + A[ 2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[ 4]; + A[ 4] = A[24]; + A[24] = A[21]; + A[21] = A[ 8]; + A[ 8] = A[16]; + A[16] = A[ 5]; + A[ 5] = A[ 3]; + A[ 3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[ 7]; + A[ 7] = A[10]; + A[10] = A1; + /* note: A[ 0] is left as is */ +} + +/* Keccak chi() transformation */ +static void keccak_chi(uint64_t *A) +{ + int i = 0; + for (i = 0; i < 25; i += 5) { + uint64_t A0 = A[0 + i], A1 = A[1 + i]; + A[0 + i] ^= ~A1 & A[2 + i]; + A[1 + i] ^= ~A[2 + i] & A[3 + i]; + A[2 + i] ^= ~A[3 + i] & A[4 + i]; + A[3 + i] ^= ~A[4 + i] & A0; + A[4 + i] ^= ~A0 & A1; + } +} + +static void sha3_permutation(uint64_t *state) +{ + int round = 0; + for (round = 0; round < NumberOfRounds; round++) + { + keccak_theta(state); + + /* apply Keccak rho() transformation */ + state[ 1] = ROTL64(state[ 1], 1); + state[ 2] = ROTL64(state[ 2], 62); + state[ 3] = ROTL64(state[ 3], 28); + state[ 4] = ROTL64(state[ 4], 27); + state[ 5] = ROTL64(state[ 5], 36); + state[ 6] = ROTL64(state[ 6], 44); + state[ 7] = ROTL64(state[ 7], 6); + state[ 8] = ROTL64(state[ 8], 55); + state[ 9] = ROTL64(state[ 9], 20); + state[10] = ROTL64(state[10], 3); + state[11] = ROTL64(state[11], 10); + state[12] = ROTL64(state[12], 43); + state[13] = ROTL64(state[13], 25); + state[14] = ROTL64(state[14], 39); + state[15] = ROTL64(state[15], 41); + state[16] = ROTL64(state[16], 45); + state[17] = ROTL64(state[17], 15); + state[18] = ROTL64(state[18], 21); + state[19] = ROTL64(state[19], 8); + state[20] = ROTL64(state[20], 18); + state[21] = ROTL64(state[21], 2); + state[22] = ROTL64(state[22], 61); + state[23] = ROTL64(state[23], 56); + state[24] = ROTL64(state[24], 14); + + keccak_pi(state); + keccak_chi(state); + + /* apply iota(state, round) */ + *state ^= keccak_round_constants[round]; + } +} + +/** + * The core transformation. Process the specified block of data. + * + * @param hash the algorithm state + * @param block the message block to process + * @param block_size the size of the processed block in bytes + */ +static void sha3_process_block(uint64_t hash[25], const uint64_t *block, size_t block_size) +{ + /* expanded loop */ + hash[ 0] ^= le2me_64(block[ 0]); + hash[ 1] ^= le2me_64(block[ 1]); + hash[ 2] ^= le2me_64(block[ 2]); + hash[ 3] ^= le2me_64(block[ 3]); + hash[ 4] ^= le2me_64(block[ 4]); + hash[ 5] ^= le2me_64(block[ 5]); + hash[ 6] ^= le2me_64(block[ 6]); + hash[ 7] ^= le2me_64(block[ 7]); + hash[ 8] ^= le2me_64(block[ 8]); + /* if not sha3-512 */ + if (block_size > 72) { + hash[ 9] ^= le2me_64(block[ 9]); + hash[10] ^= le2me_64(block[10]); + hash[11] ^= le2me_64(block[11]); + hash[12] ^= le2me_64(block[12]); + /* if not sha3-384 */ + if (block_size > 104) { + hash[13] ^= le2me_64(block[13]); + hash[14] ^= le2me_64(block[14]); + hash[15] ^= le2me_64(block[15]); + hash[16] ^= le2me_64(block[16]); + /* if not sha3-256 */ + if (block_size > 136) { + hash[17] ^= le2me_64(block[17]); +#ifdef FULL_SHA3_FAMILY_SUPPORT + /* if not sha3-224 */ + if (block_size > 144) { + hash[18] ^= le2me_64(block[18]); + hash[19] ^= le2me_64(block[19]); + hash[20] ^= le2me_64(block[20]); + hash[21] ^= le2me_64(block[21]); + hash[22] ^= le2me_64(block[22]); + hash[23] ^= le2me_64(block[23]); + hash[24] ^= le2me_64(block[24]); + } +#endif + } + } + } + /* make a permutation of the hash */ + sha3_permutation(hash); +} + +#define SHA3_FINALIZED 0x80000000 + +/** + * Calculate message hash. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param ctx the algorithm context containing current hashing state + * @param msg message chunk + * @param size length of the message chunk + */ +void sha3_Update(SHA3_CTX *ctx, const unsigned char *msg, size_t size) +{ + size_t idx = (size_t)ctx->rest; + size_t block_size = (size_t)ctx->block_size; + + if (ctx->rest & SHA3_FINALIZED) return; /* too late for additional input */ + ctx->rest = (unsigned)((ctx->rest + size) % block_size); + + /* fill partial block */ + if (idx) { + size_t left = block_size - idx; + memcpy((char*)ctx->message + idx, msg, (size < left ? size : left)); + if (size < left) return; + + /* process partial block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + msg += left; + size -= left; + } + while (size >= block_size) { + uint64_t *aligned_message_block = NULL; + if (IS_ALIGNED_64(msg)) { + /* the most common case is processing of an already aligned message + without copying it */ + aligned_message_block = (uint64_t*)(void*)msg; + } else { + memcpy(ctx->message, msg, block_size); + aligned_message_block = ctx->message; + } + + sha3_process_block(ctx->hash, aligned_message_block, block_size); + msg += block_size; + size -= block_size; + } + if (size) { + memcpy(ctx->message, msg, size); /* save leftovers */ + } +} + +/** + * Store calculated hash into the given array. + * + * @param ctx the algorithm context containing current hashing state + * @param result calculated hash in binary form + */ +void sha3_Final(SHA3_CTX *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x06; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +#if USE_KECCAK +/** +* Store calculated hash into the given array. +* +* @param ctx the algorithm context containing current hashing state +* @param result calculated hash in binary form +*/ +void keccak_Final(SHA3_CTX *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x01; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} + +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + keccak_512_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} +#endif /* USE_KECCAK */ + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + sha3_256_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} + +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + sha3_512_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 43ca49fd..c334ebc2 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2845,7 +2845,7 @@ public: account_id_type son_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_account_id); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); if (approve) { @@ -2891,7 +2891,7 @@ public: account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) @@ -2902,7 +2902,7 @@ public: account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) @@ -2943,6 +2943,7 @@ public: case sidechain_type::peerplays : return "peerplays"; case sidechain_type::bitcoin : return "bitcoin"; case sidechain_type::hive : return "hive"; + case sidechain_type::ethereum : return "ethereum"; default: FC_THROW("Wrong sidechain type: ${sidechain}", ("sidechain", sidechain)); } diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 67c5019a..0e5d67cf 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -128,11 +128,13 @@ BOOST_AUTO_TEST_CASE( create_sons ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); auto son1_obj = con.wallet_api_ptr->get_son("son1account"); @@ -140,16 +142,20 @@ BOOST_AUTO_TEST_CASE( create_sons ) BOOST_CHECK_EQUAL(son1_obj.url, "http://son1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); auto son2_obj = con.wallet_api_ptr->get_son("son2account"); BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account")); BOOST_CHECK_EQUAL(son2_obj.url, "http://son2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2"); - BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 24); - BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 25); + BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2"); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 25); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 26); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 27); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -170,6 +176,7 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; son_test_helper sth(*this); sth.create_son("sonmember", "http://sonmember", sidechain_public_keys); @@ -182,19 +189,23 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) BOOST_CHECK(son_data.son_account == sonmember_acct.get_id()); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); // update SON sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", sidechain_public_keys, true); son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember_updated"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 2"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2"); // update SON signing key sidechain_public_keys.clear(); @@ -222,11 +233,13 @@ BOOST_AUTO_TEST_CASE( son_voting ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); BOOST_CHECK(generate_maintenance_block()); @@ -253,6 +266,7 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_TEST_MESSAGE("Voting for son1account"); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, true, true); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, true, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::ethereum, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there @@ -260,11 +274,13 @@ BOOST_AUTO_TEST_CASE( son_voting ) son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son1_end_votes[sidechain_type::hive] > son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] > son1_start_votes[sidechain_type::ethereum]); // Vote for a son2account BOOST_TEST_MESSAGE("Voting for son2account"); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, true, true); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, true, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::ethereum, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there @@ -272,6 +288,7 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] > son2_start_votes[sidechain_type::ethereum]); //! Check son1account voters auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; @@ -280,6 +297,8 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check son2account voters auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; @@ -288,6 +307,8 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check votes of nathan auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -298,11 +319,15 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son1account BOOST_TEST_MESSAGE("Withdraw vote for a son1account"); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, false, true); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, false, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::ethereum, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed @@ -310,12 +335,14 @@ BOOST_AUTO_TEST_CASE( son_voting ) son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); //! Check son1account voters voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; BOOST_REQUIRE(voters_for_son1account); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -324,11 +351,14 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son2account BOOST_TEST_MESSAGE("Withdraw vote for a son2account"); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, false, true); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, false, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::ethereum, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed @@ -336,12 +366,14 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); //! Check son2account voters voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; BOOST_REQUIRE(voters_for_son2account); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -383,6 +415,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), "http://son" + fc::to_pretty_string(i), sidechain_public_keys, @@ -400,6 +433,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -409,14 +443,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); for(unsigned int i = 0; i < son_number - 1; i++) { @@ -424,14 +461,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); for(unsigned int i = 0; i < son_number - 2; i++) { @@ -439,14 +479,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(generate_maintenance_block()); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == son_number); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -468,11 +511,13 @@ BOOST_AUTO_TEST_CASE( list_son ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); auto res = con.wallet_api_ptr->list_sons("", 100); @@ -500,11 +545,13 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); BOOST_CHECK(generate_maintenance_block()); @@ -539,6 +586,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 2, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 2, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 2, true); generate_block(); BOOST_CHECK(generate_maintenance_block()); @@ -546,10 +595,14 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] > son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] > son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] > son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check son1account voters @@ -559,6 +612,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check son2account voters auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; @@ -567,6 +622,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check votes of nathan auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -577,6 +634,9 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for SON 1 accepted.clear(); @@ -587,17 +647,23 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 1, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 1, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] < son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] < son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] < son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); // voice distribution changed, SON2 now has all voices son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); // nathan spent funds for vb, it has different voting power + BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] > son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check son1account voters @@ -605,6 +671,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_REQUIRE(voters_for_son1account); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -613,6 +680,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son2_obj.id.instance()); // Try to reject incorrect SON accepted.clear(); @@ -622,16 +691,22 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 1, true), fc::exception); BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 1, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true), fc::exception); generate_block(); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check votes of nathan @@ -641,6 +716,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son2_obj.id.instance()); // Reject SON2 accepted.clear(); @@ -650,16 +727,22 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 0, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 0, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 0, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] < son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] < son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] < son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check son2account voters @@ -667,6 +750,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_REQUIRE(voters_for_son2account); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -681,16 +765,22 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 1, true), fc::exception); BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check votes of nathan @@ -702,16 +792,24 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.clear(); BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check votes of nathan @@ -734,6 +832,7 @@ BOOST_AUTO_TEST_CASE( related_functions ) global_property_object gpo = con.wallet_api_ptr->get_global_properties(); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 0); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 0); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == 0); flat_map sidechain_public_keys; @@ -742,16 +841,19 @@ BOOST_AUTO_TEST_CASE( related_functions ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 2); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 2); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == 2); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -783,6 +885,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), "http://son" + fc::to_pretty_string(i), sidechain_public_keys, @@ -799,6 +902,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) std::string name = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -808,14 +912,17 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == son_number); map active_sons = con.wallet_api_ptr->list_active_sons(); BOOST_CHECK(active_sons.size() == son_number); @@ -855,6 +962,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), "http://son" + fc::to_pretty_string(i), sidechain_public_keys, @@ -870,12 +978,14 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::bitcoin, true, true); con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::hive, true, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); son_object son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -885,6 +995,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); // restore SON activity con.wallet_api_ptr->cancel_request_son_maintenance(name, true); @@ -894,6 +1005,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -903,6 +1015,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); // process maintenance BOOST_CHECK(generate_maintenance_block()); @@ -911,7 +1024,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::in_maintenance); - + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::in_maintenance); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); diff --git a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp new file mode 100644 index 00000000..769c35b1 --- /dev/null +++ b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp @@ -0,0 +1,147 @@ +#include + +#include +#include +#include + +using namespace graphene::peerplays_sidechain::ethereum; + +BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests) + +BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) { + const withdrawal_encoder encoder; + const auto tx = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0"); + BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); + + const auto tx1 = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1"); + BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { + std::vector> owners_weights; + owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1); + owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1); + + const update_owners_encoder encoder; + const auto tx = encoder.encode(owners_weights, "1.35.0"); + BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); + + owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1); + const auto tx1 = encoder.encode(owners_weights, "1.36.1"); + BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto tx = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"); + + //! Change value + raw_tr.value = "0x1BC16D674EC80000"; + const auto tx1 = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx1, "0xEC80843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000080218080"); + + //! Change data + raw_tr.data = "0x893d20e8"; + const auto tx2 = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx2, "0xF080843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000084893D20E8218080"); +} + +BOOST_AUTO_TEST_CASE(raw_transaction_deserialization_test) { + const raw_transaction raw_tr{"E480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"}; + + BOOST_CHECK_EQUAL(raw_tr.nonce, "0x0"); + BOOST_CHECK_EQUAL(raw_tr.gas_price, "0x3b9aca07"); + BOOST_CHECK_EQUAL(raw_tr.gas_limit, "0x7a1200"); + BOOST_CHECK_EQUAL(raw_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348"); + BOOST_CHECK_EQUAL(raw_tr.value, "0x0"); + BOOST_CHECK_EQUAL(raw_tr.data, ""); + BOOST_CHECK_EQUAL(raw_tr.chain_id, "0x21"); +} + +BOOST_AUTO_TEST_CASE(raw_transaction_hash_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto tx = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"); + + const auto hash = raw_tr.hash(); + const auto hash_str = fc::to_hex((char *)&hash[0], hash.size()); + BOOST_CHECK_EQUAL(hash_str, "34934410cd305f4fa4e75a2c9294d625d6fbba729b5642ed2ca757ead50bb1fb"); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060"); + BOOST_CHECK_EQUAL(sign_tr.r, "5f09de6ac850b2a9e94acd709c12d4e9adbabc6b72281ec0bbe13bca7e57c7ce"); + BOOST_CHECK_EQUAL(sign_tr.v, "65"); + BOOST_CHECK_EQUAL(sign_tr.s, "7ca5f26c5b3e25f14a32b18ac9a2a41b7c68efd3b04b118e1b1f4bf1c4e299b0"); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_serialization_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060"); + const auto tx = sign_tr.serialize(); + BOOST_CHECK_EQUAL(tx, "0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0"); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_deserialization_test) { + const signed_transaction sign_tr{"0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0"}; + + BOOST_CHECK_EQUAL(sign_tr.nonce, "0x0"); + BOOST_CHECK_EQUAL(sign_tr.gas_price, "0x3b9aca07"); + BOOST_CHECK_EQUAL(sign_tr.gas_limit, "0x7a1200"); + BOOST_CHECK_EQUAL(sign_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348"); + BOOST_CHECK_EQUAL(sign_tr.value, "0x0"); + BOOST_CHECK_EQUAL(sign_tr.data, ""); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_recover_test) { + const std::string chain_id = "0x21"; + + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = chain_id; + + const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060"); + const auto from = sign_tr.recover(chain_id); + BOOST_CHECK_EQUAL(from, "0x5fbbb31be52608d2f52247e8400b7fcaa9e0bc12"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index 72f50b9d..818c1743 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -135,6 +135,7 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { flat_map sidechain_public_keys; sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; sidechain_public_keys[sidechain_type::hive] = "hive address"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address"; son_create_operation op; op.owner_account = bob_id; diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 5bb003aa..a128b474 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -87,6 +87,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { { flat_map sidechain_public_keys; sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + sidechain_public_keys[sidechain_type::hive] = "hive account"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address"; son_create_operation op; op.owner_account = alice_id; @@ -111,6 +113,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { BOOST_CHECK( obj->url == test_url ); BOOST_CHECK( obj->signing_key == alice_public_key ); BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::hive) == "hive account" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::ethereum) == "ethereum address" ); BOOST_CHECK( obj->deposit.instance == deposit.instance.value ); BOOST_CHECK( obj->pay_vb.instance == payment.instance.value ); } @@ -124,7 +128,9 @@ BOOST_AUTO_TEST_CASE( update_son_test ) { { flat_map sidechain_public_keys; - sidechain_public_keys[sidechain_type::bitcoin] = "new bitcoin address"; + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + sidechain_public_keys[sidechain_type::hive] = "hive account"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address"; son_update_operation op; op.son_id = son_id_type(0); @@ -143,7 +149,9 @@ BOOST_AUTO_TEST_CASE( update_son_test ) { auto obj = idx.find( alice_id ); BOOST_REQUIRE( obj != idx.end() ); BOOST_CHECK( obj->url == new_url ); - BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "new bitcoin address" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::hive) == "hive account" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::ethereum) == "ethereum address" ); } BOOST_AUTO_TEST_CASE( deregister_son_test ) { @@ -195,12 +203,14 @@ try { { _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; _s.statuses[sidechain_type::hive] = son_status::in_maintenance; + _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); }); auto deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -222,6 +232,7 @@ try { BOOST_REQUIRE( idx.size() == 1 ); BOOST_REQUIRE( obj->statuses.at(sidechain_type::bitcoin) == son_status::deregistered ); BOOST_REQUIRE( obj->statuses.at(sidechain_type::hive) == son_status::deregistered ); + BOOST_REQUIRE( obj->statuses.at(sidechain_type::ethereum) == son_status::deregistered ); BOOST_REQUIRE( son_stats_obj->deregistered_timestamp == now ); deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -496,30 +507,38 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) { _s.txs_signed[sidechain_type::bitcoin] = 2; _s.txs_signed[sidechain_type::hive] = 4; + _s.txs_signed[sidechain_type::ethereum] = 6; _s.total_txs_signed[sidechain_type::bitcoin] = 2; _s.total_txs_signed[sidechain_type::hive] = 4; + _s.total_txs_signed[sidechain_type::ethereum] = 6; _s.sidechain_txs_reported[sidechain_type::bitcoin] = 4; _s.sidechain_txs_reported[sidechain_type::hive] = 8; + _s.sidechain_txs_reported[sidechain_type::ethereum] = 12; _s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 4; _s.total_sidechain_txs_reported[sidechain_type::hive] = 8; + _s.total_sidechain_txs_reported[sidechain_type::ethereum] = 12; }); // Modify the transaction signed statistics of Bob's SON db.modify( *son_stats_obj2, [&]( son_statistics_object& _s) { _s.txs_signed[sidechain_type::bitcoin] = 3; _s.txs_signed[sidechain_type::hive] = 6; + _s.txs_signed[sidechain_type::ethereum] = 9; _s.total_txs_signed[sidechain_type::bitcoin] = 3; _s.total_txs_signed[sidechain_type::hive] = 6; + _s.total_txs_signed[sidechain_type::ethereum] = 9; _s.sidechain_txs_reported[sidechain_type::bitcoin] = 6; _s.sidechain_txs_reported[sidechain_type::hive] = 12; + _s.sidechain_txs_reported[sidechain_type::ethereum] = 18; _s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 6; _s.total_sidechain_txs_reported[sidechain_type::hive] = 12; + _s.total_sidechain_txs_reported[sidechain_type::ethereum] = 18; }); // Note the balances before the maintenance @@ -531,21 +550,29 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) // Check if the signed transaction statistics are reset for both SONs BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::bitcoin), 2); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::hive), 4); + BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::ethereum), 6); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::bitcoin), 3); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::hive), 6); + BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::ethereum), 9); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 4); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::hive), 8); + BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::ethereum), 12); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 6); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::hive), 12); + BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::ethereum), 18); // Check that Alice and Bob are paid for signing the transactions in the previous day/cycle BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance); BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance); @@ -609,12 +636,14 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { _s.statuses[sidechain_type::bitcoin] = son_status::active; _s.statuses[sidechain_type::hive] = son_status::active; + _s.statuses[sidechain_type::ethereum] = son_status::active; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time()); _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time()); + _s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time()); }); { @@ -633,6 +662,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); } { @@ -651,6 +681,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); } // Modify SON's status to in_maintenance @@ -658,11 +689,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; _s.statuses[sidechain_type::hive] = son_status::in_maintenance; + _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; }); flat_map downtime; downtime[sidechain_type::bitcoin] = 0; downtime[sidechain_type::hive] = 0; + downtime[sidechain_type::ethereum] = 0; { generate_block(); @@ -680,12 +713,16 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::inactive); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); } // Modify SON's status to in_maintenance @@ -693,6 +730,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; _s.statuses[sidechain_type::hive] = son_status::in_maintenance; + _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; }); // SON is selected as one of the active SONs @@ -702,6 +740,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { son_inf.son_id = son_id_type(0); _gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf); _gpo.active_sons[sidechain_type::hive].push_back(son_inf); + _gpo.active_sons[sidechain_type::ethereum].push_back(son_inf); }); { @@ -721,12 +760,16 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); } { @@ -745,10 +788,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin)); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive)); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum)); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); } } FC_LOG_AND_RETHROW() } @@ -775,6 +821,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); { // Check that transaction fails if down_ts < last_active_timestamp @@ -828,8 +875,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::in_maintenance); BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts); BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts); + BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum) == op.down_ts); } { From c1f93f58ee401804c3aca0e19c668fd4b3dee398 Mon Sep 17 00:00:00 2001 From: Meheboob Khan Date: Mon, 26 Sep 2022 20:14:31 +0000 Subject: [PATCH 048/106] Added functionality to check if the device size is less than 50 MB [Issue 389] --- libraries/chain/db_block.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 9606c59e..65f10cc7 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -40,8 +40,10 @@ #include #include #include +#include #include +#include namespace { @@ -196,6 +198,9 @@ bool database::push_block(const signed_block& new_block, uint32_t skip) bool database::_push_block(const signed_block& new_block) { try { + boost::filesystem::space_info si = boost::filesystem::space(get_data_dir()); + FC_ASSERT((si.available) > 104857600, "Rejecting block due to low disk space"); // 104857600 bytes = 100 MB + uint32_t skip = get_node_properties().skip_flags; const auto now = fc::time_point::now().sec_since_epoch(); @@ -726,7 +731,7 @@ void database::_apply_block( const signed_block& next_block ) check_ending_lotteries(); check_ending_nft_lotteries(); - + create_block_summary(next_block); place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time clear_expired_transactions(); From 2accee53e2c1088a44ce789edd20e0753afb66ea Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 27 Sep 2022 10:41:35 +0000 Subject: [PATCH 049/106] bug/451-update-son-list-on-maintenance --- libraries/chain/db_maint.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7474edac..f4a1bca5 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -91,20 +91,20 @@ vector> database::sort_votable_objects< } count = std::min(count, refs.size()); std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), - [this, sidechain](const son_object& a, const son_object& b)->bool { - FC_ASSERT(sidechain == sidechain_type::bitcoin || - sidechain == sidechain_type::ethereum || - sidechain == sidechain_type::hive, - "Unexpected sidechain type"); + [this, sidechain](const son_object& a, const son_object& b)->bool { + FC_ASSERT(sidechain == sidechain_type::bitcoin || + sidechain == sidechain_type::ethereum || + sidechain == sidechain_type::hive, + "Unexpected sidechain type"); - const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; - const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; + const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; + const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; - if( oa_vote != ob_vote ) - return oa_vote > ob_vote; + if( oa_vote != ob_vote ) + return oa_vote > ob_vote; - return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain); -}); + return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain); + }); refs.resize(count, refs.front()); return refs; @@ -1463,17 +1463,19 @@ void rolling_period_start(database& db) { if(db.head_block_time() >= HARDFORK_GPOS_TIME) { - auto gpo = db.get_global_properties(); + const auto gpo = db.get_global_properties(); auto period_start = db.get_global_properties().parameters.gpos_period_start(); - auto vesting_period = db.get_global_properties().parameters.gpos_period(); + const auto vesting_period = db.get_global_properties().parameters.gpos_period(); - auto now = db.head_block_time(); - if(now.sec_since_epoch() >= (period_start + vesting_period)) + const auto now = db.head_block_time(); + while(now.sec_since_epoch() >= (period_start + vesting_period)) { // roll db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) { p.parameters.extensions.value.gpos_period_start = period_start + vesting_period; }); + + period_start = db.get_global_properties().parameters.gpos_period_start(); } } } From 2d6dec5943761594efb73627ebb27e29852d3ae4 Mon Sep 17 00:00:00 2001 From: timur <12267899-timur.5@users.noreply.gitlab.com> Date: Tue, 27 Sep 2022 12:25:33 +0000 Subject: [PATCH 050/106] Update debug_witness plugin --- libraries/app/application.cpp | 2 +- .../include/graphene/chain/protocol/types.hpp | 7 + .../plugins/debug_witness/CMakeLists.txt | 1 + libraries/plugins/debug_witness/debug_api.cpp | 4 +- .../plugins/debug_witness/debug_witness.cpp | 14 +- .../graphene/debug_witness/debug_witness.hpp | 14 +- libraries/wallet/wallet.cpp | 2 +- programs/CMakeLists.txt | 1 - programs/debug_node/CMakeLists.txt | 21 -- programs/debug_node/README.md | 104 ------ programs/debug_node/main.cpp | 307 ------------------ 11 files changed, 31 insertions(+), 446 deletions(-) delete mode 100644 programs/debug_node/CMakeLists.txt delete mode 100644 programs/debug_node/README.md delete mode 100644 programs/debug_node/main.cpp diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index db9ef028..4f5f97c3 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -917,7 +917,7 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti wanted.insert("accounts_list"); wanted.insert("affiliate_stats"); } - if (!wanted.count("delayed_node") && !wanted.count("witness")) // explicitly requested delayed_node functionality suppresses witness functions + if (!wanted.count("delayed_node") && !wanted.count("debug_witness") && !wanted.count("witness")) // explicitly requested delayed_node or debug_witness functionality suppresses witness functions wanted.insert("witness"); wanted.insert("bookie"); diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 098e5dd1..33c7df66 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -395,6 +395,13 @@ namespace graphene { namespace chain { bool is_valid_muse( const std::string& base58str ); }; + class pubkey_comparator { + public: + inline bool operator()(const public_key_type& a, const public_key_type& b) const { + return a.key_data < b.key_data; + } + }; + struct extended_public_key_type { struct binary_key diff --git a/libraries/plugins/debug_witness/CMakeLists.txt b/libraries/plugins/debug_witness/CMakeLists.txt index ac7d7fc9..b9582548 100644 --- a/libraries/plugins/debug_witness/CMakeLists.txt +++ b/libraries/plugins/debug_witness/CMakeLists.txt @@ -16,3 +16,4 @@ install( TARGETS LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/debug_witness" ) diff --git a/libraries/plugins/debug_witness/debug_api.cpp b/libraries/plugins/debug_witness/debug_api.cpp index 43ffd6cd..7fd8792a 100644 --- a/libraries/plugins/debug_witness/debug_api.cpp +++ b/libraries/plugins/debug_witness/debug_api.cpp @@ -34,7 +34,9 @@ class debug_api_impl }; debug_api_impl::debug_api_impl( graphene::app::application& _app ) : app( _app ) -{} +{ + // Nothing else to do +} void debug_api_impl::debug_push_blocks( const std::string& src_filename, uint32_t count ) diff --git a/libraries/plugins/debug_witness/debug_witness.cpp b/libraries/plugins/debug_witness/debug_witness.cpp index 7268006d..1a953316 100644 --- a/libraries/plugins/debug_witness/debug_witness.cpp +++ b/libraries/plugins/debug_witness/debug_witness.cpp @@ -38,7 +38,10 @@ using std::vector; namespace bpo = boost::program_options; -debug_witness_plugin::~debug_witness_plugin() {} +debug_witness_plugin::~debug_witness_plugin() +{ + cleanup(); +} void debug_witness_plugin::plugin_set_program_options( boost::program_options::options_description& command_line_options, @@ -62,7 +65,7 @@ void debug_witness_plugin::plugin_initialize(const boost::program_options::varia ilog("debug_witness plugin: plugin_initialize() begin"); _options = &options; - if( options.count("debug-private-key") ) + if( options.count("debug-private-key") > 0 ) { const std::vector key_id_to_wif_pair_strings = options["debug-private-key"].as>(); for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings) @@ -100,7 +103,6 @@ void debug_witness_plugin::plugin_startup() _changed_objects_conn = db.changed_objects.connect([this](const std::vector& ids, const fc::flat_set& impacted_accounts){ on_changed_objects(ids, impacted_accounts); }); _removed_objects_conn = db.removed_objects.connect([this](const std::vector& ids, const std::vector& objs, const fc::flat_set& impacted_accounts){ on_removed_objects(ids, objs, impacted_accounts); }); - return; } void debug_witness_plugin::on_changed_objects( const std::vector& ids, const fc::flat_set& impacted_accounts ) @@ -155,11 +157,15 @@ void debug_witness_plugin::flush_json_object_stream() } void debug_witness_plugin::plugin_shutdown() +{ + cleanup(); +} + +void debug_witness_plugin::cleanup() { if( _json_object_stream ) { _json_object_stream->close(); _json_object_stream.reset(); } - return; } diff --git a/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp b/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp index 907d26ae..8b7cfd08 100644 --- a/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp +++ b/libraries/plugins/debug_witness/include/graphene/debug_witness/debug_witness.hpp @@ -34,23 +34,25 @@ namespace graphene { namespace debug_witness_plugin { class debug_witness_plugin : public graphene::app::plugin { public: - ~debug_witness_plugin(); + using graphene::app::plugin::plugin; + ~debug_witness_plugin() override; std::string plugin_name()const override; - virtual void plugin_set_program_options( + void plugin_set_program_options( boost::program_options::options_description &command_line_options, boost::program_options::options_description &config_file_options ) override; - virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; - virtual void plugin_startup() override; - virtual void plugin_shutdown() override; + void plugin_initialize( const boost::program_options::variables_map& options ) override; + void plugin_startup() override; + void plugin_shutdown() override; void set_json_object_stream( const std::string& filename ); void flush_json_object_stream(); private: + void cleanup(); void on_changed_objects( const std::vector& ids, const fc::flat_set& impacted_accounts ); void on_removed_objects( const std::vector& ids, const std::vector objs, const fc::flat_set& impacted_accounts ); @@ -58,7 +60,7 @@ private: boost::program_options::variables_map _options; - std::map _private_keys; + std::map _private_keys; std::shared_ptr< std::ofstream > _json_object_stream; boost::signals2::scoped_connection _applied_block_conn; diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 628c8866..10513586 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -4114,7 +4114,7 @@ public: "to access the debug API on the node you are connecting to.\n" "\n" "To fix this problem:\n" - "- Please ensure you are running debug_node, not witness_node.\n" + "- Please ensure you enable debug_witness plugin in witness_node.\n" "- Please follow the instructions in README.md to set up an apiaccess file.\n" "\n"; } diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index 7b9b9918..0c3bb135 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -3,7 +3,6 @@ if( BUILD_PEERPLAYS_PROGRAMS ) add_subdirectory( cli_wallet ) add_subdirectory( genesis_util ) add_subdirectory( witness_node ) - add_subdirectory( debug_node ) add_subdirectory( js_operation_serializer ) add_subdirectory( size_checker ) endif( BUILD_PEERPLAYS_PROGRAMS ) diff --git a/programs/debug_node/CMakeLists.txt b/programs/debug_node/CMakeLists.txt deleted file mode 100644 index e39ea929..00000000 --- a/programs/debug_node/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -add_executable( debug_node main.cpp ) -if( UNIX AND NOT APPLE ) - set(rt_library rt ) -endif() - -find_package( Gperftools QUIET ) -if( GPERFTOOLS_FOUND ) - message( STATUS "Found gperftools; compiling debug_node with TCMalloc") - list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -endif() - -target_link_libraries( debug_node - PRIVATE graphene_app graphene_egenesis_full ${PLATFORM_SPECIFIC_LIBS} ) - -install( TARGETS - debug_node - - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib -) diff --git a/programs/debug_node/README.md b/programs/debug_node/README.md deleted file mode 100644 index 53d56733..00000000 --- a/programs/debug_node/README.md +++ /dev/null @@ -1,104 +0,0 @@ - -Introduction ------------- - -The `debug_node` is a tool to allow developers to run many interesting sorts of "what-if" tests using state from a production blockchain. -Like "what happens if I produce enough blocks for the next hardfork time to arrive?" or "what would happen if this account (which I don't have a private key for) did this transaction?" - -Setup ------ - -Be sure you've built the right build targets: - - $ make get_dev_key debug_node cli_wallet witness_node - -Use the `get_dev_key` utility to generate a keypair: - - $ programs/genesis_util/get_dev_key "" nathan - [{"private_key":"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3","public_key":"BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","address":"BTSFAbAx7yuxt725qSZvfwWqkdCwp9ZnUama"}] - -Obtain a copy of the blockchain in `block_db` directory: - $ programs/witness_node/witness_node --data-dir data/mydatadir - # ... wait for chain to sync - ^C - $ cp -Rp data/mydatadir/blockchain/database/block_num_to_block ./block_db - -Set up a new datadir with the following `config.ini` settings: - - # setup API endpoint - rpc-endpoint = 127.0.0.1:8090 - # setting this to empty effectively disables the p2p network - seed-nodes = [] - # set apiaccess.json so we can set up - api-access = "data/debug_datadir/api-access.json" - -Then set up `data/debug_datadir/api-access.json` to allow access to the debug API like this: - - { - "permission_map" : - [ - [ - "bytemaster", - { - "password_hash_b64" : "9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=", - "password_salt_b64" : "INDdM6iCi/8=", - "allowed_apis" : ["database_api", "network_broadcast_api", "history_api", "network_node_api", "debug_api"] - } - ], - [ - "*", - { - "password_hash_b64" : "*", - "password_salt_b64" : "*", - "allowed_apis" : ["database_api", "network_broadcast_api", "history_api"] - } - ] - ] - } - -See [here](https://github.com/cryptonomex/graphene#accessing-restricted-apis) for more detail on the `api-access.json` format. - -Once that is set up, run `debug_node` against your newly prepared datadir: - - programs/debug_node/debug_node --data-dir data/debug_datadir - -Run `cli_wallet` to connect to the `debug_node` port, using the username and password to access the new `debug_api` (and also a different wallet file): - - programs/cli_wallet/cli_wallet -s 127.0.0.1:8090 -w debug.wallet -u bytemaster -p supersecret - -Example usage -------------- - -Load some blocks from the datadir: - - dbg_push_blocks block_db 20000 - -Note, when pushing a very large number of blocks sometimes `cli_wallet` hangs and you must Ctrl+C and restart it (leaving the `debug_node` running). - -Generate (fake) blocks with our own private key: - - dbg_generate_blocks 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 1000 - -Update `angel` account to be controlled by our own private key and generate a (fake) transfer: - - dbg_update_object {"_action":"update", "id":"1.2.1090", "active":{"weight_threshold":1,"key_auths":[["BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",1]]}} - import_key angel 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 - transfer angel init0 999999 BTS "" true - -How it works ------------- - -The commands work by creating diff(s) from the main chain that are applied to the local chain at specified block height(s). It lets you easily check out "what-if" -scenarios in a fantasy debug toy world forked from the real chain, e.g. "if we take all of the blocks until today, then generate a bunch more until a hardfork time -in the future arrives, does the chain stay up? Can I do transactions X, Y, and Z in the wallet after the hardfork?" Anyone connecting to this node sees the same -fantasy world, so you can e.g. make changes with the `cli_wallet` and see them exist in other `cli_wallet` instances (or GUI wallets or API scripts). - -Limitations ------------ - -The main limitations are: - -- No export format for the diffs, so you can't really [1] connect multiple `debug_node` to each other. -- Once faked block(s) or tx(s) have been produced on your chain, you can't really [1] stream blocks or tx's from the main network to your chain. - -[1] It should theoretically be possible, but it's non-trivial and totally untested. diff --git a/programs/debug_node/main.cpp b/programs/debug_node/main.cpp deleted file mode 100644 index c94d3fd2..00000000 --- a/programs/debug_node/main.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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 - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#ifdef WIN32 -# include -#else -# include -#endif - -using namespace graphene; -namespace bpo = boost::program_options; - -void write_default_logging_config_to_stream(std::ostream& out); -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename); - -int main(int argc, char** argv) { - app::application* node = new app::application(); - fc::oexception unhandled_exception; - try { - bpo::options_description app_options("Graphene Witness Node"); - bpo::options_description cfg_options("Graphene Witness Node"); - app_options.add_options() - ("help,h", "Print this help message and exit.") - ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.") - ; - - bpo::variables_map options; - - auto witness_plug = node->register_plugin(); - auto history_plug = node->register_plugin(); - auto market_history_plug = node->register_plugin(); - - try - { - bpo::options_description cli, cfg; - node->set_program_options(cli, cfg); - app_options.add(cli); - cfg_options.add(cfg); - bpo::store(bpo::parse_command_line(argc, argv, app_options), options); - } - catch (const boost::program_options::error& e) - { - std::cerr << "Error parsing command line: " << e.what() << "\n"; - return 1; - } - - if( options.count("help") ) - { - std::cout << app_options << "\n"; - return 0; - } - - fc::path data_dir; - if( options.count("data-dir") ) - { - data_dir = options["data-dir"].as(); - if( data_dir.is_relative() ) - data_dir = fc::current_path() / data_dir; - } - - fc::path config_ini_path = data_dir / "config.ini"; - if( fc::exists(config_ini_path) ) - { - // get the basic options - bpo::store(bpo::parse_config_file(config_ini_path.preferred_string().c_str(), cfg_options, true), options); - - // try to get logging options from the config file. - try - { - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } - catch (const fc::exception&) - { - wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string())); - } - } - else - { - ilog("Writing new config file at ${path}", ("path", config_ini_path)); - if( !fc::exists(data_dir) ) - fc::create_directories(data_dir); - - std::ofstream out_cfg(config_ini_path.preferred_string()); - for( const boost::shared_ptr od : cfg_options.options() ) - { - if( !od->description().empty() ) - out_cfg << "# " << od->description() << "\n"; - boost::any store; - if( !od->semantic()->apply_default(store) ) - out_cfg << "# " << od->long_name() << " = \n"; - else { - auto example = od->format_parameter(); - if( example.empty() ) - // This is a boolean switch - out_cfg << od->long_name() << " = " << "false\n"; - else { - // The string is formatted "arg (=)" - example.erase(0, 6); - example.erase(example.length()-1); - out_cfg << od->long_name() << " = " << example << "\n"; - } - } - out_cfg << "\n"; - } - write_default_logging_config_to_stream(out_cfg); - out_cfg.close(); - // read the default logging config we just wrote out to the file and start using it - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } - - bpo::notify(options); - node->initialize(data_dir, options); - node->initialize_plugins( options ); - - node->startup(); - node->startup_plugins(); - - fc::promise::ptr exit_promise = new fc::promise("UNIX Signal Handler"); - - fc::set_signal_handler([&exit_promise](int signal) { - elog( "Caught SIGINT attempting to exit cleanly" ); - exit_promise->set_value(signal); - }, SIGINT); - - fc::set_signal_handler([&exit_promise](int signal) { - elog( "Caught SIGTERM attempting to exit cleanly" ); - exit_promise->set_value(signal); - }, SIGTERM); - - ilog("Started witness node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num())); - ilog("Chain ID is ${id}", ("id", node->chain_database()->get_chain_id()) ); - - int signal = exit_promise->wait(); - ilog("Exiting from signal ${n}", ("n", signal)); - node->shutdown_plugins(); - node->shutdown(); - delete node; - return 0; - } catch( const fc::exception& e ) { - // deleting the node can yield, so do this outside the exception handler - unhandled_exception = e; - } - - if (unhandled_exception) - { - elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string())); - node->shutdown(); - delete node; - return 1; - } -} - -// logging config is too complicated to be parsed by boost::program_options, -// so we do it by hand -// -// Currently, you can only specify the filenames and logging levels, which -// are all most users would want to change. At a later time, options can -// be added to control rotation intervals, compression, and other seldom- -// used features -void write_default_logging_config_to_stream(std::ostream& out) -{ - out << "# declare an appender named \"stderr\" that writes messages to the console\n" - "[log.console_appender.stderr]\n" - "stream=std_error\n\n" - "# declare an appender named \"p2p\" that writes messages to p2p.log\n" - "[log.file_appender.p2p]\n" - "filename=logs/p2p/p2p.log\n" - "# filename can be absolute or relative to this config file\n\n" - "# route any messages logged to the default logger to the \"stderr\" logger we\n" - "# declared above, if they are info level are higher\n" - "[logger.default]\n" - "level=info\n" - "appenders=stderr\n\n" - "# route messages sent to the \"p2p\" logger to the p2p appender declared above\n" - "[logger.p2p]\n" - "level=info\n" - "appenders=p2p\n\n"; -} - -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename) -{ - try - { - fc::logging_config logging_config; - bool found_logging_config = false; - - boost::property_tree::ptree config_ini_tree; - boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree); - for (const auto& section : config_ini_tree) - { - const std::string& section_name = section.first; - const boost::property_tree::ptree& section_tree = section.second; - - const std::string console_appender_section_prefix = "log.console_appender."; - const std::string file_appender_section_prefix = "log.file_appender."; - const std::string logger_section_prefix = "logger."; - - if (boost::starts_with(section_name, console_appender_section_prefix)) - { - std::string console_appender_name = section_name.substr(console_appender_section_prefix.length()); - std::string stream_name = section_tree.get("stream"); - - // construct a default console appender config here - // stdout/stderr will be taken from ini file, everything else hard-coded here - fc::console_appender::config console_appender_config; - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::debug, - fc::console_appender::color::green)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::warn, - fc::console_appender::color::brown)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::error, - fc::console_appender::color::cyan)); - console_appender_config.stream = fc::variant(stream_name, 1).as(1); - logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config, 20))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, file_appender_section_prefix)) - { - std::string file_appender_name = section_name.substr(file_appender_section_prefix.length()); - fc::path file_name = section_tree.get("filename"); - if (file_name.is_relative()) - file_name = fc::absolute(config_ini_filename).parent_path() / file_name; - - - // construct a default file appender config here - // filename will be taken from ini file, everything else hard-coded here - fc::file_appender::config file_appender_config; - file_appender_config.filename = file_name; - file_appender_config.flush = true; - file_appender_config.rotate = true; - file_appender_config.rotation_interval = fc::hours(1); - file_appender_config.rotation_limit = fc::days(1); - logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config, 20))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, logger_section_prefix)) - { - std::string logger_name = section_name.substr(logger_section_prefix.length()); - std::string level_string = section_tree.get("level"); - std::string appenders_string = section_tree.get("appenders"); - fc::logger_config logger_config(logger_name); - logger_config.level = fc::variant(level_string, 1).as(1); - boost::split(logger_config.appenders, appenders_string, - boost::is_any_of(" ,"), - boost::token_compress_on); - logging_config.loggers.push_back(logger_config); - found_logging_config = true; - } - } - if (found_logging_config) - return logging_config; - else - return fc::optional(); - } - FC_RETHROW_EXCEPTIONS(warn, "") -} From 2c95ac0b9df15d654f2e493fc42608b07db43d44 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 27 Sep 2022 13:58:07 +0000 Subject: [PATCH 051/106] bug/455-sidechain-enabled --- .../peerplays_sidechain_plugin.cpp | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index e2a8d9b8..b52f9906 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -88,6 +88,7 @@ private: std::mutex access_approve_prop_mutex; std::mutex access_son_down_prop_mutex; + std::map sidechain_enabled; std::map> net_handlers; std::set sons; std::map private_keys; @@ -118,6 +119,13 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec } return current_son_id; }()), + sidechain_enabled([] { + std::map sidechain_enabled; + for (const auto &active_sidechain_type : active_sidechain_types) { + sidechain_enabled.emplace(active_sidechain_type, false); + } + return sidechain_enabled; + }()), net_handlers([] { std::map> net_handlers; for (const auto &active_sidechain_type : active_sidechain_types) { @@ -288,21 +296,25 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { sidechain_net_handler_factory net_handler_factory(plugin); if (sidechain_enabled_bitcoin && config_ready_bitcoin) { + sidechain_enabled.at(sidechain_type::bitcoin) = true; net_handlers.at(sidechain_type::bitcoin) = net_handler_factory.create_handler(sidechain_type::bitcoin, options); ilog("Bitcoin sidechain handler running"); } if (sidechain_enabled_ethereum && config_ready_ethereum) { + sidechain_enabled.at(sidechain_type::ethereum) = true; net_handlers.at(sidechain_type::ethereum) = net_handler_factory.create_handler(sidechain_type::ethereum, options); ilog("Ethereum sidechain handler running"); } if (sidechain_enabled_hive && config_ready_hive) { + sidechain_enabled.at(sidechain_type::hive) = true; net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options); ilog("Hive sidechain handler running"); } if (sidechain_enabled_peerplays && config_ready_peerplays) { + sidechain_enabled.at(sidechain_type::peerplays) = true; net_handlers.at(sidechain_type::peerplays) = net_handler_factory.create_handler(sidechain_type::peerplays, options); ilog("Peerplays sidechain handler running"); } @@ -451,8 +463,10 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { //! Check that son is active (at least for one sidechain_type) bool is_son_active = false; for (const auto &active_sidechain_type : active_sidechain_types) { - if (is_active_son(active_sidechain_type, son_id)) - is_son_active = true; + if(sidechain_enabled.at(active_sidechain_type)) { + if (is_active_son(active_sidechain_type, son_id)) + is_son_active = true; + } } if (is_son_active || status_in_maintenance) { @@ -488,8 +502,10 @@ void peerplays_sidechain_plugin_impl::schedule_son_processing() { for (const auto &active_sidechain_type : active_sidechain_types) { _son_processing_task[active_sidechain_type] = std::async(std::launch::async, [this, next_wakeup, active_sidechain_type] { - std::this_thread::sleep_until(next_wakeup); - son_processing(active_sidechain_type); + if(sidechain_enabled.at(active_sidechain_type)) { + std::this_thread::sleep_until(next_wakeup); + son_processing(active_sidechain_type); + } }); } } @@ -597,7 +613,9 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidecha std::map> peerplays_sidechain_plugin_impl::get_son_listener_log() { std::map> result; for (const auto &active_sidechain_type : active_sidechain_types) { - result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log()); + if(net_handlers.at(active_sidechain_type)) { + result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log()); + } } return result; } @@ -760,35 +778,51 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_ } void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) { - net_handlers.at(sidechain)->process_proposals(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->process_proposals(); + } } void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) { - net_handlers.at(sidechain)->process_active_sons_change(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->process_active_sons_change(); + } } void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) { - net_handlers.at(sidechain)->create_deposit_addresses(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->create_deposit_addresses(); + } } void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) { - net_handlers.at(sidechain)->process_deposits(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->process_deposits(); + } } void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) { - net_handlers.at(sidechain)->process_withdrawals(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->process_withdrawals(); + } } void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) { - net_handlers.at(sidechain)->process_sidechain_transactions(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->process_sidechain_transactions(); + } } void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) { - net_handlers.at(sidechain)->send_sidechain_transactions(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->send_sidechain_transactions(); + } } void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) { - net_handlers.at(sidechain)->settle_sidechain_transactions(); + if(net_handlers.at(sidechain)) { + net_handlers.at(sidechain)->settle_sidechain_transactions(); + } } void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { From e6474f5f2a923dec19276e8b1591754c5404f135 Mon Sep 17 00:00:00 2001 From: timur <12267899-timur.5@users.noreply.gitlab.com> Date: Sat, 1 Oct 2022 19:11:54 -0300 Subject: [PATCH 052/106] Fix API docs generation for map<> and flat_map<> --- libraries/wallet/generate_api_documentation.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/wallet/generate_api_documentation.pl b/libraries/wallet/generate_api_documentation.pl index 3e1b2e06..c1a8bb40 100755 --- a/libraries/wallet/generate_api_documentation.pl +++ b/libraries/wallet/generate_api_documentation.pl @@ -130,8 +130,9 @@ sub explainCType $type =~ s/\s+$/$1/; $type =~ s/\b(u?int(8|16|32|64)_t|int|unsigned)\b/integer/; # spare the user from width and signedness $type =~ s/\bbool\b/boolean/; # they're not C++ people - $type =~ s/^(?:vector|set|flat_set)<(.+)>$/[$1, ...]/; # represent as JSon-like array notation - $type =~ s/^(?:map|flat_map)<(.+)\s*,\s*(.+)>$/{$1 => $2, ...}/; # same for map + $type =~ s/^(?:vector|set|flat_set)<(.+)>$/[$1, ...]/; # represent as JSon-like array notation + $type =~ s/^map<(.+)\s*,\s*(.+)>$/{$1: $2, ...}/; # same for map + $type =~ s/^flat_map<(.+)\s*,\s*(.+)>$/[[$1, $2], ...]/; # same for flat_map $type =~ s/^time_point_sec$/time, e.g. 2021-12-25T14:30:05/; return $type; } From d4c015d4006fb5f60da64037ad0e6b8f5a8e09a3 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Mon, 3 Oct 2022 17:04:56 +0000 Subject: [PATCH 053/106] bug/457-multithread-son-processing --- libraries/chain/db_block.cpp | 101 +++++++++++------- libraries/chain/db_init.cpp | 6 +- libraries/chain/db_management.cpp | 1 + .../chain/include/graphene/chain/database.hpp | 2 + .../include/graphene/db/object_database.hpp | 2 + .../account_history_plugin.cpp | 1 + .../elasticsearch/elasticsearch_plugin.cpp | 1 + .../peerplays_sidechain_plugin.cpp | 33 +++--- 8 files changed, 97 insertions(+), 50 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 65f10cc7..d49f1dc5 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -162,10 +162,13 @@ void database::check_transaction_for_duplicated_operations(const signed_transact existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() ); }); - for (auto& pending_transaction: _pending_tx) { - auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction); - existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end()); + const std::lock_guard pending_tx_lock{_pending_tx_mutex}; + for (auto &pending_transaction : _pending_tx) + { + auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction); + existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end()); + } } auto proposed_operations_digests = gather_proposed_operations_digests(trx); @@ -187,7 +190,12 @@ bool database::push_block(const signed_block& new_block, uint32_t skip) bool result; detail::with_skip_flags( *this, skip, [&]() { - detail::without_pending_transactions( *this, std::move(_pending_tx), + std::vector pending_tx = [this] { + const std::lock_guard pending_tx_lock{_pending_tx_mutex}; + return std::move(_pending_tx); + }(); + + detail::without_pending_transactions( *this, std::move(pending_tx), [&]() { result = _push_block(new_block); @@ -387,17 +395,26 @@ processed_transaction database::_push_transaction( const signed_transaction& trx { // If this is the first transaction pushed after applying a block, start a new undo session. // This allows us to quickly rewind to the clean state of the head block, in case a new block arrives. - if( !_pending_tx_session.valid() ) - _pending_tx_session = _undo_db.start_undo_session(); + { + const std::lock_guard pending_tx_session_lock{_pending_tx_session_mutex}; + if (!_pending_tx_session.valid()) { + const std::lock_guard undo_db_lock{_undo_db_mutex}; + _pending_tx_session = _undo_db.start_undo_session(); + } + } // Create a temporary undo session as a child of _pending_tx_session. // The temporary session will be discarded by the destructor if // _apply_transaction fails. If we make it to merge(), we // apply the changes. + const std::lock_guard undo_db_lock{_undo_db_mutex}; auto temp_session = _undo_db.start_undo_session(); - auto processed_trx = _apply_transaction( trx ); - _pending_tx.push_back(processed_trx); + auto processed_trx = _apply_transaction(trx); + { + const std::lock_guard pending_tx_lock{_pending_tx_mutex}; + _pending_tx.push_back(processed_trx); + } // notify_changed_objects(); // The transaction applied successfully. Merge its changes into the pending block session. @@ -410,6 +427,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx processed_transaction database::validate_transaction( const signed_transaction& trx ) { + const std::lock_guard undo_db_lock{_undo_db_mutex}; auto session = _undo_db.start_undo_session(); return _apply_transaction( trx ); } @@ -509,47 +527,52 @@ signed_block database::_generate_block( // the value of the "when" variable is known, which means we need to // re-apply pending transactions in this method. // - _pending_tx_session.reset(); - _pending_tx_session = _undo_db.start_undo_session(); + { + const std::lock_guard pending_tx_session_lock{_pending_tx_session_mutex}; + _pending_tx_session.reset(); + _pending_tx_session = _undo_db.start_undo_session(); + } uint64_t postponed_tx_count = 0; // pop pending state (reset to head block state) - for( const processed_transaction& tx : _pending_tx ) { - size_t new_total_size = total_block_size + fc::raw::pack_size( tx ); + const std::lock_guard pending_tx_lock{_pending_tx_mutex}; + for (const processed_transaction &tx : _pending_tx) { + size_t new_total_size = total_block_size + fc::raw::pack_size(tx); - // postpone transaction if it would make block too big - if( new_total_size >= maximum_block_size ) - { - postponed_tx_count++; - continue; - } + // postpone transaction if it would make block too big + if (new_total_size >= maximum_block_size) { + postponed_tx_count++; + continue; + } - try - { - auto temp_session = _undo_db.start_undo_session(); - processed_transaction ptx = _apply_transaction( tx ); - temp_session.merge(); + try { + auto temp_session = _undo_db.start_undo_session(); + processed_transaction ptx = _apply_transaction(tx); + temp_session.merge(); - // We have to recompute pack_size(ptx) because it may be different - // than pack_size(tx) (i.e. if one or more results increased - // their size) - total_block_size += fc::raw::pack_size( ptx ); - pending_block.transactions.push_back( ptx ); - } - catch ( const fc::exception& e ) - { - // Do nothing, transaction will not be re-applied - wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) ); - wlog( "The transaction was ${t}", ("t", tx) ); + // We have to recompute pack_size(ptx) because it may be different + // than pack_size(tx) (i.e. if one or more results increased + // their size) + total_block_size += fc::raw::pack_size(ptx); + pending_block.transactions.push_back(ptx); + } catch (const fc::exception &e) { + // Do nothing, transaction will not be re-applied + wlog("Transaction was not processed while generating block due to ${e}", ("e", e)); + wlog("The transaction was ${t}", ("t", tx)); + } } } + if( postponed_tx_count > 0 ) { wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) ); } - _pending_tx_session.reset(); + { + const std::lock_guard pending_tx_session_lock{_pending_tx_session_mutex}; + _pending_tx_session.reset(); + } // We have temporarily broken the invariant that // _pending_tx_session is the result of applying _pending_tx, as @@ -597,7 +620,11 @@ signed_block database::_generate_block( */ void database::pop_block() { try { - _pending_tx_session.reset(); + { + const std::lock_guard pending_tx_session_lock{_pending_tx_session_mutex}; + _pending_tx_session.reset(); + } + auto head_id = head_block_id(); optional head_block = fetch_block_by_id( head_id ); GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" ); @@ -611,6 +638,8 @@ void database::pop_block() void database::clear_pending() { try { + const std::lock_guard pending_tx_lock{_pending_tx_mutex}; + const std::lock_guard pending_tx_session_lock{_pending_tx_session_mutex}; assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() ); _pending_tx.clear(); _pending_tx_session.reset(); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index e9f3b9f5..82d7fde1 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -374,7 +374,9 @@ void database::initialize_hardforks() void database::initialize_indexes() { reset_indexes(); - _undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY ); + + const std::lock_guard undo_db_lock{_undo_db_mutex}; + _undo_db.set_max_size(GRAPHENE_MIN_UNDO_HISTORY); //Protocol object indexes add_index< primary_index >(); // 8192 assets per chunk @@ -474,7 +476,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(), "initial_active_witnesses is larger than the number of candidate witnesses."); + const std::lock_guard undo_db_lock{_undo_db_mutex}; _undo_db.disable(); + struct auth_inhibitor { auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags) { db.node_properties().skip_flags |= skip_authority_check; } diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 4a3b519f..dea75bc6 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -112,6 +112,7 @@ void database::reindex( fc::path data_dir ) uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50; ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) ); + const std::lock_guard undo_db_lock{_undo_db_mutex}; auto_undo_enabler undo(_slow_replays, _undo_db); if( head_block_num() >= undo_point ) { diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index eeb25167..2a432732 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -520,6 +520,7 @@ namespace graphene { namespace chain { void notify_changed_objects(); private: + std::mutex _pending_tx_session_mutex; optional _pending_tx_session; vector< unique_ptr > _operation_evaluators; @@ -602,6 +603,7 @@ namespace graphene { namespace chain { ///@} ///@} + std::mutex _pending_tx_mutex; vector< processed_transaction > _pending_tx; fork_database _fork_db; diff --git a/libraries/db/include/graphene/db/object_database.hpp b/libraries/db/include/graphene/db/object_database.hpp index fa2109aa..e76e5a83 100644 --- a/libraries/db/include/graphene/db/object_database.hpp +++ b/libraries/db/include/graphene/db/object_database.hpp @@ -29,6 +29,7 @@ #include #include +#include namespace graphene { namespace db { @@ -144,6 +145,7 @@ namespace graphene { namespace db { fc::path get_data_dir()const { return _data_dir; } /** public for testing purposes only... should be private in practice. */ + mutable std::mutex _undo_db_mutex; undo_database _undo_db; protected: template diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 60ce64f8..a47496f4 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -85,6 +85,7 @@ void account_history_plugin_impl::update_account_histories( const signed_block& vector >& hist = db.get_applied_operations(); bool is_first = true; auto skip_oho_id = [&is_first,&db,this]() { + const std::lock_guard undo_db_lock{db._undo_db_mutex}; if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo { db.remove( db.create( []( operation_history_object& obj) {} ) ); diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp index e306054a..fae54da2 100644 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -127,6 +127,7 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b const vector >& hist = db.get_applied_operations(); bool is_first = true; auto skip_oho_id = [&is_first,&db,this]() { + const std::lock_guard undo_db_lock{db._undo_db_mutex}; if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo { db.remove( db.create( []( operation_history_object& obj) {} ) ); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index b52f9906..27784720 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -87,6 +87,7 @@ private: std::mutex access_db_mutex; std::mutex access_approve_prop_mutex; std::mutex access_son_down_prop_mutex; + std::mutex access_son_deregister_prop_mutex; std::map sidechain_enabled; std::map> net_handlers; @@ -463,7 +464,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { //! Check that son is active (at least for one sidechain_type) bool is_son_active = false; for (const auto &active_sidechain_type : active_sidechain_types) { - if(sidechain_enabled.at(active_sidechain_type)) { + if (sidechain_enabled.at(active_sidechain_type)) { if (is_active_son(active_sidechain_type, son_id)) is_son_active = true; } @@ -501,8 +502,13 @@ void peerplays_sidechain_plugin_impl::schedule_son_processing() { const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing); for (const auto &active_sidechain_type : active_sidechain_types) { + if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).wait_for(std::chrono::seconds{0}) != std::future_status::ready) { + wlog("Son doesn't process in time for sidechain: ${active_sidechain_type}", ("active_sidechain_type", active_sidechain_type)); + _son_processing_task.at(active_sidechain_type).wait(); + } + _son_processing_task[active_sidechain_type] = std::async(std::launch::async, [this, next_wakeup, active_sidechain_type] { - if(sidechain_enabled.at(active_sidechain_type)) { + if (sidechain_enabled.at(active_sidechain_type)) { std::this_thread::sleep_until(next_wakeup); son_processing(active_sidechain_type); } @@ -613,7 +619,7 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidecha std::map> peerplays_sidechain_plugin_impl::get_son_listener_log() { std::map> result; for (const auto &active_sidechain_type : active_sidechain_types) { - if(net_handlers.at(active_sidechain_type)) { + if (net_handlers.at(active_sidechain_type)) { result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log()); } } @@ -626,7 +632,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain // into problem of approving the same propsal since it might happens that previous // approved proposal didn't have time or chance to populate the list of available // active proposals which is consulted here in the code. - std::lock_guard lck(access_approve_prop_mutex); + const std::lock_guard lck{access_approve_prop_mutex}; auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) { if (!is_valid_son_proposal(proposal)) { return; @@ -676,7 +682,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain } void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type sidechain) { - std::lock_guard lck(access_son_down_prop_mutex); + const std::lock_guard lck{access_son_down_prop_mutex}; auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { chain::database &d = plugin.database(); const chain::global_property_object &gpo = d.get_global_properties(); @@ -740,6 +746,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s } void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) { + const std::lock_guard lck{access_son_down_prop_mutex}; chain::database &d = plugin.database(); std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); chain::son_id_type my_son_id = get_current_son_id(sidechain); @@ -778,49 +785,49 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_ } void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->process_proposals(); } } void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->process_active_sons_change(); } } void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->create_deposit_addresses(); } } void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->process_deposits(); } } void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->process_withdrawals(); } } void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->process_sidechain_transactions(); } } void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->send_sidechain_transactions(); } } void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) { - if(net_handlers.at(sidechain)) { + if (net_handlers.at(sidechain)) { net_handlers.at(sidechain)->settle_sidechain_transactions(); } } From 4db9f3a15bfb25ac823a73b50d23dd2e381d87e0 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Mon, 3 Oct 2022 17:34:01 +0000 Subject: [PATCH 054/106] Update cli wallet docs --- .../wallet/include/graphene/wallet/wallet.hpp | 89 ++++++++++++++++++- libraries/wallet/wallet.cpp | 7 +- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 305526c7..f17239a4 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -437,42 +437,90 @@ class wallet_api /** * Get the WIF private key corresponding to a public key. The * private key must already be in the wallet. + * @param pubkey a public key in Base58 format + * @return the WIF private key */ string get_private_key( public_key_type pubkey )const; /** * @ingroup Transaction Builder API + * + * Create a new transaction builder. + * @return handle of the new transaction builder */ transaction_handle_type begin_builder_transaction(); + /** * @ingroup Transaction Builder API + * + * Append a new operation to a transaction builder. + * @param transaction_handle handle of the transaction builder + * @param op the operation in JSON format */ void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op); + /** * @ingroup Transaction Builder API + * + * Replace an operation in a transaction builder with a new operation. + * @param handle handle of the transaction builder + * @param operation_index the index of the old operation in the builder to be replaced + * @param new_op the new operation in JSON format */ void replace_operation_in_builder_transaction(transaction_handle_type handle, unsigned operation_index, const operation& new_op); + /** * @ingroup Transaction Builder API + * + * Calculate and update fees for the operations in a transaction builder. + * @param handle handle of the transaction builder + * @param fee_asset symbol or ID of an asset that to be used to pay fees + * @return total fees */ asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL); + /** * @ingroup Transaction Builder API + * + * Show content of a transaction builder. + * @param handle handle of the transaction builder + * @return a transaction */ transaction preview_builder_transaction(transaction_handle_type handle); + /** * @ingroup Transaction Builder API + * + * Sign the transaction in a transaction builder and optionally broadcast to the network. + * @param transaction_handle handle of the transaction builder + * @param broadcast whether to broadcast the signed transaction to the network + * @return a signed transaction */ signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true); + /** Broadcast signed transaction * @param tx signed transaction * @returns the transaction ID along with the signed transaction. */ pair broadcast_transaction(signed_transaction tx); + /** * @ingroup Transaction Builder API + * + * Create a proposal containing the operations in a transaction builder (create a new proposal_create + * operation, then replace the transaction builder with the new operation), then sign the transaction + * and optionally broadcast to the network. + * + * Note: this command is buggy because unable to specify proposer. It will be deprecated in a future release. + * Please use \c propose_builder_transaction2() instead. + * + * @param handle handle of the transaction builder + * @param expiration when the proposal will expire + * @param review_period_seconds review period of the proposal in seconds + * @param broadcast whether to broadcast the signed transaction to the network + * @return a signed transaction */ signed_transaction propose_builder_transaction( transaction_handle_type handle, @@ -481,6 +529,20 @@ class wallet_api bool broadcast = true ); + /** + * @ingroup Transaction Builder API + * + * Create a proposal containing the operations in a transaction builder (create a new proposal_create + * operation, then replace the transaction builder with the new operation), then sign the transaction + * and optionally broadcast to the network. + * + * @param handle handle of the transaction builder + * @param account_name_or_id name or ID of the account who would pay fees for creating the proposal + * @param expiration when the proposal will expire + * @param review_period_seconds review period of the proposal in seconds + * @param broadcast whether to broadcast the signed transaction to the network + * @return a signed transaction + */ signed_transaction propose_builder_transaction2( transaction_handle_type handle, string account_name_or_id, @@ -491,6 +553,9 @@ class wallet_api /** * @ingroup Transaction Builder API + * + * Destroy a transaction builder. + * @param handle handle of the transaction builder */ void remove_builder_transaction(transaction_handle_type handle); @@ -528,6 +593,11 @@ class wallet_api * * The wallet must be either 'new' or 'unlocked' to * execute this command. + * + * When used in command line, if typed "set_password" without a password followed, the user will be prompted + * to input a password without echo. + * + * @param password a new password * @ingroup Wallet Management */ void set_password(string password); @@ -669,6 +739,10 @@ class wallet_api /** * This call will construct transaction(s) that will claim all balances controled * by wif_keys and deposit them into the given account. + * + * @param account_name_or_id name or ID of an account that to claim balances to + * @param wif_keys private WIF keys of balance objects to claim balances from + * @param broadcast true to broadcast the transaction on the network */ vector< signed_transaction > import_balance( string account_name_or_id, const vector& wif_keys, bool broadcast ); @@ -808,7 +882,16 @@ class wallet_api /** * This method works just like transfer, except it always broadcasts and - * returns the transaction ID along with the signed transaction. + * returns the transaction ID (hash) along with the signed transaction. + * @param from the name or id of the account sending the funds + * @param to the name or id of the account receiving the funds + * @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5) + * @param asset_symbol the symbol or id of the asset to send + * @param memo a memo to attach to the transaction. The memo will be encrypted in the + * transaction and readable for the receiver. There is no length limit + * other than the limit imposed by maximum transaction size, but transaction + * increase with transaction size + * @returns the transaction ID (hash) along with the signed transaction transferring funds */ pair transfer2(string from, string to, @@ -821,7 +904,9 @@ class wallet_api /** - * Convert a JSON transaction to its transactin ID. + * This method is used to convert a JSON transaction to its transactin ID. + * @param trx a JSON transaction + * @return the ID (hash) of the transaction */ transaction_id_type get_transaction_id( const signed_transaction& trx )const { return trx.id(); } diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 10513586..fd7bcc2b 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -5758,25 +5758,24 @@ string wallet_api::gethelp(const string& method)const if( method == "import_key" ) { ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n"; - ss << "example: import_key \"1.3.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; + ss << "example: import_key \"1.2.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; } else if( method == "transfer" ) { ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n"; - ss << "example: transfer \"1.3.11\" \"1.3.4\" 1000.03 CORE \"memo\" true\n"; + ss << "example: transfer \"1.2.11\" \"1.2.4\" 1000.03 CORE \"memo\" true\n"; ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n"; } else if( method == "create_account_with_brain_key" ) { ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n"; - ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.3.11\" \"1.3.11\" true\n"; + ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.2.11\" \"1.2.11\" true\n"; ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n"; ss << "\n"; ss << "This method should be used if you would like the wallet to generate new keys derived from the brain key.\n"; ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY. Use\n"; ss << "register_account if you already know the keys you know the public keys that you would like to register.\n"; - } else if( method == "register_account" ) { From 846366139f0c4afb6db4dcbe1574ff53024a48eb Mon Sep 17 00:00:00 2001 From: Meheboob Khan Date: Tue, 11 Oct 2022 00:13:04 +0000 Subject: [PATCH 055/106] Updated CLI Tests [Issue 436] --- libraries/app/database_api.cpp | 4 +- .../wallet/include/graphene/wallet/wallet.hpp | 8 - libraries/wallet/wallet.cpp | 49 -- tests/cli/cli_fixture.cpp | 6 + tests/cli/cli_fixture.hpp | 1 + tests/cli/son.cpp | 696 +++++++++++++++++- 6 files changed, 670 insertions(+), 94 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 4bf0756a..a385ba47 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1906,10 +1906,10 @@ map database_api_impl::get_son_network_status_by_sidechain( string status; if (sso.last_active_timestamp.find(sidechain) != sso.last_active_timestamp.end()) { - if (sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { + if (time_point_sec(sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_heartbeat_frequency())) > _db.head_block_time()) { status = "OK, regular SON heartbeat"; } else { - if (sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { + if (time_point_sec(sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_down_time())) > _db.head_block_time()) { status = "OK, irregular SON heartbeat, but not triggering SON down proposal"; } else { status = "NOT OK, irregular SON heartbeat, triggering SON down proposal]"; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index f17239a4..cedaa21d 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1493,13 +1493,6 @@ class wallet_api */ map list_sons(const string& lowerbound, uint32_t limit); - /** Lists active at the moment SONs. - * This returns a list of all account names that own active SON, and the associated SON id, - * sorted by name. - * @returns a list of active SONs mapping SON names to SON ids - */ - map list_active_sons(); - /** * @brief Get list of active sons * @return List of active SONs @@ -2741,7 +2734,6 @@ FC_API( graphene::wallet::wallet_api, (update_son_vesting_balances) (activate_deregistered_son) (list_sons) - (list_active_sons) (get_active_sons) (get_active_sons_by_sidechain) (get_son_network_status) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fd7bcc2b..7185b7d3 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2230,50 +2230,6 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account) ) } - //! Fixme - do we need to specify sidechain_type as params here? - map list_active_sons() - { - try - { - const global_property_object& gpo = get_global_properties(); - set son_ids_set; - for(const auto& active_sidechain_type : active_sidechain_types) - { - std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), - std::inserter(son_ids_set, son_ids_set.end()), - [](const son_info &swi) { - return swi.son_id; - }); - } - vector son_ids; - son_ids.reserve(son_ids_set.size()); - for(const auto& son_id : son_ids_set) - { - son_ids.emplace_back(son_id); - } - - std::vector> son_objects = _remote_db->get_sons(son_ids); - vector owners; - for(auto obj: son_objects) - { - std::string acc_id = account_id_to_string(obj->son_account); - owners.push_back(acc_id); - } - vector< optional< account_object> > accs = _remote_db->get_accounts(owners); - std::remove_if(son_objects.begin(), son_objects.end(), - [](const fc::optional& obj) -> bool { return obj.valid(); }); - map result; - std::transform(accs.begin(), accs.end(), son_objects.begin(), - std::inserter(result, result.end()), - [](fc::optional& acct, fc::optional son) { - FC_ASSERT(acct, "Invalid active SONs list in global properties."); - return std::make_pair(string(acct->name), std::move(son->id)); - }); - return result; - } - FC_CAPTURE_AND_RETHROW() - } - flat_map> get_active_sons() { try { return _remote_db->get_active_sons(); @@ -5276,11 +5232,6 @@ map wallet_api::list_sons(const string& lowerbound, uint32_ return my->_remote_db->lookup_son_accounts(lowerbound, limit); } -map wallet_api::list_active_sons() -{ - return my->list_active_sons(); -} - flat_map> wallet_api::get_active_sons() { return my->get_active_sons(); diff --git a/tests/cli/cli_fixture.cpp b/tests/cli/cli_fixture.cpp index 70bdfb7c..46192350 100644 --- a/tests/cli/cli_fixture.cpp +++ b/tests/cli/cli_fixture.cpp @@ -231,6 +231,12 @@ signed_block cli_fixture::generate_block(uint32_t skip, const fc::ecc::private_k return block; } +void cli_fixture::generate_blocks( uint32_t block_count ) +{ + for( uint32_t i = 0; i < block_count; ++i ) + generate_block(); +} + bool cli_fixture::generate_maintenance_block() { try { fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); diff --git a/tests/cli/cli_fixture.hpp b/tests/cli/cli_fixture.hpp index 79004f3a..f0486af8 100644 --- a/tests/cli/cli_fixture.hpp +++ b/tests/cli/cli_fixture.hpp @@ -65,6 +65,7 @@ struct cli_fixture int miss_blocks = 0); void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0); + void generate_blocks( uint32_t block_count ); /////////// /// @brief Skip intermediate blocks, and generate a maintenance block diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 0e5d67cf..7858664c 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -863,9 +863,9 @@ BOOST_AUTO_TEST_CASE( related_functions ) BOOST_TEST_MESSAGE("SON-related functions cli wallet tests end"); } -BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) +BOOST_FIXTURE_TEST_CASE( get_active_sons, cli_fixture ) { - BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons begin"); + BOOST_TEST_MESSAGE("SON cli wallet tests for get_active_sons begin"); try { son_test_helper sth(*this); @@ -874,12 +874,16 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) global_property_object gpo; gpo = con.wallet_api_ptr->get_global_properties(); - //! Set son number as 5 (as the begining son count) - unsigned int son_number = 5; + unsigned int son_number = 15; //gpo.parameters.maximum_son_count(); flat_map sidechain_public_keys; - + BOOST_TEST_MESSAGE("Verify that there are no sons"); + BOOST_CHECK(0 == gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_CHECK(0 == gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_CHECK(0 == gpo.active_sons.at(sidechain_type::ethereum).size()); + auto gpo_active_sons = gpo.active_sons; // create son accounts + BOOST_TEST_MESSAGE("Create son accounts"); for(unsigned int i = 1; i < son_number + 1; i++) { sidechain_public_keys.clear(); @@ -895,50 +899,674 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); } BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); - BOOST_TEST_MESSAGE("Voting for SONs"); - for(unsigned int i = 1; i < son_number + 1; i++) - { - std::string name = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); - vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); - vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::ethereum, true, true); - } - BOOST_CHECK(generate_maintenance_block()); + gpo_active_sons = gpo.active_sons; + auto cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); - for(unsigned int i = 1; i < son_number; i++) + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 5; i < son_number - 1; i++) { - std::string name1 = "sonaccount" + fc::to_pretty_string(i); - std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); + std::string name = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); - BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); - BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); - BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); - BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); - BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == son_number); + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); - map active_sons = con.wallet_api_ptr->list_active_sons(); - BOOST_CHECK(active_sons.size() == son_number); - for(unsigned int i = 1; i < son_number + 1; i++) + BOOST_TEST_MESSAGE("Unvoting for Specific SON"); + + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount5", "sonaccount6", sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount5", "sonaccount6", sidechain_type::hive, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount5", "sonaccount6", sidechain_type::ethereum, false, true); + + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + BOOST_TEST_MESSAGE("Unvoting for Specific SON in Specific Sidechain"); + + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount6", "sonaccount7", sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount7", "sonaccount8", sidechain_type::hive, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount8", "sonaccount9", sidechain_type::ethereum, false, true); + + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + BOOST_TEST_MESSAGE("Unvoting for all SONs"); + + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount6", "sonaccount7", sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount7", "sonaccount8", sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount8", "sonaccount9", sidechain_type::ethereum, true, true); + BOOST_CHECK(generate_maintenance_block()); + + for(unsigned int i = 6; i < son_number - 1; i++) { - std::string name = "sonaccount" + fc::to_pretty_string(i); - BOOST_CHECK(active_sons.find(name) != active_sons.end()); + std::string name = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, false, true); } + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + BOOST_CHECK_NO_THROW(con.wallet_api_ptr->get_active_sons()); - BOOST_CHECK_NO_THROW(con.wallet_api_ptr->list_active_sons()); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); edump((e.to_detail_string())); throw; } - BOOST_TEST_MESSAGE("SON cli wallet tests for list_active_sons end"); + BOOST_TEST_MESSAGE("SON cli wallet tests for get_active_sons end"); +} + +BOOST_FIXTURE_TEST_CASE( get_active_sons_by_sidechain, cli_fixture ) +{ + BOOST_TEST_MESSAGE("SON cli wallet tests for get_active_sons_by_sidechain begin"); + try + { + son_test_helper sth(*this); + + signed_transaction vote_tx; + global_property_object gpo; + + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = 15; + + flat_map sidechain_public_keys; + BOOST_TEST_MESSAGE("Verify that there are no sons"); + BOOST_CHECK(0 == gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_CHECK(0 == gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_CHECK(0 == gpo.active_sons.at(sidechain_type::ethereum).size()); + + auto gpo_active_sons = gpo.active_sons; + // create son accounts + BOOST_TEST_MESSAGE("Create son accounts"); + for(unsigned int i = 1; i < son_number + 1; i++) + { + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + } + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + auto cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + auto cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::bitcoin)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::bitcoin) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::ethereum)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::ethereum) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::hive)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::hive) == cmd_active_sons2); + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 5; i < son_number - 1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::bitcoin)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::bitcoin) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::ethereum)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::ethereum) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::hive)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::hive) == cmd_active_sons2); + + BOOST_TEST_MESSAGE("Unvoting for Specific SON"); + + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount5", "sonaccount6", sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount5", "sonaccount6", sidechain_type::hive, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount5", "sonaccount6", sidechain_type::ethereum, false, true); + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::bitcoin)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::bitcoin) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::ethereum)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::ethereum) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::hive)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::hive) == cmd_active_sons2); + + BOOST_TEST_MESSAGE("Unvoting for Specific SON in Specific Sidechain"); + + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount6", "sonaccount7", sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount7", "sonaccount8", sidechain_type::hive, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount8", "sonaccount9", sidechain_type::ethereum, false, true); + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::bitcoin)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::bitcoin) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::ethereum)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::ethereum) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::hive)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::hive) == cmd_active_sons2); + + BOOST_TEST_MESSAGE("Unvoting for all SONs"); + + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount6", "sonaccount7", sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount7", "sonaccount8", sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son("sonaccount8", "sonaccount9", sidechain_type::ethereum, true, true); + BOOST_CHECK(generate_maintenance_block()); + + for(unsigned int i = 6; i < son_number - 1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); + if(i == 6) + { + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, false, true); + } + else if(i == 7) + { + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, false, true); + } + else if(i == 8) + { + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, false, true); + } + else{ + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, false, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, false, true); + } + } + + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + BOOST_CHECK(gpo.active_sons != gpo_active_sons); + + gpo_active_sons = gpo.active_sons; + cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::bitcoin)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::bitcoin) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::ethereum)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::ethereum) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::hive)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::hive) == cmd_active_sons2); + + BOOST_CHECK_NO_THROW(con.wallet_api_ptr->get_active_sons_by_sidechain(sidechain_type::bitcoin)); + BOOST_CHECK_NO_THROW(con.wallet_api_ptr->get_active_sons_by_sidechain(sidechain_type::hive)); + BOOST_CHECK_NO_THROW(con.wallet_api_ptr->get_active_sons_by_sidechain(sidechain_type::ethereum)); + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON cli wallet tests for get_active_sons_by_sidechain end"); +} + +BOOST_FIXTURE_TEST_CASE( get_son_network_status, cli_fixture ) +{ + BOOST_TEST_MESSAGE("SON get_son_network_status cli wallet tests begin"); + try + { + son_test_helper sth(*this); + + auto db = app1->chain_database(); + signed_transaction vote_tx; + + global_property_object gpo; + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = gpo.parameters.maximum_son_count(); + BOOST_TEST_MESSAGE("son_number"< sidechain_public_keys; + + // create son accounts + for(unsigned int i = 1; i < son_number + 1; i++) + { + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + + } + BOOST_CHECK(generate_maintenance_block()); + + auto network_status_obj = con.wallet_api_ptr->get_son_network_status(); + + for(map>::iterator outer_iter=network_status_obj.begin(); outer_iter!=network_status_obj.end(); ++outer_iter) + { + for(map::iterator inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "No heartbeats sent"); + } + } + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 1; i < son_number-1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 2); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + auto gpo_active_sons = gpo.active_sons; + auto cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + BOOST_TEST_MESSAGE("Sending Heartbeat for sonaccount3"); + son_object son_obj1 = con.wallet_api_ptr->get_son("sonaccount3"); + signed_transaction trx1; + son_heartbeat_operation op1; + op1.owner_account = son_obj1.son_account; + op1.son_id = son_obj1.id; + op1.ts = db->head_block_time()+fc::seconds(2*db->block_interval()); + trx1.operations.push_back(op1); + con.wallet_api_ptr->sign_transaction(trx1, true); + + generate_blocks(50); + + BOOST_TEST_MESSAGE("Checking Network Status"); + network_status_obj = con.wallet_api_ptr->get_son_network_status(); + for(map>::iterator outer_iter=network_status_obj.begin(); outer_iter!=network_status_obj.end(); ++outer_iter) + { + for(map::iterator inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) + { + if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "OK, regular SON heartbeat"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "No heartbeats sent"); + } + } + } + + BOOST_TEST_MESSAGE("Sending Heartbeat for sonaccount4"); + + son_object son_obj2 = con.wallet_api_ptr->get_son("sonaccount4"); + signed_transaction trx2; + son_heartbeat_operation op2; + op2.owner_account = son_obj2.son_account; + op2.son_id = son_obj2.id; + op2.ts = db->head_block_time()+fc::seconds(2*db->block_interval()); + trx2.operations.push_back(op2); + con.wallet_api_ptr->sign_transaction(trx2, true); + + generate_blocks(50); + + BOOST_TEST_MESSAGE("Checking Network Status"); + network_status_obj = con.wallet_api_ptr->get_son_network_status(); + for(map>::iterator outer_iter=network_status_obj.begin(); outer_iter!=network_status_obj.end(); ++outer_iter) + { + for(map::iterator inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) + { + if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "OK, irregular SON heartbeat, but not triggering SON down proposal"); + } + else if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "OK, regular SON heartbeat"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "No heartbeats sent"); + } + } + } + + generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency(), false); + BOOST_TEST_MESSAGE("Checking Network Status"); + network_status_obj = con.wallet_api_ptr->get_son_network_status(); + for(map>::iterator outer_iter=network_status_obj.begin(); outer_iter!=network_status_obj.end(); ++outer_iter) + { + for(map::iterator inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) + { + if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + } + else if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "OK, irregular SON heartbeat, but not triggering SON down proposal"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "No heartbeats sent"); + } + } + } + + generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency() + gpo.parameters.son_down_time(), false); + BOOST_TEST_MESSAGE("Checking Network Status"); + + network_status_obj = con.wallet_api_ptr->get_son_network_status(); + for(map>::iterator outer_iter=network_status_obj.begin(); outer_iter!=network_status_obj.end(); ++outer_iter) + { + for(map::iterator inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) + { + if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + } + else if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && + (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< inner_iter->second); + BOOST_CHECK(inner_iter->second == "No heartbeats sent"); + } + } + } + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON get_son_network_status cli wallet tests end"); +} + +BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) +{ + BOOST_TEST_MESSAGE("SON get_son_network_status_by_sidechain cli wallet tests begin"); + try + { + son_test_helper sth(*this); + signed_transaction vote_tx; + auto db = app1->chain_database(); + + global_property_object gpo; + gpo = con.wallet_api_ptr->get_global_properties(); + unsigned int son_number = gpo.parameters.maximum_son_count(); + BOOST_TEST_MESSAGE("son_number"< sidechain_public_keys; + + // create son accounts + for(unsigned int i = 1; i < son_number + 1; i++) + { + sidechain_public_keys.clear(); + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); + sth.create_son("sonaccount" + fc::to_pretty_string(i), + "http://son" + fc::to_pretty_string(i), + sidechain_public_keys, + false); + con.wallet_api_ptr->transfer( + "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); + con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); + + } + + // Check Network Status Before sending Heartbeats + BOOST_CHECK(generate_maintenance_block()); + for(sidechain_type sidechain:active_sidechain_types) + { + auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); + for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "No heartbeats sent"); + } + } + + BOOST_TEST_MESSAGE("Voting for SONs"); + for(unsigned int i = 1; i < son_number-1; i++) + { + std::string name = "sonaccount" + fc::to_pretty_string(i); + std::string name2 = "sonaccount" + fc::to_pretty_string(i + 2); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name2, sidechain_type::ethereum, true, true); + } + BOOST_CHECK(generate_maintenance_block()); + gpo = con.wallet_api_ptr->get_global_properties(); + + auto gpo_active_sons = gpo.active_sons; + auto cmd_active_sons = con.wallet_api_ptr->get_active_sons(); + BOOST_CHECK(gpo_active_sons == cmd_active_sons); + + auto cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::bitcoin)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::bitcoin) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::ethereum)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::ethereum) == cmd_active_sons2); + + cmd_active_sons2 = con.wallet_api_ptr->get_active_sons_by_sidechain((sidechain_type::hive)); + BOOST_CHECK(gpo_active_sons.at(sidechain_type::hive) == cmd_active_sons2); + + BOOST_TEST_MESSAGE("Sending Heartbeat for sonaccount3"); + son_object son_obj1 = con.wallet_api_ptr->get_son("sonaccount3"); + signed_transaction trx1; + son_heartbeat_operation op1; + op1.owner_account = son_obj1.son_account; + op1.son_id = son_obj1.id; + op1.ts = db->head_block_time()+fc::seconds(2*db->block_interval()); + trx1.operations.push_back(op1); + con.wallet_api_ptr->sign_transaction(trx1, true); + + generate_blocks(50); + + BOOST_TEST_MESSAGE("Checking Network Status"); + for(sidechain_type sidechain:active_sidechain_types) + { + auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); + for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) + { + if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "OK, regular SON heartbeat"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "No heartbeats sent"); + } + + } + } + + BOOST_TEST_MESSAGE("Sending Heartbeat for sonaccount4"); + son_object son_obj2 = con.wallet_api_ptr->get_son("sonaccount4"); + signed_transaction trx2; + son_heartbeat_operation op2; + op2.owner_account = son_obj2.son_account; + op2.son_id = son_obj2.id; + op2.ts = db->head_block_time()+fc::seconds(2*db->block_interval()); + trx2.operations.push_back(op2); + con.wallet_api_ptr->sign_transaction(trx2, true); + + generate_blocks(50); + for(sidechain_type sidechain:active_sidechain_types) + { + auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); + for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) + { + if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "OK, irregular SON heartbeat, but not triggering SON down proposal"); + } + else if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "OK, regular SON heartbeat"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "No heartbeats sent"); + } + } + } + + generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency(), false); + BOOST_TEST_MESSAGE("Checking Network Status"); + for(sidechain_type sidechain:active_sidechain_types) + { + auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); + for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) + { + if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + } + else if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "OK, irregular SON heartbeat, but not triggering SON down proposal"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "No heartbeats sent"); + } + } + } + + generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency() + gpo.parameters.son_down_time(), false);; + BOOST_TEST_MESSAGE("Checking Network Status"); + for(sidechain_type sidechain:active_sidechain_types) + { + auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); + for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) + { + if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::hive).at(0).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + } + else if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && + (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) + { + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + } + else{ + BOOST_TEST_MESSAGE("status: "<< iter->second); + BOOST_CHECK(iter->second == "No heartbeats sent"); + } + } + } + + } catch( fc::exception& e ) { + BOOST_TEST_MESSAGE("SON cli wallet tests exception"); + edump((e.to_detail_string())); + throw; + } + BOOST_TEST_MESSAGE("SON get_son_network_status_by_sidechain cli wallet tests end"); } BOOST_AUTO_TEST_CASE( maintenance_test ) @@ -1036,5 +1664,3 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) BOOST_AUTO_TEST_SUITE_END() - - From 283fbd28f7e4e69af3617eb56d39216ab554841f Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 18 Oct 2022 10:19:03 +0300 Subject: [PATCH 056/106] #461 - handle exception when execute POST request --- .../peerplays_sidechain/common/rpc_client.cpp | 98 +++++--- .../sidechain_net_handler_bitcoin.cpp | 230 +++++++++--------- .../sidechain_net_handler_ethereum.cpp | 37 +-- .../sidechain_net_handler_hive.cpp | 46 ++-- 4 files changed, 223 insertions(+), 188 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index 4c1365f3..ac526715 100644 --- a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp @@ -62,47 +62,67 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor } std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx) { - std::stringstream ss(reply_str); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - if (json.find("result") == json.not_found()) { + if (reply_str.empty()) { + wlog("RPC call ${function}, empty reply string", ("function", __FUNCTION__)); return ""; } - auto json_result = json.get_child_optional("result"); - if (json_result) { - boost::property_tree::ptree array_ptree = json_result.get(); - if (!array_path.empty()) { - array_ptree = json_result.get().get_child(array_path); + try { + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.find("result") == json.not_found()) { + return ""; } - uint32_t array_el_idx = -1; - for (const auto &array_el : array_ptree) { - array_el_idx = array_el_idx + 1; - if (array_el_idx == idx) { - std::stringstream ss_res; - boost::property_tree::json_parser::write_json(ss_res, array_el.second); - return ss_res.str(); + + auto json_result = json.get_child_optional("result"); + if (json_result) { + boost::property_tree::ptree array_ptree = json_result.get(); + if (!array_path.empty()) { + array_ptree = json_result.get().get_child(array_path); + } + uint32_t array_el_idx = -1; + for (const auto &array_el : array_ptree) { + array_el_idx = array_el_idx + 1; + if (array_el_idx == idx) { + std::stringstream ss_res; + boost::property_tree::json_parser::write_json(ss_res, array_el.second); + return ss_res.str(); + } } } + } catch (const boost::property_tree::json_parser::json_parser_error &e) { + wlog("RPC call ${function} failed: ${e}", ("function", __FUNCTION__)("e", e.what())); } return ""; } std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::string value_path) { - std::stringstream ss(reply_str); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - if (json.find("result") == json.not_found()) { + if (reply_str.empty()) { + wlog("RPC call ${function}, empty reply string", ("function", __FUNCTION__)); return ""; } - auto json_result = json.get_child_optional("result"); - if (json_result) { - return json_result.get().get(value_path); + try { + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.find("result") == json.not_found()) { + return ""; + } + + auto json_result = json.get_child_optional("result"); + if (json_result) { + return json_result.get().get(value_path); + } + + return json.get("result"); + } catch (const boost::property_tree::json_parser::json_parser_error &e) { + wlog("RPC call ${function} failed: ${e}", ("function", __FUNCTION__)("e", e.what())); } - return json.get("result"); + return ""; } std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) { @@ -118,23 +138,27 @@ std::string rpc_client::send_post_request(std::string method, std::string params body << " }"; - const auto reply = send_post_request(body.str(), show_log); + try { + const auto reply = send_post_request(body.str(), show_log); - if (reply.body.empty()) { - wlog("RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (json.count("error") && !json.get_child("error").empty()) { - wlog("RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body.str())("msg", ss.str())); - } + if (json.count("error") && !json.get_child("error").empty()) { + wlog("RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body.str())("msg", ss.str())); + } - if (reply.status == 200) { - return ss.str(); + if (reply.status == 200) { + return ss.str(); + } + } catch (const boost::system::system_error &e) { + elog("RPC call ${function} failed: ${e}", ("function", __FUNCTION__)("e", e.what())); } return ""; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index cace6fb3..713c5ca3 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -30,14 +30,13 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _url, std::string _user, std: } std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { - std::string params = std::string("[\"") + wallet_name + std::string("\"]"); - std::string str = send_post_request("createwallet", params, debug_rpc_calls); - return str; + const std::string params = std::string("[\"") + wallet_name + std::string("\"]"); + return send_post_request("createwallet", params, debug_rpc_calls); } uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { - std::string params = std::string("[") + std::to_string(conf_target) + std::string("]"); - std::string str = send_post_request("estimatesmartfee", params, debug_rpc_calls); + const std::string params = std::string("[") + std::to_string(conf_target) + std::string("]"); + const std::string str = send_post_request("estimatesmartfee", params, debug_rpc_calls); if (str.length() == 0) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -60,26 +59,23 @@ uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { } std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { - std::string params = std::string("[\"") + block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); - std::string str = send_post_request("getblock", params, debug_rpc_calls); - return str; + const std::string params = std::string("[\"") + block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); + return send_post_request("getblock", params, debug_rpc_calls); } std::string bitcoin_rpc_client::getnetworkinfo() { - std::string params = std::string("[]"); - std::string str = send_post_request("getnetworkinfo", params, debug_rpc_calls); - return str; + static const std::string params = std::string("[]"); + return send_post_request("getnetworkinfo", params, debug_rpc_calls); } std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) { - std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]"); - std::string str = send_post_request("getrawtransaction", params, debug_rpc_calls); - return str; + const std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]"); + return send_post_request("getrawtransaction", params, debug_rpc_calls); } std::string bitcoin_rpc_client::getblockchaininfo() { - std::string params = std::string("[]"); - std::string str = send_post_request("getblockchaininfo", params, debug_rpc_calls); + static const std::string params = std::string("[]"); + const std::string str = send_post_request("getblockchaininfo", params, debug_rpc_calls); if (str.length() > 0) { @@ -130,8 +126,8 @@ void bitcoin_rpc_client::importmulti(const std::vector &address_or std::vector bitcoin_rpc_client::listunspent(const uint32_t minconf, const uint32_t maxconf) { std::vector result; - std::string params = std::string("[") + std::to_string(minconf) + "," + std::to_string(maxconf) + std::string("]"); - std::string str = send_post_request("listunspent", params, debug_rpc_calls); + const std::string params = std::string("[") + std::to_string(minconf) + "," + std::to_string(maxconf) + std::string("]"); + const std::string str = send_post_request("listunspent", params, debug_rpc_calls); if (str.length() == 0) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -167,7 +163,7 @@ std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(con params += std::string("} ]"); std::vector result; - std::string str = send_post_request("listunspent", params, debug_rpc_calls); + const std::string str = send_post_request("listunspent", params, debug_rpc_calls); if (str.length() == 0) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -194,16 +190,13 @@ std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(con } std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { - - std::string params = std::string("[\"") + filename + std::string("\"]"); - std::string str = send_post_request("loadwallet", params, debug_rpc_calls); - return str; + const std::string params = std::string("[\"") + filename + std::string("\"]"); + return send_post_request("loadwallet", params, debug_rpc_calls); } std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { - - std::string params = std::string("[\"") + tx_hex + std::string("\"]"); - std::string str = send_post_request("sendrawtransaction", params, debug_rpc_calls); + const std::string params = std::string("[\"") + tx_hex + std::string("\"]"); + const std::string str = send_post_request("sendrawtransaction", params, debug_rpc_calls); if (str.length() > 0) { std::stringstream ss(str); @@ -225,8 +218,8 @@ std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { } std::string bitcoin_rpc_client::walletlock() { - std::string params = std::string("[]"); - std::string str = send_post_request("walletlock", params, debug_rpc_calls); + static const std::string params = std::string("[]"); + const std::string str = send_post_request("walletlock", params, debug_rpc_calls); if (str.length() == 0) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -243,8 +236,8 @@ std::string bitcoin_rpc_client::walletlock() { } bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { - std::string params = std::string("[\"") + passphrase + std::string("\",") + std::to_string(timeout) + std::string("]"); - std::string str = send_post_request("walletpassphrase", params, debug_rpc_calls); + const std::string params = std::string("[\"") + passphrase + std::string("\",") + std::to_string(timeout) + std::string("]"); + const std::string str = send_post_request("walletpassphrase", params, debug_rpc_calls); if (str.length() == 0) return false; else @@ -366,6 +359,10 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } std::string blockchain_info = bitcoin_client->getblockchaininfo(); + if (blockchain_info.empty()) { + elog("No Bitcoin node running at ${url}", ("url", url)); + FC_ASSERT(false); + } std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end())); boost::property_tree::ptree bci_json; boost::property_tree::read_json(bci_ss, bci_json); @@ -385,6 +382,10 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } std::string network_info_str = bitcoin_client->getnetworkinfo(); + if (network_info_str.empty()) { + elog("No Bitcoin node running at ${url}", ("url", url)); + FC_ASSERT(false); + } std::stringstream network_info_ss(network_info_str); boost::property_tree::ptree network_info_json; boost::property_tree::read_json(network_info_ss, network_info_json); @@ -526,48 +527,50 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) uint64_t swdo_amount = swdo->sidechain_amount.value; uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1)); - std::string tx_str = bitcoin_client->getrawtransaction(swdo_txid, true); - std::stringstream tx_ss(tx_str); - boost::property_tree::ptree tx_json; - boost::property_tree::read_json(tx_ss, tx_json); + const std::string tx_str = bitcoin_client->getrawtransaction(swdo_txid, true); + if (tx_str != "") { + std::stringstream tx_ss(tx_str); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(tx_ss, tx_json); - if (tx_json.count("error") && tx_json.get_child("error").empty()) { + if (tx_json.count("error") && tx_json.get_child("error").empty()) { - std::string tx_txid = tx_json.get("result.txid"); - uint32_t tx_confirmations = tx_json.get("result.confirmations"); - std::string tx_address = ""; - uint64_t tx_amount = -1; - uint64_t tx_vout = -1; + std::string tx_txid = tx_json.get("result.txid"); + uint32_t tx_confirmations = tx_json.get("result.confirmations"); + std::string tx_address = ""; + uint64_t tx_amount = -1; + uint64_t tx_vout = -1; - for (auto &input : tx_json.get_child("result.vout")) { - std::string tx_vout_s = input.second.get("n"); - tx_vout = std::stoll(tx_vout_s); - if (tx_vout == swdo_vout) { - if (bitcoin_major_version > 21) { - std::string address = input.second.get("scriptPubKey.address"); - if (address == swdo_address) { - tx_address = address; - } - } else { - for (auto &address : input.second.get_child("scriptPubKey.addresses")) { - if (address.second.data() == swdo_address) { - tx_address = address.second.data(); - break; + for (auto &input : tx_json.get_child("result.vout")) { + std::string tx_vout_s = input.second.get("n"); + tx_vout = std::stoll(tx_vout_s); + if (tx_vout == swdo_vout) { + if (bitcoin_major_version > 21) { + std::string address = input.second.get("scriptPubKey.address"); + if (address == swdo_address) { + tx_address = address; + } + } else { + for (auto &address : input.second.get_child("scriptPubKey.addresses")) { + if (address.second.data() == swdo_address) { + tx_address = address.second.data(); + break; + } } } + std::string tx_amount_s = input.second.get("value"); + tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + tx_amount = std::stoll(tx_amount_s); + break; } - std::string tx_amount_s = input.second.get("value"); - tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); - tx_amount = std::stoll(tx_amount_s); - break; } - } - process_ok = (swdo_txid == tx_txid) && - (swdo_address == tx_address) && - (swdo_amount == tx_amount) && - (swdo_vout == tx_vout) && - (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); + process_ok = (swdo_txid == tx_txid) && + (swdo_address == tx_address) && + (swdo_amount == tx_amount) && + (swdo_vout == tx_vout) && + (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); + } } object_id_type object_id = op_obj_idx_1.get().object_id; @@ -936,59 +939,61 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain return false; } - std::string tx_str = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true); - std::stringstream tx_ss(tx_str); - boost::property_tree::ptree tx_json; - boost::property_tree::read_json(tx_ss, tx_json); + const std::string tx_str = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true); + if (tx_str != "") { + std::stringstream tx_ss(tx_str); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(tx_ss, tx_json); - if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) { - return false; - } + if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) { + return false; + } - const chain::global_property_object &gpo = database.get_global_properties(); + const chain::global_property_object &gpo = database.get_global_properties(); - using namespace bitcoin; - std::vector> pubkey_weights; - for (auto si : sto.signers) { - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); - pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); - } - btc_weighted_multisig_address addr(pubkey_weights, network_type); + using namespace bitcoin; + std::vector> pubkey_weights; + for (auto si : sto.signers) { + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); + pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); + } + btc_weighted_multisig_address addr(pubkey_weights, network_type); - std::string tx_txid = tx_json.get("result.txid"); - uint32_t tx_confirmations = tx_json.get("result.confirmations"); - std::string tx_address = addr.get_address(); - int64_t tx_amount = -1; + std::string tx_txid = tx_json.get("result.txid"); + uint32_t tx_confirmations = tx_json.get("result.confirmations"); + std::string tx_address = addr.get_address(); + int64_t tx_amount = -1; - if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { - if (sto.object_id.is()) { - for (auto &input : tx_json.get_child("result.vout")) { - if (bitcoin_major_version > 21) { - std::string address = input.second.get("scriptPubKey.address"); - if (address == tx_address) { - std::string tx_amount_s = input.second.get("value"); - tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); - tx_amount = std::stoll(tx_amount_s); - } - } else { - for (auto &address : input.second.get_child("scriptPubKey.addresses")) { - if (address.second.data() == tx_address) { + if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { + if (sto.object_id.is()) { + for (auto &input : tx_json.get_child("result.vout")) { + if (bitcoin_major_version > 21) { + std::string address = input.second.get("scriptPubKey.address"); + if (address == tx_address) { std::string tx_amount_s = input.second.get("value"); tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); tx_amount = std::stoll(tx_amount_s); - break; + } + } else { + for (auto &address : input.second.get_child("scriptPubKey.addresses")) { + if (address.second.data() == tx_address) { + std::string tx_amount_s = input.second.get("value"); + tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + tx_amount = std::stoll(tx_amount_s); + break; + } } } } + settle_amount = asset(tx_amount, database.get_global_properties().parameters.btc_asset()); + return true; } - settle_amount = asset(tx_amount, database.get_global_properties().parameters.btc_asset()); - return true; - } - if (sto.object_id.is()) { - auto swwo = database.get(sto.object_id); - settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset()); - return true; + if (sto.object_id.is()) { + auto swwo = database.get(sto.object_id); + settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset()); + return true; + } } } return false; @@ -1038,11 +1043,11 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con } uint64_t fee_rate = bitcoin_client->estimatesmartfee(); - uint64_t min_fee_rate = 1000; + const uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); uint64_t total_amount = 0.0; - std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0); + const std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0); if (inputs.size() == 0) { elog("Failed to find UTXOs to spend for ${pw}", ("pw", prev_pw_address)); @@ -1085,7 +1090,7 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_ std::string nvout = suid.substr(suid.find_last_of("-") + 1); uint64_t deposit_amount = swdo.sidechain_amount.value; uint64_t fee_rate = bitcoin_client->estimatesmartfee(); - uint64_t min_fee_rate = 1000; + const uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); if (fee_rate >= deposit_amount) { @@ -1128,11 +1133,11 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s std::string redeem_script = json.get("redeemScript"); int64_t fee_rate = bitcoin_client->estimatesmartfee(); - int64_t min_fee_rate = 1000; + const int64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); int64_t total_amount = 0; - std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); + const std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); if (inputs.size() == 0) { elog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); @@ -1240,14 +1245,11 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran // Add redeemscripts to vins and make tx ready for sending sign_witness_transaction_finalize(tx, redeem_scripts, false); std::string final_tx_hex = fc::to_hex(pack(tx)); - std::string res = bitcoin_client->sendrawtransaction(final_tx_hex); - - return res; + return bitcoin_client->sendrawtransaction(final_tx_hex); } void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { - - std::string block = bitcoin_client->getblock(event_data); + const std::string block = bitcoin_client->getblock(event_data); if (block.empty()) return; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 066c9dd8..ce75ec5c 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -33,13 +33,13 @@ std::string ethereum_rpc_client::admin_node_info() { } std::string ethereum_rpc_client::eth_get_block_by_number(std::string block_number, bool full_block) { - std::string params = "[ \"" + block_number + "\", " + (full_block ? "true" : "false") + "]"; + const std::string params = "[ \"" + block_number + "\", " + (full_block ? "true" : "false") + "]"; return send_post_request("eth_getBlockByNumber", params, debug_rpc_calls); } std::string ethereum_rpc_client::eth_get_logs(std::string wallet_contract_address) { - std::string params = "[{\"address\": \"" + wallet_contract_address + "\"}]"; - std::string reply_str = send_post_request("eth_getLogs", params, debug_rpc_calls); + const std::string params = "[{\"address\": \"" + wallet_contract_address + "\"}]"; + const std::string reply_str = send_post_request("eth_getLogs", params, debug_rpc_calls); return retrieve_value_from_reply(reply_str, ""); } @@ -56,28 +56,32 @@ std::string ethereum_rpc_client::eth_gas_price() { } std::string ethereum_rpc_client::get_chain_id() { - std::string reply_str = net_version(); + const std::string reply_str = net_version(); return retrieve_value_from_reply(reply_str, ""); } std::string ethereum_rpc_client::get_network_id() { - std::string reply_str = admin_node_info(); + const std::string reply_str = admin_node_info(); return retrieve_value_from_reply(reply_str, "protocols.eth.network"); } std::string ethereum_rpc_client::get_nonce(const std::string &address) { - std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]"); - const auto nonce_val = ethereum::from_hex(retrieve_value_from_reply(reply_str, "")); - return nonce_val == 0 ? ethereum::add_0x("0") : ethereum::add_0x(ethereum::to_hex(nonce_val)); + const std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]"); + const auto nonce_string = retrieve_value_from_reply(reply_str, ""); + if (!nonce_string.empty()) { + const auto nonce_val = ethereum::from_hex(nonce_string); + return nonce_val == 0 ? ethereum::add_0x("0") : ethereum::add_0x(ethereum::to_hex(nonce_val)); + } + return ""; } std::string ethereum_rpc_client::get_gas_price() { - std::string reply_str = eth_gas_price(); + const std::string reply_str = eth_gas_price(); return retrieve_value_from_reply(reply_str, ""); } std::string ethereum_rpc_client::get_gas_limit() { - std::string reply_str = eth_get_block_by_number("latest", false); + const std::string reply_str = eth_get_block_by_number("latest", false); if (!reply_str.empty()) { std::stringstream ss(reply_str); boost::property_tree::ptree json; @@ -138,13 +142,18 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha rpc_client = new ethereum_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); - std::string chain_id_str = rpc_client->get_chain_id(); + const std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { elog("No Ethereum node running at ${url}", ("url", rpc_url)); FC_ASSERT(false); } chain_id = std::stoll(chain_id_str); - std::string network_id_str = rpc_client->get_network_id(); + + const std::string network_id_str = rpc_client->get_network_id(); + if (network_id_str.empty()) { + elog("No Ethereum node running at ${url}", ("url", rpc_url)); + FC_ASSERT(false); + } network_id = std::stoll(network_id_str); ilog("Running on Ethereum network, chain id ${chain_id_str}, network id ${network_id_str}", ("chain_id_str", chain_id_str)("network_id_str", network_id_str)); @@ -678,7 +687,7 @@ void sidechain_net_handler_ethereum::schedule_ethereum_listener() { void sidechain_net_handler_ethereum::ethereum_listener_loop() { schedule_ethereum_listener(); - std::string reply = rpc_client->eth_get_block_by_number("latest", false); + const std::string reply = rpc_client->eth_get_block_by_number("latest", false); //std::string reply = rpc_client->eth_get_logs(wallet_contract_address); if (!reply.empty()) { std::stringstream ss(reply); @@ -697,7 +706,7 @@ void sidechain_net_handler_ethereum::ethereum_listener_loop() { } void sidechain_net_handler_ethereum::handle_event(const std::string &event_data) { - std::string block = rpc_client->eth_get_block_by_number("latest", true); + const std::string block = rpc_client->eth_get_block_by_number("latest", true); if (block != "") { add_to_son_listener_log("BLOCK : " + event_data); std::stringstream ss(block); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 7d3c4de9..3980f516 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -35,12 +35,12 @@ hive_rpc_client::hive_rpc_client(const std::string &url, const std::string &user } std::string hive_rpc_client::account_history_api_get_transaction(std::string transaction_id) { - std::string params = "{ \"id\": \"" + transaction_id + "\" }"; + const std::string params = "{ \"id\": \"" + transaction_id + "\" }"; return send_post_request("account_history_api.get_transaction", params, debug_rpc_calls); } std::string hive_rpc_client::block_api_get_block(uint32_t block_number) { - std::string params = "{ \"block_num\": " + std::to_string(block_number) + " }"; + const std::string params = "{ \"block_num\": " + std::to_string(block_number) + " }"; return send_post_request("block_api.get_block", params, debug_rpc_calls); } @@ -57,7 +57,7 @@ std::string hive_rpc_client::condenser_api_get_accounts(std::vector } std::string hive_rpc_client::condenser_api_get_config() { - std::string params = "[]"; + static const std::string params = "[]"; return send_post_request("condenser_api.get_config", params, debug_rpc_calls); } @@ -70,7 +70,7 @@ std::string hive_rpc_client::database_api_get_version() { } std::string hive_rpc_client::network_broadcast_api_broadcast_transaction(std::string htrx) { - std::string params = "{ \"trx\": " + htrx + ", \"max_block_age\": -1 }"; + const std::string params = "{ \"trx\": " + htrx + ", \"max_block_age\": -1 }"; return send_post_request("network_broadcast_api.broadcast_transaction", params, debug_rpc_calls); } @@ -88,27 +88,27 @@ std::string hive_rpc_client::get_account_memo_key(std::string account) { } std::string hive_rpc_client::get_chain_id() { - std::string reply_str = database_api_get_version(); + const std::string reply_str = database_api_get_version(); return retrieve_value_from_reply(reply_str, "chain_id"); } std::string hive_rpc_client::get_head_block_id() { - std::string reply_str = database_api_get_dynamic_global_properties(); + const std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "head_block_id"); } std::string hive_rpc_client::get_head_block_time() { - std::string reply_str = database_api_get_dynamic_global_properties(); + const std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "time"); } std::string hive_rpc_client::get_is_test_net() { - std::string reply_str = condenser_api_get_config(); + const std::string reply_str = condenser_api_get_config(); return retrieve_value_from_reply(reply_str, "IS_TEST_NET"); } std::string hive_rpc_client::get_last_irreversible_block_num() { - std::string reply_str = database_api_get_dynamic_global_properties(); + const std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "last_irreversible_block_num"); } @@ -148,14 +148,14 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi rpc_client = new hive_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); - std::string chain_id_str = rpc_client->get_chain_id(); + const std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { elog("No Hive node running at ${url}", ("url", rpc_url)); FC_ASSERT(false); } chain_id = chain_id_type(chain_id_str); - std::string is_test_net = rpc_client->get_is_test_net(); + const std::string is_test_net = rpc_client->get_is_test_net(); network_type = is_test_net.compare("true") == 0 ? hive::network::testnet : hive::network::mainnet; if (network_type == hive::network::mainnet) { ilog("Running on Hive mainnet, chain id ${chain_id_str}", ("chain_id_str", chain_id_str)); @@ -254,7 +254,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { account_auths[wallet_son.public_key] = wallet_son.weight; } - std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); + const std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); hive::authority active; active.weight_threshold = total_weight * 2 / 3 + 1; @@ -303,7 +303,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value; uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-"))); - std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid); + const std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid); if (tx_str != "") { std::stringstream ss_tx(tx_str); @@ -495,7 +495,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { account_auths[active_son.public_key] = active_son.weight; } - std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); + const std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); if (memo_key.empty()) { return; @@ -510,10 +510,10 @@ void sidechain_net_handler_hive::process_primary_wallet() { auo.active = active; auo.memo_key = hive::public_key_type(memo_key); - std::string block_id_str = rpc_client->get_head_block_id(); + const std::string block_id_str = rpc_client->get_head_block_id(); hive::block_id_type head_block_id(block_id_str); - std::string head_block_time_str = rpc_client->get_head_block_time(); + const std::string head_block_time_str = rpc_client->get_head_block_time(); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str); hive::signed_transaction htrx; @@ -668,10 +668,10 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob t_op.amount.symbol = symbol; t_op.memo = ""; - std::string block_id_str = rpc_client->get_head_block_id(); + const std::string block_id_str = rpc_client->get_head_block_id(); hive::block_id_type head_block_id(block_id_str); - std::string head_block_time_str = rpc_client->get_head_block_time(); + const std::string head_block_time_str = rpc_client->get_head_block_time(); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str); hive::signed_transaction htrx; @@ -727,7 +727,7 @@ std::string sidechain_net_handler_hive::process_sidechain_transaction(const side hive::signed_transaction htrx; fc::raw::unpack(ss_trx, htrx, 1000); - std::string chain_id_str = rpc_client->get_chain_id(); + const std::string chain_id_str = rpc_client->get_chain_id(); const hive::chain_id_type chain_id(chain_id_str); fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain))); @@ -770,7 +770,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr return false; } - std::string tx_str = rpc_client->account_history_api_get_transaction(sto.sidechain_transaction); + const std::string tx_str = rpc_client->account_history_api_get_transaction(sto.sidechain_transaction); if (tx_str != "") { std::stringstream ss_tx(tx_str); @@ -781,7 +781,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr std::string tx_txid = tx_json.get("result.transaction_id"); uint32_t tx_block_num = tx_json.get("result.block_num"); - uint32_t last_irreversible_block = std::stoul(rpc_client->get_last_irreversible_block_num()); + const uint32_t last_irreversible_block = std::stoul(rpc_client->get_last_irreversible_block_num()); //std::string tx_address = addr.get_address(); //int64_t tx_amount = -1; @@ -817,7 +817,7 @@ void sidechain_net_handler_hive::schedule_hive_listener() { void sidechain_net_handler_hive::hive_listener_loop() { schedule_hive_listener(); - std::string reply = rpc_client->database_api_get_dynamic_global_properties(); + const std::string reply = rpc_client->database_api_get_dynamic_global_properties(); if (!reply.empty()) { std::stringstream ss(reply); boost::property_tree::ptree json; @@ -844,7 +844,7 @@ void sidechain_net_handler_hive::hive_listener_loop() { } void sidechain_net_handler_hive::handle_event(const std::string &event_data) { - std::string block = rpc_client->block_api_get_block(std::atoll(event_data.c_str())); + const std::string block = rpc_client->block_api_get_block(std::atoll(event_data.c_str())); if (block != "") { add_to_son_listener_log("BLOCK : " + event_data); std::stringstream ss(block); From 194fa6abfa844187b13c20a8aab36af79e59cfb5 Mon Sep 17 00:00:00 2001 From: Milos Milosevic Date: Wed, 19 Oct 2022 17:37:23 +0000 Subject: [PATCH 057/106] #463 Fix unused variable son_count_histogram_buffer in Release mode --- libraries/chain/db_maint.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index f4a1bca5..8673362d 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -720,11 +720,11 @@ void database::update_active_sons() } assert( _son_count_histogram_buffer.size() > 0 ); - for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ #ifndef NDEBUG + for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ assert( son_count_histogram_buffer.second.size() > 0 ); -#endif } +#endif const flat_map stake_target = [this]{ flat_map stake_target; From 06bc65cc7929074bffe6acc63f5ad95178351241 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Sun, 30 Oct 2022 03:21:18 +0100 Subject: [PATCH 058/106] #450 Add documentation for undocumented methods --- .../wallet/include/graphene/wallet/wallet.hpp | 85 ++++++++++++++++++- libraries/wallet/wallet.cpp | 51 ++--------- 2 files changed, 88 insertions(+), 48 deletions(-) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index cedaa21d..aac79252 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -263,12 +263,25 @@ class wallet_api fc::ecc::private_key derive_private_key(const std::string& prefix_string, int sequence_number) const; + /** Returns info about head block, chain_id, maintenance, participation, current active witnesses and + * committee members. + * @returns runtime info about the blockchain + */ variant info(); /** Returns info such as client version, git version of graphene/fc, version of boost, openssl. * @returns compile time info and client and dependencies versions */ variant_object about() const; + /** Returns info about a specified block. + * @param num height of the block to retrieve + * @returns info about the block, or null if not found + */ optional get_block( uint32_t num ); + /** Get signed blocks + * @param block_num_from The lowest block number + * @param block_num_to The highest block number + * @returns A list of signed blocks from block_num_from till block_num_to + */ vector> get_blocks(uint32_t block_num_from, uint32_t block_num_to)const; /** Returns the number of accounts registered on the blockchain * @returns the number of registered accounts @@ -351,9 +364,38 @@ class wallet_api vector list_core_accounts()const; + /** Get OHLCV data of a trading pair in a time range + * @param symbol Asset symbol or ID in a trading pair + * @param symbol2 The other asset symbol or ID in the trading pair + * @param bucket Length of each time bucket in seconds. + * Note: it need to be within result of get_market_history_buckets() API, otherwise no data will be returned + * @param start The start of a time range, E.G. "2018-01-01T00:00:00" + * @param end The end of the time range + * @return A list of OHLCV data, in "least recent first" order. + * If there are more than 200 records in the specified time range, the first 200 records will be returned. + */ vector get_market_history(string symbol, string symbol2, uint32_t bucket, fc::time_point_sec start, fc::time_point_sec end)const; + + /** Get limit orders in a given market + * @param a symbol or ID of asset being sold + * @param b symbol or ID of asset being purchased + * @param limit Maximum number of orders to retrieve + * @return The limit orders, ordered from least price to greatest + */ vector get_limit_orders(string a, string b, uint32_t limit)const; + + /** Get call orders (aka margin positions) for a given asset + * @param a symbol or ID of the debt asset + * @param limit Maximum number of orders to retrieve + * @return The call orders, ordered from earliest to be called to latest + */ vector get_call_orders(string a, uint32_t limit)const; + + /** Get forced settlement orders in a given asset + * @param a Symbol or ID of asset being settled + * @param limit Maximum number of orders to retrieve + * @return The settle orders, ordered from earliest settlement date to latest + */ vector get_settle_orders(string a, uint32_t limit)const; /** Returns the block chain's slowly-changing settings. @@ -732,8 +774,26 @@ class wallet_api */ bool import_key(string account_name_or_id, string wif_key); + /** Imports accounts from Peerplays 0.x wallet file. + * Current wallet file must be unlocked to perform the import. + * + * @param filename the Peerplays 0.x wallet file to import + * @param password the password to encrypt the Peerplays 0.x wallet file + * @returns a map containing the accounts found and whether imported + */ map import_accounts( string filename, string password ); + /** Imports from a Peerplays 0.x wallet file, find keys that were bound to a given account name on the + * Peerplays 0.x chain, rebind them to an account name on the 2.0 chain. + * Current wallet file must be unlocked to perform the import. + * + * @param filename the Peerplays 0.x wallet file to import + * @param password the password to encrypt the Peerplays 0.x wallet file + * @param src_account_name name of the account on Peerplays 0.x chain + * @param dest_account_name name of the account on Peerplays 2.0 chain, + * can be same or different to \c src_account_name + * @returns whether the import has succeeded + */ bool import_account_keys( string filename, string password, string src_account_name, string dest_account_name ); /** @@ -921,9 +981,17 @@ class wallet_api * @return true if the label was set, otherwise false */ bool set_key_label( public_key_type, string label ); + + /** Get label of a public key. + * @param key a public key + * @return the label if already set by \c set_key_label(), or an empty string if not set + */ string get_key_label( public_key_type )const; - /** @return the public key associated with the given label */ + /* Get the public key associated with a given label. + * @param label a label + * @return the public key associated with the given label + */ public_key_type get_public_key( string label )const; ///@} @@ -1620,6 +1688,7 @@ class wallet_api * @param url Same as for create_witness. The empty string makes it remain the same. * @param block_signing_key The new block signing public key. The empty string makes it remain the same. * @param broadcast true if you wish to broadcast the transaction. + * @return the signed transaction */ signed_transaction update_witness(string witness_name, string url, @@ -1638,6 +1707,7 @@ class wallet_api * @param url Any text * @param worker_settings {"type" : "burn"|"refund"|"vesting", "pay_vesting_period_days" : x} * @param broadcast true if you wish to broadcast the transaction. + * @return the signed transaction */ signed_transaction create_worker( string owner_account, @@ -1656,6 +1726,7 @@ class wallet_api * @param account The account which will pay the fee and update votes. * @param delta {"vote_for" : [...], "vote_against" : [...], "vote_abstain" : [...]} * @param broadcast true if you wish to broadcast the transaction. + * @return the signed transaction */ signed_transaction update_worker_votes( string account, @@ -1670,7 +1741,7 @@ class wallet_api * @param asset_symbol the symbol of the asset to vest * @param vesting_type "normal", "gpos" or "son" * @param broadcast true to broadcast the transaction on the network - * @returns the signed transaction registering a vesting object + * @return the signed transaction registering a vesting object */ signed_transaction create_vesting_balance(string owner_account, string amount, @@ -1682,6 +1753,7 @@ class wallet_api * Get information about a vesting balance object. * * @param account_name An account name, account ID, or vesting balance object ID. + * @return a list of vesting balance objects with additional info */ vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ); @@ -1692,6 +1764,7 @@ class wallet_api * @param amount The amount to withdraw. * @param asset_symbol The symbol of the asset to withdraw. * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction */ signed_transaction withdraw_vesting( string witness_name, @@ -1705,6 +1778,7 @@ class wallet_api * @param amount The amount to withdraw. * @param asset_symbol The symbol of the asset to withdraw. * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction */ signed_transaction withdraw_GPOS_vesting_balance( string account_name, @@ -2064,6 +2138,13 @@ class wallet_api bool broadcast /* = false */ ); + /** + * Returns the order book for the market base:quote. + * @param base symbol or ID of the base asset + * @param quote symbol or ID of the quote asset + * @param limit depth of the order book to retrieve, for bids and asks each, capped at 50 + * @return Order book of the market + */ order_book get_order_book( const string& base, const string& quote, unsigned limit = 50); asset get_total_matched_bet_amount_for_betting_market_group(betting_market_group_id_type group_id); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 7185b7d3..d8d79c4b 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -5706,53 +5706,12 @@ string wallet_api::gethelp(const string& method)const std::stringstream ss; ss << "\n"; - if( method == "import_key" ) - { - ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n"; - ss << "example: import_key \"1.2.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; - ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n"; - } - else if( method == "transfer" ) - { - ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n"; - ss << "example: transfer \"1.2.11\" \"1.2.4\" 1000.03 CORE \"memo\" true\n"; - ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n"; - } - else if( method == "create_account_with_brain_key" ) - { - ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n"; - ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.2.11\" \"1.2.11\" true\n"; - ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n"; - ss << "\n"; - ss << "This method should be used if you would like the wallet to generate new keys derived from the brain key.\n"; - ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY. Use\n"; - ss << "register_account if you already know the keys you know the public keys that you would like to register.\n"; - } - else if( method == "register_account" ) - { - ss << "usage: register_account ACCOUNT_NAME OWNER_PUBLIC_KEY ACTIVE_PUBLIC_KEY REGISTRAR REFERRER REFERRER_PERCENT BROADCAST\n\n"; - ss << "example: register_account \"newaccount\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"CORE6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV\" \"1.3.11\" \"1.3.11\" 50 true\n"; - ss << "\n"; - ss << "Use this method to register an account for which you do not know the private keys."; - } - else if( method == "create_asset" ) - { - ss << "usage: ISSUER SYMBOL PRECISION_DIGITS OPTIONS BITASSET_OPTIONS BROADCAST\n\n"; - ss << "PRECISION_DIGITS: the number of digits after the decimal point\n\n"; - ss << "Example value of OPTIONS: \n"; - ss << fc::json::to_pretty_string( graphene::chain::asset_options() ); - ss << "\nExample value of BITASSET_OPTIONS: \n"; - ss << fc::json::to_pretty_string( graphene::chain::bitasset_options() ); - ss << "\nBITASSET_OPTIONS may be null\n"; - } + std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); + if (!doxygenHelpString.empty()) + ss << doxygenHelpString; else - { - std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); - if (!doxygenHelpString.empty()) - ss << doxygenHelpString; - else - ss << "No help defined for method " << method << "\n"; - } + ss << "No help defined for method " << method << "\n"; + return ss.str(); } From c3b2a598b46c2668b9d2eecdb874768796e20cba Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Sun, 30 Oct 2022 01:35:43 +0000 Subject: [PATCH 059/106] Fix for undo crash --- .../chain/sidechain_address_evaluator.cpp | 14 +++-- tests/tests/sidechain_addresses_test.cpp | 60 +++++++++++++++++-- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/libraries/chain/sidechain_address_evaluator.cpp b/libraries/chain/sidechain_address_evaluator.cpp index bd97fef5..0efd54cf 100644 --- a/libraries/chain/sidechain_address_evaluator.cpp +++ b/libraries/chain/sidechain_address_evaluator.cpp @@ -24,9 +24,10 @@ object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address if (addr_itr != sidechain_addresses_idx.end()) { - db().modify(*addr_itr, [&](sidechain_address_object &sao) { - sao.expires = db().head_block_time(); - }); + //db().modify(*addr_itr, [&](sidechain_address_object &sao) { + // sao.expires = db().head_block_time(); + //}); + db().remove(*addr_itr); } const auto& new_sidechain_address_object = db().create( [&]( sidechain_address_object& obj ){ @@ -106,9 +107,10 @@ void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address const auto& idx = db().get_index_type().indices().get(); auto sidechain_address = idx.find(op.sidechain_address_id); if(sidechain_address != idx.end()) { - db().modify(*sidechain_address, [&](sidechain_address_object &sao) { - sao.expires = db().head_block_time(); - }); + //db().modify(*sidechain_address, [&](sidechain_address_object &sao) { + // sao.expires = db().head_block_time(); + //}); + db().remove(*sidechain_address); } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index 818c1743..5f4885e6 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -266,18 +266,66 @@ BOOST_AUTO_TEST_CASE( sidechain_address_delete_test ) { sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); } - time_point_sec now = db.head_block_time(); + //time_point_sec now = db.head_block_time(); generate_block(); { BOOST_TEST_MESSAGE("Check sidechain_address_delete_operation results"); const auto& idx = db.get_index_type().indices().get(); - BOOST_REQUIRE( idx.size() == 1 ); - auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); - BOOST_REQUIRE( obj == idx.end() ); - auto expired_obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, now ) ); - BOOST_REQUIRE( expired_obj != idx.end() ); + //BOOST_REQUIRE( idx.size() == 1 ); + //auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); + //BOOST_REQUIRE( obj == idx.end() ); + //auto expired_obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, now ) ); + //BOOST_REQUIRE( expired_obj != idx.end() ); + BOOST_REQUIRE( idx.size() == 0 ); + } +} + +BOOST_AUTO_TEST_CASE(sidechain_address_delete_create_test) { + + BOOST_TEST_MESSAGE("sidechain_address_delete_create_test"); + + INVOKE(sidechain_address_add_test); + + GET_ACTOR(alice); + + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 1); + auto obj = idx.find(boost::make_tuple(alice_id, sidechain_type::bitcoin, time_point_sec::maximum())); + BOOST_REQUIRE(obj != idx.end()); + + { + BOOST_TEST_MESSAGE("Delete and create sidechain address"); + sidechain_address_delete_operation op_del; + op_del.payer = alice_id; + op_del.sidechain_address_id = sidechain_address_id_type(0); + op_del.sidechain_address_account = alice_id; + op_del.sidechain = obj->sidechain; + + sidechain_address_add_operation op_create; + op_create.payer = alice_id; + op_create.sidechain_address_account = alice_id; + op_create.sidechain = sidechain_type::bitcoin; + op_create.deposit_public_key = "deposit_public_key"; + op_create.withdraw_public_key = "withdraw_public_key"; + op_create.withdraw_address = "withdraw_address"; + + trx.operations.push_back(op_del); + trx.operations.push_back(op_create); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + + // both transactions should goes in one block (delete + create) + generate_block(); + + { + BOOST_TEST_MESSAGE("Check sidechain_address_delete_add_operation results"); + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 1); + auto obj = idx.find(boost::make_tuple(alice_id, sidechain_type::bitcoin, time_point_sec::maximum())); + BOOST_REQUIRE(obj != idx.end()); } } From aa90f715fdb6395f6ea1e9086cb381a3005ff860 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Sun, 30 Oct 2022 02:30:21 +0000 Subject: [PATCH 060/106] #467 - fix create_primary_wallet_transaction signers --- .../sidechain_net_handler_bitcoin.cpp | 5 ++++- .../sidechain_net_handler_ethereum.cpp | 21 ++++++++++++++++++- .../sidechain_net_handler_hive.cpp | 4 ++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index cace6fb3..a2dfa750 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -706,6 +706,10 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { const auto &active_sw = swi.rbegin(); if (active_sw != swi.rend()) { + const auto &prev_sw = std::next(active_sw); + if (prev_sw != swi.rend() && active_sw->sons.at(sidechain) == prev_sw->sons.at(sidechain)) + return; + if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { @@ -742,7 +746,6 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { proposal_op.proposed_ops.emplace_back(swu_op); - const auto &prev_sw = std::next(active_sw); if (prev_sw != swi.rend()) { std::string new_pw_address = active_pw_pt.get("result.address"); std::string tx_str = create_primary_wallet_transaction(*prev_sw, new_pw_address); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 066c9dd8..22b933ca 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -399,6 +399,10 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { const auto &active_sw = swi.rbegin(); if (active_sw != swi.rend()) { + const auto &prev_sw = std::next(active_sw); + if (prev_sw != swi.rend() && active_sw->sons.at(sidechain) == prev_sw->sons.at(sidechain)) + return; + if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { @@ -423,6 +427,21 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { swu_op.address = wallet_contract_address; proposal_op.proposed_ops.emplace_back(swu_op); + const auto signers = [this, &prev_sw, &active_sw, &swi] { + std::vector signers; + //! Check if we don't have any previous set of active SONs use the current one + if (prev_sw != swi.rend()) { + if (!prev_sw->sons.at(sidechain).empty()) + signers = prev_sw->sons.at(sidechain); + else + signers = active_sw->sons.at(sidechain); + } else { + signers = active_sw->sons.at(sidechain); + } + + return signers; + }(); + std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), active_sw->id.operator std::string()); if (!tx_str.empty()) { sidechain_transaction_create_operation stc_op; @@ -430,7 +449,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { stc_op.object_id = active_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons.at(sidechain); + stc_op.signers = signers; proposal_op.proposed_ops.emplace_back(stc_op); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 7d3c4de9..a5d266fd 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -478,6 +478,10 @@ void sidechain_net_handler_hive::process_primary_wallet() { const auto &active_sw = swi.rbegin(); if (active_sw != swi.rend()) { + const auto &prev_sw = std::next(active_sw); + if (prev_sw != swi.rend() && active_sw->sons.at(sidechain) == prev_sw->sons.at(sidechain)) + return; + if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { From a30325660dcfefb88b694b975f8896ab5c736813 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Fri, 4 Nov 2022 07:51:22 +0200 Subject: [PATCH 061/106] #470 Don't use admin_nodeInfo when get network_id --- .../sidechain_net_handler_ethereum.hpp | 2 +- .../sidechain_net_handler_ethereum.cpp | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index 3944d0a2..322cf5f9 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -15,9 +15,9 @@ class ethereum_rpc_client : public rpc_client { public: ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); - std::string admin_node_info(); std::string eth_get_block_by_number(std::string block_number, bool full_block); std::string eth_get_logs(std::string wallet_contract_address); + std::string eth_chainId(); std::string net_version(); std::string eth_get_transaction_count(const std::string ¶ms); std::string eth_gas_price(); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 02ec5bce..c71c4748 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -28,10 +28,6 @@ ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::stri rpc_client(url, user_name, password, debug_rpc_calls) { } -std::string ethereum_rpc_client::admin_node_info() { - return send_post_request("admin_nodeInfo", "", debug_rpc_calls); -} - std::string ethereum_rpc_client::eth_get_block_by_number(std::string block_number, bool full_block) { const std::string params = "[ \"" + block_number + "\", " + (full_block ? "true" : "false") + "]"; return send_post_request("eth_getBlockByNumber", params, debug_rpc_calls); @@ -43,6 +39,10 @@ std::string ethereum_rpc_client::eth_get_logs(std::string wallet_contract_addres return retrieve_value_from_reply(reply_str, ""); } +std::string ethereum_rpc_client::eth_chainId() { + return send_post_request("eth_chainId", "", debug_rpc_calls); +} + std::string ethereum_rpc_client::net_version() { return send_post_request("net_version", "", debug_rpc_calls); } @@ -56,13 +56,14 @@ std::string ethereum_rpc_client::eth_gas_price() { } std::string ethereum_rpc_client::get_chain_id() { - const std::string reply_str = net_version(); - return retrieve_value_from_reply(reply_str, ""); + const std::string reply_str = eth_chainId(); + const auto chain_id_string = retrieve_value_from_reply(reply_str, ""); + return chain_id_string.empty() ? "" : std::to_string(ethereum::from_hex(chain_id_string)); } std::string ethereum_rpc_client::get_network_id() { - const std::string reply_str = admin_node_info(); - return retrieve_value_from_reply(reply_str, "protocols.eth.network"); + const std::string reply_str = net_version(); + return retrieve_value_from_reply(reply_str, ""); } std::string ethereum_rpc_client::get_nonce(const std::string &address) { From 759dac5d413d70226683ce9176dbf305249a6f21 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Mon, 7 Nov 2022 12:16:59 +0200 Subject: [PATCH 062/106] #476 - fix calculating v value from chain id --- .../plugins/peerplays_sidechain/ethereum/transaction.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp index a75327c3..e243670d 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp @@ -100,14 +100,14 @@ signed_transaction raw_transaction::sign(const std::string &private_key) const { for (int i = 1; i < 33; i++) r.emplace_back((char)result.at(i)); - bytes v = bytes{char(recid + from_hex(chain_id) * 2 + 35)}; + unsigned int v = recid + from_hex(chain_id) * 2 + 35; bytes s; for (int i = 33; i < 65; i++) s.emplace_back((char)result.at(i)); tr.r = fc::to_hex((char *)&r[0], r.size()); - tr.v = fc::to_hex((char *)&v[0], v.size()); + tr.v = to_hex(v); tr.s = fc::to_hex((char *)&s[0], s.size()); return tr; @@ -157,8 +157,8 @@ signed_transaction::signed_transaction(const std::string &raw_tx) : std::string signed_transaction::recover(const std::string &chain_id) const { fc::ecc::compact_signature input64; fc::from_hex(r, (char *)&input64.at(1), 32); - fc::from_hex(v, (char *)&input64.at(0), 1); - int recid = input64.at(0) - from_hex(chain_id) * 2 - 35; + const int recid = from_hex(v) - from_hex(chain_id) * 2 - 35; + fc::from_hex(std::to_string(recid), (char *)&input64.at(0), 1); fc::from_hex(s, (char *)&input64.at(33), 32); secp256k1_ecdsa_recoverable_signature sig; From 9620e3c21187c6fa5385a72a7651634a4b126fa6 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Mon, 14 Nov 2022 13:42:23 +0000 Subject: [PATCH 063/106] #481 ethereum listener --- .../peerplays_sidechain/ethereum/utils.hpp | 8 ++-- .../sidechain_net_handler_ethereum.hpp | 3 +- .../sidechain_net_handler_ethereum.cpp | 47 +++++++++++++------ 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp index b4461713..13a89278 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp @@ -15,12 +15,14 @@ std::string add_0x(const std::string &s); std::string remove_0x(const std::string &s); template -std::string to_hex(const T &val) { +std::string to_hex(const T &val, bool add_front_zero = true) { std::stringstream stream; stream << std::hex << val; std::string result(stream.str()); - if (result.size() % 2) - result = "0" + result; + if(add_front_zero) { + if (result.size() % 2) + result = "0" + result; + } return result; } diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index 322cf5f9..ccc1aad4 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -15,6 +15,7 @@ class ethereum_rpc_client : public rpc_client { public: ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + std::string eth_blockNumber(); std::string eth_get_block_by_number(std::string block_number, bool full_block); std::string eth_get_logs(std::string wallet_contract_address); std::string eth_chainId(); @@ -69,7 +70,7 @@ private: boost::signals2::signal event_received; void schedule_ethereum_listener(); void ethereum_listener_loop(); - void handle_event(const std::string &event_data); + void handle_event(const std::string &block_number); }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index c71c4748..ce0e29e9 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -28,6 +28,11 @@ ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::stri rpc_client(url, user_name, password, debug_rpc_calls) { } +std::string ethereum_rpc_client::eth_blockNumber() { + const std::string reply_str = send_post_request("eth_blockNumber", "", debug_rpc_calls); + return retrieve_value_from_reply(reply_str, ""); +} + std::string ethereum_rpc_client::eth_get_block_by_number(std::string block_number, bool full_block) { const std::string params = "[ \"" + block_number + "\", " + (full_block ? "true" : "false") + "]"; return send_post_request("eth_getBlockByNumber", params, debug_rpc_calls); @@ -159,7 +164,8 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha ilog("Running on Ethereum network, chain id ${chain_id_str}, network id ${network_id_str}", ("chain_id_str", chain_id_str)("network_id_str", network_id_str)); - last_block_received = 0; + const auto block_number = rpc_client->eth_blockNumber(); + last_block_received = !block_number.empty() ? ethereum::from_hex(block_number) : 0; schedule_ethereum_listener(); event_received.connect([this](const std::string &event_data) { std::thread(&sidechain_net_handler_ethereum::handle_event, this, event_data).detach(); @@ -707,32 +713,43 @@ void sidechain_net_handler_ethereum::schedule_ethereum_listener() { void sidechain_net_handler_ethereum::ethereum_listener_loop() { schedule_ethereum_listener(); - const std::string reply = rpc_client->eth_get_block_by_number("latest", false); + const auto reply = rpc_client->eth_blockNumber(); //std::string reply = rpc_client->eth_get_logs(wallet_contract_address); + if (!reply.empty()) { - std::stringstream ss(reply); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - if (json.count("result")) { - std::string head_block_number_s = json.get("result.number"); - uint64_t head_block_number = std::strtoul(head_block_number_s.c_str(), nullptr, 16); - if (head_block_number != last_block_received) { - std::string event_data = std::to_string(head_block_number); - handle_event(event_data); - last_block_received = head_block_number; + uint64_t head_block_number = ethereum::from_hex(reply); + + if (head_block_number != last_block_received) { + //! Check that current block number is greater than last one + if (head_block_number < last_block_received) { + wlog("Head block ${head_block_number} is greater than last received block ${last_block_received}", ("head_block_number", head_block_number)("last_block_received", last_block_received)); + return; } + + //! Send event data for all blocks that passed + for (uint64_t i = last_block_received + 1; i <= head_block_number; ++i) { + const std::string block_number = ethereum::add_0x(ethereum::to_hex(i, false)); + handle_event(block_number); + } + + last_block_received = head_block_number; } } } -void sidechain_net_handler_ethereum::handle_event(const std::string &event_data) { - const std::string block = rpc_client->eth_get_block_by_number("latest", true); +void sidechain_net_handler_ethereum::handle_event(const std::string &block_number) { + const std::string block = rpc_client->eth_get_block_by_number(block_number, true); if (block != "") { - add_to_son_listener_log("BLOCK : " + event_data); + add_to_son_listener_log("BLOCK : " + block_number); std::stringstream ss(block); boost::property_tree::ptree block_json; boost::property_tree::read_json(ss, block_json); + if (block_json.get("result") == "null") { + wlog("No data for block ${block_number}", ("block_number", block_number)); + return; + } + size_t tx_idx = -1; for (const auto &tx_child : block_json.get_child("result.transactions")) { boost::property_tree::ptree tx = tx_child.second; From 8853a767529c9b3839d2e7ff0203a11efcd8d86e Mon Sep 17 00:00:00 2001 From: Milos Milosevic Date: Tue, 15 Nov 2022 22:33:04 +0000 Subject: [PATCH 064/106] #433 Down active sons are not substituted --- libraries/app/database_api.cpp | 2 +- libraries/chain/db_getter.cpp | 4 ++-- tests/cli/son.cpp | 12 ++++++------ tests/tests/son_operations_tests.cpp | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index a385ba47..64fb82c2 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1912,7 +1912,7 @@ map database_api_impl::get_son_network_status_by_sidechain( if (time_point_sec(sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_down_time())) > _db.head_block_time()) { status = "OK, irregular SON heartbeat, but not triggering SON down proposal"; } else { - status = "NOT OK, irregular SON heartbeat, triggering SON down proposal]"; + status = "NOT OK, irregular SON heartbeat, triggering SON down proposal"; } } } else { diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 0bb9b10b..1b61ee79 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -236,7 +236,7 @@ std::set database::get_sons_to_be_deregistered() // TODO : We need to add a function that returns if we can deregister SON // i.e. with introduction of PW code, we have to make a decision if the SON // is needed for release of funds from the PW - if (head_block_time() - stats.last_down_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { + if (head_block_time() - stats.last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { need_to_be_deregistered = false; } } @@ -311,7 +311,7 @@ bool database::is_son_dereg_valid( son_id_type son_id ) if(status_son_dereg_valid) { - if(head_block_time() - son->statistics(*this).last_down_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) + if(head_block_time() - son->statistics(*this).last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { status_son_dereg_valid = false; } diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 7858664c..83db8d48 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -1313,7 +1313,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status, cli_fixture ) (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) { BOOST_TEST_MESSAGE("status: "<< inner_iter->second); - BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal"); } else if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && @@ -1342,14 +1342,14 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status, cli_fixture ) (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) { BOOST_TEST_MESSAGE("status: "<< inner_iter->second); - BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal"); } else if((inner_iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && (inner_iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && (inner_iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) { BOOST_TEST_MESSAGE("status: "<< inner_iter->second); - BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + BOOST_CHECK(inner_iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal"); } else{ BOOST_TEST_MESSAGE("status: "<< inner_iter->second); @@ -1517,7 +1517,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) { BOOST_TEST_MESSAGE("status: "<< iter->second); - BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal"); } else if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && (iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && @@ -1545,14 +1545,14 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(0).son_id)) { BOOST_TEST_MESSAGE("status: "<< iter->second); - BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal"); } else if((iter->first == gpo.active_sons.at(sidechain_type::bitcoin).at(1).son_id) && (iter->first == gpo.active_sons.at(sidechain_type::hive).at(1).son_id) && (iter->first == gpo.active_sons.at(sidechain_type::ethereum).at(1).son_id)) { BOOST_TEST_MESSAGE("status: "<< iter->second); - BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal]"); + BOOST_CHECK(iter->second == "NOT OK, irregular SON heartbeat, triggering SON down proposal"); } else{ BOOST_TEST_MESSAGE("status: "<< iter->second); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index a128b474..23ddfd1c 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -208,9 +208,9 @@ try { db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { - _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); - _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); - _s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_active_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_active_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_active_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); }); auto deposit_vesting = db.get(vesting_balance_id_type(0)); From 811d68ef4d700f7a5717433faf79dd4dcea65970 Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Tue, 15 Nov 2022 22:34:05 +0000 Subject: [PATCH 065/106] Fix for undo crash --- libraries/chain/hardfork.d/SIDECHAIN.hf | 7 +++++ .../chain/sidechain_address_evaluator.cpp | 27 +++++++++++-------- tests/tests/sidechain_addresses_test.cpp | 14 +++++----- 3 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 libraries/chain/hardfork.d/SIDECHAIN.hf diff --git a/libraries/chain/hardfork.d/SIDECHAIN.hf b/libraries/chain/hardfork.d/SIDECHAIN.hf new file mode 100644 index 00000000..3a0a3990 --- /dev/null +++ b/libraries/chain/hardfork.d/SIDECHAIN.hf @@ -0,0 +1,7 @@ +#ifndef HARDFORK_SIDECHAIN_DELETE_TIME +#ifdef BUILD_PEERPLAYS_TESTNET +#define HARDFORK_SIDECHAIN_DELETE_TIME (fc::time_point_sec::from_iso_string("2022-11-16T02:00:00")) +#else +#define HARDFORK_SIDECHAIN_DELETE_TIME (fc::time_point_sec::from_iso_string("2022-11-16T02:00:00")) +#endif +#endif diff --git a/libraries/chain/sidechain_address_evaluator.cpp b/libraries/chain/sidechain_address_evaluator.cpp index 0efd54cf..3cffc61e 100644 --- a/libraries/chain/sidechain_address_evaluator.cpp +++ b/libraries/chain/sidechain_address_evaluator.cpp @@ -22,12 +22,14 @@ object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address const auto &sidechain_addresses_idx = db().get_index_type().indices().get(); const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(op.sidechain_address_account, op.sidechain, time_point_sec::maximum())); - if (addr_itr != sidechain_addresses_idx.end()) - { - //db().modify(*addr_itr, [&](sidechain_address_object &sao) { - // sao.expires = db().head_block_time(); - //}); - db().remove(*addr_itr); + if (addr_itr != sidechain_addresses_idx.end()) { + if (db().head_block_time() >= HARDFORK_SIDECHAIN_DELETE_TIME) { + db().remove(*addr_itr); + } else { + db().modify(*addr_itr, [&](sidechain_address_object &sao) { + sao.expires = db().head_block_time(); + }); + } } const auto& new_sidechain_address_object = db().create( [&]( sidechain_address_object& obj ){ @@ -106,11 +108,14 @@ void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address { try { const auto& idx = db().get_index_type().indices().get(); auto sidechain_address = idx.find(op.sidechain_address_id); - if(sidechain_address != idx.end()) { - //db().modify(*sidechain_address, [&](sidechain_address_object &sao) { - // sao.expires = db().head_block_time(); - //}); - db().remove(*sidechain_address); + if (sidechain_address != idx.end()) { + if (db().head_block_time() >= HARDFORK_SIDECHAIN_DELETE_TIME) { + db().remove(*sidechain_address); + } else { + db().modify(*sidechain_address, [&](sidechain_address_object &sao) { + sao.expires = db().head_block_time(); + }); + } } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index 5f4885e6..8c3229d3 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -244,6 +244,9 @@ BOOST_AUTO_TEST_CASE( sidechain_address_delete_test ) { BOOST_TEST_MESSAGE("sidechain_address_delete_test"); + generate_blocks(HARDFORK_SIDECHAIN_DELETE_TIME); + generate_block(); + INVOKE(sidechain_address_add_test); GET_ACTOR(alice); @@ -266,18 +269,12 @@ BOOST_AUTO_TEST_CASE( sidechain_address_delete_test ) { sign(trx, alice_private_key); PUSH_TX(db, trx, ~0); } - //time_point_sec now = db.head_block_time(); - generate_block(); + generate_block(); { BOOST_TEST_MESSAGE("Check sidechain_address_delete_operation results"); const auto& idx = db.get_index_type().indices().get(); - //BOOST_REQUIRE( idx.size() == 1 ); - //auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); - //BOOST_REQUIRE( obj == idx.end() ); - //auto expired_obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, now ) ); - //BOOST_REQUIRE( expired_obj != idx.end() ); BOOST_REQUIRE( idx.size() == 0 ); } } @@ -286,6 +283,9 @@ BOOST_AUTO_TEST_CASE(sidechain_address_delete_create_test) { BOOST_TEST_MESSAGE("sidechain_address_delete_create_test"); + generate_blocks(HARDFORK_SIDECHAIN_DELETE_TIME); + generate_block(); + INVOKE(sidechain_address_add_test); GET_ACTOR(alice); From 022fdeb40a3344c0e5e5d47d74369e1b7bb21f94 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 22 Nov 2022 20:44:07 +0000 Subject: [PATCH 066/106] #478 estimate transaction fee --- .../peerplays_sidechain_plugin.hpp | 1 + .../peerplays_sidechain/sidechain_api.hpp | 3 +- .../sidechain_net_handler.hpp | 1 + .../sidechain_net_handler_bitcoin.hpp | 1 + .../sidechain_net_handler_ethereum.hpp | 3 + .../sidechain_net_handler_hive.hpp | 1 + .../sidechain_net_handler_peerplays.hpp | 1 + .../peerplays_sidechain_plugin.cpp | 14 ++++ .../peerplays_sidechain/sidechain_api.cpp | 9 +++ .../sidechain_net_handler_bitcoin.cpp | 5 ++ .../sidechain_net_handler_ethereum.cpp | 47 +++++++++++++ .../sidechain_net_handler_hive.cpp | 5 ++ .../sidechain_net_handler_peerplays.cpp | 5 ++ .../wallet/include/graphene/wallet/wallet.hpp | 15 +++++ libraries/wallet/wallet.cpp | 67 +++++++++++++++++++ 15 files changed, 177 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 114f9bc7..d890ae68 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -54,6 +54,7 @@ public: void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp index b4636537..5d6df6af 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp @@ -26,9 +26,10 @@ public: std::shared_ptr my; std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); }; }} // namespace graphene::peerplays_sidechain FC_API(graphene::peerplays_sidechain::sidechain_api, - (get_son_listener_log)) + (get_son_listener_log)(estimate_withdrawal_transaction_fee)) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index a9257e54..fc295b4c 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -50,6 +50,7 @@ public: void add_to_son_listener_log(std::string trx_id); std::vector get_son_listener_log(); + virtual optional estimate_withdrawal_transaction_fee() const = 0; protected: peerplays_sidechain_plugin &plugin; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 62eeeab6..eaa48526 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -106,6 +106,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: std::string ip; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index ccc1aad4..e6e8f298 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -22,12 +22,14 @@ public: std::string net_version(); std::string eth_get_transaction_count(const std::string ¶ms); std::string eth_gas_price(); + std::string eth_estimateGas(const std::string ¶ms); std::string get_chain_id(); std::string get_network_id(); std::string get_nonce(const std::string &address); std::string get_gas_price(); std::string get_gas_limit(); + std::string get_estimate_gas(const std::string ¶ms); std::string eth_send_transaction(const std::string ¶ms); std::string eth_send_raw_transaction(const std::string ¶ms); @@ -47,6 +49,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: std::string rpc_url; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp index 72539db2..440a2520 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp @@ -45,6 +45,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: std::string rpc_url; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp index 69eea1a9..139db7bf 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp @@ -19,6 +19,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 27784720..c8da6ce6 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -48,6 +48,7 @@ public: void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); void schedule_heartbeat_loop(); void heartbeat_loop(); @@ -626,6 +627,15 @@ std::map> peerplays_sidechain_plugin_im return result; } +optional peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + if (!net_handlers.at(sidechain)) { + wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional(); + } + + return net_handlers.at(sidechain)->estimate_withdrawal_transaction_fee(); +} + void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) { // prevent approving duplicate proposals with lock for parallel execution. // We can have the same propsals, but in the case of parallel execution we can run @@ -922,4 +932,8 @@ std::map> peerplays_sidechain_plugin::g return my->get_son_listener_log(); } +optional peerplays_sidechain_plugin::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + return my->estimate_withdrawal_transaction_fee(sidechain); +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_api.cpp b/libraries/plugins/peerplays_sidechain/sidechain_api.cpp index 2a85d034..833d91ff 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_api.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_api.cpp @@ -12,6 +12,7 @@ public: std::shared_ptr get_plugin(); std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); private: app::application &app; @@ -32,6 +33,10 @@ std::map> sidechain_api_impl::get_son_l return get_plugin()->get_son_listener_log(); } +optional sidechain_api_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + return get_plugin()->estimate_withdrawal_transaction_fee(sidechain); +} + } // namespace detail sidechain_api::sidechain_api(graphene::app::application &_app) : @@ -45,4 +50,8 @@ std::map> sidechain_api::get_son_listen return my->get_son_listener_log(); } +optional sidechain_api::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + return my->estimate_withdrawal_transaction_fee(sidechain); +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index ea4621c0..862f991a 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1002,6 +1002,11 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain return false; } +optional sidechain_net_handler_bitcoin::estimate_withdrawal_transaction_fee() const { + wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; +} + std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector &son_pubkeys) { using namespace bitcoin; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index ce0e29e9..c827a721 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -60,6 +60,10 @@ std::string ethereum_rpc_client::eth_gas_price() { return send_post_request("eth_gasPrice", "", debug_rpc_calls); } +std::string ethereum_rpc_client::eth_estimateGas(const std::string ¶ms) { + return send_post_request("eth_estimateGas", params, debug_rpc_calls); +} + std::string ethereum_rpc_client::get_chain_id() { const std::string reply_str = eth_chainId(); const auto chain_id_string = retrieve_value_from_reply(reply_str, ""); @@ -100,6 +104,11 @@ std::string ethereum_rpc_client::get_gas_limit() { return std::string{}; } +std::string ethereum_rpc_client::get_estimate_gas(const std::string ¶ms) { + const std::string reply_str = eth_estimateGas(params); + return retrieve_value_from_reply(reply_str, ""); +} + std::string ethereum_rpc_client::eth_send_transaction(const std::string ¶ms) { return send_post_request("eth_sendTransaction", "[" + params + "]", debug_rpc_calls); } @@ -651,6 +660,44 @@ bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechai return false; } +optional sidechain_net_handler_ethereum::estimate_withdrawal_transaction_fee() const { + const auto &gpo = database.get_global_properties(); + if (gpo.active_sons.at(sidechain).empty()) { + wlog("No active sons for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; + } + + const auto &active_son = gpo.active_sons.at(sidechain).at(0); + const auto &s_idx = database.get_index_type().indices().get(); + const auto son = s_idx.find(active_son.son_id); + if (son == s_idx.end()) { + wlog("Can't find son for id: ${son_id}", ("son_id", active_son.son_id)); + return optional{}; + } + + if (!son->sidechain_public_keys.contains(sidechain)) { + wlog("No public keys for current son: ${account_id}", ("account_id", son->son_account)); + return optional{}; + } + + const auto &assets_by_symbol = database.get_index_type().indices().get(); + auto asset_itr = assets_by_symbol.find("ETH"); + if (asset_itr == assets_by_symbol.end()) { + wlog("Could not find asset matching ETH"); + return optional{}; + } + + const auto &public_key = son->sidechain_public_keys.at(sidechain); + const ethereum::withdrawal_encoder encoder; + const auto data = encoder.encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); + const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; + + const auto estimate_gas = ethereum::from_hex(rpc_client->get_estimate_gas(params)); + const auto gas_price = ethereum::from_hex(rpc_client->get_gas_price()); + const auto eth_gas_fee = double(estimate_gas * gas_price) / double{1000000000000000000}; + return asset_itr->amount_from_string(std::to_string(eth_gas_fee)); +} + std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id) { std::vector> owners_weights; for (auto &son : son_pubkeys) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index a4c06175..63ae22c6 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -806,6 +806,11 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr return false; } +optional sidechain_net_handler_hive::estimate_withdrawal_transaction_fee() const { + wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; +} + void sidechain_net_handler_hive::schedule_hive_listener() { fc::time_point now = fc::time_point::now(); int64_t time_to_next = 1000; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index b2945251..76fbbb3e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -290,4 +290,9 @@ bool sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidecha return true; } +optional sidechain_net_handler_peerplays::estimate_withdrawal_transaction_fee() const { + wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index aac79252..6a0b5ee5 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -2676,6 +2676,19 @@ class wallet_api */ std::map> get_son_listener_log() const; + /** + * @brief Estimate transaction fee for withdrawal + * @param sidechain Sidechain type (bitcoin, HIVE, etc) + * @return Transaction fee + */ + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain) const; + + /** + * @brief Estimate gas fee for withdrawal transaction for ETH + * @return Gas fee in ETH + */ + std::string eth_estimate_withdrawal_transaction_fee() const; + fc::signal lock_changed; std::shared_ptr my; void encrypt_keys(); @@ -2976,4 +2989,6 @@ FC_API( graphene::wallet::wallet_api, (get_voters_by_id) (get_voters) (get_son_listener_log) + (estimate_withdrawal_transaction_fee) + (eth_estimate_withdrawal_transaction_fee) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index d8d79c4b..6b1b2eb1 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3291,6 +3291,25 @@ public: return sign_transaction(trx, broadcast); } FC_CAPTURE_AND_RETHROW((order_id)) } + sidechain_type get_sidechain_type_from_asset(asset_id_type asset_id) const + { + const auto& gpo = _remote_db->get_global_properties(); + + if(asset_id == gpo.parameters.btc_asset()) + return sidechain_type::bitcoin; + + if(asset_id == gpo.parameters.eth_asset()) + return sidechain_type::ethereum; + + if(asset_id == gpo.parameters.hbd_asset()) + return sidechain_type::hive; + + if(asset_id == gpo.parameters.hive_asset()) + return sidechain_type::hive; + + return sidechain_type::unknown; + } + signed_transaction transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast = false) { try { @@ -3323,6 +3342,19 @@ public: set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); + //! For sidechain withdrawal check if amount is greater than fee + if(to_id == _remote_db->get_global_properties().parameters.son_account()) { + const auto sidechain = get_sidechain_type_from_asset(asset_obj->id); + const auto transaction_fee = estimate_withdrawal_transaction_fee(sidechain); + + if(transaction_fee) { + if (*transaction_fee >= xfer_op.amount) { + FC_THROW("Transaction fee: ${sidechain_fee}, would be grater than transfer amount ${amount}", + ("sidechain_fee", get_asset(transaction_fee->asset_id).amount_to_pretty_string(transaction_fee->amount))("amount", get_asset(xfer_op.amount.asset_id).amount_to_pretty_string(xfer_op.amount.amount))); + } + } + } + return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) } @@ -4222,6 +4254,31 @@ public: FC_CAPTURE_AND_RETHROW() } + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain) + { + use_sidechain_api(); + try + { + return (*_remote_sidechain)->estimate_withdrawal_transaction_fee(sidechain); + } + FC_CAPTURE_AND_RETHROW() + } + + std::string eth_estimate_withdrawal_transaction_fee() + { + try + { + const auto transaction_fee = estimate_withdrawal_transaction_fee(sidechain_type::ethereum); + if(transaction_fee) + { + return get_asset(transaction_fee->asset_id).amount_to_pretty_string(transaction_fee->amount); + } + + return "Can't get fee value"; + } + FC_CAPTURE_AND_RETHROW() + } + string _wallet_filename; wallet_data _wallet; @@ -7273,6 +7330,16 @@ std::map> wallet_api::get_son_listener_ return my->get_son_listener_log(); } +optional wallet_api::estimate_withdrawal_transaction_fee(sidechain_type sidechain) const +{ + return my->estimate_withdrawal_transaction_fee(sidechain); +} + +std::string wallet_api::eth_estimate_withdrawal_transaction_fee() const +{ + return my->eth_estimate_withdrawal_transaction_fee(); +} + // default ctor necessary for FC_REFLECT signed_block_with_info::signed_block_with_info( const signed_block& block ) : signed_block( block ) From b7113c4ff3495c6bb310ed60505dcbb3654c6316 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Mon, 28 Nov 2022 09:35:00 +0200 Subject: [PATCH 067/106] #478 - fix information warning --- libraries/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 6b1b2eb1..7eefae27 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3349,7 +3349,7 @@ public: if(transaction_fee) { if (*transaction_fee >= xfer_op.amount) { - FC_THROW("Transaction fee: ${sidechain_fee}, would be grater than transfer amount ${amount}", + FC_THROW("Transaction fee: ${sidechain_fee}, is greater than transferred amount ${amount}", ("sidechain_fee", get_asset(transaction_fee->asset_id).amount_to_pretty_string(transaction_fee->amount))("amount", get_asset(xfer_op.amount.asset_id).amount_to_pretty_string(xfer_op.amount.amount))); } } From c3eab0a80b015cea267ca9b283dc7c9e85aebce8 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 1 Dec 2022 01:53:39 +0000 Subject: [PATCH 068/106] #479 - Send one transaction for all owners --- .../peerplays_sidechain/ethereum/encoders.cpp | 57 ++++++++++- .../ethereum/transaction.cpp | 70 ++++++++------ .../peerplays_sidechain/ethereum/types.cpp | 31 ++++++ .../peerplays_sidechain/ethereum/encoders.hpp | 60 +++++------- .../ethereum/transaction.hpp | 89 +---------------- .../peerplays_sidechain/ethereum/types.hpp | 13 +++ .../peerplays_sidechain/ethereum/utils.hpp | 2 +- .../sidechain_net_handler_ethereum.cpp | 96 +++++++++++-------- .../ethereum_transaction_tests.cpp | 40 ++++++-- 9 files changed, 254 insertions(+), 204 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp index ad42088b..feab921f 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -2,7 +2,6 @@ #include #include -#include #include @@ -19,12 +18,16 @@ std::string base_encoder::encode_address(const std::string &value) { std::string base_encoder::encode_string(const std::string &value) { std::string data = (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value.size())).str(); - data += boost::algorithm::hex(value) + std::string((64 - value.size() * 2 % 64), '0'); + data += boost::algorithm::hex(value); + if (value.size() % 32 != 0) { + data += std::string((64 - value.size() * 2 % 64), '0'); + } return data; } //! update_owners_encoder -std::string update_owners_encoder::encode(const std::vector> &owners_weights, const std::string &object_id) const { +const std::string update_owners_encoder::function_signature = "f6afbeff"; //! updateOwners_(address,(address,uint256)[],string) +std::string update_owners_encoder::encode(const std::vector> &owners_weights, const std::string &object_id) { std::string data = "0x" + function_signature; data += base_encoder::encode_uint256(64); data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32); @@ -39,7 +42,8 @@ std::string update_owners_encoder::encode(const std::vector &transactions) const { + std::string data = "0x" + function_signature; + data += base_encoder::encode_uint256(32); + data += base_encoder::encode_uint256(transactions.size()); + size_t offset = (transactions.size()) * 32; + for (const auto &transaction : transactions) { + data += base_encoder::encode_uint256(offset); + const auto transaction_data = remove_0x(transaction.data); + offset += 5 * 32 + transaction_data.size() / 2; + if (transaction_data.size() / 2 % 32 != 0) { + offset += 32 - transaction_data.size() / 2 % 32; + } + } + for (const auto &transaction : transactions) { + data += base_encoder::encode_uint256(4 * 32); + data += base_encoder::encode_address(transaction.sign.v); + data += base_encoder::encode_address(transaction.sign.r); + data += base_encoder::encode_address(transaction.sign.s); + const auto transaction_data = remove_0x(transaction.data); + data += base_encoder::encode_uint256(transaction_data.size() / 2); + data += transaction_data; + if (transaction_data.size() % 64 != 0) { + data += std::string((64 - transaction_data.size() % 64), '0'); + } + } + + return data; +} + //! rlp_encoder std::string rlp_encoder::encode(const std::string &s) { return encode_rlp(hex2bytes(s)); diff --git a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp index e243670d..dd1179b4 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp @@ -22,7 +22,43 @@ const secp256k1_context *eth_context() { return ctx; } -//! transaction +bytes keccak_hash(const std::string &data) { + bytes hash; + hash.resize(32); + const auto transaction_string = boost::algorithm::unhex(remove_0x(data)); + keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data()); + + return hash; +} + +signature sign_hash(const bytes &hash, const std::string &chain_id, const std::string &private_key) { + const bytes priv_key = parse_hex(private_key); + + int recid = 0; + secp256k1_ecdsa_recoverable_signature sig; + FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash.data(), (const unsigned char *)priv_key.data(), NULL, NULL)); + fc::ecc::compact_signature result; + FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig)); + + unsigned int v = recid + from_hex(chain_id) * 2 + 35; + + bytes r; + for (int i = 1; i < 33; i++) + r.emplace_back((char)result.at(i)); + + bytes s; + for (int i = 33; i < 65; i++) + s.emplace_back((char)result.at(i)); + + signature eth_sig; + eth_sig.v = to_hex(v); + eth_sig.r = fc::to_hex((char *)&r[0], r.size()); + eth_sig.s = fc::to_hex((char *)&s[0], s.size()); + + return eth_sig; +} + +//! base_transaction base_transaction::base_transaction(const std::string &raw_tx) { } @@ -70,12 +106,7 @@ raw_transaction::raw_transaction(const std::string &raw_tx) : } bytes raw_transaction::hash() const { - bytes hash; - hash.resize(32); - const auto transaction_string = boost::algorithm::unhex(remove_0x(serialize())); - keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data()); - - return hash; + return keccak_hash(serialize()); } signed_transaction raw_transaction::sign(const std::string &private_key) const { @@ -88,27 +119,10 @@ signed_transaction raw_transaction::sign(const std::string &private_key) const { tr.value = value; tr.data = data; - const bytes priv_key = parse_hex(private_key); - - int recid = 0; - secp256k1_ecdsa_recoverable_signature sig; - FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash().data(), (const unsigned char *)priv_key.data(), NULL, NULL)); - fc::ecc::compact_signature result; - FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig)); - - bytes r; - for (int i = 1; i < 33; i++) - r.emplace_back((char)result.at(i)); - - unsigned int v = recid + from_hex(chain_id) * 2 + 35; - - bytes s; - for (int i = 33; i < 65; i++) - s.emplace_back((char)result.at(i)); - - tr.r = fc::to_hex((char *)&r[0], r.size()); - tr.v = to_hex(v); - tr.s = fc::to_hex((char *)&s[0], s.size()); + const auto sig = sign_hash(hash(), chain_id, private_key); + tr.v = sig.v; + tr.r = sig.r; + tr.s = sig.s; return tr; } diff --git a/libraries/plugins/peerplays_sidechain/ethereum/types.cpp b/libraries/plugins/peerplays_sidechain/ethereum/types.cpp index f85d0e61..e33e0c4a 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/types.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/types.cpp @@ -1,5 +1,36 @@ #include +#include +#include + namespace graphene { namespace peerplays_sidechain { namespace ethereum { +signature::signature(const std::string &sign) { + deserialize(sign); +} + +std::string signature::serialize() const { + boost::property_tree::ptree pt; + pt.put("v", v); + pt.put("r", r); + pt.put("s", s); + + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, pt); + return ss.str(); +} + +void signature::deserialize(const std::string &raw_tx) { + std::stringstream ss_tx(raw_tx); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(ss_tx, tx_json); + + if (tx_json.count("v")) + v = tx_json.get("v"); + if (tx_json.count("r")) + r = tx_json.get("r"); + if (tx_json.count("s")) + s = tx_json.get("s"); +} + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp index 1ff97978..ce02f7ed 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp @@ -4,8 +4,18 @@ #include #include +#include + namespace graphene { namespace peerplays_sidechain { namespace ethereum { +const std::string update_owners_function_signature = "9d608673"; //! updateOwners((bytes,(uint8,bytes32,bytes32))[]) +const std::string withdrawal_function_signature = "daac6c81"; //! withdraw((bytes,(uint8,bytes32,bytes32))[]) + +struct encoded_sign_transaction { + std::string data; + signature sign; +}; + class base_encoder { public: static std::string encode_uint256(boost::multiprecision::uint256_t value); @@ -15,16 +25,27 @@ public: class update_owners_encoder { public: - const std::string function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string) + static const std::string function_signature; - std::string encode(const std::vector> &owners_weights, const std::string &object_id) const; + static std::string encode(const std::vector> &owners_weights, const std::string &object_id); }; class withdrawal_encoder { public: - const std::string function_signature = "e088747b"; //! withdraw(address,uint256,string) + static const std::string function_signature; - std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) const; + static std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id); +}; + +class signature_encoder { +public: + const std::string function_signature; + + signature_encoder(const std::string &function_hash); + + static std::string get_function_signature_from_transaction(const std::string &transaction); + + std::string encode(const std::vector &transactions) const; }; class rlp_encoder { @@ -39,35 +60,4 @@ private: static void hex2bin(const char *src, char *target); }; -/*class ethereum_function_call_encoder { -public: - enum operation_t { - OPERATION_CALL, - OPERATION_DELEGATE_CALL - }; - - static constexpr const char *const default_prev_addr = "0000000000000000000000000000000000000001"; - - std::string encode_function_signature(const std::string &function_signature); - std::string encode_address(const std::string &addr); - std::string encode_uint256(const std::string &value); - std::string encode_uint8(uint8_t value); - std::string encode_bytes(const std::string &values); -}; - -class safe_transaction_encoder { -public: - static constexpr const char *const default_safe_tx_gas = "0"; - static constexpr const char *const default_data_gas = "0"; - static constexpr const char *const default_gas_price = "0"; - static constexpr const char *const default_gas_token = "0000000000000000000000000000000000000000"; - static constexpr const char *const default_refund_receiver = "0000000000000000000000000000000000000000"; - - std::string create_safe_address(const std::vector &owner_addresses, uint32_t threshold); - std::string build_transaction(const std::string &safe_account_addr, const std::string &value, const std::string &data, uint8_t operation, const std::string &safeTxGas, const std::string &dataGas, const std::string &gasPrice, const std::string &gasToken, const std::string &refundReceiver); - -private: - ethereum_function_call_encoder m_ethereum_function_call_encoder; -};*/ - }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp index 693c7284..77ef444b 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp @@ -8,6 +8,9 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum { +bytes keccak_hash(const std::string &data); +signature sign_hash(const bytes &hash, const std::string &chain_id, const std::string &private_key); + class base_transaction { public: base_transaction() = default; @@ -75,89 +78,3 @@ public: }; }}} // namespace graphene::peerplays_sidechain::ethereum - -// Example 1 -//{ -// "blockHash": "0x64a6706ecaf5a97b7f3e047abb20ff223ce82c6994d80e68fdb1fdfb38d0209c", -// "blockNumber": "0xe5827c", -// "from": "0x8614c67e085f2334010f2a28e806c6f1cc176d12", -// "gas": "0x38822", -// "gasPrice": "0xce42cba69", -// "maxFeePerGas": "0xddb4d8d16", -// "maxPriorityFeePerGas": "0x3b9aca00", -// "hash": "0xeac92ea09fa8eb3ca2fb0d156cceb38ae69d4345869d41e8e49d5ecbcbb622dc", -// "input": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000", -// "nonce": "0x32", -// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", -// "transactionIndex": "0xb6", -// "value": "0x2514d9d7d7d8000", -// "type": "0x2", -// "accessList": [], -// "chainId": "0x1", -// "v": "0x1", -// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c", -// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" -//} -// -//"0xf9021332850ce42cba69830388229468b3465833fb72a70ecdf485e0e4c7bd8665fc458802514d9d7d7d8000b901a45ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde0312150000000000000000000000000000000000000000000000000000000001a02f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28ca0782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" -// -//{ -// "nonce": 50, -// "gasPrice": { -// "_hex": "0x0ce42cba69" -// }, -// "gasLimit": { -// "_hex": "0x038822" -// }, -// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", -// "value": { -// "_hex": "0x02514d9d7d7d8000" -// }, -// "data": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000", -// "v": 1, -// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c", -// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" -//} - -// Example 2 -//{ -// "blockHash": "0xe2ae3afd86dc7343c7fb753441447a0a51bb19499325ad6e278256f0cd1b5894", -// "blockNumber": "0xe58271", -// "from": "0xb895ade6d337fbb8cb97f2ea7da43106c7f5cc26", -// "gas": "0x5208", -// "gasPrice": "0xe6f3b322e", -// "maxFeePerGas": "0x1322455fd3", -// "maxPriorityFeePerGas": "0x53724e00", -// "hash": "0xed29b56e52ad2d452e25b8ec70c37f59d935cd6d0f8fe8e83b256f3ffdfd3fce", -// "input": "0x", -// "nonce": "0x37", -// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373", -// "transactionIndex": "0x8a", -// "value": "0x4563918244f40000", -// "type": "0x2", -// "accessList": [], -// "chainId": "0x1", -// "v": "0x0", -// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c", -// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" -//} -// -//"0xf86c37850e6f3b322e82520894176386b6ffc469ac049f9ec1f6cc0efd1d09b373884563918244f400008000a0dcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4ca028c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" -// -//{ -// "nonce": 55, -// "gasPrice": { -// "_hex": "0x0e6f3b322e" -// }, -// "gasLimit": { -// "_hex": "0x5208" -// }, -// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373", -// "value": { -// "_hex": "0x4563918244f40000" -// }, -// "data": "0x", -// "v": 0, -// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c", -// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" -//} diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp index 963244fa..3acc8b8f 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp @@ -9,4 +9,17 @@ typedef uint64_t network_id_type; using bytes = std::vector; +class signature { +public: + std::string v; + std::string r; + std::string s; + + signature() = default; + signature(const std::string &sign); + + std::string serialize() const; + void deserialize(const std::string &sign); +}; + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp index 13a89278..b2db9331 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp @@ -19,7 +19,7 @@ std::string to_hex(const T &val, bool add_front_zero = true) { std::stringstream stream; stream << std::hex << val; std::string result(stream.str()); - if(add_front_zero) { + if (add_front_zero) { if (result.size() % 2) result = "0" + result; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index c827a721..951228d5 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -585,33 +585,65 @@ std::string sidechain_net_handler_ethereum::process_sidechain_transaction(const std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sidechain_transaction_object &sto) { boost::property_tree::ptree pt; boost::property_tree::ptree pt_array; + + std::vector transactions; for (const auto &signature : sto.signatures) { - const auto &transaction = signature.second; //! Check if we have this signed transaction, if not, don't send it - if (transaction.empty()) + if (signature.second.empty()) continue; + ethereum::encoded_sign_transaction transaction{sto.transaction, ethereum::signature{signature.second}}; + transactions.emplace_back(transaction); + } + + const auto ¤t_son = plugin.get_current_son_object(sidechain); + FC_ASSERT(current_son.sidechain_public_keys.contains(sidechain), "No public keys for current son: ${account_id}", ("account_id", current_son.son_account)); + const auto &public_key = current_son.sidechain_public_keys.at(sidechain); + + const auto function_signature = ethereum::signature_encoder::get_function_signature_from_transaction(sto.transaction); + if (function_signature.empty()) { + elog("Function signature is empty for transaction id ${id}, transaction ${transaction}", ("id", sto.id)("transaction", sto.transaction)); + return std::string{}; //! Return empty string, as we have error in sending + } + + const ethereum::signature_encoder encoder{function_signature}; #ifdef SEND_RAW_TRANSACTION - const std::string sidechain_transaction = rpc_client->eth_send_raw_transaction(transaction); + ethereum::raw_transaction raw_tr; + raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key)); + raw_tr.gas_price = rpc_client->get_gas_price(); + raw_tr.gas_limit = rpc_client->get_gas_limit(); + raw_tr.to = wallet_contract_address; + raw_tr.value = ""; + raw_tr.data = encoder.encode(transactions); + raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id)); + + const auto sign_tr = raw_tr.sign(get_private_key(public_key)); + const std::string sidechain_transaction = rpc_client->eth_send_raw_transaction(sign_tr.serialize()); #else - const std::string sidechain_transaction = rpc_client->eth_send_transaction(transaction); + ethereum::transaction raw_tr; + raw_tr.data = encoder.encode(transactions); + raw_tr.to = wallet_contract_address; + raw_tr.from = ethereum::add_0x(public_key); + + const auto sign_tr = raw_tr.sign(get_private_key(public_key)); + const std::string sidechain_transaction = rpc_client->eth_send_transaction(sign_tr.serialize()); #endif - std::stringstream ss_tx(sidechain_transaction); - boost::property_tree::ptree tx_json; - boost::property_tree::read_json(ss_tx, tx_json); - if (tx_json.count("result") && !tx_json.count("error")) { - boost::property_tree::ptree node; - node.put("transaction", transaction); - node.put("transaction_receipt", tx_json.get("result")); - pt_array.push_back(std::make_pair("", node)); - } else { - //! Fixme - //! How should we proceed with error in eth_send_transaction - elog("Error in eth_send_transaction for transaction ${id}, transaction ${transaction}", ("id", sto.id)("transaction", transaction)); - return std::string{}; //! Return empty string, as we have error in sending - } + std::stringstream ss_tx(sidechain_transaction); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(ss_tx, tx_json); + if (tx_json.count("result") && !tx_json.count("error")) { + boost::property_tree::ptree node; + node.put("transaction", sto.transaction); + node.put("sidechain_transaction", sidechain_transaction); + node.put("transaction_receipt", tx_json.get("result")); + pt_array.push_back(std::make_pair("", node)); + } else { + //! Fixme + //! How should we proceed with error in eth_send_transaction + elog("Error in eth send transaction for transaction id ${id}, transaction ${transaction}, sidechain_transaction ${sidechain_transaction}", ("id", sto.id)("transaction", sto.transaction)("sidechain_transaction", sidechain_transaction)); + return std::string{}; //! Return empty string, as we have error in sending } pt.add_child("result_array", pt_array); @@ -705,8 +737,7 @@ std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(co owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight)); } - const ethereum::update_owners_encoder encoder; - return encoder.encode(owners_weights, object_id); + return ethereum::update_owners_encoder::encode(owners_weights, object_id); } std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) { @@ -714,8 +745,7 @@ std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son } std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { - const ethereum::withdrawal_encoder encoder; - return encoder.encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); + return ethereum::withdrawal_encoder::encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); } std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) { @@ -724,25 +754,11 @@ std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_tra const auto &public_key = current_son.sidechain_public_keys.at(sidechain); -#ifdef SEND_RAW_TRANSACTION - ethereum::raw_transaction raw_tr; - raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key)); - raw_tr.gas_price = rpc_client->get_gas_price(); - raw_tr.gas_limit = rpc_client->get_gas_limit(); - raw_tr.to = wallet_contract_address; - raw_tr.value = ""; - raw_tr.data = sto.transaction; - raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id)); + //! We need to change v value according to chain_id + auto signature = ethereum::sign_hash(ethereum::keccak_hash(sto.transaction), ethereum::add_0x(ethereum::to_hex(chain_id)), get_private_key(public_key)); + signature.v = ethereum::to_hex(ethereum::from_hex(signature.v) - 2 * chain_id - 35 + 27); - const auto sign_tr = raw_tr.sign(get_private_key(public_key)); - return sign_tr.serialize(); -#else - ethereum::transaction sign_transaction; - sign_transaction.data = sto.transaction; - sign_transaction.to = wallet_contract_address; - sign_transaction.from = "0x" + public_key; - return sign_transaction.sign(get_private_key(public_key)).serialize(); -#endif + return signature.serialize(); } void sidechain_net_handler_ethereum::schedule_ethereum_listener() { diff --git a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp index 769c35b1..5bcc0543 100644 --- a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp +++ b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp @@ -9,12 +9,22 @@ using namespace graphene::peerplays_sidechain::ethereum; BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests) BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) { - const withdrawal_encoder encoder; - const auto tx = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0"); - BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); + const auto tx = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0"); + BOOST_CHECK_EQUAL(tx, "0xcf7c8f6d0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); - const auto tx1 = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1"); - BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); + const auto tx1 = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1"); + BOOST_CHECK_EQUAL(tx1, "0xcf7c8f6d0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(withdrawal_signature_encoder_test) { + const signature_encoder encoder{withdrawal_function_signature}; + + encoded_sign_transaction transaction; + transaction.data = "0xcf7c8f6d0000000000000000000000005c79a9f5767e3c1b926f963fa24e21d8a04289ae0000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33372E300000000000000000000000000000000000000000000000000000"; + transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); + + const auto tx = encoder.encode({transaction}); + BOOST_CHECK_EQUAL(tx, "0xdaac6c8100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000065ef357c2788df103d443e3c4a9e0864d6f52fbaab479684b8eb6ea09422b6b54310ca0511c6cc02402a668a73f9613c00f6f535dbd2d78468ecba540a8a0dba3d00000000000000000000000000000000000000000000000000000000000000a4cf7c8f6d0000000000000000000000005c79a9f5767e3c1b926f963fa24e21d8a04289ae0000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33372E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { @@ -22,13 +32,23 @@ BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1); owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1); - const update_owners_encoder encoder; - const auto tx = encoder.encode(owners_weights, "1.35.0"); - BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); + const auto tx = update_owners_encoder::encode(owners_weights, "1.35.0"); + BOOST_CHECK_EQUAL(tx, "0xf6afbeff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1); - const auto tx1 = encoder.encode(owners_weights, "1.36.1"); - BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); + const auto tx1 = update_owners_encoder::encode(owners_weights, "1.36.1"); + BOOST_CHECK_EQUAL(tx1, "0xf6afbeff0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(update_owners_signature_encoder_test) { + const signature_encoder encoder{update_owners_function_signature}; + + encoded_sign_transaction transaction; + transaction.data = "0xf6afbeff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"; + transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); + + const auto tx = encoder.encode({transaction}); + BOOST_CHECK_EQUAL(tx, "0x9d608673000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000665762851a05348a4900393b3f2778c5078689422e43e3a2aa9b517a4141133bd42c08169979785256a2f26d1da13e297125ee84c2d79be07212cfde592fc03dbb0000000000000000000000000000000000000000000000000000000000000124f6afbeff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) { From 42b3890a7c3e9abfa79dd24baef68d50466e2068 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 6 Dec 2022 12:17:09 +0000 Subject: [PATCH 069/106] #482 burn eth from son account --- .../sidechain_net_handler_ethereum.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 951228d5..8faa5cdd 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -686,8 +686,18 @@ bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechai if (count != json.get_child("result_array").size()) { wlog("Not all receipts received for transaction ${id}", ("id", sto.id)); return false; - } else + } else { + if (sto.object_id.is()) { + settle_amount = asset(0, database.get_global_properties().parameters.eth_asset()); + } + + if (sto.object_id.is()) { + auto swwo = database.get(sto.object_id); + settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.eth_asset()); + } + return true; + } return false; } From ab1e08a7566ab5dd62322909a7b473492b65f029 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 8 Dec 2022 14:02:53 +0000 Subject: [PATCH 070/106] #479 one bunch transaction --- .../peerplays_sidechain/ethereum/encoders.cpp | 4 ++-- .../sidechain_net_handler_ethereum.cpp | 3 +-- .../ethereum_transaction_tests.cpp | 16 ++++++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp index feab921f..ce1e0083 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -26,7 +26,7 @@ std::string base_encoder::encode_string(const std::string &value) { } //! update_owners_encoder -const std::string update_owners_encoder::function_signature = "f6afbeff"; //! updateOwners_(address,(address,uint256)[],string) +const std::string update_owners_encoder::function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string) std::string update_owners_encoder::encode(const std::vector> &owners_weights, const std::string &object_id) { std::string data = "0x" + function_signature; data += base_encoder::encode_uint256(64); @@ -42,7 +42,7 @@ std::string update_owners_encoder::encode(const std::vector sidechain_net_handler_ethereum::estimate_withdrawal_transaction_ } const auto &public_key = son->sidechain_public_keys.at(sidechain); - const ethereum::withdrawal_encoder encoder; - const auto data = encoder.encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); + const auto data = ethereum::withdrawal_encoder::encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; const auto estimate_gas = ethereum::from_hex(rpc_client->get_estimate_gas(params)); diff --git a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp index 5bcc0543..47cc090c 100644 --- a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp +++ b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp @@ -10,21 +10,21 @@ BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests) BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) { const auto tx = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0"); - BOOST_CHECK_EQUAL(tx, "0xcf7c8f6d0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); const auto tx1 = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1"); - BOOST_CHECK_EQUAL(tx1, "0xcf7c8f6d0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(withdrawal_signature_encoder_test) { const signature_encoder encoder{withdrawal_function_signature}; encoded_sign_transaction transaction; - transaction.data = "0xcf7c8f6d0000000000000000000000005c79a9f5767e3c1b926f963fa24e21d8a04289ae0000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33372E300000000000000000000000000000000000000000000000000000"; + transaction.data = "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"; transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); const auto tx = encoder.encode({transaction}); - BOOST_CHECK_EQUAL(tx, "0xdaac6c8100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000065ef357c2788df103d443e3c4a9e0864d6f52fbaab479684b8eb6ea09422b6b54310ca0511c6cc02402a668a73f9613c00f6f535dbd2d78468ecba540a8a0dba3d00000000000000000000000000000000000000000000000000000000000000a4cf7c8f6d0000000000000000000000005c79a9f5767e3c1b926f963fa24e21d8a04289ae0000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33372E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx, "0xdaac6c8100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000065c4622d2ff2b2d89c5c6f8225ab0f979bc69d4fcd4fd47db757b66fb8a39e2bc5522be5d101aa11e66da78db973f136b323be10bd107ff0b648f06b4c71ef2a4f00000000000000000000000000000000000000000000000000000000000000a4e088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { @@ -33,22 +33,22 @@ BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1); const auto tx = update_owners_encoder::encode(owners_weights, "1.35.0"); - BOOST_CHECK_EQUAL(tx, "0xf6afbeff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1); const auto tx1 = update_owners_encoder::encode(owners_weights, "1.36.1"); - BOOST_CHECK_EQUAL(tx1, "0xf6afbeff0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(update_owners_signature_encoder_test) { const signature_encoder encoder{update_owners_function_signature}; encoded_sign_transaction transaction; - transaction.data = "0xf6afbeff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"; + transaction.data = "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"; transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); const auto tx = encoder.encode({transaction}); - BOOST_CHECK_EQUAL(tx, "0x9d608673000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000665762851a05348a4900393b3f2778c5078689422e43e3a2aa9b517a4141133bd42c08169979785256a2f26d1da13e297125ee84c2d79be07212cfde592fc03dbb0000000000000000000000000000000000000000000000000000000000000124f6afbeff000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx, "0x9d6086730000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006698877eafa525c1a55f6b5e0a7187dfae484c97d9f77c4421a00276a9408a3e713d24402b44c05a883142fcffa84e1a802be37c17bb360f6f4810eb0415c8bbfd000000000000000000000000000000000000000000000000000000000000012423ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) { From 4883dfe38d0eb3dfa71fcc2203b220a802e3453f Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 8 Dec 2022 14:03:14 +0000 Subject: [PATCH 071/106] #484 multiple eth withdrawals --- .../ethereum/transaction.cpp | 8 ++--- .../peerplays_sidechain/ethereum/utils.cpp | 20 +++++++++++ .../peerplays_sidechain/ethereum/utils.hpp | 4 +++ .../sidechain_net_handler.hpp | 9 ++--- .../sidechain_net_handler.cpp | 36 ++++++++++++------- .../sidechain_net_handler_ethereum.cpp | 2 +- 6 files changed, 57 insertions(+), 22 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp index dd1179b4..9a1383df 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp @@ -210,8 +210,8 @@ std::string signed_transaction::serialize() const { rlp_encoder::encode(remove_0x(value)) + rlp_encoder::encode(remove_0x(data)) + rlp_encoder::encode(remove_0x(v)) + - rlp_encoder::encode(remove_0x(r)) + - rlp_encoder::encode(remove_0x(s)); + rlp_encoder::encode(remove_leading_00(remove_0x(r))) + + rlp_encoder::encode(remove_leading_00(remove_0x(s))); return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized)); } @@ -234,9 +234,9 @@ void signed_transaction::deserialize(const std::string &raw_tx) { boost::algorithm::to_lower(data); v = add_0x(rlp_array.at(6)); boost::algorithm::to_lower(v); - r = add_0x(rlp_array.at(7)); + r = add_0x(add_leading_00(rlp_array.at(7))); boost::algorithm::to_lower(r); - s = add_0x(rlp_array.at(8)); + s = add_0x(add_leading_00(rlp_array.at(8))); boost::algorithm::to_lower(s); } diff --git a/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp index ce64e1ae..0f34cd9c 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp @@ -49,4 +49,24 @@ std::string remove_0x(const std::string &s) { return s; } +std::string add_leading_00(const std::string &s) { + std::string result = s; + + while (result.size() < 64) { + result = "00" + result; + } + + return result; +} + +std::string remove_leading_00(const std::string &s) { + std::string result = s; + + while (result.size() > 1 && result.substr(0, 2) == "00") { + result = result.substr(2); + } + + return result; +} + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp index b2db9331..4ac70094 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp @@ -14,6 +14,10 @@ std::string add_0x(const std::string &s); std::string remove_0x(const std::string &s); +std::string add_leading_00(const std::string &s); + +std::string remove_leading_00(const std::string &s); + template std::string to_hex(const T &val, bool add_front_zero = true) { std::stringstream stream; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index fc295b4c..0e7e2fc1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -20,10 +20,11 @@ public: sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); virtual ~sidechain_net_handler(); - sidechain_type get_sidechain(); - std::vector get_sidechain_deposit_addresses(); - std::vector get_sidechain_withdraw_addresses(); - std::string get_private_key(std::string public_key); + sidechain_type get_sidechain() const; + std::vector get_sidechain_deposit_addresses() const; + std::vector get_sidechain_withdraw_addresses() const; + std::vector get_sidechain_transaction_objects(sidechain_transaction_status status) const; + std::string get_private_key(std::string public_key) const; bool proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional proposal_op = boost::none); bool signer_expected(const sidechain_transaction_object &sto, son_id_type signer); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 971df7c7..38345935 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -21,11 +21,11 @@ sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin sidechain_net_handler::~sidechain_net_handler() { } -sidechain_type sidechain_net_handler::get_sidechain() { +sidechain_type sidechain_net_handler::get_sidechain() const { return sidechain; } -std::vector sidechain_net_handler::get_sidechain_deposit_addresses() { +std::vector sidechain_net_handler::get_sidechain_deposit_addresses() const { std::vector result; const auto &sidechain_addresses_idx = database.get_index_type(); @@ -38,7 +38,7 @@ std::vector sidechain_net_handler::get_sidechain_deposit_addresses( return result; } -std::vector sidechain_net_handler::get_sidechain_withdraw_addresses() { +std::vector sidechain_net_handler::get_sidechain_withdraw_addresses() const { std::vector result; const auto &sidechain_addresses_idx = database.get_index_type(); @@ -51,7 +51,20 @@ std::vector sidechain_net_handler::get_sidechain_withdraw_addresses return result; } -std::string sidechain_net_handler::get_private_key(std::string public_key) { +std::vector sidechain_net_handler::get_sidechain_transaction_objects(sidechain_transaction_status status) const { + std::vector result; + + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, status)); + std::for_each(idx_range.first, idx_range.second, + [&result](const sidechain_transaction_object &sto) { + result.push_back(sto); + }); + + return result; +} + +std::string sidechain_net_handler::get_private_key(std::string public_key) const { auto private_key_itr = private_keys.find(public_key); if (private_key_itr != private_keys.end()) { return private_key_itr->second; @@ -473,10 +486,9 @@ void sidechain_net_handler::process_withdrawals() { } void sidechain_net_handler::process_sidechain_transactions() { - const auto &idx = database.get_index_type().indices().get(); - const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid)); + const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::valid); - std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) { if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id(sidechain))) { return; } @@ -520,10 +532,9 @@ void sidechain_net_handler::process_sidechain_transactions() { } void sidechain_net_handler::send_sidechain_transactions() { - const auto &idx = database.get_index_type().indices().get(); - const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::complete)); + const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::complete); - std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) { if (sto.id == object_id_type(0, 0, 0)) { return; } @@ -555,10 +566,9 @@ void sidechain_net_handler::send_sidechain_transactions() { } void sidechain_net_handler::settle_sidechain_transactions() { - const auto &idx = database.get_index_type().indices().get(); - const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::sent)); + const auto stos = get_sidechain_transaction_objects(sidechain_transaction_status::sent); - std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + std::for_each(stos.cbegin(), stos.cend(), [&](const sidechain_transaction_object &sto) { if (sto.id == object_id_type(0, 0, 0)) { return; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 8faa5cdd..358c352c 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -76,7 +76,7 @@ std::string ethereum_rpc_client::get_network_id() { } std::string ethereum_rpc_client::get_nonce(const std::string &address) { - const std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]"); + const std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"pending\"]"); const auto nonce_string = retrieve_value_from_reply(reply_str, ""); if (!nonce_string.empty()) { const auto nonce_val = ethereum::from_hex(nonce_string); From d387e324fe515924197fa6d695c4a296c20eef8a Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Wed, 14 Dec 2022 15:52:28 +0200 Subject: [PATCH 072/106] #489 - Fix error message --- libraries/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 7eefae27..16a9cb38 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3349,7 +3349,7 @@ public: if(transaction_fee) { if (*transaction_fee >= xfer_op.amount) { - FC_THROW("Transaction fee: ${sidechain_fee}, is greater than transferred amount ${amount}", + FC_THROW("Transaction fee: ${sidechain_fee}, is greater than or equal to the transferred amount ${amount}", ("sidechain_fee", get_asset(transaction_fee->asset_id).amount_to_pretty_string(transaction_fee->amount))("amount", get_asset(xfer_op.amount.asset_id).amount_to_pretty_string(xfer_op.amount.amount))); } } From 578edc56d864a4637c9ef8dc2cb8efc3edcdae53 Mon Sep 17 00:00:00 2001 From: timur <12267899-timur.5@users.noreply.gitlab.com> Date: Fri, 16 Dec 2022 00:34:33 +0000 Subject: [PATCH 073/106] Fix Ubuntu 18 build --- Dockerfile | 65 +++++++++++++++++-- Dockerfile.18.04 | 44 +++++++++++-- .../wallet/include/graphene/wallet/wallet.hpp | 4 +- 3 files changed, 102 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 50c51c3a..3f4c62b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,15 +11,14 @@ RUN \ apt-utils \ autoconf \ bash \ + bison \ build-essential \ ca-certificates \ - cmake \ dnsutils \ - doxygen \ expect \ + flex \ git \ graphviz \ - libboost-all-dev \ libbz2-dev \ libcurl4-openssl-dev \ libncurses-dev \ @@ -35,7 +34,6 @@ RUN \ ntp \ openssh-server \ pkg-config \ - perl \ python3 \ python3-jinja2 \ sudo \ @@ -53,6 +51,31 @@ RUN echo 'peerplays:peerplays' | chpasswd # SSH EXPOSE 22 +#=============================================================================== +# Boost setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \ + tar -xzvf boost_1_72_0.tar.gz boost_1_72_0 && \ + cd boost_1_72_0/ && \ + ./bootstrap.sh && \ + ./b2 install + +#=============================================================================== +# cmake setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ + chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ + ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ + cmake --version + #=============================================================================== # libzmq setup #=============================================================================== @@ -85,6 +108,37 @@ RUN \ make -j$(nproc) install && \ ldconfig +#=============================================================================== +# Doxygen setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + sudo apt install -y bison flex && \ + wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \ + tar -xvf Release_1_8_17.tar.gz && \ + cd doxygen-Release_1_8_17 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install && \ + ldconfig + +#=============================================================================== +# Perl setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \ + tar -xvf v5.30.0.tar.gz && \ + cd perl5-5.30.0 && \ + ./Configure -des && \ + make -j$(nproc) install && \ + ldconfig + #=============================================================================== # Peerplays setup #=============================================================================== @@ -106,6 +160,9 @@ ADD . peerplays # Configure Peerplays RUN \ cd peerplays && \ + git submodule update --init --recursive && \ + git symbolic-ref --short HEAD && \ + git log --oneline -n 5 && \ mkdir build && \ cd build && \ cmake -DCMAKE_BUILD_TYPE=Release .. diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index 49a06fa1..2e153962 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -11,11 +11,12 @@ RUN \ apt-utils \ autoconf \ bash \ + bison \ build-essential \ ca-certificates \ dnsutils \ - doxygen \ expect \ + flex \ git \ graphviz \ libbz2-dev \ @@ -33,7 +34,6 @@ RUN \ ntp \ openssh-server \ pkg-config \ - perl \ python3 \ python3-jinja2 \ sudo \ @@ -58,9 +58,9 @@ EXPOSE 22 WORKDIR /home/peerplays/ RUN \ - wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 && \ - tar xjf boost_1_71_0.tar.bz2 && \ - cd boost_1_71_0/ && \ + wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \ + tar -xzvf boost_1_72_0.tar.gz boost_1_72_0 && \ + cd boost_1_72_0/ && \ ./bootstrap.sh && \ ./b2 install @@ -108,6 +108,37 @@ RUN \ make -j$(nproc) install && \ ldconfig +#=============================================================================== +# Doxygen setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + sudo apt install -y bison flex && \ + wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \ + tar -xvf Release_1_8_17.tar.gz && \ + cd doxygen-Release_1_8_17 && \ + mkdir build && \ + cd build && \ + cmake .. && \ + make -j$(nproc) install && \ + ldconfig + +#=============================================================================== +# Perl setup +#=============================================================================== + +WORKDIR /home/peerplays/ + +RUN \ + wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \ + tar -xvf v5.30.0.tar.gz && \ + cd perl5-5.30.0 && \ + ./Configure -des && \ + make -j$(nproc) install && \ + ldconfig + #=============================================================================== # Peerplays setup #=============================================================================== @@ -129,6 +160,9 @@ ADD . peerplays # Configure Peerplays RUN \ cd peerplays && \ + git submodule update --init --recursive && \ + git symbolic-ref --short HEAD && \ + git log --oneline -n 5 && \ mkdir build && \ cd build && \ cmake -DCMAKE_BUILD_TYPE=Release .. diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 6a0b5ee5..a7807596 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -980,13 +980,13 @@ class wallet_api * * @return true if the label was set, otherwise false */ - bool set_key_label( public_key_type, string label ); + bool set_key_label( public_key_type key, string label ); /** Get label of a public key. * @param key a public key * @return the label if already set by \c set_key_label(), or an empty string if not set */ - string get_key_label( public_key_type )const; + string get_key_label( public_key_type key )const; /* Get the public key associated with a given label. * @param label a label From bb7c534b10b114cbf3571f54a20f5ca8cba6e255 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Fri, 16 Dec 2022 04:27:31 +0100 Subject: [PATCH 074/106] Update Hive's son-account owner authority on primary wallet update --- .../sidechain_net_handler_hive.cpp | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 63ae22c6..873fa08e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -256,13 +256,14 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { const std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); - hive::authority active; - active.weight_threshold = total_weight * 2 / 3 + 1; - active.account_auths = account_auths; + hive::authority a; + a.weight_threshold = total_weight * 2 / 3 + 1; + a.account_auths = account_auths; hive::account_update_operation auo; auo.account = wallet_account_name; - auo.active = active; + auo.owner = a; + auo.active = a; auo.memo_key = op_trx.operations[0].get().memo_key; hive::signed_transaction htrx; @@ -505,13 +506,14 @@ void sidechain_net_handler_hive::process_primary_wallet() { return; } - hive::authority active; - active.weight_threshold = total_weight * 2 / 3 + 1; - active.account_auths = account_auths; + hive::authority a; + a.weight_threshold = total_weight * 2 / 3 + 1; + a.account_auths = account_auths; hive::account_update_operation auo; auo.account = wallet_account_name; - auo.active = active; + auo.owner = a; + auo.active = a; auo.memo_key = hive::public_key_type(memo_key); const std::string block_id_str = rpc_client->get_head_block_id(); @@ -546,12 +548,27 @@ void sidechain_net_handler_hive::process_primary_wallet() { proposal_op.proposed_ops.emplace_back(swu_op); + const auto signers = [this, &prev_sw, &active_sw, &swi] { + std::vector signers; + //! Check if we don't have any previous set of active SONs use the current one + if (prev_sw != swi.rend()) { + if (!prev_sw->sons.at(sidechain).empty()) + signers = prev_sw->sons.at(sidechain); + else + signers = active_sw->sons.at(sidechain); + } else { + signers = active_sw->sons.at(sidechain); + } + + return signers; + }(); + sidechain_transaction_create_operation stc_op; stc_op.payer = gpo.parameters.son_account(); stc_op.object_id = active_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons.at(sidechain); + stc_op.signers = signers; proposal_op.proposed_ops.emplace_back(stc_op); From ca69a692cce0b660806b699b6a1fb1b1b536c5ea Mon Sep 17 00:00:00 2001 From: timur <12267899-timur.5@users.noreply.gitlab.com> Date: Wed, 21 Dec 2022 17:45:42 +0000 Subject: [PATCH 075/106] Add or revive a few NFT-listing APIs --- libraries/app/database_api.cpp | 61 ++- .../app/include/graphene/app/database_api.hpp | 16 +- .../include/graphene/chain/nft_object.hpp | 3 + .../wallet/include/graphene/wallet/wallet.hpp | 24 +- libraries/wallet/wallet.cpp | 23 +- tests/tests/nft_tests.cpp | 361 +++++++++++++----- 6 files changed, 384 insertions(+), 104 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 64fb82c2..e22e1dc4 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -269,8 +269,9 @@ public: uint64_t nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const; nft_object nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const; nft_object nft_token_of_owner_by_index(const nft_metadata_id_type nft_metadata_id, const account_id_type owner, const uint64_t token_idx) const; - vector nft_get_all_tokens() const; - vector nft_get_tokens_by_owner(const account_id_type owner) const; + vector nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const; + vector nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const; + vector nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const; // Marketplace vector list_offers(const offer_id_type lower_id, uint32_t limit) const; @@ -291,6 +292,7 @@ public: uint32_t api_limit_get_limit_orders_by_account = 101; uint32_t api_limit_get_order_book = 50; uint32_t api_limit_all_offers_count = 100; + uint32_t api_limit_nft_tokens = 100; uint32_t api_limit_lookup_accounts = 1000; uint32_t api_limit_lookup_witness_accounts = 1000; uint32_t api_limit_lookup_committee_member_accounts = 1000; @@ -3102,30 +3104,61 @@ nft_object database_api_impl::nft_token_of_owner_by_index(const nft_metadata_id_ return {}; } -vector database_api::nft_get_all_tokens() const { - return my->nft_get_all_tokens(); +vector database_api::nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const { + return my->nft_get_all_tokens(lower_id, limit); } -vector database_api_impl::nft_get_all_tokens() const { +vector database_api_impl::nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const { + FC_ASSERT(limit <= api_limit_nft_tokens, + "Number of queried nft tokens can not be greater than ${configured_limit}", + ("configured_limit", api_limit_nft_tokens)); + const auto &idx_nft = _db.get_index_type().indices().get(); vector result; - for (auto itr = idx_nft.begin(); itr != idx_nft.end(); ++itr) { - result.push_back(*itr); - } + result.reserve(limit); + auto itr = idx_nft.lower_bound(lower_id); + while (limit-- && itr != idx_nft.end()) + result.emplace_back(*itr++); return result; } -vector database_api::nft_get_tokens_by_owner(const account_id_type owner) const { - return my->nft_get_tokens_by_owner(owner); +vector database_api::nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const { + return my->nft_get_tokens_by_owner(owner, lower_id, limit); } -vector database_api_impl::nft_get_tokens_by_owner(const account_id_type owner) const { +vector database_api_impl::nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const { + FC_ASSERT(limit <= api_limit_nft_tokens, + "Number of queried nft tokens can not be greater than ${configured_limit}", + ("configured_limit", api_limit_nft_tokens)); const auto &idx_nft = _db.get_index_type().indices().get(); auto idx_nft_range = idx_nft.equal_range(owner); vector result; - for (auto itr = idx_nft_range.first; itr != idx_nft_range.second; ++itr) { - result.push_back(*itr); - } + result.reserve(limit); + auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_object &obj) { + return !(obj.id.instance() < lower_id.instance); + }); + while (limit-- && itr != idx_nft_range.second) + result.emplace_back(*itr++); + return result; +} + +vector database_api::nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const { + return my->nft_get_metadata_by_owner(owner, lower_id, limit); +} + +vector database_api_impl::nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const { + FC_ASSERT(limit <= api_limit_nft_tokens, + "Number of queried nft metadata objects can not be greater than ${configured_limit}", + ("configured_limit", api_limit_nft_tokens)); + const auto &idx_nft = _db.get_index_type().indices().get(); + auto idx_nft_range = idx_nft.equal_range(owner); + vector result; + result.reserve(limit); + auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_metadata_object &obj) { + return !(obj.id.instance() < lower_id.instance); + }); + while (limit-- && itr != idx_nft_range.second) + result.emplace_back(*itr++); return result; } diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index b53c8eeb..55da8e5f 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -1027,14 +1027,25 @@ public: * @brief Returns list of all available NTF's * @return List of all available NFT's */ - vector nft_get_all_tokens() const; + vector nft_get_all_tokens(const nft_id_type lower_id, uint32_t limit) const; /** * @brief Returns NFT's owned by owner * @param owner NFT owner + * @param lower_id ID of the first NFT to return + * @param limit Maximum number of results to return * @return List of NFT owned by owner */ - vector nft_get_tokens_by_owner(const account_id_type owner) const; + vector nft_get_tokens_by_owner(const account_id_type owner, const nft_id_type lower_id, uint32_t limit) const; + + /** + * @brief Returns NFT metadata owned by owner + * @param owner NFT owner + * @param lower_id ID of the first NFT metadata to return + * @param limit Maximum number of results to return + * @return List of NFT owned by owner + */ + vector nft_get_metadata_by_owner(const account_id_type owner, const nft_metadata_id_type lower_id, uint32_t limit) const; ////////////////// // MARKET PLACE // @@ -1249,6 +1260,7 @@ FC_API(graphene::app::database_api, (nft_token_of_owner_by_index) (nft_get_all_tokens) (nft_get_tokens_by_owner) + (nft_get_metadata_by_owner) // Marketplace (list_offers) diff --git a/libraries/chain/include/graphene/chain/nft_object.hpp b/libraries/chain/include/graphene/chain/nft_object.hpp index fe026da5..a54992c4 100644 --- a/libraries/chain/include/graphene/chain/nft_object.hpp +++ b/libraries/chain/include/graphene/chain/nft_object.hpp @@ -130,6 +130,9 @@ namespace graphene { namespace chain { std::greater< uint32_t >, std::greater< object_id_type > > + >, + ordered_non_unique< tag, + member > > >; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index a7807596..3d8d5b06 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -2571,9 +2571,29 @@ class wallet_api /** * @brief Returns all tokens + * @param limit the maximum number of NFT objects to return (max: 100) + * @param lower_id ID of the first NFT object to include in the list. * @return Returns vector of NFT objects, empty vector if none */ - vector nft_get_all_tokens() const; + vector nft_get_all_tokens(uint32_t limit, optional lower_id) const; + + /** + * @brief Returns all tokens owned by owner + * @param owner NFT owner account ID + * @param limit the maximum number of NFT objects to return (max: 100) + * @param lower_id ID of the first NFT object to include in the list. + * @return Returns vector of NFT objects, empty vector if none + */ + vector nft_get_tokens_by_owner(account_id_type owner, uint32_t limit, optional lower_id) const; + + /** + * @brief Returns all NFT metadata objects owned by owner + * @param owner NFT owner account ID + * @param limit the maximum number of NFT metadata objects to return (max: 100) + * @param lower_id ID of the first NFT metadata object to include in the list. + * @return Returns vector of NFT metadata objects, empty vector if none + */ + vector nft_get_metadata_by_owner(account_id_type owner, uint32_t limit, optional lower_id) const; signed_transaction nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast ); signed_transaction create_offer(set item_ids, @@ -2943,6 +2963,8 @@ FC_API( graphene::wallet::wallet_api, (nft_get_approved) (nft_is_approved_for_all) (nft_get_all_tokens) + (nft_get_tokens_by_owner) + (nft_get_metadata_by_owner) (nft_lottery_buy_ticket) (create_offer) (create_bid) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 7eefae27..bf239679 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -7027,9 +7027,28 @@ bool wallet_api::nft_is_approved_for_all(string owner_account_id_or_name, string return my->_remote_db->nft_is_approved_for_all(owner_account.id, operator_account.id); } -vector wallet_api::nft_get_all_tokens() const +vector wallet_api::nft_get_all_tokens(uint32_t limit, optional lower_id) const { - return my->_remote_db->nft_get_all_tokens(); + nft_id_type lb_id; + if(lower_id) + lb_id = *lower_id; + return my->_remote_db->nft_get_all_tokens(lb_id, limit); +} + +vector wallet_api::nft_get_tokens_by_owner(account_id_type owner, uint32_t limit, optional lower_id) const +{ + nft_id_type lb_id; + if(lower_id) + lb_id = *lower_id; + return my->_remote_db->nft_get_tokens_by_owner(owner, lb_id, limit); +} + +vector wallet_api::nft_get_metadata_by_owner(account_id_type owner, uint32_t limit, optional lower_id) const +{ + nft_metadata_id_type lb_id; + if(lower_id) + lb_id = *lower_id; + return my->_remote_db->nft_get_metadata_by_owner(owner, lb_id, limit); } signed_transaction wallet_api::nft_lottery_buy_ticket( nft_metadata_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy, bool broadcast ) diff --git a/tests/tests/nft_tests.cpp b/tests/tests/nft_tests.cpp index b8fd19ea..83643eed 100644 --- a/tests/tests/nft_tests.cpp +++ b/tests/tests/nft_tests.cpp @@ -4,76 +4,204 @@ #include #include +#include + using namespace graphene::chain; using namespace graphene::chain::test; + +class nft_test_helper +{ + database_fixture& fixture_; + +public: + nft_test_helper(database_fixture& fixture): + fixture_(fixture) + { + fixture_.generate_blocks(HARDFORK_NFT_TIME); + fixture_.generate_block(); + fixture_.generate_block(); + set_expiration(fixture_.db, fixture_.trx); + } + + nft_metadata_object create_metadata(const std::string& name, const std::string& symbol, const std::string& uri, const account_id_type& owner, const fc::ecc::private_key &priv_key) + { + const auto& idx_by_id = fixture_.db.get_index_type().indices().get(); + size_t obj_count0 = idx_by_id.size(); + + fixture_.generate_block(); + + signed_transaction trx; + set_expiration(fixture_.db, trx); + + nft_metadata_create_operation op; + op.owner = owner; + op.symbol = symbol; + op.base_uri = uri; + op.name = name; + op.is_transferable = true; + BOOST_CHECK_NO_THROW(op.validate()); + trx.operations.push_back(op); + fixture_.sign(trx, priv_key); + PUSH_TX(fixture_.db, trx, ~0); + fixture_.generate_block(); + + BOOST_REQUIRE( idx_by_id.size() == obj_count0 + 1 ); // one more metadata created + + const auto& idx_by_name = fixture_.db.get_index_type().indices().get(); + auto obj = idx_by_name.find(name); + BOOST_CHECK( obj->owner == owner ); + BOOST_CHECK( obj->name == name ); + BOOST_CHECK( obj->symbol == symbol ); + BOOST_CHECK( obj->base_uri == uri ); + return *obj; + } + + + nft_object mint(const nft_metadata_id_type& metadata, const account_id_type& owner, const account_id_type& payer, + const fc::optional& approved, const std::vector& approved_operators, + const fc::ecc::private_key &priv_key) + { + const auto& idx_by_id = fixture_.db.get_index_type().indices().get(); + size_t obj_count0 = idx_by_id.size(); + + fixture_.generate_block(); + + signed_transaction trx; + set_expiration(fixture_.db, trx); + + nft_mint_operation op; + op.nft_metadata_id = metadata; + op.payer = payer; + op.owner = owner; + if (approved) + op.approved = *approved; + op.approved_operators = approved_operators; + + trx.operations.push_back(op); + fixture_.sign(trx, priv_key); + PUSH_TX(fixture_.db, trx, ~0); + + fixture_.generate_block(); + + BOOST_REQUIRE(idx_by_id.size() == obj_count0 + 1); // one more created + + auto obj = idx_by_id.rbegin(); + + BOOST_REQUIRE(obj != idx_by_id.rend()); + BOOST_CHECK(obj->owner == owner); + BOOST_CHECK(obj->approved_operators.size() == approved_operators.size()); + BOOST_CHECK(obj->approved_operators == approved_operators); + + return *obj; + } +}; + + BOOST_FIXTURE_TEST_SUITE( nft_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( nft_metadata_name_validation_test ) { + BOOST_TEST_MESSAGE("nft_metadata_name_validation_test"); + ACTORS((mdowner)); + nft_metadata_create_operation op; + op.owner = mdowner_id; + op.symbol = "NFT"; + op.base_uri = "http://nft.example.com"; + op.name = "123"; + op.is_transferable = true; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = ""; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = "1ab"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = ".abc"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = "abc."; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = "ABC"; + BOOST_CHECK_NO_THROW(op.validate()); + op.name = "abcdefghijklmnopq"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = "ab"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = "***"; + BOOST_CHECK_THROW(op.validate(), fc::exception); + op.name = "a12"; + BOOST_CHECK_NO_THROW(op.validate()); + op.name = "a1b"; + BOOST_CHECK_NO_THROW(op.validate()); + op.name = "abc"; + BOOST_CHECK_NO_THROW(op.validate()); + op.name = "abc123defg12345"; + BOOST_CHECK_NO_THROW(op.validate()); + op.name = "NFT Test"; + BOOST_CHECK_NO_THROW(op.validate()); +} + + BOOST_AUTO_TEST_CASE( nft_metadata_create_test ) { BOOST_TEST_MESSAGE("nft_metadata_create_test"); - generate_blocks(HARDFORK_NFT_TIME); - generate_block(); - generate_block(); - set_expiration(db, trx); - + nft_test_helper nfth(*this); ACTORS((mdowner)); + nfth.create_metadata("NFT Test", "NFT", "http://nft.example.com", mdowner_id, mdowner_private_key); +} - generate_block(); - set_expiration(db, trx); +BOOST_AUTO_TEST_CASE( nft_metadata_listing_test ) { + + BOOST_TEST_MESSAGE("nft_metadata_listing_test"); + + nft_test_helper nfth(*this); + + ACTORS((mdowner1)); + ACTORS((mdowner2)); + + // prepare metadata set + for (int i=0; i < 200; i++) { - BOOST_TEST_MESSAGE("Send nft_metadata_create_operation"); - - nft_metadata_create_operation op; - op.owner = mdowner_id; - op.symbol = "NFT"; - op.base_uri = "http://nft.example.com"; - op.name = "123"; - op.is_transferable = true; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = ""; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = "1ab"; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = ".abc"; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = "abc."; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = "ABC"; - BOOST_CHECK_NO_THROW(op.validate()); - op.name = "abcdefghijklmnopq"; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = "ab"; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = "***"; - BOOST_CHECK_THROW(op.validate(), fc::exception); - op.name = "a12"; - BOOST_CHECK_NO_THROW(op.validate()); - op.name = "a1b"; - BOOST_CHECK_NO_THROW(op.validate()); - op.name = "abc"; - BOOST_CHECK_NO_THROW(op.validate()); - op.name = "abc123defg12345"; - BOOST_CHECK_NO_THROW(op.validate()); - op.name = "NFT Test"; - trx.operations.push_back(op); - sign(trx, mdowner_private_key); - PUSH_TX(db, trx, ~0); + string sfx = fc::to_pretty_string(i); + nft_metadata_object md = nfth.create_metadata("NFT Test " + sfx, "NFT" + sfx, "http://nft.example.com", mdowner1_id, mdowner1_private_key); + BOOST_REQUIRE(md.id == nft_metadata_id_type(i)); + } + for (int i=200; i < 250; i++) + { + string sfx = fc::to_pretty_string(i); + nft_metadata_object md = nfth.create_metadata("NFT Test " + sfx, "NFT" + sfx, "http://nft.example.com", mdowner2_id, mdowner2_private_key); + BOOST_REQUIRE(md.id == nft_metadata_id_type(i)); } - generate_block(); - BOOST_TEST_MESSAGE("Check nft_metadata_create_operation results"); + graphene::app::database_api db_api(db); + vector listed; - const auto& idx = db.get_index_type().indices().get(); - BOOST_REQUIRE( idx.size() == 1 ); - auto obj = idx.begin(); - BOOST_REQUIRE( obj != idx.end() ); - BOOST_CHECK( obj->owner == mdowner_id ); - BOOST_CHECK( obj->name == "NFT Test" ); - BOOST_CHECK( obj->symbol == "NFT" ); - BOOST_CHECK( obj->base_uri == "http://nft.example.com" ); + // first 100 returned + listed = db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(0), 100); + BOOST_REQUIRE(listed.size() == 100); + BOOST_REQUIRE(listed[ 0].id == nft_metadata_id_type( 0)); + BOOST_REQUIRE(listed[99].id == nft_metadata_id_type(99)); + + // 100 starting from 50 + listed = db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(50), 100); + BOOST_REQUIRE(listed.size() == 100); + BOOST_REQUIRE(listed[ 0].id == nft_metadata_id_type( 50)); + BOOST_REQUIRE(listed[99].id == nft_metadata_id_type(149)); + + // the last 5 must be returned + listed = db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(195), 10); + BOOST_REQUIRE(listed.size() == 5); + BOOST_REQUIRE(listed[0].id == nft_metadata_id_type(195)); + BOOST_REQUIRE(listed[4].id == nft_metadata_id_type(199)); + + // too much requested at once + BOOST_CHECK_THROW(db_api.nft_get_metadata_by_owner(mdowner1_id, nft_metadata_id_type(0), 101), fc::exception); + + // the last 40 must be returned + listed = db_api.nft_get_metadata_by_owner(mdowner2_id, nft_metadata_id_type(210), 100); + BOOST_REQUIRE(listed.size() == 40); + BOOST_REQUIRE(listed[ 0].id == nft_metadata_id_type(210)); + BOOST_REQUIRE(listed[39].id == nft_metadata_id_type(249)); } @@ -120,49 +248,112 @@ BOOST_AUTO_TEST_CASE( nft_mint_test ) { generate_block(); set_expiration(db, trx); - INVOKE(nft_metadata_create_test); + nft_test_helper nfth(*this); + ACTORS((mdowner)); ACTORS((alice)); ACTORS((bob)); ACTORS((operator1)); ACTORS((operator2)); - GET_ACTOR(mdowner); + nft_metadata_object md = nfth.create_metadata("NFT Test", "NFT", "http://nft.example.com", mdowner_id, mdowner_private_key); - generate_block(); - set_expiration(db, trx); + nfth.mint(md.id, alice_id, mdowner_id, alice_id, {operator1_id, operator2_id}, alice_private_key); +} + +BOOST_AUTO_TEST_CASE( nft_object_listing_test ) { + + BOOST_TEST_MESSAGE("nft_object_listing_test"); + + nft_test_helper nfth(*this); + + ACTORS((mdowner1)); + ACTORS((mdowner2)); + ACTORS((alice)); + ACTORS((bob)); + + nft_metadata_object md1 = nfth.create_metadata("NFT Test 1", "NFT1", "http://nft.example.com", mdowner1_id, mdowner1_private_key); + nft_metadata_object md2 = nfth.create_metadata("NFT Test 2", "NFT2", "http://nft.example.com", mdowner2_id, mdowner2_private_key); + + // create NFT objects: 200 owned by alice and 200 by bob + for (int i=0; i < 200; i++) { - BOOST_TEST_MESSAGE("Send nft_mint_operation"); - - const auto& idx = db.get_index_type().indices().get(); - BOOST_REQUIRE( idx.size() == 1 ); - auto nft_md_obj = idx.begin(); - - nft_mint_operation op; - op.payer = mdowner_id; - op.nft_metadata_id = nft_md_obj->id; - op.owner = alice_id; - op.approved = alice_id; - op.approved_operators.push_back(operator1_id); - op.approved_operators.push_back(operator2_id); - - trx.operations.push_back(op); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); + nft_object nft = nfth.mint(md1.id, alice_id, mdowner1_id, alice_id, {}, alice_private_key); + BOOST_REQUIRE(nft.id == nft_id_type(i)); + } + for (int i=200; i < 250; i++) + { + nft_object nft = nfth.mint(md1.id, bob_id, mdowner1_id, bob_id, {}, bob_private_key); + BOOST_REQUIRE(nft.id == nft_id_type(i)); } - generate_block(); - BOOST_TEST_MESSAGE("Check nft_mint_operation results"); + graphene::app::database_api db_api(db); + vector listed; - const auto& idx = db.get_index_type().indices().get(); - BOOST_REQUIRE( idx.size() == 1 ); - auto obj = idx.begin(); - BOOST_REQUIRE( obj != idx.end() ); - BOOST_CHECK( obj->owner == alice_id ); - BOOST_CHECK( obj->approved_operators.size() == 2 ); - BOOST_CHECK( obj->approved_operators.at(0) == operator1_id ); - BOOST_CHECK( obj->approved_operators.at(1) == operator2_id ); + // + // listing all tokens: + // + // first 100 returned, all alice's + listed = db_api.nft_get_all_tokens(nft_id_type(0), 100); + BOOST_REQUIRE(listed.size() == 100); + BOOST_REQUIRE(listed[ 0].id == nft_id_type( 0)); + BOOST_REQUIRE(listed[99].id == nft_id_type(99)); + BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; })); + + // 100 starting from 50, all alice's + listed = db_api.nft_get_all_tokens(nft_id_type(50), 100); + BOOST_REQUIRE(listed.size() == 100); + BOOST_REQUIRE(listed[ 0].id == nft_id_type( 50)); + BOOST_REQUIRE(listed[99].id == nft_id_type(149)); + BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; })); + + // the last 5 must be returned, all bob's + listed = db_api.nft_get_all_tokens(nft_id_type(245), 10); + BOOST_REQUIRE(listed.size() == 5); + BOOST_REQUIRE(listed[0].id == nft_id_type(245)); + BOOST_REQUIRE(listed[4].id == nft_id_type(249)); + BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [bob_id](const nft_object &obj){ return obj.owner == bob_id; })); + + // 10 from the middle of the set, half alice's, half bob's + listed = db_api.nft_get_all_tokens(nft_id_type(195), 10); + BOOST_REQUIRE(listed.size() == 10); + BOOST_REQUIRE(listed[0].id == nft_id_type(195)); + BOOST_REQUIRE(listed[9].id == nft_id_type(204)); + BOOST_REQUIRE(listed[0].owner == alice_id); + BOOST_REQUIRE(listed[4].owner == alice_id); + BOOST_REQUIRE(listed[5].owner == bob_id); + BOOST_REQUIRE(listed[9].owner == bob_id); + + // too much requested at once + BOOST_CHECK_THROW(db_api.nft_get_all_tokens(nft_id_type(0), 101), fc::exception); + + // + // listing tokens by owner: + // + // first 100 alice's + listed = db_api.nft_get_tokens_by_owner(alice_id, nft_id_type(0), 100); + BOOST_REQUIRE(listed.size() == 100); + BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; })); + BOOST_REQUIRE(listed[ 0].id == nft_id_type( 0)); + BOOST_REQUIRE(listed[99].id == nft_id_type(99)); + + // the last 5 alice's must be returned + listed = db_api.nft_get_tokens_by_owner(alice_id, nft_id_type(195), 10); + BOOST_REQUIRE(listed.size() == 5); + BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [alice_id](const nft_object &obj){ return obj.owner == alice_id; })); + BOOST_REQUIRE(listed[0].id == nft_id_type(195)); + BOOST_REQUIRE(listed[4].id == nft_id_type(199)); + + // all 50 bob's + listed = db_api.nft_get_tokens_by_owner(bob_id, nft_id_type(0), 60); + BOOST_REQUIRE(listed.size() == 50); + BOOST_REQUIRE(all_of(listed.begin(), listed.end(), [bob_id](const nft_object &obj){ return obj.owner == bob_id; })); + BOOST_REQUIRE(listed[ 0].id == nft_id_type(200)); + BOOST_REQUIRE(listed[49].id == nft_id_type(249)); + + // too much requested at once + BOOST_CHECK_THROW(db_api.nft_get_tokens_by_owner(alice_id, nft_id_type(0), 101), fc::exception); } From d264398a6f7293ec1e30df3b9717e0e52d6e6efe Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Fri, 23 Dec 2022 15:37:15 +0200 Subject: [PATCH 076/106] #499 - son_wallet_deposit_process_operation approve fix --- libraries/app/database_api.cpp | 10 +- .../sidechain_net_handler_ethereum.hpp | 1 + .../sidechain_net_handler_ethereum.cpp | 132 ++++++++---------- 3 files changed, 67 insertions(+), 76 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index e22e1dc4..30f2e2df 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -3135,10 +3135,10 @@ vector database_api_impl::nft_get_tokens_by_owner(const account_id_t vector result; result.reserve(limit); auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_object &obj) { - return !(obj.id.instance() < lower_id.instance); - }); + return !(obj.id.instance() < lower_id.instance); + }); while (limit-- && itr != idx_nft_range.second) - result.emplace_back(*itr++); + result.emplace_back(*itr++); return result; } @@ -3155,8 +3155,8 @@ vector database_api_impl::nft_get_metadata_by_owner(const a vector result; result.reserve(limit); auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_metadata_object &obj) { - return !(obj.id.instance() < lower_id.instance); - }); + return !(obj.id.instance() < lower_id.instance); + }); while (limit-- && itr != idx_nft_range.second) result.emplace_back(*itr++); return result; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index e6e8f298..8bc463ab 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -34,6 +34,7 @@ public: std::string eth_send_transaction(const std::string ¶ms); std::string eth_send_raw_transaction(const std::string ¶ms); std::string eth_get_transaction_receipt(const std::string ¶ms); + std::string eth_get_transaction_by_hash(const std::string ¶ms); }; class sidechain_net_handler_ethereum : public sidechain_net_handler { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index a237c4f4..6fe4a91d 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -121,6 +121,10 @@ std::string ethereum_rpc_client::eth_get_transaction_receipt(const std::string & return send_post_request("eth_getTransactionReceipt", "[\"" + params + "\"]", debug_rpc_calls); } +std::string ethereum_rpc_client::eth_get_transaction_by_hash(const std::string ¶ms) { + return send_post_request("eth_getTransactionByHash", "[\"" + params + "\"]", debug_rpc_calls); +} + sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::ethereum; @@ -213,13 +217,13 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) case chain::operation::tag::value: { bool address_ok = false; bool transaction_ok = false; - son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; + const son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; const auto &idx = database.get_index_type().indices().get(); const auto swo = idx.find(swo_id); if (swo != idx.end()) { - auto active_sons = gpo.active_sons.at(sidechain); - vector wallet_sons = swo->sons.at(sidechain); + const auto active_sons = gpo.active_sons.at(sidechain); + const vector wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -234,8 +238,8 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) } if (po.proposed_transaction.operations.size() >= 2) { - object_id_type object_id = op_obj_idx_1.get().object_id; - std::string op_tx_str = op_obj_idx_1.get().transaction; + const object_id_type object_id = op_obj_idx_1.get().object_id; + const std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); const auto st = st_idx.find(object_id); @@ -265,64 +269,45 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) case chain::operation::tag::value: { bool process_ok = false; - son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; + const son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; const auto &idx = database.get_index_type().indices().get(); const auto swdo = idx.find(swdo_id); if (swdo != idx.end()) { - //std::string swdo_txid = swdo->sidechain_transaction_id; - //std::string swdo_sidechain_from = swdo->sidechain_from; - //std::string swdo_sidechain_currency = swdo->sidechain_currency; - //uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value; - //uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-"))); - // - //std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid); - //if (tx_str != "") { - // - // std::stringstream ss_tx(tx_str); - // boost::property_tree::ptree tx; - // boost::property_tree::read_json(ss_tx, tx); - // - // uint64_t op_idx = -1; - // for (const auto &ops : tx.get_child("result.operations")) { - // const auto &op = ops.second; - // op_idx = op_idx + 1; - // if (op_idx == swdo_op_idx) { - // std::string operation_type = op.get("type"); - // - // if (operation_type == "transfer_operation") { - // const auto &op_value = op.get_child("value"); - // - // std::string sidechain_from = op_value.get("from"); - // - // const auto &amount_child = op_value.get_child("amount"); - // - // uint64_t amount = amount_child.get("amount"); - // std::string nai = amount_child.get("nai"); - // std::string sidechain_currency = ""; - // if ((nai == "@@000000013" /*?? HBD*/) || (nai == "@@000000013" /*TBD*/)) { - // sidechain_currency = "HBD"; - // } - // if ((nai == "@@000000021") /*?? HIVE*/ || (nai == "@@000000021" /*TESTS*/)) { - // sidechain_currency = "HIVE"; - // } - // - // std::string memo = op_value.get("memo"); - // boost::trim(memo); - // if (!memo.empty()) { - // sidechain_from = memo; - // } - // - // process_ok = (swdo_sidechain_from == sidechain_from) && - // (swdo_sidechain_currency == sidechain_currency) && - // (swdo_sidechain_amount == amount); - // } - // } - // } - //} - } + const std::string swdo_txid = swdo->sidechain_transaction_id; + const std::string swdo_sidechain_from = swdo->sidechain_from; + const std::string swdo_sidechain_currency = swdo->sidechain_currency; + const uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value; - process_ok = true; + const std::string tx_str = rpc_client->eth_get_transaction_by_hash(swdo_txid); + if (tx_str != "") { + + std::stringstream ss_tx(tx_str); + boost::property_tree::ptree tx; + boost::property_tree::read_json(ss_tx, tx); + + if (tx.get("result") != "null") { + + const std::string sidechain_from = tx.get("result.from"); + const std::string sidechain_to = tx.get("result.to"); + const std::string value_s = tx.get("result.value"); + boost::multiprecision::uint256_t amount(value_s); + amount = amount / 100000; + amount = amount / 100000; + const fc::safe sidechain_amount = amount; + + std::string cmp_sidechain_to = sidechain_to; + std::transform(cmp_sidechain_to.begin(), cmp_sidechain_to.end(), cmp_sidechain_to.begin(), ::toupper); + std::string cmp_wallet_contract_address = wallet_contract_address; + std::transform(cmp_wallet_contract_address.begin(), cmp_wallet_contract_address.end(), cmp_wallet_contract_address.begin(), ::toupper); + + process_ok = (swdo_sidechain_from == sidechain_from) && + (cmp_sidechain_to == cmp_wallet_contract_address) && + (swdo_sidechain_currency == "ETH") && + (swdo_sidechain_amount == sidechain_amount.value); + } + } + } should_approve = process_ok; break; @@ -331,23 +316,23 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) case chain::operation::tag::value: { bool process_ok = false; bool transaction_ok = false; - son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; + const son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; const auto &idx = database.get_index_type().indices().get(); const auto swwo = idx.find(swwo_id); if (swwo != idx.end()) { - uint32_t swwo_block_num = swwo->block_num; - std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id; - uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1)); + const uint32_t swwo_block_num = swwo->block_num; + const std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id; + const uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1)); const auto &block = database.fetch_block_by_number(swwo_block_num); for (const auto &tx : block->transactions) { if (tx.id().str() == swwo_peerplays_transaction_id) { - operation op = tx.operations[swwo_op_idx]; - transfer_operation t_op = op.get(); + const operation op = tx.operations[swwo_op_idx]; + const transfer_operation t_op = op.get(); - price asset_price = database.get(t_op.amount.asset_id).options.core_exchange_rate; - asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); + const price asset_price = database.get(t_op.amount.asset_id).options.core_exchange_rate; + const asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); process_ok = (t_op.to == gpo.parameters.son_account()) && (swwo->peerplays_from == t_op.from) && @@ -356,8 +341,8 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) } } - object_id_type object_id = op_obj_idx_1.get().object_id; - std::string op_tx_str = op_obj_idx_1.get().transaction; + const object_id_type object_id = op_obj_idx_1.get().object_id; + const std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); const auto st = st_idx.find(object_id); @@ -384,9 +369,9 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) case chain::operation::tag::value: { should_approve = true; - son_id_type signer = op_obj_idx_0.get().signer; - std::string signature = op_obj_idx_0.get().signature; - sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get().sidechain_transaction_id; + const son_id_type signer = op_obj_idx_0.get().signer; + const std::string signature = op_obj_idx_0.get().signature; + const sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get().sidechain_transaction_id; const auto &st_idx = database.get_index_type().indices().get(); const auto sto = st_idx.find(sidechain_transaction_id); if (sto == st_idx.end()) { @@ -497,6 +482,11 @@ void sidechain_net_handler_ethereum::process_sidechain_addresses() { } bool sidechain_net_handler_ethereum::process_deposit(const son_wallet_deposit_object &swdo) { + + if (proposal_exists(chain::operation::tag::value, swdo.id)) { + return false; + } + const chain::global_property_object &gpo = database.get_global_properties(); price asset_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; From 674b38910d785d33a3eb233524360df799a70e68 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Wed, 28 Dec 2022 08:23:29 +0100 Subject: [PATCH 077/106] Streamline get_block API from database and cli wallet --- libraries/app/database_api.cpp | 33 ++++++++++++++++--- .../app/include/graphene/app/database_api.hpp | 19 +++++++++++ .../wallet/include/graphene/wallet/wallet.hpp | 22 ++++--------- libraries/wallet/wallet.cpp | 22 ++++--------- 4 files changed, 60 insertions(+), 36 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index e22e1dc4..ef48a88d 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -71,6 +71,17 @@ std::string object_id_to_string(object_id_type id) { return object_id; } +signed_block_with_info::signed_block_with_info(){}; + +signed_block_with_info::signed_block_with_info(const signed_block &block) : + signed_block(block) { + block_id = id(); + signing_key = signee(); + transaction_ids.reserve(transactions.size()); + for (const processed_transaction &tx : transactions) + transaction_ids.push_back(tx.id()); +} + class database_api_impl : public std::enable_shared_from_this { public: database_api_impl(graphene::chain::database &db); @@ -89,6 +100,7 @@ public: optional get_block_header(uint32_t block_num) const; map> get_block_header_batch(const vector block_nums) const; optional get_block(uint32_t block_num) const; + optional get_block2(uint32_t block_num) const; vector> get_blocks(uint32_t block_num_from, uint32_t block_num_to) const; processed_transaction get_transaction(uint32_t block_num, uint32_t trx_in_block) const; @@ -532,6 +544,17 @@ optional database_api_impl::get_block(uint32_t block_num) const { return _db.fetch_block_by_number(block_num); } +optional database_api::get_block2(uint32_t block_num) const { + return my->get_block2(block_num); +} + +optional database_api_impl::get_block2(uint32_t block_num) const { + auto result = _db.fetch_block_by_number(block_num); + if (result) + return signed_block_with_info(*result); + return {}; +} + vector> database_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to) const { return my->get_blocks(block_num_from, block_num_to); } @@ -3135,10 +3158,10 @@ vector database_api_impl::nft_get_tokens_by_owner(const account_id_t vector result; result.reserve(limit); auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_object &obj) { - return !(obj.id.instance() < lower_id.instance); - }); + return !(obj.id.instance() < lower_id.instance); + }); while (limit-- && itr != idx_nft_range.second) - result.emplace_back(*itr++); + result.emplace_back(*itr++); return result; } @@ -3155,8 +3178,8 @@ vector database_api_impl::nft_get_metadata_by_owner(const a vector result; result.reserve(limit); auto itr = std::find_if(idx_nft_range.first, idx_nft_range.second, [&lower_id](const nft_metadata_object &obj) { - return !(obj.id.instance() < lower_id.instance); - }); + return !(obj.id.instance() < lower_id.instance); + }); while (limit-- && itr != idx_nft_range.second) result.emplace_back(*itr++); return result; diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 55da8e5f..948b6d55 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -82,6 +82,15 @@ using namespace std; class database_api_impl; +struct signed_block_with_info : public signed_block { + signed_block_with_info(); + signed_block_with_info(const signed_block &block); + signed_block_with_info(const signed_block_with_info &block) = default; + block_id_type block_id; + public_key_type signing_key; + vector transaction_ids; +}; + struct order { double price; double quote; @@ -202,6 +211,13 @@ public: */ optional get_block(uint32_t block_num) const; + /** + * @brief Retrieve a full, signed block, with some extra info + * @param block_num Height of the block to be returned + * @return the referenced block, or null if no matching block was found + */ + optional get_block2(uint32_t block_num) const; + /** * @brief Retrieve a list of signed blocks * @param block_num_from start @@ -1075,6 +1091,8 @@ extern template class fc::api; // clang-format off +FC_REFLECT_DERIVED(graphene::app::signed_block_with_info, (graphene::chain::signed_block), (block_id)(signing_key)(transaction_ids)); + FC_REFLECT(graphene::app::order, (price)(quote)(base)); FC_REFLECT(graphene::app::order_book, (base)(quote)(bids)(asks)); FC_REFLECT(graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume)); @@ -1097,6 +1115,7 @@ FC_API(graphene::app::database_api, (get_block_header) (get_block_header_batch) (get_block) + (get_block2) (get_blocks) (get_transaction) (get_recent_transaction_by_id) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 3d8d5b06..6d97f647 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -191,17 +191,6 @@ struct worker_vote_delta flat_set vote_abstain; }; -struct signed_block_with_info : public signed_block -{ - signed_block_with_info(); - signed_block_with_info( const signed_block& block ); - signed_block_with_info( const signed_block_with_info& block ) = default; - - block_id_type block_id; - public_key_type signing_key; - vector< transaction_id_type > transaction_ids; -}; - struct vesting_balance_object_with_info : public vesting_balance_object { vesting_balance_object_with_info(); @@ -276,7 +265,12 @@ class wallet_api * @param num height of the block to retrieve * @returns info about the block, or null if not found */ - optional get_block( uint32_t num ); + optional get_block( uint32_t num ); + /** Returns info about a specified block, with some extra info. + * @param num height of the block to retrieve + * @returns info about the block, or null if not found + */ + optional get_block2( uint32_t num ); /** Get signed blocks * @param block_num_from The lowest block number * @param block_num_to The highest block number @@ -2762,9 +2756,6 @@ FC_REFLECT( graphene::wallet::worker_vote_delta, (vote_abstain) ) -FC_REFLECT_DERIVED( graphene::wallet::signed_block_with_info, (graphene::chain::signed_block), - (block_id)(signing_key)(transaction_ids) ) - FC_REFLECT_DERIVED( graphene::wallet::vesting_balance_object_with_info, (graphene::chain::vesting_balance_object), (allowed_withdraw)(allowed_withdraw_time) ) @@ -2882,6 +2873,7 @@ FC_API( graphene::wallet::wallet_api, (get_account) (get_account_id) (get_block) + (get_block2) (get_blocks) (get_account_count) (get_account_history) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 237815c1..794e8692 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -4549,11 +4549,16 @@ bool wallet_api::copy_wallet_file(string destination_filename) return my->copy_wallet_file(destination_filename); } -optional wallet_api::get_block(uint32_t num) +optional wallet_api::get_block(uint32_t num) { return my->_remote_db->get_block(num); } +optional wallet_api::get_block2(uint32_t num) +{ + return my->_remote_db->get_block2(num); +} + vector> wallet_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to) const { return my->_remote_db->get_blocks(block_num_from, block_num_to); @@ -7289,10 +7294,6 @@ vector wallet_api::get_account_roles_by_owner(string owner_ account_object owner_account = my->get_account(owner_account_id_or_name); return my->_remote_db->get_account_roles_by_owner(owner_account.id); } -// default ctor necessary for FC_REFLECT -signed_block_with_info::signed_block_with_info() -{ -} order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit ) { @@ -7359,17 +7360,6 @@ std::string wallet_api::eth_estimate_withdrawal_transaction_fee() const return my->eth_estimate_withdrawal_transaction_fee(); } -// default ctor necessary for FC_REFLECT -signed_block_with_info::signed_block_with_info( const signed_block& block ) - : signed_block( block ) -{ - block_id = id(); - signing_key = signee(); - transaction_ids.reserve( transactions.size() ); - for( const processed_transaction& tx : transactions ) - transaction_ids.push_back( tx.id() ); -} - vesting_balance_object_with_info::vesting_balance_object_with_info() : vesting_balance_object() { From 68fbd6f40bb012bc2c7ceab496f92d7467caa350 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Wed, 28 Dec 2022 07:44:29 +0000 Subject: [PATCH 078/106] #496 process primary wallet --- libraries/chain/db_getter.cpp | 13 +++--- libraries/chain/db_maint.cpp | 5 ++- libraries/chain/son_evaluator.cpp | 5 ++- .../peerplays_sidechain_plugin.cpp | 44 ++++++++++++------- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 1b61ee79..4dc72649 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -236,8 +236,10 @@ std::set database::get_sons_to_be_deregistered() // TODO : We need to add a function that returns if we can deregister SON // i.e. with introduction of PW code, we have to make a decision if the SON // is needed for release of funds from the PW - if (head_block_time() - stats.last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { - need_to_be_deregistered = false; + if(stats.last_active_timestamp.contains(sidechain)) { + if (head_block_time() - stats.last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { + need_to_be_deregistered = false; + } } } } @@ -311,9 +313,10 @@ bool database::is_son_dereg_valid( son_id_type son_id ) if(status_son_dereg_valid) { - if(head_block_time() - son->statistics(*this).last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) - { - status_son_dereg_valid = false; + if(son->statistics(*this).last_active_timestamp.contains(sidechain)) { + if (head_block_time() - son->statistics(*this).last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { + status_son_dereg_valid = false; + } } } } diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 8673362d..f7b30caf 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -310,7 +310,10 @@ void database::update_son_metrics(const flat_mapstatuses.at(sidechain) == son_status::in_maintenance) { db().modify(itr->statistics(db()), [&](son_statistics_object &sso) { - sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - sso.last_down_timestamp.at(sidechain).sec_since_epoch(); + sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - (sso.last_down_timestamp.contains(sidechain) ? sso.last_down_timestamp.at(sidechain).sec_since_epoch() : op.ts.sec_since_epoch()); sso.last_active_timestamp[sidechain] = op.ts; }); @@ -245,7 +245,8 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati } FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); for(const auto& active_sidechain_type : active_sidechain_types) { - FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type)); + if(stats.last_active_timestamp.contains(active_sidechain_type)) + FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type)); } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index c8da6ce6..541b8537 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -398,14 +398,14 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio const chain::global_property_object &gpo = d.get_global_properties(); const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); const auto &idx = d.get_index_type().indices().get(); - son_report_down_operation down_op = op.get(); - auto son_obj = idx.find(down_op.son_id); + const son_report_down_operation down_op = op.get(); + const auto son_obj = idx.find(down_op.son_id); if (son_obj == idx.end()) { return false; } - auto stats = son_obj->statistics(d); - fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; - int64_t down_threshold = gpo.parameters.son_down_time(); + const auto stats = son_obj->statistics(d); + const fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; + const int64_t down_threshold = gpo.parameters.son_down_time(); bool status_son_down_op_valid = true; for (const auto &status : son_obj->statuses) { @@ -414,9 +414,11 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio } if (status_son_down_op_valid) { for (const auto &active_sidechain_type : active_sidechain_types) { - fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time); - if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) { - status_son_down_op_valid = false; + if (stats.last_active_timestamp.contains(active_sidechain_type)) { + const fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time); + if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) { + status_son_down_op_valid = false; + } } } } @@ -705,7 +707,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s proposal_create_operation proposal_op; proposal_op.fee_paying_account = get_current_son_object(sidechain).son_account; proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime); return proposal_op; }; @@ -714,19 +716,27 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s const chain::global_property_object &gpo = d.get_global_properties(); const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); const auto &idx = d.get_index_type().indices().get(); - std::set sons_being_reported_down = d.get_sons_being_reported_down(); - chain::son_id_type my_son_id = get_current_son_id(sidechain); + const std::set sons_being_reported_down = d.get_sons_being_reported_down(); + const chain::son_id_type my_son_id = get_current_son_id(sidechain); //! Fixme - check this part of the code for (auto son_inf : gpo.active_sons.at(sidechain)) { if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) { continue; } - auto son_obj = idx.find(son_inf.son_id); - auto stats = son_obj->statistics(d); - fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; - fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time); - int64_t down_threshold = gpo.parameters.son_down_time(); + + const auto son_obj = idx.find(son_inf.son_id); + const auto stats = son_obj->statistics(d); + const fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; + const fc::time_point_sec last_active_ts = [&stats, &sidechain, &last_maintenance_time] { + fc::time_point_sec last_active_ts; + if (stats.last_active_timestamp.contains(sidechain)) { + last_active_ts = (stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time; + } else + last_active_ts = last_maintenance_time; + return last_active_ts; + }(); + const int64_t down_threshold = gpo.parameters.son_down_time(); bool status_son_down_valid = true; for (const auto &status : son_obj->statuses) { @@ -756,7 +766,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s } void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) { - const std::lock_guard lck{access_son_down_prop_mutex}; + const std::lock_guard lck{access_son_deregister_prop_mutex}; chain::database &d = plugin.database(); std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); chain::son_id_type my_son_id = get_current_son_id(sidechain); From d89e5e1f2341888a24a03de0833a819904368db5 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Mon, 30 Jan 2023 19:27:27 +0200 Subject: [PATCH 079/106] #500 - fix son_wallet_update_operation --- libraries/chain/son_wallet_evaluator.cpp | 8 +++-- .../sidechain_net_handler_bitcoin.cpp | 30 ++++++++++++------- .../sidechain_net_handler_ethereum.cpp | 27 ++++++++++------- .../sidechain_net_handler_hive.cpp | 23 +++++++++----- 4 files changed, 58 insertions(+), 30 deletions(-) diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 9b1ff1b7..1a6b6f94 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -64,7 +64,9 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); - FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); + const auto id = (op.son_wallet_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(op.sidechain))) / active_sidechain_types.size(); + const son_wallet_id_type son_wallet_id{ id }; + FC_ASSERT( idx.find(son_wallet_id) != idx.end() ); //auto itr = idx.find(op.son_wallet_id); //FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() || // itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set"); @@ -74,7 +76,9 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) { try { const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_wallet_id); + const auto id = (op.son_wallet_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(op.sidechain))) / active_sidechain_types.size(); + const son_wallet_id_type son_wallet_id{ id }; + auto itr = idx.find(son_wallet_id); if (itr != idx.end()) { if (itr->addresses.find(op.sidechain) == itr->addresses.end()) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 862f991a..eabcac8e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -448,8 +448,10 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) bool transaction_ok = false; std::string new_pw_address = ""; son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; + const auto id = (swo_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const son_wallet_id_type op_id{ id }; const auto &idx = database.get_index_type().indices().get(); - const auto swo = idx.find(swo_id); + const auto swo = idx.find(op_id); if (swo != idx.end()) { const auto &active_sons = gpo.active_sons.at(sidechain); @@ -485,18 +487,20 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) } if (po.proposed_transaction.operations.size() >= 2) { - object_id_type object_id = op_obj_idx_1.get().object_id; + const object_id_type object_id = op_obj_idx_1.get().object_id; + const auto id = (object_id.instance() - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const object_id_type obj_id{ object_id.space(), object_id.type(), id }; std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); - const auto st = st_idx.find(object_id); + const auto st = st_idx.find(obj_id); if (st == st_idx.end()) { std::string tx_str = ""; - if (object_id.is()) { + if (obj_id.is()) { const auto &idx = database.get_index_type().indices().get(); - const auto swo = idx.find(object_id); + const auto swo = idx.find(obj_id); if (swo != idx.end()) { tx_str = create_primary_wallet_transaction(*swo, new_pw_address); } @@ -716,7 +720,10 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { - if (proposal_exists(chain::operation::tag::value, active_sw->id)) { + const auto id = active_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; + + if (proposal_exists(chain::operation::tag::value, op_id)) { return; } @@ -729,7 +736,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { boost::property_tree::ptree active_pw_pt; boost::property_tree::read_json(active_pw_ss, active_pw_pt); if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { - if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, active_sw->id)) { + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, op_id)) { return; } @@ -743,7 +750,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { son_wallet_update_operation swu_op; swu_op.payer = gpo.parameters.son_account(); - swu_op.son_wallet_id = active_sw->id; + swu_op.son_wallet_id = op_id; swu_op.sidechain = sidechain; swu_op.address = res.str(); @@ -753,9 +760,12 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { std::string new_pw_address = active_pw_pt.get("result.address"); std::string tx_str = create_primary_wallet_transaction(*prev_sw, new_pw_address); if (!tx_str.empty()) { + const auto prev_id = prev_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const object_id_type prev_op_id{ prev_sw->id.space(), prev_sw->id.type(), prev_id }; + sidechain_transaction_create_operation stc_op; stc_op.payer = gpo.parameters.son_account(); - stc_op.object_id = prev_sw->id; + stc_op.object_id = prev_op_id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; stc_op.signers = prev_sw->sons.at(sidechain); @@ -769,7 +779,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, active_sw->id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, op_id); } catch (fc::exception &e) { elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 6fe4a91d..09bd8b49 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -218,8 +218,10 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) bool address_ok = false; bool transaction_ok = false; const son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; + const auto id = (swo_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const son_wallet_id_type op_id{ id }; const auto &idx = database.get_index_type().indices().get(); - const auto swo = idx.find(swo_id); + const auto swo = idx.find(op_id); if (swo != idx.end()) { const auto active_sons = gpo.active_sons.at(sidechain); @@ -239,17 +241,19 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) if (po.proposed_transaction.operations.size() >= 2) { const object_id_type object_id = op_obj_idx_1.get().object_id; + const auto id = (object_id.instance() - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const object_id_type obj_id{ object_id.space(), object_id.type(), id }; const std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); - const auto st = st_idx.find(object_id); + const auto st = st_idx.find(obj_id); if (st == st_idx.end()) { std::string tx_str = ""; - if (object_id.is()) { + if (obj_id.is()) { const auto &idx = database.get_index_type().indices().get(); - const auto swo = idx.find(object_id); + const auto swo = idx.find(obj_id); if (swo != idx.end()) { tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), object_id.operator std::string()); } @@ -416,11 +420,14 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { - if (proposal_exists(chain::operation::tag::value, active_sw->id)) { + const auto id = active_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; + + if (proposal_exists(chain::operation::tag::value, op_id)) { return; } - if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, active_sw->id)) { + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, op_id)) { return; } @@ -432,7 +439,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { son_wallet_update_operation swu_op; swu_op.payer = gpo.parameters.son_account(); - swu_op.son_wallet_id = active_sw->id; + swu_op.son_wallet_id = op_id; swu_op.sidechain = sidechain; swu_op.address = wallet_contract_address; proposal_op.proposed_ops.emplace_back(swu_op); @@ -452,11 +459,11 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { return signers; }(); - std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), active_sw->id.operator std::string()); + std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), op_id.operator std::string()); if (!tx_str.empty()) { sidechain_transaction_create_operation stc_op; stc_op.payer = gpo.parameters.son_account(); - stc_op.object_id = active_sw->id; + stc_op.object_id = op_id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; stc_op.signers = signers; @@ -469,7 +476,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, active_sw->id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, op_id); } catch (fc::exception &e) { elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 873fa08e..50d59121 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -209,8 +209,10 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { bool address_ok = false; bool transaction_ok = false; son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; + const auto id = (swo_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const son_wallet_id_type op_id{ id }; const auto &idx = database.get_index_type().indices().get(); - const auto swo = idx.find(swo_id); + const auto swo = idx.find(op_id); if (swo != idx.end()) { auto active_sons = gpo.active_sons.at(sidechain); @@ -229,18 +231,20 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { } if (po.proposed_transaction.operations.size() >= 2) { - object_id_type object_id = op_obj_idx_1.get().object_id; + const object_id_type object_id = op_obj_idx_1.get().object_id; + const auto id = (object_id.instance() - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const object_id_type obj_id{ object_id.space(), object_id.type(), id }; std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); - const auto st = st_idx.find(object_id); + const auto st = st_idx.find(obj_id); if (st == st_idx.end()) { std::string tx_str = ""; - if (object_id.is()) { + if (obj_id.is()) { const auto &idx = database.get_index_type().indices().get(); - const auto swo = idx.find(object_id); + const auto swo = idx.find(obj_id); if (swo != idx.end()) { std::stringstream ss_trx(boost::algorithm::unhex(op_tx_str)); @@ -486,7 +490,10 @@ void sidechain_net_handler_hive::process_primary_wallet() { if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { - if (proposal_exists(chain::operation::tag::value, active_sw->id)) { + const auto id = active_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; + + if (proposal_exists(chain::operation::tag::value, op_id)) { return; } @@ -542,7 +549,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { son_wallet_update_operation swu_op; swu_op.payer = gpo.parameters.son_account(); - swu_op.son_wallet_id = active_sw->id; + swu_op.son_wallet_id = op_id; swu_op.sidechain = sidechain; swu_op.address = wallet_account_name; @@ -565,7 +572,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { sidechain_transaction_create_operation stc_op; stc_op.payer = gpo.parameters.son_account(); - stc_op.object_id = active_sw->id; + stc_op.object_id = op_id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; stc_op.signers = signers; From 0b64f0cfccb6f606799c802e9283aeed2561a9c8 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 31 Jan 2023 10:48:45 +0000 Subject: [PATCH 080/106] #473 erc20-support --- .../peerplays_sidechain/common/utils.cpp | 14 ++ .../peerplays_sidechain/ethereum/decoders.cpp | 45 ++++- .../peerplays_sidechain/ethereum/encoders.cpp | 25 ++- .../peerplays_sidechain/common/utils.hpp | 1 + .../graphene/peerplays_sidechain/defs.hpp | 6 + .../peerplays_sidechain/ethereum/decoders.hpp | 21 +++ .../peerplays_sidechain/ethereum/encoders.hpp | 10 +- .../sidechain_net_handler_ethereum.hpp | 6 +- .../peerplays_sidechain_plugin.cpp | 7 + .../sidechain_net_handler.cpp | 45 +++-- .../sidechain_net_handler_bitcoin.cpp | 1 + .../sidechain_net_handler_ethereum.cpp | 163 +++++++++++++----- .../sidechain_net_handler_hive.cpp | 1 + .../ethereum_transaction_tests.cpp | 67 +++++-- 14 files changed, 329 insertions(+), 83 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/common/utils.cpp b/libraries/plugins/peerplays_sidechain/common/utils.cpp index 5bd1dfd7..e135afa2 100644 --- a/libraries/plugins/peerplays_sidechain/common/utils.cpp +++ b/libraries/plugins/peerplays_sidechain/common/utils.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -47,4 +48,17 @@ std::string object_id_to_string(graphene::chain::object_id_type id) { return object_id; } +graphene::chain::object_id_type string_to_object_id(const std::string &id) { + std::vector strs; + boost::split(strs, id, boost::is_any_of(".")); + if (strs.size() != 3) { + elog("Wrong object_id format: ${id}", ("id", id)); + return graphene::chain::object_id_type{}; + } + + auto s = boost::lexical_cast(strs.at(0)); + auto t = boost::lexical_cast(strs.at(1)); + return graphene::chain::object_id_type{(uint8_t)s, (uint8_t)t, boost::lexical_cast(strs.at(2))}; +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp index 02f334f5..009eec69 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp @@ -5,8 +5,51 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum { -//! rlp_decoder +//! base_decoder +boost::multiprecision::uint256_t base_decoder::decode_uint256(const std::string &value) { + boost::multiprecision::uint256_t result = 0; + boost::multiprecision::uint256_t power(1); + uint8_t digit; + int pos = value.size() - 1; + while (pos >= 0) { + digit = 0; + if ('0' <= value[pos] && value[pos] <= '9') { + digit = value[pos] - '0'; + } else if ('a' <= value[pos] && value[pos] <= 'z') { + digit = value[pos] - 'a' + 10; + } + result += digit * power; + pos--; + power *= 16; + } + + return result; +} + +std::string base_decoder::decode_address(const std::string &value) { + return value.substr(24, 40); +} + +//! deposit_erc20_decoder +const std::string deposit_erc20_decoder::function_signature = "97feb926"; //! depositERC20(address,uint256) +fc::optional deposit_erc20_decoder::decode(const std::string &input) { + const auto input_without_0x = remove_0x(input); + if (function_signature != input_without_0x.substr(0, 8)) { + return fc::optional{}; + } + if (input_without_0x.size() != 136) { + return fc::optional{}; + } + + deposit_erc20_transaction erc_20; + erc_20.token = add_0x(base_decoder::decode_address(input_without_0x.substr(8, 64))); + erc_20.amount = base_decoder::decode_uint256(input_without_0x.substr(72, 64)); + + return erc_20; +} + +//! rlp_decoder namespace { const signed char p_util_hexdigit[256] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp index ce1e0083..872a5720 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -28,7 +28,7 @@ std::string base_encoder::encode_string(const std::string &value) { //! update_owners_encoder const std::string update_owners_encoder::function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string) std::string update_owners_encoder::encode(const std::vector> &owners_weights, const std::string &object_id) { - std::string data = "0x" + function_signature; + std::string data = add_0x(function_signature); data += base_encoder::encode_uint256(64); data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32); data += base_encoder::encode_uint256(owners_weights.size()); @@ -44,7 +44,7 @@ std::string update_owners_encoder::encode(const std::vector &transactions) const { - std::string data = "0x" + function_signature; + std::string data = add_0x(function_signature); data += base_encoder::encode_uint256(32); data += base_encoder::encode_uint256(transactions.size()); size_t offset = (transactions.size()) * 32; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp index 066a36fe..5b0f3f7d 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp @@ -8,5 +8,6 @@ std::string base64_encode(const std::string &s); std::string base64_decode(const std::string &s); std::string object_id_to_string(graphene::chain::object_id_type id); +graphene::chain::object_id_type string_to_object_id(const std::string &id); }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index 70618236..b3063a99 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -57,10 +57,16 @@ struct info_for_vin { bool resend = false; }; +enum class sidechain_event_type { + deposit, + withdrawal +}; + struct sidechain_event_data { fc::time_point_sec timestamp; uint32_t block_num; sidechain_type sidechain; + sidechain_event_type type; std::string sidechain_uid; std::string sidechain_transaction_id; std::string sidechain_from; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp index 9edb6ae3..68bd325b 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp @@ -1,10 +1,31 @@ #pragma once +#include #include #include +#include + namespace graphene { namespace peerplays_sidechain { namespace ethereum { +class base_decoder { +public: + static boost::multiprecision::uint256_t decode_uint256(const std::string &value); + static std::string decode_address(const std::string &value); +}; + +struct deposit_erc20_transaction { + std::string token; + boost::multiprecision::uint256_t amount; +}; + +class deposit_erc20_decoder { +public: + static const std::string function_signature; + + static fc::optional decode(const std::string &input); +}; + class rlp_decoder { private: enum RLP_constants { diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp index ce02f7ed..43a2a862 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp @@ -8,9 +8,6 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum { -const std::string update_owners_function_signature = "9d608673"; //! updateOwners((bytes,(uint8,bytes32,bytes32))[]) -const std::string withdrawal_function_signature = "daac6c81"; //! withdraw((bytes,(uint8,bytes32,bytes32))[]) - struct encoded_sign_transaction { std::string data; signature sign; @@ -37,6 +34,13 @@ public: static std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id); }; +class withdrawal_erc20_encoder { +public: + static const std::string function_signature; + + static std::string encode(const std::string &token, const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id); +}; + class signature_encoder { public: const std::string function_signature; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index 8bc463ab..5e4b6a61 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -4,6 +4,7 @@ #include +#include #include #include @@ -52,11 +53,15 @@ public: bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); virtual optional estimate_withdrawal_transaction_fee() const override; +private: + using bimap_type = boost::bimap; + private: std::string rpc_url; std::string rpc_user; std::string rpc_password; std::string wallet_contract_address; + bimap_type erc20_addresses; ethereum_rpc_client *rpc_client; @@ -64,7 +69,6 @@ private: ethereum::network_id_type network_id; std::string create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id); - std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); std::string sign_transaction(const sidechain_transaction_object &sto); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 541b8537..ae9967f7 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -192,6 +192,8 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("ethereum-node-rpc-user", bpo::value(), "Ethereum RPC user"); cli.add_options()("ethereum-node-rpc-password", bpo::value(), "Ethereum RPC password"); cli.add_options()("ethereum-wallet-contract-address", bpo::value(), "Ethereum wallet contract address"); + cli.add_options()("erc-20-address", bpo::value>()->composing()->multitoken(), + "Tuple of [ERC-20 symbol, ERC-20 address] (may specify multiple times)"); cli.add_options()("ethereum-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")), "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); @@ -630,6 +632,11 @@ std::map> peerplays_sidechain_plugin_im } optional peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + if (net_handlers.count(sidechain) == 0) { + wlog("No net handler for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional(); + } + if (!net_handlers.at(sidechain)) { wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain)); return optional(); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 38345935..490cfe0b 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -190,22 +190,25 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ // (sed.sidechain_currency.compare("HIVE") != 0); #endif - bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) && - (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) || - ((sed.sidechain == sidechain_type::ethereum) && (sed.sidechain_currency.compare("ETH") == 0)) || - ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) || - ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || - enable_peerplays_asset_deposits); + const bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) && + (sed.sidechain == sidechain) && + (sed.type == sidechain_event_type::deposit) && + (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) || + ((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) || + ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) || + ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || + enable_peerplays_asset_deposits); - bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays - ((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || - (sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) || - (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) || - (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))); + const bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && + (sed.sidechain == sidechain) && + (sed.type == sidechain_event_type::withdrawal) && + (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset()))) || + ((sed.sidechain == sidechain_type::ethereum) && (!sed.sidechain_currency.empty())) || + ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset()))) || + ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset())))); // Deposit request if (deposit_condition) { - for (son_id_type son_id : plugin.get_sons()) { if (plugin.is_active_son(sidechain, son_id)) { @@ -269,7 +272,17 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ withdraw_currency_price = database.get(database.get_global_properties().parameters.hive_asset()).options.core_exchange_rate; } if (withdraw_currency.empty()) { - return; + //! This is ERC-20 withdrawal + const auto asset_object_id = string_to_object_id(sed.sidechain_currency); + const auto &assets_by_id = database.get_index_type().indices().get(); + const auto asset_itr = assets_by_id.find(asset_object_id); + if (asset_itr == assets_by_id.end()) { + wlog("Could not find asset: ${asset_object_id}", ("asset_object_id", asset_object_id)); + return; + } + + withdraw_currency = asset_itr->symbol; + withdraw_currency_price = asset_itr->options.core_exchange_rate; } for (son_id_type son_id : plugin.get_sons()) { @@ -663,9 +676,10 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { continue; } - bool is_tracked_asset = + const bool is_tracked_asset = ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || + (sidechain == sidechain_type::ethereum) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); @@ -691,7 +705,8 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { sidechain_event_data sed; sed.timestamp = database.head_block_time(); sed.block_num = database.head_block_num(); - sed.sidechain = sidechain; //! Fixme -> sidechain_type::peerplays + sed.sidechain = sidechain; + sed.type = sidechain_event_type::withdrawal; sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = trx.id().str(); sed.sidechain_from = sidechain_from; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 862f991a..f1594c0f 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1282,6 +1282,7 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) sed.timestamp = database.head_block_time(); sed.block_num = database.head_block_num(); sed.sidechain = addr_itr->sidechain; + sed.type = sidechain_event_type::deposit; sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = v.out.hash_tx; sed.sidechain_from = v.address; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 6fe4a91d..80a11b16 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,21 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha wallet_contract_address = options.at("ethereum-wallet-contract-address").as(); + if (options.count("erc-20-address")) { + const std::vector symbol_addresses = options["erc-20-address"].as>(); + for (const std::string &itr : symbol_addresses) { + auto itr_pair = graphene::app::dejsonify>(itr, 5); + ilog("ERC-20 symbol: ${symbol}, address: ${address}", ("symbol", itr_pair.first)("address", itr_pair.second)); + if (!itr_pair.first.length() || !itr_pair.second.length()) { + FC_THROW("Invalid symbol address pair."); + } + + auto address = itr_pair.second; + std::transform(address.begin(), address.end(), address.begin(), ::tolower); + erc20_addresses.insert(bimap_type::value_type{itr_pair.first, address}); + } + } + if (options.count("ethereum-private-key")) { const std::vector pub_priv_keys = options["ethereum-private-key"].as>(); for (const std::string &itr_key_pair : pub_priv_keys) { @@ -290,21 +306,41 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) const std::string sidechain_from = tx.get("result.from"); const std::string sidechain_to = tx.get("result.to"); - const std::string value_s = tx.get("result.value"); - boost::multiprecision::uint256_t amount(value_s); - amount = amount / 100000; - amount = amount / 100000; - const fc::safe sidechain_amount = amount; std::string cmp_sidechain_to = sidechain_to; std::transform(cmp_sidechain_to.begin(), cmp_sidechain_to.end(), cmp_sidechain_to.begin(), ::toupper); std::string cmp_wallet_contract_address = wallet_contract_address; std::transform(cmp_wallet_contract_address.begin(), cmp_wallet_contract_address.end(), cmp_wallet_contract_address.begin(), ::toupper); - process_ok = (swdo_sidechain_from == sidechain_from) && + //! Check whether it is ERC-20 token deposit + std::string symbol; + boost::multiprecision::uint256_t amount; + bool error_in_deposit = false; + const auto deposit_erc_20 = ethereum::deposit_erc20_decoder::decode(tx.get("result.input")); + if (deposit_erc_20.valid()) { + std::string cmp_token = deposit_erc_20->token; + std::transform(cmp_token.begin(), cmp_token.end(), cmp_token.begin(), ::tolower); + + const auto it = erc20_addresses.right.find(cmp_token); + if (it == erc20_addresses.right.end()) { + wlog("No erc-20 token with address: ${address}", ("address", cmp_token)); + error_in_deposit = true; + } + symbol = it->second; + amount = deposit_erc_20->amount; + } else { + symbol = "ETH"; + const std::string value_s = tx.get("result.value"); + amount = boost::multiprecision::uint256_t{value_s}; + amount = amount / 100000; + amount = amount / 100000; + } + + process_ok = (!error_in_deposit) && + (swdo_sidechain_from == sidechain_from) && (cmp_sidechain_to == cmp_wallet_contract_address) && - (swdo_sidechain_currency == "ETH") && - (swdo_sidechain_amount == sidechain_amount.value); + (swdo_sidechain_currency == symbol) && + (swdo_sidechain_amount == fc::safe{amount}.value); } } } @@ -427,7 +463,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { const chain::global_property_object &gpo = database.get_global_properties(); proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); son_wallet_update_operation swu_op; @@ -489,12 +525,19 @@ bool sidechain_net_handler_ethereum::process_deposit(const son_wallet_deposit_ob const chain::global_property_object &gpo = database.get_global_properties(); - price asset_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; - asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, database.get_global_properties().parameters.eth_asset()); + const auto &assets_by_symbol = database.get_index_type().indices().get(); + const auto asset_itr = assets_by_symbol.find(swdo.sidechain_currency); + if (asset_itr == assets_by_symbol.end()) { + wlog("Could not find asset: ${symbol}", ("symbol", swdo.sidechain_currency)); + return false; + } + + const price asset_price = asset_itr->options.core_exchange_rate; + const asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, asset_itr->get_id()); proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); son_wallet_deposit_process_operation swdp_op; @@ -537,7 +580,7 @@ bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdra proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + const uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); son_wallet_withdraw_process_operation swwp_op; @@ -683,7 +726,13 @@ bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechai if (sto.object_id.is()) { auto swwo = database.get(sto.object_id); - settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.eth_asset()); + const auto &assets_by_symbol = database.get_index_type().indices().get(); + const auto asset_itr = assets_by_symbol.find(swwo.withdraw_currency); + if (asset_itr == assets_by_symbol.end()) { + wlog("Could not find asset: ${symbol}", ("symbol", swwo.withdraw_currency)); + return false; + } + settle_amount = asset(swwo.withdraw_amount, asset_itr->get_id()); } return true; @@ -712,13 +761,6 @@ optional sidechain_net_handler_ethereum::estimate_withdrawal_transaction_ return optional{}; } - const auto &assets_by_symbol = database.get_index_type().indices().get(); - auto asset_itr = assets_by_symbol.find("ETH"); - if (asset_itr == assets_by_symbol.end()) { - wlog("Could not find asset matching ETH"); - return optional{}; - } - const auto &public_key = son->sidechain_public_keys.at(sidechain); const auto data = ethereum::withdrawal_encoder::encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; @@ -726,7 +768,9 @@ optional sidechain_net_handler_ethereum::estimate_withdrawal_transaction_ const auto estimate_gas = ethereum::from_hex(rpc_client->get_estimate_gas(params)); const auto gas_price = ethereum::from_hex(rpc_client->get_gas_price()); const auto eth_gas_fee = double(estimate_gas * gas_price) / double{1000000000000000000}; - return asset_itr->amount_from_string(std::to_string(eth_gas_fee)); + + const auto asset = database.get(database.get_global_properties().parameters.eth_asset()); + return asset.amount_from_string(std::to_string(eth_gas_fee)); } std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id) { @@ -739,12 +783,19 @@ std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(co return ethereum::update_owners_encoder::encode(owners_weights, object_id); } -std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) { - return "Deposit-Transaction"; -} - std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { - return ethereum::withdrawal_encoder::encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); + if (swwo.withdraw_currency == "ETH") { + return ethereum::withdrawal_encoder::encode(ethereum::remove_0x(swwo.withdraw_address), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); + } else { + const auto it = erc20_addresses.left.find(swwo.withdraw_currency); + if (it == erc20_addresses.left.end()) { + elog("No erc-20 token: ${symbol}", ("symbol", swwo.withdraw_currency)); + return ""; + } + return ethereum::withdrawal_erc20_encoder::encode(ethereum::remove_0x(it->second), ethereum::remove_0x(swwo.withdraw_address), swwo.withdraw_amount.value, swwo.id.operator std::string()); + } + + return ""; } std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) { @@ -761,10 +812,10 @@ std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_tra } void sidechain_net_handler_ethereum::schedule_ethereum_listener() { - fc::time_point now = fc::time_point::now(); - int64_t time_to_next = 5000; + const fc::time_point now = fc::time_point::now(); + const int64_t time_to_next = 5000; - fc::time_point next_wakeup(now + fc::milliseconds(time_to_next)); + const fc::time_point next_wakeup(now + fc::milliseconds(time_to_next)); _listener_task = fc::schedule([this] { ethereum_listener_loop(); @@ -776,10 +827,9 @@ void sidechain_net_handler_ethereum::ethereum_listener_loop() { schedule_ethereum_listener(); const auto reply = rpc_client->eth_blockNumber(); - //std::string reply = rpc_client->eth_get_logs(wallet_contract_address); if (!reply.empty()) { - uint64_t head_block_number = ethereum::from_hex(reply); + const uint64_t head_block_number = ethereum::from_hex(reply); if (head_block_number != last_block_received) { //! Check that current block number is greater than last one @@ -814,11 +864,11 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe size_t tx_idx = -1; for (const auto &tx_child : block_json.get_child("result.transactions")) { - boost::property_tree::ptree tx = tx_child.second; + const boost::property_tree::ptree tx = tx_child.second; tx_idx = tx_idx + 1; - std::string from = tx.get("from"); - std::string to = tx.get("to"); + const std::string from = tx.get("from"); + const std::string to = tx.get("to"); std::string cmp_to = to; std::transform(cmp_to.begin(), cmp_to.end(), cmp_to.begin(), ::toupper); @@ -827,10 +877,35 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe if (cmp_to == cmp_wallet_contract_address) { - std::string value_s = tx.get("value"); - boost::multiprecision::uint256_t amount(value_s); - amount = amount / 100000; - amount = amount / 100000; + //! Check whether it is ERC-20 token deposit + std::string symbol; + boost::multiprecision::uint256_t amount; + const auto deposit_erc_20 = ethereum::deposit_erc20_decoder::decode(tx.get("input")); + if (deposit_erc_20.valid()) { + std::string cmp_token = deposit_erc_20->token; + std::transform(cmp_token.begin(), cmp_token.end(), cmp_token.begin(), ::tolower); + + const auto it = erc20_addresses.right.find(cmp_token); + if (it == erc20_addresses.right.end()) { + wlog("No erc-20 token with address: ${address}", ("address", cmp_token)); + continue; + } + symbol = it->second; + amount = deposit_erc_20->amount; + } else { + symbol = "ETH"; + const std::string value_s = tx.get("value"); + amount = boost::multiprecision::uint256_t{value_s}; + amount = amount / 100000; + amount = amount / 100000; + } + + const auto &assets_by_symbol = database.get_index_type().indices().get(); + const auto asset_itr = assets_by_symbol.find(symbol); + if (asset_itr == assets_by_symbol.end()) { + wlog("Could not find asset: ${symbol}", ("symbol", symbol)); + continue; + } const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, from, time_point_sec::maximum())); @@ -841,22 +916,22 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe std::stringstream ss; ss << "ethereum" << "-" << tx.get("hash") << "-" << tx_idx; - std::string sidechain_uid = ss.str(); sidechain_event_data sed; sed.timestamp = database.head_block_time(); sed.block_num = database.head_block_num(); sed.sidechain = sidechain; - sed.sidechain_uid = sidechain_uid; + sed.type = sidechain_event_type::deposit; + sed.sidechain_uid = ss.str(); sed.sidechain_transaction_id = tx.get("hash"); sed.sidechain_from = from; sed.sidechain_to = to; - sed.sidechain_currency = "ETH"; + sed.sidechain_currency = symbol; sed.sidechain_amount = amount; sed.peerplays_from = addr_itr->sidechain_address_account; sed.peerplays_to = database.get_global_properties().parameters.son_account(); - price eth_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; - sed.peerplays_asset = asset(sed.sidechain_amount * eth_price.base.amount / eth_price.quote.amount); + const price price = asset_itr->options.core_exchange_rate; + sed.peerplays_asset = asset(sed.sidechain_amount * price.base.amount / price.quote.amount); add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 873fa08e..c3897f74 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -950,6 +950,7 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) { sed.timestamp = database.head_block_time(); sed.block_num = database.head_block_num(); sed.sidechain = sidechain; + sed.type = sidechain_event_type::deposit; sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = transaction_id; sed.sidechain_from = from; diff --git a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp index 47cc090c..b3f094c8 100644 --- a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp +++ b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp @@ -2,6 +2,7 @@ #include #include +#include #include using namespace graphene::peerplays_sidechain::ethereum; @@ -9,22 +10,43 @@ using namespace graphene::peerplays_sidechain::ethereum; BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests) BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) { - const auto tx = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0"); - BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); + const auto tx = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.39.0"); + BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000"); - const auto tx1 = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1"); - BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); + const auto tx1 = withdrawal_encoder::encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.39.1"); + BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(withdrawal_signature_encoder_test) { - const signature_encoder encoder{withdrawal_function_signature}; - encoded_sign_transaction transaction; - transaction.data = "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"; + transaction.data = "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000"; transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); + const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data); + BOOST_REQUIRE_EQUAL(function_signature.empty(), false); + const signature_encoder encoder{function_signature}; const auto tx = encoder.encode({transaction}); - BOOST_CHECK_EQUAL(tx, "0xdaac6c8100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000065c4622d2ff2b2d89c5c6f8225ab0f979bc69d4fcd4fd47db757b66fb8a39e2bc5522be5d101aa11e66da78db973f136b323be10bd107ff0b648f06b4c71ef2a4f00000000000000000000000000000000000000000000000000000000000000a4e088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx, "0xdaac6c810000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006689c3a93d7059430d19ff952900dfada310c0dcced9ed046c335f886091c7e50c1a01016a488777b41a1815ca01a7d809ed47c36dcb0d5f86a43b079ce0d04afe00000000000000000000000000000000000000000000000000000000000000a4e088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(withdrawal_erc20_encoder_test) { + const auto tx = withdrawal_erc20_encoder::encode("cc806da9df9d634b5dac0aa36dca1e7780e42C60", "5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10, "1.39.0"); + BOOST_CHECK_EQUAL(tx, "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000"); + + const auto tx1 = withdrawal_erc20_encoder::encode("cc806da9df9d634b5dac0aa36dca1e7780e42C60", "5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10, "1.39.1"); + BOOST_CHECK_EQUAL(tx1, "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(withdrawal_erc20_signature_encoder_test) { + encoded_sign_transaction transaction; + transaction.data = "0x483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000"; + transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); + + const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data); + BOOST_REQUIRE_EQUAL(function_signature.empty(), false); + const signature_encoder encoder{function_signature}; + const auto tx = encoder.encode({transaction}); + BOOST_CHECK_EQUAL(tx, "0xd2bf286600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000066f51f7732435016936f0e21aa3c290023ea96ddbc369a957aca28a865cb5004a46675855fccd4bd5a283e1ff61aa60ca9b8b63664e770689e5cfc1a0c6bbdc79a00000000000000000000000000000000000000000000000000000000000000c4483c0467000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42C600000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc12000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { @@ -32,23 +54,36 @@ BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1); owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1); - const auto tx = update_owners_encoder::encode(owners_weights, "1.35.0"); - BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); + const auto tx = update_owners_encoder::encode(owners_weights, "1.39.0"); + BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000"); owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1); - const auto tx1 = update_owners_encoder::encode(owners_weights, "1.36.1"); - BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); + const auto tx1 = update_owners_encoder::encode(owners_weights, "1.39.1"); + BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E310000000000000000000000000000000000000000000000000000"); } BOOST_AUTO_TEST_CASE(update_owners_signature_encoder_test) { - const signature_encoder encoder{update_owners_function_signature}; - encoded_sign_transaction transaction; - transaction.data = "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"; + transaction.data = "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E300000000000000000000000000000000000000000000000000000"; transaction.sign = sign_hash(keccak_hash(transaction.data), "0x21", "eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060" ); + const auto function_signature = signature_encoder::get_function_signature_from_transaction(transaction.data); + BOOST_REQUIRE_EQUAL(function_signature.empty(), false); + const signature_encoder encoder{function_signature}; const auto tx = encoder.encode({transaction}); - BOOST_CHECK_EQUAL(tx, "0x9d6086730000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000006698877eafa525c1a55f6b5e0a7187dfae484c97d9f77c4421a00276a9408a3e713d24402b44c05a883142fcffa84e1a802be37c17bb360f6f4810eb0415c8bbfd000000000000000000000000000000000000000000000000000000000000012423ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + BOOST_CHECK_EQUAL(tx, "0x9d608673000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000667121da3c6ab5054b1a77cac477f24e7ce7bfcb5e3a857cfcaf48e67fc8f003ac38dfa8821525383608a68c9f215f9a2a232e192ae80079cd2f31b0e01caa6e1d000000000000000000000000000000000000000000000000000000000000012423ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33392E30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(deposit_erc20_decoder_test) { + const auto erc_20_1 = deposit_erc20_decoder::decode("0x97feb926000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42c600000000000000000000000000000000000000000000000000000000000000064"); + BOOST_REQUIRE_EQUAL(erc_20_1.valid(), true); + BOOST_CHECK_EQUAL(erc_20_1->token, "0xcc806da9df9d634b5dac0aa36dca1e7780e42c60"); + BOOST_CHECK_EQUAL(erc_20_1->amount, 100); + + const auto erc_20_2 = deposit_erc20_decoder::decode("0x97feb926000000000000000000000000cc806da9df9d634b5dac0aa36dca1e7780e42c600000000000000000000000000000000000000000000000006400000000000000"); + BOOST_REQUIRE_EQUAL(erc_20_2.valid(), true); + BOOST_CHECK_EQUAL(erc_20_2->token, "0xcc806da9df9d634b5dac0aa36dca1e7780e42c60"); + BOOST_CHECK_EQUAL(erc_20_2->amount, 7205759403792793600); } BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) { From da3a858aa660b7bd95d308fcf104c6cbd5d77cd7 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Wed, 1 Feb 2023 17:13:02 +0000 Subject: [PATCH 081/106] Fix replay blockchain --- libraries/app/database_api.cpp | 20 +- .../app/include/graphene/app/database_api.hpp | 4 +- libraries/chain/account_evaluator.cpp | 16 +- libraries/chain/db_block.cpp | 2 +- libraries/chain/db_getter.cpp | 2 +- libraries/chain/db_init.cpp | 2 +- libraries/chain/db_maint.cpp | 197 +++++++++++------- libraries/chain/db_management.cpp | 1 - libraries/chain/db_witness_schedule.cpp | 9 +- libraries/chain/hardfork.d/SON3.hf | 7 - .../chain/hardfork.d/SON_FOR_ETHEREUM.hf | 4 +- .../chain/include/graphene/chain/database.hpp | 10 +- .../graphene/chain/global_property_object.hpp | 16 +- .../graphene/chain/protocol/account.hpp | 2 +- .../chain/protocol/sidechain_transaction.hpp | 2 +- .../graphene/chain/protocol/son_wallet.hpp | 49 +++-- .../include/graphene/chain/sidechain_defs.hpp | 22 +- .../chain/sidechain_transaction_object.hpp | 4 +- .../chain/include/graphene/chain/son_info.hpp | 30 +-- .../include/graphene/chain/son_object.hpp | 38 ++-- .../graphene/chain/son_sidechain_info.hpp | 31 +++ .../graphene/chain/son_wallet_object.hpp | 4 +- libraries/chain/protocol/account.cpp | 34 +-- .../chain/sidechain_transaction_evaluator.cpp | 20 +- libraries/chain/son_evaluator.cpp | 39 ++-- libraries/chain/son_object.cpp | 28 ++- libraries/chain/son_wallet_evaluator.cpp | 44 +++- .../chain/son_wallet_withdraw_evaluator.cpp | 33 ++- .../sidechain_net_handler_bitcoin.hpp | 2 +- .../sidechain_net_handler_ethereum.hpp | 3 +- .../peerplays_sidechain_plugin.cpp | 18 +- .../sidechain_net_handler_bitcoin.cpp | 35 +++- .../sidechain_net_handler_ethereum.cpp | 26 ++- .../sidechain_net_handler_hive.cpp | 27 ++- .../sidechain_net_handler_peerplays.cpp | 9 +- .../wallet/include/graphene/wallet/wallet.hpp | 4 +- libraries/wallet/wallet.cpp | 20 +- tests/cli/son.cpp | 39 ++-- tests/tests/block_tests.cpp | 2 +- tests/tests/son_operations_tests.cpp | 135 ++++++------ tests/tests/son_wallet_tests.cpp | 8 +- 41 files changed, 629 insertions(+), 369 deletions(-) delete mode 100644 libraries/chain/hardfork.d/SON3.hf create mode 100644 libraries/chain/include/graphene/chain/son_sidechain_info.hpp diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index ef48a88d..c4e69607 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -196,8 +196,8 @@ public: fc::optional get_son_by_account(const std::string account_id_or_name) const; map lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const; uint64_t get_son_count() const; - flat_map> get_active_sons(); - vector get_active_sons_by_sidechain(sidechain_type sidechain); + flat_map> get_active_sons(); + vector get_active_sons_by_sidechain(sidechain_type sidechain); map> get_son_network_status(); map get_son_network_status_by_sidechain(sidechain_type sidechain); @@ -1877,22 +1877,22 @@ uint64_t database_api_impl::get_son_count() const { return _db.get_index_type().indices().size(); } -flat_map> database_api::get_active_sons() { +flat_map> database_api::get_active_sons() { return my->get_active_sons(); } -flat_map> database_api_impl::get_active_sons() { +flat_map> database_api_impl::get_active_sons() { return get_global_properties().active_sons; } -vector database_api::get_active_sons_by_sidechain(sidechain_type sidechain) { +vector database_api::get_active_sons_by_sidechain(sidechain_type sidechain) { return my->get_active_sons_by_sidechain(sidechain); } -vector database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) { +vector database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) { const global_property_object &gpo = get_global_properties(); - vector result; + vector result; if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) { result = gpo.active_sons.at(sidechain); @@ -1908,7 +1908,7 @@ map> database_api::get_son_network_stat map> database_api_impl::get_son_network_status() { map> result; - for (auto active_sidechain_type : active_sidechain_types) { + for (auto active_sidechain_type : active_sidechain_types(_db.head_block_time())) { result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type); } @@ -2340,7 +2340,9 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const votes_for_sons[sidechain].reserve(sidechain_ids.size()); for (const auto &son : sidechain_ids) { const auto &son_obj = son.as(6); - votes_for_sons[sidechain].emplace_back(votes_info_object{son_obj.get_sidechain_vote_id(sidechain), son_obj.id}); + if(son_obj.get_sidechain_vote_id(sidechain).valid()) { + votes_for_sons[sidechain].emplace_back(votes_info_object{*son_obj.get_sidechain_vote_id(sidechain), son_obj.id}); + } } } result.votes_for_sons = std::move(votes_for_sons); diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 948b6d55..ddd909f1 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -695,14 +695,14 @@ public: * @brief Get list of active sons * @return List of active SONs */ - flat_map> get_active_sons(); + flat_map> get_active_sons(); /** * @brief Get list of active sons * @param sidechain Sidechain type [bitcoin|ethereum|hive] * @return List of active SONs */ - vector get_active_sons_by_sidechain(sidechain_type sidechain); + vector get_active_sons_by_sidechain(sidechain_type sidechain); /** * @brief Get SON network status diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 5f1a4416..4ecb3307 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -69,11 +69,13 @@ void verify_account_votes( const database& db, const account_options& options ) FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" ); - FC_ASSERT( options.extensions.value.num_son.valid() , "Invalid son number" ); - for(const auto& num_sons : *options.extensions.value.num_son) + if ( options.extensions.value.num_son.valid() ) { - FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count, - "Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) ); + for(const auto& num_sons : *options.extensions.value.num_son) + { + FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count, + "Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) ); + } } FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." ); @@ -188,6 +190,12 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.owner = o.owner; obj.active = o.active; obj.options = o.options; + + if (!obj.options.extensions.value.num_son.valid()) + { + obj.options.extensions.value = account_options::ext(); + } + obj.statistics = d.create([&obj](account_statistics_object& s){ s.owner = obj.id; s.name = obj.name; diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index d49f1dc5..adabcffe 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -784,7 +784,7 @@ void database::_apply_block( const signed_block& next_block ) update_witness_schedule(); bool need_update_son_schedule = false; - for(const auto& active_sidechain_type : active_sidechain_types) { + for(const auto& active_sidechain_type : active_sidechain_types(dynamic_global_props.time)) { if(global_props.active_sons.at(active_sidechain_type).size() > 0) { need_update_son_schedule = true; } diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 4dc72649..9510692c 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -342,7 +342,7 @@ bool database::is_son_active( sidechain_type type, son_id_type son_id ) active_son_ids.reserve(gpo_as.size()); std::transform(gpo_as.cbegin(), gpo_as.cend(), std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info& swi) { + [](const son_sidechain_info& swi) { return swi.son_id; }); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 82d7fde1..2e296e39 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -365,7 +365,7 @@ void database::initialize_hardforks() _hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME); _hardfork_times.emplace_back(HARDFORK_SON_TIME); _hardfork_times.emplace_back(HARDFORK_SON2_TIME); - _hardfork_times.emplace_back(HARDFORK_SON3_TIME); + _hardfork_times.emplace_back(HARDFORK_SON_FOR_ETHEREUM_TIME); _hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME); std::sort(_hardfork_times.begin(), _hardfork_times.end()); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index f7b30caf..e4ccc45d 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -84,7 +84,7 @@ vector> database::sort_votable_objects< std::vector> refs; for( auto& son : all_sons ) { - if(son.has_valid_config(head_block_time()) && son.statuses.at(sidechain) != son_status::deregistered) + if(son.has_valid_config(head_block_time(), sidechain) && son.statuses.at(sidechain) != son_status::deregistered) { refs.push_back(std::cref(son)); } @@ -97,8 +97,10 @@ vector> database::sort_votable_objects< sidechain == sidechain_type::hive, "Unexpected sidechain type"); - const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; - const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; + FC_ASSERT(a.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", a)); + FC_ASSERT(b.get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", b)); + const share_type oa_vote = _vote_tally_buffer.size() > *a.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*a.get_sidechain_vote_id(sidechain)] : 0; + const share_type ob_vote = _vote_tally_buffer.size() > *b.get_sidechain_vote_id(sidechain) ? _vote_tally_buffer[*b.get_sidechain_vote_id(sidechain)] : 0; if( oa_vote != ob_vote ) return oa_vote > ob_vote; @@ -190,7 +192,7 @@ void database::pay_sons() // Current requirement is that we have to pay every 24 hours, so the following check if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { - for(const auto& active_sidechain_type : active_sidechain_types) + for(const auto& active_sidechain_type : active_sidechain_types(now)) { assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 ); const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2; @@ -207,7 +209,14 @@ void database::pay_sons() } } - const auto sons = sort_votable_objects(active_sidechain_type, + const sidechain_type st = [&now, &active_sidechain_type]{ + if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) + return sidechain_type::bitcoin; + else + return active_sidechain_type; + }(); + + const auto sons = sort_votable_objects(st, (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)) ); @@ -215,7 +224,8 @@ void database::pay_sons() uint64_t total_votes = 0; for( const son_object& son : sons ) { - total_votes += _vote_tally_buffer[son.sidechain_vote_ids.at(active_sidechain_type)]; + FC_ASSERT(son.get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", son)); + total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(st)]; } const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { @@ -230,35 +240,37 @@ void database::pay_sons() }; uint64_t weighted_total_txs_signed = 0; const share_type son_budget = dpo.son_budget; - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type](const object& o) { + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type, &st](const object& o) { const son_statistics_object& s = static_cast(o); const auto& idx = get_index_type().indices().get(); const auto son_obj = idx.find( s.owner ); uint16_t son_weight = 0; + FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj)); if( now >= HARDFORK_SON2_TIME ) { - son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); } else { - son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); } const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; weighted_total_txs_signed += (txs_signed * son_weight); }); // Now pay off each SON proportional to the number of transactions signed. - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type](const object& o) { + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type, &st](const object& o) { const son_statistics_object& s = static_cast(o); const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; - if(txs_signed > 0){ + if(txs_signed > 0) { const auto& idx = get_index_type().indices().get(); auto son_obj = idx.find( s.owner ); uint16_t son_weight = 0; + FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj)); if( now >= HARDFORK_SON2_TIME ) { - son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); } else { - son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); } const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; modify( *son_obj, [&]( son_object& _son_obj) @@ -287,7 +299,7 @@ void database::pay_sons() } } -void database::update_son_metrics(const flat_map >& curr_active_sons) +void database::update_son_metrics(const flat_map >& curr_active_sons) { for(const auto& curr_active_sidechain_sons : curr_active_sons) { const auto& sidechain = curr_active_sidechain_sons.first; @@ -298,7 +310,7 @@ void database::update_son_metrics(const flat_map >& curr_active_sons, - const flat_map >& new_active_sons ) +void database::update_son_statuses( const flat_map >& curr_active_sons, + const flat_map >& new_active_sons ) { for(const auto& new_active_sidechain_sons : new_active_sons) { const auto& sidechain = new_active_sidechain_sons.first; @@ -335,7 +347,7 @@ void database::update_son_statuses( const flat_mapstatuses.at(sidechain) == son_status::inactive) { modify(*son, [&](son_object &obj) { @@ -414,7 +426,7 @@ void database::update_son_statuses( const flat_map >& new_active_sons) +void database::update_son_wallet(const flat_map >& new_active_sons) { bool should_recreate_pw = true; @@ -729,39 +741,22 @@ void database::update_active_sons() } #endif - const flat_map stake_target = [this]{ - flat_map stake_target; - for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ - const auto sidechain = son_count_histogram_buffer.first; - stake_target[sidechain] = (_total_voting_stake-son_count_histogram_buffer.second[0]) / 2; - } - return stake_target; - }(); + const auto supported_active_sidechain_types = active_sidechain_types(head_block_time()); + flat_map son_count; + for(const auto& active_sidechain_type : supported_active_sidechain_types) + { + const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2; - /// accounts that vote for 0 or 1 son do not get to express an opinion on - /// the number of sons to have (they abstain and are non-voting accounts) - flat_map stake_tally = []{ - flat_map stake_tally; - for(const auto& active_sidechain_type : active_sidechain_types){ - stake_tally[active_sidechain_type] = 0; - } - return stake_tally; - }(); - flat_map son_count = []{ - flat_map son_count; - for(const auto& active_sidechain_type : active_sidechain_types){ - son_count[active_sidechain_type] = 0; - } - return son_count; - }(); - for( const auto& stake_target_sidechain : stake_target ){ - const auto sidechain = stake_target_sidechain.first; - if( stake_target_sidechain.second > 0 ) + /// accounts that vote for 0 or 1 son do not get to express an opinion on + /// the number of sons to have (they abstain and are non-voting accounts) + share_type stake_tally = 0; + son_count[active_sidechain_type] = 0; + if( stake_target > 0 ) { - while( (son_count[sidechain] < _son_count_histogram_buffer.at(sidechain).size() - 1) - && (stake_tally[sidechain] <= stake_target_sidechain.second) ) + while( (son_count.at(active_sidechain_type) < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1) + && (stake_tally <= stake_target) ) { - stake_tally[sidechain] += _son_count_histogram_buffer.at(sidechain)[ ++son_count[sidechain] ]; + stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[ ++son_count[active_sidechain_type] ]; } } } @@ -770,18 +765,17 @@ void database::update_active_sons() const chain_property_object& cpo = get_chain_properties(); const auto& all_sons = get_index_type().indices(); flat_map > > sons; - for(const auto& active_sidechain_type : active_sidechain_types) + for(const auto& active_sidechain_type : supported_active_sidechain_types) { - if(head_block_time() >= HARDFORK_SON3_TIME) { + if(head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) { sons[active_sidechain_type] = sort_votable_objects(active_sidechain_type, (std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count))); } else { - sons[active_sidechain_type] = sort_votable_objects(active_sidechain_type, get_global_properties().parameters.maximum_son_count()); + sons[active_sidechain_type] = sort_votable_objects(sidechain_type::bitcoin, get_global_properties().parameters.maximum_son_count()); } } - auto& local_vote_buffer_ref = _vote_tally_buffer; for( const son_object& son : all_sons ) { for(const auto& status: son.statuses) @@ -796,9 +790,9 @@ void database::update_active_sons() } } - modify( son, [local_vote_buffer_ref]( son_object& obj ){ + modify( son, [this]( son_object& obj ){ for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){ - obj.total_votes[sidechain_vote_id.first] = local_vote_buffer_ref[sidechain_vote_id.second]; + obj.total_votes[sidechain_vote_id.first] = _vote_tally_buffer.size() > sidechain_vote_id.second ? _vote_tally_buffer[sidechain_vote_id.second] : 0; } for(auto& status: obj.statuses) { @@ -853,7 +847,7 @@ void database::update_active_sons() // Compare current and to-be lists of active sons const auto cur_active_sons = gpo.active_sons; - flat_map > new_active_sons; + flat_map > new_active_sons; const auto &acc = get(gpo.parameters.son_account()); for( const auto& sidechain_sons : sons ){ const auto& sidechain = sidechain_sons.first; @@ -861,11 +855,12 @@ void database::update_active_sons() new_active_sons[sidechain].reserve(sons_array.size()); for( const son_object& son : sons_array ) { - son_info swi; + son_sidechain_info swi; swi.son_id = son.id; swi.weight = acc.active.account_auths.at(son.son_account); swi.signing_key = son.signing_key; - swi.public_key = son.sidechain_public_keys.at(sidechain); + if (son.sidechain_public_keys.find(sidechain) != son.sidechain_public_keys.end()) + swi.public_key = son.sidechain_public_keys.at(sidechain); new_active_sons[sidechain].push_back(swi); } } @@ -905,7 +900,7 @@ void database::update_active_sons() } }); - for(const auto& active_sidechain_type : active_sidechain_types) + for(const auto& active_sidechain_type : supported_active_sidechain_types) { const son_schedule_object& sidechain_sso = son_schedule_id_type(get_son_schedule_id(active_sidechain_type))(*this); modify(sidechain_sso, [&](son_schedule_object& _sso) @@ -914,12 +909,13 @@ void database::update_active_sons() active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size()); std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), std::inserter(active_sons, active_sons.end()), - [](const son_info& swi) { + [](const son_sidechain_info& swi) { return swi.son_id; }); _sso.scheduler.update(active_sons); // similar to witness, produce schedule for sons - if(cur_active_sons.at(active_sidechain_type).size() == 0 && new_active_sons.at(active_sidechain_type).size() > 0) + if( ((cur_active_sons.contains(active_sidechain_type) && cur_active_sons.at(active_sidechain_type).size() == 0) || + !cur_active_sons.contains(active_sidechain_type)) && new_active_sons.at(active_sidechain_type).size() > 0 ) { witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); for( size_t i=0; iextensions.value.hive_asset = hive_asset.get_id(); }); } + // Pay the SONs if (head_block_time() >= HARDFORK_SON_TIME) { @@ -2197,18 +2194,57 @@ void database::perform_son_tasks() // and modify the global son funds accordingly, whatever is left is passed on to next budget pay_sons(); } + + // Split vote_ids + if (head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) { + // Get SON 1.33.0 and check if it has HIVE vote_id + const son_id_type sid = son_id_type(0); + const auto p_son = find(sid); + if(p_son != nullptr) { + if (p_son->sidechain_vote_ids.find(sidechain_type::hive) == p_son->sidechain_vote_ids.end()) { + // Add vote_ids for HIVE and ETHEREUM to all existing SONs + const auto &all_sons = get_index_type().indices().get(); + for (const son_object &son : all_sons) { + vote_id_type existing_vote_id_bitcoin; + vote_id_type new_vote_id_hive; + vote_id_type new_vote_id_eth; + + modify(gpo, [&new_vote_id_hive, &new_vote_id_eth](global_property_object &p) { + new_vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive); + new_vote_id_eth = get_next_vote_id(p, vote_id_type::son_ethereum); + }); + + modify(son, [new_vote_id_hive, new_vote_id_eth](son_object &obj) { + obj.sidechain_vote_ids[sidechain_type::hive] = new_vote_id_hive; + obj.sidechain_vote_ids[sidechain_type::ethereum] = new_vote_id_eth; + }); + + // Duplicate all votes from bitcoin to hive + const auto &all_accounts = get_index_type().indices().get(); + for (const auto &account : all_accounts) { + if (account.options.votes.count(existing_vote_id_bitcoin) != 0) { + modify(account, [new_vote_id_hive](account_object &a) { + a.options.votes.insert(new_vote_id_hive); + }); + } + } + } + } + } + } } void update_son_params(database& db) { - if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON3_TIME) ) + if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME) ) { const auto& gpo = db.get_global_properties(); db.modify( gpo, []( global_property_object& gpo ) { gpo.parameters.extensions.value.maximum_son_count = 7; }); } - else + + if( (db.head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) ) { const auto& gpo = db.get_global_properties(); db.modify( gpo, []( global_property_object& gpo ) { @@ -2343,20 +2379,23 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // same rationale as for witnesses d._committee_count_histogram_buffer[offset] += voting_stake; } - FC_ASSERT( opinion_account.options.extensions.value.num_son.valid() , "Invalid son number" ); - for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) { - const auto sidechain = num_sidechain_son.first; - const auto& num_son = num_sidechain_son.second; - if (num_son <= props.parameters.maximum_son_count()) { - uint16_t offset = std::min(size_t(num_son / 2), - d._son_count_histogram_buffer.at(sidechain).size() - 1); - // votes for a number greater than maximum_son_count - // are turned into votes for maximum_son_count. - // - // in particular, this takes care of the case where a - // member was voting for a high number, then the - // parameter was lowered. - d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake; + + if ( opinion_account.options.extensions.value.num_son.valid() ) + { + for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) { + const auto sidechain = num_sidechain_son.first; + const auto& num_son = num_sidechain_son.second; + if (num_son <= props.parameters.maximum_son_count()) { + uint16_t offset = std::min(size_t(num_son / 2), + d._son_count_histogram_buffer.at(sidechain).size() - 1); + // votes for a number greater than maximum_son_count + // are turned into votes for maximum_son_count. + // + // in particular, this takes care of the case where a + // member was voting for a high number, then the + // parameter was lowered. + d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake; + } } } diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index dea75bc6..4a3b519f 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -112,7 +112,6 @@ void database::reindex( fc::path data_dir ) uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50; ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) ); - const std::lock_guard undo_db_lock{_undo_db_mutex}; auto_undo_enabler undo(_slow_replays, _undo_db); if( head_block_num() >= undo_point ) { diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index b4c4bb6a..12d4a6dd 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -204,17 +204,18 @@ void database::update_son_schedule() { const global_property_object& gpo = get_global_properties(); - for(const auto& active_sidechain_type : active_sidechain_types) + for(const auto& active_sidechain_type : active_sidechain_types(head_block_time())) { const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); - if( head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0) + if( gpo.active_sons.at(active_sidechain_type).size() != 0 && + head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0) { modify( sidechain_sso, [&]( son_schedule_object& _sso ) { _sso.current_shuffled_sons.clear(); _sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() ); - for ( const son_info &w : gpo.active_sons.at(active_sidechain_type) ) { + for ( const auto &w : gpo.active_sons.at(active_sidechain_type) ) { _sso.current_shuffled_sons.push_back(w.son_id); } @@ -350,7 +351,7 @@ void database::update_son_schedule(const signed_block& next_block) assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); - for(const auto& active_sidechain_type : active_sidechain_types) + for(const auto& active_sidechain_type : active_sidechain_types(head_block_time())) { const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); son_id_type first_son; diff --git a/libraries/chain/hardfork.d/SON3.hf b/libraries/chain/hardfork.d/SON3.hf deleted file mode 100644 index d9556e30..00000000 --- a/libraries/chain/hardfork.d/SON3.hf +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef HARDFORK_SON3_TIME -#ifdef BUILD_PEERPLAYS_TESTNET -#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00")) -#else -#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-07-16T00:00:00")) -#endif -#endif diff --git a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf index 72e929fe..0eb8cc04 100644 --- a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf +++ b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf @@ -1,7 +1,7 @@ #ifndef HARDFORK_SON_FOR_ETHEREUM_TIME #ifdef BUILD_PEERPLAYS_TESTNET -#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-01-24T00:00:00")) #else -#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-03-24T00:00:00")) #endif #endif diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 2a432732..c8e1f214 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -584,14 +584,14 @@ namespace graphene { namespace chain { void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void update_active_witnesses(); void update_active_committee_members(); - void update_son_metrics( const flat_map >& curr_active_sons ); + void update_son_metrics( const flat_map >& curr_active_sons ); void update_active_sons(); void remove_son_proposal( const proposal_object& proposal ); void remove_inactive_son_down_proposals( const vector& son_ids_to_remove ); void remove_inactive_son_proposals( const vector& son_ids_to_remove ); - void update_son_statuses( const flat_map >& curr_active_sons, - const flat_map >& new_active_sons ); - void update_son_wallet( const flat_map >& new_active_sons ); + void update_son_statuses( const flat_map >& curr_active_sons, + const flat_map >& new_active_sons ); + void update_son_wallet( const flat_map >& new_active_sons ); void update_worker_votes(); public: @@ -636,7 +636,7 @@ namespace graphene { namespace chain { vector _committee_count_histogram_buffer; flat_map > _son_count_histogram_buffer = []{ flat_map > son_count_histogram_buffer; - for(const auto& active_sidechain_type : active_sidechain_types){ + for(const auto& active_sidechain_type : all_sidechain_types){ son_count_histogram_buffer[active_sidechain_type] = vector{}; } return son_count_histogram_buffer; diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 71aba1dd..78798202 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include namespace graphene { namespace chain { @@ -49,15 +49,15 @@ namespace graphene { namespace chain { chain_parameters parameters; optional pending_parameters; - uint32_t next_available_vote_id = 0; - vector active_committee_members; // updated once per maintenance interval - flat_set active_witnesses; // updated once per maintenance interval - flat_map > active_sons = []() // updated once per maintenance interval + uint32_t next_available_vote_id = 0; + vector active_committee_members; // updated once per maintenance interval + flat_set active_witnesses; // updated once per maintenance interval + flat_map > active_sons = []() // updated once per maintenance interval { - flat_map > active_sons; - for(const auto& active_sidechain_type : active_sidechain_types) + flat_map > active_sons; + for(const auto& active_sidechain_type : all_sidechain_types) { - active_sons[active_sidechain_type] = vector(); + active_sons[active_sidechain_type] = vector(); } return active_sons; }(); diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 9c4c16d5..c8074885 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -44,7 +44,7 @@ namespace graphene { namespace chain { /// Must not exceed the actual number of son members voted for in @ref votes optional< flat_map > num_son = []{ flat_map num_son; - for(const auto& active_sidechain_type : active_sidechain_types){ + for(const auto& active_sidechain_type : all_sidechain_types){ num_son[active_sidechain_type] = 0; } return num_son; diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp index 01a4ce74..593432bf 100644 --- a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp @@ -71,7 +71,7 @@ FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer (sidechain) (object_id) (transaction) - (signers) ) + (signers)) FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(signer)(payer) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp index 75c3db85..3165554f 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp @@ -1,40 +1,47 @@ #pragma once #include #include +#include namespace graphene { namespace chain { - struct son_wallet_recreate_operation : public base_operation - { - struct fee_parameters_type { uint64_t fee = 0; }; + struct son_wallet_recreate_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + struct ext + { + optional > > sidechain_sons; + }; - asset fee; - account_id_type payer; + asset fee; + account_id_type payer; - flat_map > sons; + vector sons; + extension< ext > extensions; - account_id_type fee_payer()const { return payer; } - share_type calculate_fee(const fee_parameters_type& k)const { return 0; } - }; + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; - struct son_wallet_update_operation : public base_operation - { - struct fee_parameters_type { uint64_t fee = 0; }; + struct son_wallet_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; - asset fee; - account_id_type payer; + asset fee; + account_id_type payer; - son_wallet_id_type son_wallet_id; - sidechain_type sidechain; - string address; + son_wallet_id_type son_wallet_id; + sidechain_type sidechain; + string address; - account_id_type fee_payer()const { return payer; } - share_type calculate_fee(const fee_parameters_type& k)const { return 0; } - }; + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; } } // namespace graphene::chain FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) ) -FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) ) +FC_REFLECT(graphene::chain::son_wallet_recreate_operation::ext, (sidechain_sons)) +FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons)(extensions) ) FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) ) diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp index a717f778..421ef597 100644 --- a/libraries/chain/include/graphene/chain/sidechain_defs.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -1,7 +1,11 @@ #pragma once #include + +#include + #include +#include namespace graphene { namespace chain { @@ -14,9 +18,23 @@ enum class sidechain_type { hive }; -static const std::set active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive}; +static const std::set all_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive}; -} } +inline std::set active_sidechain_types(const fc::time_point_sec block_time) { + std::set active_sidechain_types{}; + + if (block_time >= HARDFORK_SON_TIME) + active_sidechain_types.insert(sidechain_type::bitcoin); + if (block_time >= HARDFORK_SON_FOR_HIVE_TIME) + active_sidechain_types.insert(sidechain_type::hive); + if (block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME) + active_sidechain_types.insert(sidechain_type::ethereum); + + return active_sidechain_types; +} + +} // namespace chain +} // namespace graphene FC_REFLECT_ENUM(graphene::chain::sidechain_type, (unknown) diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp index 30a0dd5e..171c564a 100644 --- a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -30,7 +30,7 @@ namespace graphene { namespace chain { sidechain_type sidechain = sidechain_type::unknown; object_id_type object_id; std::string transaction; - std::vector signers; + std::vector signers; std::vector> signatures; std::string sidechain_transaction; diff --git a/libraries/chain/include/graphene/chain/son_info.hpp b/libraries/chain/include/graphene/chain/son_info.hpp index 75e58319..afaf9506 100644 --- a/libraries/chain/include/graphene/chain/son_info.hpp +++ b/libraries/chain/include/graphene/chain/son_info.hpp @@ -3,34 +3,40 @@ #include namespace graphene { namespace chain { - using namespace graphene::db; /** - * @class son_info - * @brief tracks information about a SON info required to re/create primary wallet - * @ingroup object + * @class son_info + * @brief tracks information about a SON info required to re/create primary wallet + * @ingroup object */ struct son_info { son_id_type son_id; weight_type weight = 0; public_key_type signing_key; - string public_key; + flat_map sidechain_public_keys; - bool operator==(const son_info& rhs) const { + bool operator==(const son_info& rhs) { bool son_sets_equal = (son_id == rhs.son_id) && (weight == rhs.weight) && (signing_key == rhs.signing_key) && - (public_key == rhs.public_key); + (sidechain_public_keys.size() == rhs.sidechain_public_keys.size()); + if (son_sets_equal) { + bool sidechain_public_keys_equal = true; + for (size_t i = 0; i < sidechain_public_keys.size(); i++) { + const auto lhs_scpk = sidechain_public_keys.nth(i); + const auto rhs_scpk = rhs.sidechain_public_keys.nth(i); + sidechain_public_keys_equal = sidechain_public_keys_equal && + (lhs_scpk->first == rhs_scpk->first) && + (lhs_scpk->second == rhs_scpk->second); + } + son_sets_equal = son_sets_equal && sidechain_public_keys_equal; + } return son_sets_equal; } }; } } -FC_REFLECT( graphene::chain::son_info, - (son_id) - (weight) - (signing_key) - (public_key) ) +FC_REFLECT( graphene::chain::son_info, (son_id) (weight) (signing_key) (sidechain_public_keys) ) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index afe7230f..61ada2e5 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -65,7 +65,15 @@ namespace graphene { namespace chain { account_id_type son_account; flat_map sidechain_vote_ids; - flat_map total_votes; + flat_map total_votes = []() + { + flat_map total_votes; + for(const auto& active_sidechain_type : all_sidechain_types) + { + total_votes[active_sidechain_type] = 0; + } + return total_votes; + }(); string url; vesting_balance_id_type deposit; public_key_type signing_key; @@ -74,7 +82,7 @@ namespace graphene { namespace chain { flat_map statuses = []() { flat_map statuses; - for(const auto& active_sidechain_type : active_sidechain_types) + for(const auto& active_sidechain_type : all_sidechain_types) { statuses[active_sidechain_type] = son_status::inactive; } @@ -83,13 +91,15 @@ namespace graphene { namespace chain { flat_map sidechain_public_keys; void pay_son_fee(share_type pay, database& db); - bool has_valid_config()const; - bool has_valid_config(time_point_sec head_block_time)const; + bool has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const; - inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); } - inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); } - inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); } - inline vote_id_type get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); } + inline optional get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.contains(sidechain) ? sidechain_vote_ids.at(sidechain) : optional{}; } + inline optional get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); } + inline optional get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); } + inline optional get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); } + + private: + bool has_valid_config(sidechain_type sidechain) const; }; struct by_account; @@ -105,14 +115,14 @@ namespace graphene { namespace chain { ordered_unique< tag, member >, - ordered_unique< tag, - const_mem_fun + ordered_non_unique< tag, + const_mem_fun, &son_object::get_bitcoin_vote_id> >, - ordered_unique< tag, - const_mem_fun + ordered_non_unique< tag, + const_mem_fun, &son_object::get_hive_vote_id> >, - ordered_unique< tag, - const_mem_fun + ordered_non_unique< tag, + const_mem_fun, &son_object::get_ethereum_vote_id> > > >; diff --git a/libraries/chain/include/graphene/chain/son_sidechain_info.hpp b/libraries/chain/include/graphene/chain/son_sidechain_info.hpp new file mode 100644 index 00000000..8d81f2a4 --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_sidechain_info.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + + /** + * @class son_sidechain_info + * @brief tracks information about a SON info required to re/create primary wallet + * @ingroup object + */ + struct son_sidechain_info { + son_id_type son_id; + weight_type weight = 0; + public_key_type signing_key; + string public_key; + + bool operator==(const son_sidechain_info& rhs) const { + bool son_sets_equal = + (son_id == rhs.son_id) && + (weight == rhs.weight) && + (signing_key == rhs.signing_key) && + (public_key == rhs.public_key); + + return son_sets_equal; + } + }; + +} } + +FC_REFLECT( graphene::chain::son_sidechain_info, (son_id) (weight) (signing_key) (public_key) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp index 63b546ea..79ffb2e0 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include namespace graphene { namespace chain { @@ -21,7 +21,7 @@ namespace graphene { namespace chain { time_point_sec expires; flat_map addresses; - flat_map > sons; + flat_map > sons; }; struct by_valid_from; diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index a1b7994e..2a5c1045 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -174,31 +174,37 @@ void account_options::validate() const { auto needed_witnesses = num_witness; auto needed_committee = num_committee; - FC_ASSERT( extensions.value.num_son.valid() , "Invalid son number" ); - flat_map needed_sons = *extensions.value.num_son; for( vote_id_type id : votes ) if( id.type() == vote_id_type::witness && needed_witnesses ) --needed_witnesses; else if ( id.type() == vote_id_type::committee && needed_committee ) --needed_committee; - else if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] ) - --needed_sons[sidechain_type::bitcoin]; - else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] ) - --needed_sons[sidechain_type::hive]; - else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] ) - --needed_sons[sidechain_type::ethereum]; FC_ASSERT( needed_witnesses == 0, "May not specify fewer witnesses than the number voted for."); FC_ASSERT( needed_committee == 0, "May not specify fewer committee members than the number voted for."); - FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0, - "May not specify fewer Bitcoin SONs than the number voted for."); - FC_ASSERT( needed_sons[sidechain_type::hive] == 0, - "May not specify fewer Hive SONs than the number voted for."); - FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0, - "May not specify fewer Ethereum SONs than the number voted for."); + + if ( extensions.value.num_son.valid() ) + { + flat_map needed_sons = *extensions.value.num_son; + + for( vote_id_type id : votes ) + if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] ) + --needed_sons[sidechain_type::bitcoin]; + else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] ) + --needed_sons[sidechain_type::hive]; + else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] ) + --needed_sons[sidechain_type::ethereum]; + + FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0, + "May not specify fewer Bitcoin SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::hive] == 0, + "May not specify fewer Hive SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0, + "May not specify fewer Ethereum SONs than the number voted for."); + } } void affiliate_reward_distribution::validate() const diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp index ba256199..14b5ecea 100644 --- a/libraries/chain/sidechain_transaction_evaluator.cpp +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -11,7 +11,7 @@ namespace graphene { namespace chain { void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op) { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); FC_ASSERT((op.object_id.is() || op.object_id.is() || op.object_id.is()), "Invalid object id"); @@ -28,15 +28,27 @@ void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_ object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op) { try { const auto &new_sidechain_transaction_object = db().create([&](sidechain_transaction_object &sto) { + sto.timestamp = db().head_block_time(); sto.sidechain = op.sidechain; sto.object_id = op.object_id; sto.transaction = op.transaction; - sto.signers = op.signers; - std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_info &si) { + std::vector signers; + signers.resize(op.signers.size()); + for(const auto& signer : op.signers){ + son_sidechain_info ssi; + ssi.son_id = signer.son_id; + ssi.weight = signer.weight; + ssi.signing_key = signer.signing_key; + ssi.public_key = signer.sidechain_public_keys.at(op.sidechain); + signers.emplace_back(std::move(ssi)); + } + sto.signers = std::move(signers); + + std::transform(sto.signers.begin(), sto.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_sidechain_info &si) { return std::make_pair(si.son_id, std::string()); }); - for (const auto &si : op.signers) { + for (const auto &si : sto.signers) { sto.total_weight = sto.total_weight + si.weight; } sto.sidechain_transaction = ""; diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index e17f3a5f..18be9a5b 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -38,20 +38,29 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op) object_id_type create_son_evaluator::do_apply(const son_create_operation& op) { try { - vote_id_type vote_id_bitcoin; - vote_id_type vote_id_hive; - vote_id_type vote_id_ethereum; - db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive, &vote_id_ethereum](global_property_object& p) { - vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin); - vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive); - vote_id_ethereum = get_next_vote_id(p, vote_id_type::son_ethereum); - }); + vote_id_type vote_id; + flat_map vote_ids; - const auto& new_son_object = db().create( [&]( son_object& obj ){ + const auto now = db().head_block_time(); + if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + db().modify(db().get_global_properties(), [&vote_id](global_property_object &p) { + vote_id = get_next_vote_id(p, vote_id_type::son_bitcoin); + }); + } + else { + db().modify(db().get_global_properties(), [&vote_ids](global_property_object &p) { + vote_ids[sidechain_type::bitcoin] = get_next_vote_id(p, vote_id_type::son_bitcoin); + vote_ids[sidechain_type::hive] = get_next_vote_id(p, vote_id_type::son_hive); + vote_ids[sidechain_type::ethereum] = get_next_vote_id(p, vote_id_type::son_ethereum); + }); + } + + const auto& new_son_object = db().create( [&]( son_object& obj ) { obj.son_account = op.owner_account; - obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin; - obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive; - obj.sidechain_vote_ids[sidechain_type::ethereum] = vote_id_ethereum; + if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) + obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id; + else + obj.sidechain_vote_ids = vote_ids; obj.url = op.url; obj.deposit = op.deposit; obj.signing_key = op.signing_key; @@ -168,7 +177,7 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); // Account for server ntp sync difference fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval()); - for(const auto& active_sidechain_type : active_sidechain_types) { + for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) { if(stats.last_active_timestamp.contains(active_sidechain_type)) FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type)); if(stats.last_down_timestamp.contains(active_sidechain_type)) @@ -195,7 +204,7 @@ object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& active_son_ids.reserve(active_sons.size()); std::transform(active_sons.cbegin(), active_sons.cend(), std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info &swi) { + [](const son_sidechain_info &swi) { return swi.son_id; }); @@ -244,7 +253,7 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati status_need_to_report_down = true; } FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); - for(const auto& active_sidechain_type : active_sidechain_types) { + for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())) { if(stats.last_active_timestamp.contains(active_sidechain_type)) FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type)); } diff --git a/libraries/chain/son_object.cpp b/libraries/chain/son_object.cpp index e607c103..717285ae 100644 --- a/libraries/chain/son_object.cpp +++ b/libraries/chain/son_object.cpp @@ -6,24 +6,22 @@ namespace graphene { namespace chain { db.adjust_balance(son_account, pay); } - bool son_object::has_valid_config()const { - return ((std::string(signing_key).length() > 0) && - (sidechain_public_keys.size() > 0) && - (sidechain_public_keys.find( sidechain_type::bitcoin ) != sidechain_public_keys.end()) && - (sidechain_public_keys.at(sidechain_type::bitcoin).length() > 0)); + bool son_object::has_valid_config(sidechain_type sidechain) const { + return (sidechain_public_keys.find( sidechain ) != sidechain_public_keys.end()) && + (sidechain_public_keys.at(sidechain).length() > 0); } - bool son_object::has_valid_config(time_point_sec head_block_time)const { - bool retval = has_valid_config(); + bool son_object::has_valid_config(time_point_sec head_block_time, sidechain_type sidechain) const { + bool retval = (std::string(signing_key).length() > 0) && (sidechain_public_keys.size() > 0); - if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME) { - retval = retval && - (sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) && - (sidechain_public_keys.at(sidechain_type::hive).length() > 0); - - retval = retval && - (sidechain_public_keys.find( sidechain_type::ethereum ) != sidechain_public_keys.end()) && - (sidechain_public_keys.at(sidechain_type::ethereum).length() > 0); + if (head_block_time < HARDFORK_SON_FOR_HIVE_TIME) { + retval = retval && has_valid_config(sidechain_type::bitcoin); + } + if (head_block_time >= HARDFORK_SON_FOR_HIVE_TIME && head_block_time < HARDFORK_SON_FOR_ETHEREUM_TIME) { + retval = retval && has_valid_config(sidechain_type::bitcoin) && has_valid_config(sidechain_type::hive); + } + else if (head_block_time >= HARDFORK_SON_FOR_ETHEREUM_TIME) { + retval = retval && has_valid_config(sidechain); } return retval; diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 9b1ff1b7..7f7d830c 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -7,8 +7,9 @@ namespace graphene { namespace chain { void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) { try{ - FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + const auto now = db().head_block_time(); + FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); const auto& idx = db().get_index_type().indices().get(); auto itr = idx.rbegin(); @@ -16,7 +17,23 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate { // Compare current wallet SONs and to-be lists of active sons auto cur_wallet_sons = (*itr).sons; - auto new_wallet_sons = op.sons; + flat_map > new_wallet_sons; + if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + for(const auto& son : op.sons){ + for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){ + son_sidechain_info ssi; + ssi.son_id = son.son_id; + ssi.weight = son.weight; + ssi.signing_key = son.signing_key; + ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type); + new_wallet_sons[active_sidechain_type].emplace_back(std::move(ssi)); + } + } + } + else{ + FC_ASSERT(op.extensions.value.sidechain_sons.valid(), "Sons is not valid"); + new_wallet_sons = *op.extensions.value.sidechain_sons; + } bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size()); if (son_sets_equal) { @@ -51,9 +68,26 @@ object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate } const auto& new_son_wallet_object = db().create( [&]( son_wallet_object& obj ){ - obj.valid_from = db().head_block_time(); + const auto now = db().head_block_time(); + obj.valid_from = now; obj.expires = time_point_sec::maximum(); - obj.sons = op.sons; + if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + flat_map > sons; + for(const auto& son : op.sons){ + for(const auto& active_sidechain_type : active_sidechain_types(db().head_block_time())){ + son_sidechain_info ssi; + ssi.son_id = son.son_id; + ssi.weight = son.weight; + ssi.signing_key = son.signing_key; + ssi.public_key = son.sidechain_public_keys.at(active_sidechain_type); + sons[active_sidechain_type].emplace_back(std::move(ssi)); + } + } + obj.sons = std::move(sons); + } + else{ + obj.sons = *op.extensions.value.sidechain_sons; + } }); return new_son_wallet_object.id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index ae0a867b..7101c5fa 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -10,12 +10,13 @@ namespace graphene { namespace chain { void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op) { try { - FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); - + const auto now = db().head_block_time(); + FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); const auto &son_idx = db().get_index_type().indices().get(); const auto so = son_idx.find(op.son_id); FC_ASSERT(so != son_idx.end(), "SON not found"); FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner"); + FC_ASSERT(!(op.sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed"); const auto &ss_idx = db().get_index_type().indices().get(); FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists"); @@ -23,9 +24,17 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w const auto &swwo_idx = db().get_index_type().indices().get(); const auto swwo = swwo_idx.find(op.peerplays_uid); if (swwo == swwo_idx.end()) { + const sidechain_type sidechain = [&op]{ + if(op.sidechain == sidechain_type::peerplays){ + return op.withdraw_sidechain; + } + else + return op.sidechain; + }(); + const auto &gpo = db().get_global_properties(); bool expected = false; - for (auto &si : gpo.active_sons.at(op.sidechain)) { + for (auto &si : gpo.active_sons.at(sidechain)) { if (op.son_id == si.son_id) { expected = true; break; @@ -76,8 +85,16 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w swwo.withdraw_currency = op.withdraw_currency; swwo.withdraw_amount = op.withdraw_amount; + const sidechain_type sidechain = [&op]{ + if(op.sidechain == sidechain_type::peerplays){ + return op.withdraw_sidechain; + } + else + return op.sidechain; + }(); + const auto &gpo = db().get_global_properties(); - for (auto &si : gpo.active_sons.at(op.sidechain)) { + for (auto &si : gpo.active_sons.at(sidechain)) { swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); auto stats_itr = db().get_index_type().indices().get().find(si.son_id); @@ -138,13 +155,17 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op) { try{ - FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + const auto now = db().head_block_time(); + FC_ASSERT(now >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_withdraw_id); FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); - FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); + FC_ASSERT(!(itr->sidechain == sidechain_type::peerplays && now >= HARDFORK_SON_FOR_ETHEREUM_TIME), "Peerplays sidechain type is not allowed"); + if(itr->sidechain != sidechain_type::peerplays) { + FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); + } FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index eaa48526..e880a7ae 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -128,7 +128,7 @@ private: std::mutex event_handler_mutex; typedef std::lock_guard scoped_lock; - std::string create_primary_wallet_address(const std::vector &son_pubkeys); + std::string create_primary_wallet_address(const std::vector &son_pubkeys); std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address); std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index 5e4b6a61..0cd22f96 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -68,7 +68,8 @@ private: ethereum::chain_id_type chain_id; ethereum::network_id_type network_id; - std::string create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id); + std::string create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id); + std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); std::string sign_transaction(const sidechain_transaction_object &sto); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index ae9967f7..bd5bb504 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -116,21 +116,21 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec sidechain_enabled_peerplays(false), current_son_id([] { std::map current_son_id; - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : all_sidechain_types) { current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits().max())); } return current_son_id; }()), sidechain_enabled([] { std::map sidechain_enabled; - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : all_sidechain_types) { sidechain_enabled.emplace(active_sidechain_type, false); } return sidechain_enabled; }()), net_handlers([] { std::map> net_handlers; - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : all_sidechain_types) { net_handlers.emplace(active_sidechain_type, nullptr); } return net_handlers; @@ -149,7 +149,7 @@ peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { } try { - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : all_sidechain_types) { if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).valid()) _son_processing_task.at(active_sidechain_type).wait(); } @@ -362,7 +362,7 @@ bool peerplays_sidechain_plugin_impl::is_active_son(sidechain_type sidechain, so set active_son_ids; std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(), std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info &swi) { + [](const son_sidechain_info &swi) { return swi.son_id; }); @@ -415,7 +415,7 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio status_son_down_op_valid = false; } if (status_son_down_op_valid) { - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) { if (stats.last_active_timestamp.contains(active_sidechain_type)) { const fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time); if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) { @@ -468,7 +468,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { //! Check that son is active (at least for one sidechain_type) bool is_son_active = false; - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : active_sidechain_types(d.head_block_time())) { if (sidechain_enabled.at(active_sidechain_type)) { if (is_active_son(active_sidechain_type, son_id)) is_son_active = true; @@ -506,7 +506,7 @@ void peerplays_sidechain_plugin_impl::schedule_son_processing() { const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing); - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) { if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).wait_for(std::chrono::seconds{0}) != std::future_status::ready) { wlog("Son doesn't process in time for sidechain: ${active_sidechain_type}", ("active_sidechain_type", active_sidechain_type)); _son_processing_task.at(active_sidechain_type).wait(); @@ -623,7 +623,7 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidecha std::map> peerplays_sidechain_plugin_impl::get_son_listener_log() { std::map> result; - for (const auto &active_sidechain_type : active_sidechain_types) { + for (const auto &active_sidechain_type : active_sidechain_types(plugin.database().head_block_time())) { if (net_handlers.at(active_sidechain_type)) { result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log()); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index f1594c0f..ee60bd28 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -453,7 +453,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) if (swo != idx.end()) { const auto &active_sons = gpo.active_sons.at(sidechain); - vector wallet_sons = swo->sons.at(sidechain); + const auto &wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -466,7 +466,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) if (son_sets_equal) { const auto &active_sons = gpo.active_sons.at(sidechain); vector son_pubkeys_bitcoin; - for (const son_info &si : active_sons) { + for (const auto &si : active_sons) { son_pubkeys_bitcoin.push_back(si.public_key); } @@ -758,7 +758,14 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { stc_op.object_id = prev_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = prev_sw->sons.at(sidechain); + for (const auto &signer : prev_sw->sons.at(sidechain)) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_op.proposed_ops.emplace_back(stc_op); } } @@ -861,7 +868,14 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj stc_op.object_id = swdo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons.at(sidechain); + for (const auto &signer : gpo.active_sons.at(sidechain)) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_op.proposed_ops.emplace_back(stc_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); @@ -905,7 +919,14 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw stc_op.object_id = swwo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons.at(sidechain); + for (const auto &signer : gpo.active_sons.at(sidechain)) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_op.proposed_ops.emplace_back(stc_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); @@ -1007,7 +1028,7 @@ optional sidechain_net_handler_bitcoin::estimate_withdrawal_transaction_f return optional{}; } -std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector &son_pubkeys) { +std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector &son_pubkeys) { using namespace bitcoin; std::vector> pubkey_weights; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 80a11b16..bce2ce50 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -239,7 +239,7 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) if (swo != idx.end()) { const auto active_sons = gpo.active_sons.at(sidechain); - const vector wallet_sons = swo->sons.at(sidechain); + const vector wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -474,7 +474,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { proposal_op.proposed_ops.emplace_back(swu_op); const auto signers = [this, &prev_sw, &active_sw, &swi] { - std::vector signers; + std::vector signers; //! Check if we don't have any previous set of active SONs use the current one if (prev_sw != swi.rend()) { if (!prev_sw->sons.at(sidechain).empty()) @@ -495,7 +495,14 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { stc_op.object_id = active_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = signers; + for (const auto &signer : signers) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_op.proposed_ops.emplace_back(stc_op); } @@ -593,7 +600,14 @@ bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdra stc_op.object_id = swwo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons.at(sidechain); + for (const auto &signer : gpo.active_sons.at(sidechain)) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_op.proposed_ops.emplace_back(stc_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); @@ -773,7 +787,7 @@ optional sidechain_net_handler_ethereum::estimate_withdrawal_transaction_ return asset.amount_from_string(std::to_string(eth_gas_fee)); } -std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id) { +std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id) { std::vector> owners_weights; for (auto &son : son_pubkeys) { const std::string pub_key_str = son.public_key; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index c3897f74..2a11db85 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -213,8 +213,8 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { const auto swo = idx.find(swo_id); if (swo != idx.end()) { - auto active_sons = gpo.active_sons.at(sidechain); - vector wallet_sons = swo->sons.at(sidechain); + const auto &active_sons = gpo.active_sons.at(sidechain); + const auto &wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -549,7 +549,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { proposal_op.proposed_ops.emplace_back(swu_op); const auto signers = [this, &prev_sw, &active_sw, &swi] { - std::vector signers; + std::vector signers; //! Check if we don't have any previous set of active SONs use the current one if (prev_sw != swi.rend()) { if (!prev_sw->sons.at(sidechain).empty()) @@ -568,8 +568,14 @@ void sidechain_net_handler_hive::process_primary_wallet() { stc_op.object_id = active_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = signers; - + for (const auto &signer : gpo.active_sons.at(sidechain)) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_op.proposed_ops.emplace_back(stc_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); @@ -725,7 +731,14 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob stc_op.object_id = swwo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons.at(sidechain); + for (const auto &signer : gpo.active_sons.at(sidechain)) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_op.proposed_ops.emplace_back(stc_op); signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index 76fbbb3e..0f6d06eb 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -197,7 +197,14 @@ bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_o stc_op.object_id = swdo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons.at(sidechain); + for (const auto &signer : gpo.active_sons.at(sidechain)) { + son_info si; + si.son_id = signer.son_id; + si.weight = signer.weight; + si.signing_key = signer.signing_key; + si.sidechain_public_keys[sidechain] = signer.public_key; + stc_op.signers.emplace_back(std::move(si)); + } proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 6d97f647..1de38ffa 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1559,14 +1559,14 @@ class wallet_api * @brief Get list of active sons * @return List of active SONs */ - flat_map> get_active_sons(); + flat_map> get_active_sons(); /** * @brief Get list of active sons * @param sidechain Sidechain type [bitcoin|ethereum|hive] * @return List of active SONs */ - vector get_active_sons_by_sidechain(sidechain_type sidechain); + vector get_active_sons_by_sidechain(sidechain_type sidechain); /** * @brief Get SON network status diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 794e8692..e1363b0b 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2230,12 +2230,12 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account) ) } - flat_map> get_active_sons() + flat_map> get_active_sons() { try { return _remote_db->get_active_sons(); } FC_CAPTURE_AND_RETHROW() } - vector get_active_sons_by_sidechain(sidechain_type sidechain) + vector get_active_sons_by_sidechain(sidechain_type sidechain) { try { return _remote_db->get_active_sons_by_sidechain(sidechain); } FC_CAPTURE_AND_RETHROW() } @@ -2775,13 +2775,15 @@ public: if (approve) { - auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); + FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj)); + auto insert_result = voting_account_object.options.votes.insert(*son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) FC_THROW("Account ${account} has already voted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); } else { - unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); + FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj)); + unsigned votes_removed = voting_account_object.options.votes.erase(*son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) FC_THROW("Account ${account} has already unvoted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); } @@ -2819,7 +2821,8 @@ public: FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); - auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); + FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj)); + auto insert_result = voting_account_object.options.votes.insert(*son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); } @@ -2830,7 +2833,8 @@ public: FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); - unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); + FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj)); + unsigned votes_removed = voting_account_object.options.votes.erase(*son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); } @@ -5294,12 +5298,12 @@ map wallet_api::list_sons(const string& lowerbound, uint32_ return my->_remote_db->lookup_son_accounts(lowerbound, limit); } -flat_map> wallet_api::get_active_sons() +flat_map> wallet_api::get_active_sons() { return my->get_active_sons(); } -vector wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain) +vector wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain) { return my->get_active_sons_by_sidechain(sidechain); } diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 83db8d48..4662c57c 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -39,7 +39,7 @@ public: fixture_(fixture) { fixture_.init_nathan(); - fixture_.generate_blocks(HARDFORK_SON3_TIME); + fixture_.generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME); fixture_.generate_block(); } @@ -143,9 +143,12 @@ BOOST_AUTO_TEST_CASE( create_sons ) BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); - BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); - BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); - BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); + BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin)); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 22); + BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::hive)); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive)->instance(), 23); + BOOST_REQUIRE(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum)); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 24); auto son2_obj = con.wallet_api_ptr->get_son("son2account"); BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account")); @@ -153,9 +156,12 @@ BOOST_AUTO_TEST_CASE( create_sons ) BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2"); - BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 25); - BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 26); - BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 27); + BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin)); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 25); + BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::hive)); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive)->instance(), 26); + BOOST_REQUIRE(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum)); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 27); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -190,9 +196,12 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); - BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); - BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); - BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); + BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::bitcoin)); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin)->instance(), 22); + BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::hive)); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive)->instance(), 23); + BOOST_REQUIRE(son_data.get_sidechain_vote_id(sidechain_type::ethereum)); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum)->instance(), 24); // update SON sidechain_public_keys.clear(); @@ -1400,7 +1409,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) // Check Network Status Before sending Heartbeats BOOST_CHECK(generate_maintenance_block()); - for(sidechain_type sidechain:active_sidechain_types) + for(sidechain_type sidechain : all_sidechain_types) { auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) @@ -1448,7 +1457,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) generate_blocks(50); BOOST_TEST_MESSAGE("Checking Network Status"); - for(sidechain_type sidechain:active_sidechain_types) + for(sidechain_type sidechain : all_sidechain_types) { auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) @@ -1479,7 +1488,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) con.wallet_api_ptr->sign_transaction(trx2, true); generate_blocks(50); - for(sidechain_type sidechain:active_sidechain_types) + for(sidechain_type sidechain : all_sidechain_types) { auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) @@ -1507,7 +1516,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency(), false); BOOST_TEST_MESSAGE("Checking Network Status"); - for(sidechain_type sidechain:active_sidechain_types) + for(sidechain_type sidechain : all_sidechain_types) { auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) @@ -1535,7 +1544,7 @@ BOOST_FIXTURE_TEST_CASE( get_son_network_status_by_sidechain, cli_fixture ) generate_blocks(db->head_block_time() + gpo.parameters.son_heartbeat_frequency() + gpo.parameters.son_down_time(), false);; BOOST_TEST_MESSAGE("Checking Network Status"); - for(sidechain_type sidechain:active_sidechain_types) + for(sidechain_type sidechain : all_sidechain_types) { auto network_status_obj = con.wallet_api_ptr->get_son_network_status_by_sidechain(sidechain); for(map::iterator iter=network_status_obj.begin(); iter!=network_status_obj.end(); ++iter) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 807c495a..c69768a3 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -1038,7 +1038,7 @@ BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture ) generate_block(); // get the maintenance skip slots out of the way*/ BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 7); - generate_blocks(HARDFORK_SON3_TIME); + generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME); // after this hardfork maximum son account should not reset the value // on 7 after maintenance interval anymore. It must be HARDFORK_SON2_TIME BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 23ddfd1c..0da34705 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -15,7 +15,7 @@ using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture ) BOOST_AUTO_TEST_CASE( create_son_test ) { - generate_blocks(HARDFORK_SON_TIME); + generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME); generate_block(); set_expiration(db, trx); @@ -314,7 +314,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) const dynamic_global_property_object& dpo = db.get_dynamic_global_properties(); const auto block_interval = db.get_global_properties().parameters.block_interval; BOOST_CHECK( dpo.son_budget.value == 0); - generate_blocks(HARDFORK_SON_TIME); + generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME); while (db.head_block_time() <= HARDFORK_SON_TIME) { generate_block(); } @@ -634,16 +634,18 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { // Modify SON's status to active db.modify( *obj, [&]( son_object& _s) { - _s.statuses[sidechain_type::bitcoin] = son_status::active; - _s.statuses[sidechain_type::hive] = son_status::active; - _s.statuses[sidechain_type::ethereum] = son_status::active; + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + _s.statuses[active_sidechain_type] = son_status::active; + } }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { - _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time()); - _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time()); - _s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time()); + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + _s.last_down_timestamp[active_sidechain_type] = fc::time_point_sec(db.head_block_time()); + } }); { @@ -660,9 +662,10 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); - BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance); - BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::request_maintenance); + } } { @@ -679,23 +682,26 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active); + } } // Modify SON's status to in_maintenance db.modify( *obj, [&]( son_object& _s) { - _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; - _s.statuses[sidechain_type::hive] = son_status::in_maintenance; - _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + _s.statuses[active_sidechain_type] = son_status::in_maintenance; + } }); flat_map downtime; - downtime[sidechain_type::bitcoin] = 0; - downtime[sidechain_type::hive] = 0; - downtime[sidechain_type::ethereum] = 0; + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + downtime[active_sidechain_type] = 0; + } { generate_block(); @@ -711,36 +717,34 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); - downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); - downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); - downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); - BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive); - BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive); - BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::inactive); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); + + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch()); + downtime[active_sidechain_type] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch(); + BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::inactive); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts); + } } // Modify SON's status to in_maintenance db.modify( *obj, [&]( son_object& _s) { - _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; - _s.statuses[sidechain_type::hive] = son_status::in_maintenance; - _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + _s.statuses[active_sidechain_type] = son_status::in_maintenance; + } }); // SON is selected as one of the active SONs db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) { - son_info son_inf; + son_sidechain_info son_inf; son_inf.son_id = son_id_type(0); - _gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf); - _gpo.active_sons[sidechain_type::hive].push_back(son_inf); - _gpo.active_sons[sidechain_type::ethereum].push_back(son_inf); + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + _gpo.active_sons[active_sidechain_type].push_back(son_inf); + } }); { @@ -758,18 +762,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); - downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); - downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); - downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); - BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), downtime.at(active_sidechain_type) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch()); + downtime[active_sidechain_type] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(active_sidechain_type).sec_since_epoch(); + BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts); + } } { @@ -786,15 +785,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin)); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive)); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum)); - BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); - BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); + + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(active_sidechain_type), downtime.at(active_sidechain_type)); + BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(active_sidechain_type) == op.ts); + } } } FC_LOG_AND_RETHROW() } @@ -819,9 +816,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { auto son_stats_obj = sidx.find( obj->statistics ); BOOST_REQUIRE( son_stats_obj != sidx.end() ); - BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); - BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::active); + } { // Check that transaction fails if down_ts < last_active_timestamp @@ -873,12 +871,11 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { generate_block(); trx.clear(); - BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); - BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance); - BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::in_maintenance); - BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts); - BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts); - BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum) == op.down_ts); + for (const auto& active_sidechain_type : active_sidechain_types(db.head_block_time())) + { + BOOST_CHECK( obj->statuses.at(active_sidechain_type) == son_status::in_maintenance); + BOOST_CHECK( son_stats_obj->last_down_timestamp.at(active_sidechain_type) == op.down_ts); + } } { diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp index 4b1e4fe2..fd0d65e8 100644 --- a/tests/tests/son_wallet_tests.cpp +++ b/tests/tests/son_wallet_tests.cpp @@ -160,8 +160,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { si.son_id = son_id_type(0); si.weight = 1000; si.signing_key = alice_public_key; - si.public_key = ""; - op.sons[sidechain_type::bitcoin].push_back(si); + si.sidechain_public_keys[sidechain_type::bitcoin] = ""; + op.sons.push_back(si); } { @@ -169,8 +169,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { si.son_id = son_id_type(1); si.weight = 1000; si.signing_key = bob_public_key; - si.public_key = ""; - op.sons[sidechain_type::bitcoin].push_back(si); + si.sidechain_public_keys[sidechain_type::bitcoin] = ""; + op.sons.push_back(si); } trx.operations.push_back(op); From fc1cdf262971d1ff811b7893fcdb9762a846b2a8 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Wed, 1 Feb 2023 20:54:44 +0200 Subject: [PATCH 082/106] Fix build errors active_sidechain_types --- libraries/chain/son_wallet_evaluator.cpp | 6 ++++-- .../sidechain_net_handler_bitcoin.cpp | 12 ++++++++---- .../sidechain_net_handler_ethereum.cpp | 8 +++++--- .../sidechain_net_handler_hive.cpp | 8 +++++--- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 4eaba48f..138f8e57 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -98,7 +98,8 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); const auto& idx = db().get_index_type().indices().get(); - const auto id = (op.son_wallet_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(op.sidechain))) / active_sidechain_types.size(); + const auto ast = active_sidechain_types(db().head_block_time()); + const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size(); const son_wallet_id_type son_wallet_id{ id }; FC_ASSERT( idx.find(son_wallet_id) != idx.end() ); //auto itr = idx.find(op.son_wallet_id); @@ -110,7 +111,8 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) { try { const auto& idx = db().get_index_type().indices().get(); - const auto id = (op.son_wallet_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(op.sidechain))) / active_sidechain_types.size(); + const auto ast = active_sidechain_types(db().head_block_time()); + const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size(); const son_wallet_id_type son_wallet_id{ id }; auto itr = idx.find(son_wallet_id); if (itr != idx.end()) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 4acd91e0..3f469cc8 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -448,7 +448,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) bool transaction_ok = false; std::string new_pw_address = ""; son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; - const auto id = (swo_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); const son_wallet_id_type op_id{ id }; const auto &idx = database.get_index_type().indices().get(); const auto swo = idx.find(op_id); @@ -488,7 +489,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) if (po.proposed_transaction.operations.size() >= 2) { const object_id_type object_id = op_obj_idx_1.get().object_id; - const auto id = (object_id.instance() - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); const object_id_type obj_id{ object_id.space(), object_id.type(), id }; std::string op_tx_str = op_obj_idx_1.get().transaction; @@ -720,7 +722,8 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { - const auto id = active_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; if (proposal_exists(chain::operation::tag::value, op_id)) { @@ -760,7 +763,8 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { std::string new_pw_address = active_pw_pt.get("result.address"); std::string tx_str = create_primary_wallet_transaction(*prev_sw, new_pw_address); if (!tx_str.empty()) { - const auto prev_id = prev_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto prev_id = prev_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); const object_id_type prev_op_id{ prev_sw->id.space(), prev_sw->id.type(), prev_id }; sidechain_transaction_create_operation stc_op; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 3f912309..d0051714 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -234,7 +234,8 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) bool address_ok = false; bool transaction_ok = false; const son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; - const auto id = (swo_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); const son_wallet_id_type op_id{ id }; const auto &idx = database.get_index_type().indices().get(); const auto swo = idx.find(op_id); @@ -257,7 +258,7 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) if (po.proposed_transaction.operations.size() >= 2) { const object_id_type object_id = op_obj_idx_1.get().object_id; - const auto id = (object_id.instance() - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); const object_id_type obj_id{ object_id.space(), object_id.type(), id }; const std::string op_tx_str = op_obj_idx_1.get().transaction; @@ -456,7 +457,8 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { - const auto id = active_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; if (proposal_exists(chain::operation::tag::value, op_id)) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 2932e5f3..3f1378e7 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -209,7 +209,8 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { bool address_ok = false; bool transaction_ok = false; son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; - const auto id = (swo_id.instance.value - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); const son_wallet_id_type op_id{ id }; const auto &idx = database.get_index_type().indices().get(); const auto swo = idx.find(op_id); @@ -232,7 +233,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { if (po.proposed_transaction.operations.size() >= 2) { const object_id_type object_id = op_obj_idx_1.get().object_id; - const auto id = (object_id.instance() - std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain))) / active_sidechain_types.size(); + const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); const object_id_type obj_id{ object_id.space(), object_id.type(), id }; std::string op_tx_str = op_obj_idx_1.get().transaction; @@ -490,7 +491,8 @@ void sidechain_net_handler_hive::process_primary_wallet() { if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || (active_sw->addresses.at(sidechain).empty())) { - const auto id = active_sw->id.instance() * active_sidechain_types.size() + std::distance(active_sidechain_types.begin(), active_sidechain_types.find(sidechain)); + const auto ast = active_sidechain_types(database.head_block_time()); + const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; if (proposal_exists(chain::operation::tag::value, op_id)) { From 936f13d2a1e6fbda61270c496e230a167cd13dde Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Mon, 6 Feb 2023 08:14:05 +0200 Subject: [PATCH 083/106] #506 - fix load fees from genesis file --- libraries/chain/db_init.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 2e296e39..05326d16 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -1168,6 +1168,11 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); + // Enable fees + modify(get_global_properties(), [&genesis_state](global_property_object& p) { + p.parameters.current_fees = genesis_state.initial_parameters.current_fees; + }); + // Create FBA counters create([&]( fba_accumulator_object& acc ) { From 73b2ba635b5b98e3e64e96b63b78484f3881ebbd Mon Sep 17 00:00:00 2001 From: hirunda Date: Mon, 6 Feb 2023 21:09:28 +0100 Subject: [PATCH 084/106] Fix the issue for wrong number of signers --- libraries/chain/sidechain_transaction_evaluator.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp index 14b5ecea..b0d1a494 100644 --- a/libraries/chain/sidechain_transaction_evaluator.cpp +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -34,7 +34,6 @@ object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_ sto.object_id = op.object_id; sto.transaction = op.transaction; std::vector signers; - signers.resize(op.signers.size()); for(const auto& signer : op.signers){ son_sidechain_info ssi; ssi.son_id = signer.son_id; From e9c7021e161cb63e206dbc957fd64cb3f55afb6d Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Mon, 6 Feb 2023 22:48:40 +0000 Subject: [PATCH 085/106] Bitcoin SON based on libbitcoin --- Dockerfile | 96 ++- Dockerfile.18.04 | 94 ++- README.md | 152 ++-- libraries/app/database_api.cpp | 2 +- .../peerplays_sidechain/CMakeLists.txt | 4 +- .../bitcoin/bitcoin_address.cpp | 58 +- .../bitcoin/estimate_fee_external.cpp | 157 ++++ .../bitcoin/libbitcoin_client.cpp | 227 ++++++ .../bitcoin/bitcoin_address.hpp | 15 +- .../bitcoin/estimate_fee_external.hpp | 37 + .../bitcoin/libbitcoin_client.hpp | 53 ++ .../peerplays_sidechain/bitcoin/utils.hpp | 1 + .../sidechain_net_handler.hpp | 1 + .../sidechain_net_handler_bitcoin.hpp | 157 +++- .../peerplays_sidechain_plugin.cpp | 20 +- .../sidechain_net_handler_bitcoin.cpp | 729 +++++++++++++----- .../sidechain_net_handler_ethereum.cpp | 10 +- .../sidechain_net_handler_hive.cpp | 6 +- 18 files changed, 1396 insertions(+), 423 deletions(-) mode change 100755 => 100644 libraries/plugins/peerplays_sidechain/CMakeLists.txt create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/estimate_fee_external.cpp create mode 100644 libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp diff --git a/Dockerfile b/Dockerfile index 3f4c62b5..d49bbe08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ FROM ubuntu:20.04 -MAINTAINER Peerplays Blockchain Standards Association #=============================================================================== # Ubuntu setup @@ -51,69 +50,102 @@ RUN echo 'peerplays:peerplays' | chpasswd # SSH EXPOSE 22 +WORKDIR /home/peerplays/src + #=============================================================================== # Boost setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ - wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \ - tar -xzvf boost_1_72_0.tar.gz boost_1_72_0 && \ - cd boost_1_72_0/ && \ + wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \ + tar -xzf boost_1_72_0.tar.gz && \ + cd boost_1_72_0 && \ ./bootstrap.sh && \ - ./b2 install + ./b2 install && \ + ldconfig && \ + rm -rf /home/peerplays/src/* #=============================================================================== # cmake setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ - wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ - chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ - ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ - cmake --version + wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \ + chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \ + ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \ + cmake --version && \ + rm -rf /home/peerplays/src/* #=============================================================================== # libzmq setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ - wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ - unzip v4.3.4.zip && \ + wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \ + tar -xzvf v4.3.4.tar.gz && \ cd libzmq-4.3.4 && \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install && \ - ldconfig + make -j$(nproc) && \ + make install && \ + ldconfig && \ + rm -rf /home/peerplays/src/* #=============================================================================== # cppzmq setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ - wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ - unzip v4.8.1.zip && \ - cd cppzmq-4.8.1 && \ + wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \ + tar -xzvf v4.9.0.tar.gz && \ + cd cppzmq-4.9.0 && \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install && \ - ldconfig + make -j$(nproc) && \ + make install && \ + ldconfig && \ + rm -rf /home/peerplays/src/* + +#=============================================================================== +# gsl setup +#=============================================================================== + +RUN \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libpcre3-dev + +RUN \ + wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz && \ + tar -xzvf v4.1.4.tar.gz && \ + cd gsl-4.1.4 && \ + make -j$(nproc) && \ + make install && \ + rm -rf /home/peerplays/src/* + +#=============================================================================== +# libbitcoin-build setup +# libbitcoin-explorer setup +#=============================================================================== + +RUN \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libsodium-dev + +RUN \ + git clone https://github.com/libbitcoin/libbitcoin-build.git && \ + cd libbitcoin-build && \ + ./generate3.sh && \ + cd ../libbitcoin-explorer && \ + ./install.sh && \ + ldconfig && \ + rm -rf /home/peerplays/src/* #=============================================================================== # Doxygen setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ sudo apt install -y bison flex && \ wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \ @@ -129,8 +161,6 @@ RUN \ # Perl setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \ tar -xvf v5.30.0.tar.gz && \ @@ -143,8 +173,6 @@ RUN \ # Peerplays setup #=============================================================================== -WORKDIR /home/peerplays/ - ## Clone Peerplays #RUN \ # git clone https://gitlab.com/PBSA/peerplays.git && \ @@ -176,8 +204,8 @@ WORKDIR /home/peerplays/peerplays-network # Setup Peerplays runimage RUN \ - ln -s /home/peerplays/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ - ln -s /home/peerplays/peerplays/build/programs/witness_node/witness_node ./ + ln -s /home/peerplays/src/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ + ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./ RUN ./witness_node --create-genesis-json genesis.json && \ rm genesis.json diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index 2e153962..d09e01b2 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -1,5 +1,4 @@ FROM ubuntu:18.04 -MAINTAINER Peerplays Blockchain Standards Association #=============================================================================== # Ubuntu setup @@ -51,69 +50,102 @@ RUN echo 'peerplays:peerplays' | chpasswd # SSH EXPOSE 22 +WORKDIR /home/peerplays/src + #=============================================================================== # Boost setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz && \ - tar -xzvf boost_1_72_0.tar.gz boost_1_72_0 && \ - cd boost_1_72_0/ && \ + tar -xzf boost_1_72_0.tar.gz && \ + cd boost_1_72_0 && \ ./bootstrap.sh && \ - ./b2 install + ./b2 install && \ + ldconfig && \ + rm -rf /home/peerplays/src/* #=============================================================================== # cmake setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ - wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh && \ - chmod 755 ./cmake-3.23.1-linux-x86_64.sh && \ - ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license && \ - cmake --version + wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh && \ + chmod 755 ./cmake-3.24.2-linux-x86_64.sh && \ + ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license && \ + cmake --version && \ + rm -rf /home/peerplays/src/* #=============================================================================== # libzmq setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ - wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip && \ - unzip v4.3.4.zip && \ + wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz && \ + tar -xzvf v4.3.4.tar.gz && \ cd libzmq-4.3.4 && \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install && \ - ldconfig + make -j$(nproc) && \ + make install && \ + ldconfig && \ + rm -rf /home/peerplays/src/* #=============================================================================== # cppzmq setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ - wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip && \ - unzip v4.8.1.zip && \ - cd cppzmq-4.8.1 && \ + wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz && \ + tar -xzvf v4.9.0.tar.gz && \ + cd cppzmq-4.9.0 && \ mkdir build && \ cd build && \ cmake .. && \ - make -j$(nproc) install && \ - ldconfig + make -j$(nproc) && \ + make install && \ + ldconfig && \ + rm -rf /home/peerplays/src/* + +#=============================================================================== +# gsl setup +#=============================================================================== + +RUN \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libpcre3-dev + +RUN \ + wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz && \ + tar -xzvf v4.1.4.tar.gz && \ + cd gsl-4.1.4 && \ + make -j$(nproc) && \ + make install && \ + rm -rf /home/peerplays/src/* + +#=============================================================================== +# libbitcoin-build setup +# libbitcoin-explorer setup +#=============================================================================== + +RUN \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + libsodium-dev + +RUN \ + git clone https://github.com/libbitcoin/libbitcoin-build.git && \ + cd libbitcoin-build && \ + ./generate3.sh && \ + cd ../libbitcoin-explorer && \ + ./install.sh && \ + ldconfig && \ + rm -rf /home/peerplays/src/* #=============================================================================== # Doxygen setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ sudo apt install -y bison flex && \ wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz && \ @@ -129,8 +161,6 @@ RUN \ # Perl setup #=============================================================================== -WORKDIR /home/peerplays/ - RUN \ wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz && \ tar -xvf v5.30.0.tar.gz && \ @@ -143,8 +173,6 @@ RUN \ # Peerplays setup #=============================================================================== -WORKDIR /home/peerplays/ - ## Clone Peerplays #RUN \ # git clone https://gitlab.com/PBSA/peerplays.git && \ @@ -176,8 +204,8 @@ WORKDIR /home/peerplays/peerplays-network # Setup Peerplays runimage RUN \ - ln -s /home/peerplays/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ - ln -s /home/peerplays/peerplays/build/programs/witness_node/witness_node ./ + ln -s /home/peerplays/src/peerplays/build/programs/cli_wallet/cli_wallet ./ && \ + ln -s /home/peerplays/src/peerplays/build/programs/witness_node/witness_node ./ RUN ./witness_node --create-genesis-json genesis.json && \ rm genesis.json diff --git a/README.md b/README.md index f3d8c5b9..c3533d29 100644 --- a/README.md +++ b/README.md @@ -8,100 +8,41 @@ This is a quick introduction to get new developers and witnesses up to speed on Officially supported OS are Ubuntu 20.04 and Ubuntu 18.04. -## Ubuntu 20.04 +## Ubuntu 20.04 and 18.04 -Following dependencies are needed for a clean install of Ubuntu 20.04: +Following dependencies are needed for a clean install of Ubuntu 20.04 and Ubuntu 18.04: ``` sudo apt-get install \ - apt-utils autoconf bash build-essential ca-certificates clang-format cmake \ - dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ - libcurl4-openssl-dev libncurses-dev libsnappy-dev \ - libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \ - openssh-server pkg-config perl python3 python3-jinja2 sudo \ + autoconf bash bison build-essential ca-certificates dnsutils expect flex git \ + graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev libpcre3-dev \ + libsnappy-dev libsodium-dev libssl-dev libtool libzip-dev locales lsb-release \ + mc nano net-tools ntp openssh-server pkg-config python3 python3-jinja2 sudo \ systemd-coredump wget ``` -Install libzmq from source: +Boost libraries setup: ``` -wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip -unzip v4.3.4.zip -cd libzmq-4.3.4 -mkdir build -cd build -cmake .. -make -j$(nproc) -sudo make install -sudo ldconfig -``` - -Install cppzmq from source: -``` -wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip -unzip v4.8.1.zip -cd cppzmq-4.8.1 -mkdir build -cd build -cmake .. -make -j$(nproc) -sudo make install -sudo ldconfig -``` - -Building Peerplays -``` -git clone https://gitlab.com/PBSA/peerplays.git -cd peerplays -git submodule update --init --recursive - -# If you want to build Mainnet node -cmake -DCMAKE_BUILD_TYPE=Release - -# If you want to build Testnet node -cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1 - -# Update -j flag depending on your current system specs; -# Recommended 4GB of RAM per 1 CPU core -# make -j2 for 8GB RAM -# make -j4 for 16GB RAM -# make -j8 for 32GB RAM -make -j$(nproc) - -sudo make install # this can install the executable files under /usr/local -``` - -## Ubuntu 18.04 - -Following dependencies are needed for a clean install of Ubuntu 18.04: -``` -sudo apt-get install \ - apt-utils autoconf bash build-essential ca-certificates clang-format \ - dnsutils doxygen expect git graphviz libbz2-dev \ - libcurl4-openssl-dev libncurses-dev libsnappy-dev \ - libssl-dev libtool libzip-dev locales lsb-release mc nano net-tools ntp \ - openssh-server pkg-config perl python3 python3-jinja2 sudo \ - systemd-coredump wget -``` - -Install Boost libraries from source -``` -wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 -tar xjf boost_1_71_0.tar.bz2 -cd boost_1_71_0/ +wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz +tar -xzf boost_1_72_0.tar.gz boost_1_72_0 +cd boost_1_72_0 ./bootstrap.sh +./b2 sudo ./b2 install +sudo ldconfig ``` -Install cmake +cmake setup: ``` -wget -c 'https://cmake.org/files/v3.23/cmake-3.23.1-linux-x86_64.sh' -O cmake-3.23.1-linux-x86_64.sh -chmod 755 ./cmake-3.23.1-linux-x86_64.sh -sudo ./cmake-3.23.1-linux-x86_64.sh --prefix=/usr/ --skip-license +wget https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2-linux-x86_64.sh +chmod 755 ./cmake-3.24.2-linux-x86_64.sh +sudo ./cmake-3.24.2-linux-x86_64.sh --prefix=/usr --skip-license +cmake --version ``` -Install libzmq from source: +libzmq setup: ``` -wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.zip -unzip v4.3.4.zip +wget https://github.com/zeromq/libzmq/archive/refs/tags/v4.3.4.tar.gz +tar -xzvf v4.3.4.tar.gz cd libzmq-4.3.4 mkdir build cd build @@ -111,11 +52,11 @@ sudo make install sudo ldconfig ``` -Install cppzmq from source: +cppzmq setup: ``` -wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.zip -unzip v4.8.1.zip -cd cppzmq-4.8.1 +wget https://github.com/zeromq/cppzmq/archive/refs/tags/v4.9.0.tar.gz +tar -xzvf v4.9.0.tar.gz +cd cppzmq-4.9.0 mkdir build cd build cmake .. @@ -124,6 +65,50 @@ sudo make install sudo ldconfig ``` +gsl setup: +``` +wget https://github.com/imatix/gsl/archive/refs/tags/v4.1.4.tar.gz +tar -xzvf v4.1.4.tar.gz +cd gsl-4.1.4 +make -j$(nproc) +sudo make install +sudo ldconfig +``` + +libbitcoin-explorer setup: +``` +git clone https://github.com/libbitcoin/libbitcoin-build.git +cd libbitcoin-build +./generate3.sh +cd ../libbitcoin-explorer +sudo ./install.sh +sudo ldconfig +``` + +Doxygen setup: +``` +wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_8_17.tar.gz +tar -xvf Release_1_8_17.tar.gz +cd doxygen-Release_1_8_17 +mkdir build +cd build +cmake .. +make -j$(nproc) +sudo make install +sudo ldconfig +``` + +Perl setup: +``` +wget https://github.com/Perl/perl5/archive/refs/tags/v5.30.0.tar.gz +tar -xvf v5.30.0.tar.gz +cd perl5-5.30.0 +./Configure -des +make -j$(nproc) +sudo make install +sudo ldconfig +``` + Building Peerplays ``` git clone https://gitlab.com/PBSA/peerplays.git @@ -146,7 +131,6 @@ make -j$(nproc) sudo make install # this can install the executable files under /usr/local ``` - ## Docker images Install docker, and add current user to docker group. diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c4e69607..be60de78 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2340,7 +2340,7 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const votes_for_sons[sidechain].reserve(sidechain_ids.size()); for (const auto &son : sidechain_ids) { const auto &son_obj = son.as(6); - if(son_obj.get_sidechain_vote_id(sidechain).valid()) { + if (son_obj.get_sidechain_vote_id(sidechain).valid()) { votes_for_sons[sidechain].emplace_back(votes_info_object{*son_obj.get_sidechain_vote_id(sidechain), son_obj.id}); } } diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt old mode 100755 new mode 100644 index 4f724602..21fac167 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -16,6 +16,8 @@ add_library( peerplays_sidechain bitcoin/segwit_addr.cpp bitcoin/utils.cpp bitcoin/sign_bitcoin_transaction.cpp + bitcoin/libbitcoin_client.cpp + bitcoin/estimate_fee_external.cpp common/rpc_client.cpp common/utils.cpp ethereum/encoders.cpp @@ -42,7 +44,7 @@ endif() unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) -target_link_libraries( peerplays_sidechain PRIVATE graphene_plugin sha3 zmq ) +target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin sha3 zmq bitcoin-system bitcoin-protocol bitcoin-client bitcoin-explorer ) target_include_directories( peerplays_sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp index 69b28ee1..4eb9803d 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp @@ -242,12 +242,12 @@ bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) { } btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector> &keys_data, - network ntype) { + network ntype, payment_type type) { network_type = ntype; + this->type = type; create_redeem_script(keys_data); create_witness_script(); create_segwit_address(); - type = payment_type::P2WSH; } void btc_weighted_multisig_address::create_redeem_script(const std::vector> &keys_data) { @@ -278,26 +278,43 @@ void btc_weighted_multisig_address::create_witness_script() { script_builder builder; builder << op::_0; builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size()); - witness_script_ = builder; } void btc_weighted_multisig_address::create_segwit_address() { std::string hrp; + address_types byte_version; switch (network_type) { case (network::mainnet): hrp = "bc"; + byte_version = address_types::MAINNET_SCRIPT; break; case (network::testnet): hrp = "tb"; + byte_version = address_types::TESTNET_SCRIPT; break; case (network::regtest): hrp = "bcrt"; + byte_version = address_types::TESTNET_SCRIPT; break; } - fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); - std::vector hash_data(sh.data(), sh.data() + sh.data_size()); - address = segwit_addr::encode(hrp, 0, hash_data); + + if (type == payment_type::P2WSH) { + fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); + std::vector hash_data(sh.data(), sh.data() + sh.data_size()); + address = segwit_addr::encode(hrp, 0, hash_data); + } else if (type == payment_type::P2SH_WSH) { + fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size()); + fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size()); + raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size()); + bytes address_bytes(1, byte_version); // 1 byte version + address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end()); + fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size())); + address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum + address = fc::to_base58(address_bytes); + } else { + wlog("Unsupported payment type of address"); + } } btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, @@ -353,12 +370,12 @@ void btc_one_or_m_of_n_multisig_address::create_segwit_address() { btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data, - bitcoin_address::network ntype) { + bitcoin_address::network ntype, payment_type type) { network_type = ntype; + this->type = type; create_redeem_script(user_key_data, keys_data); create_witness_script(); create_segwit_address(); - type = payment_type::P2WSH; } void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data) { @@ -398,20 +415,39 @@ void btc_one_or_weighted_multisig_address::create_witness_script() { void btc_one_or_weighted_multisig_address::create_segwit_address() { std::string hrp; + address_types byte_version; switch (network_type) { case (network::mainnet): + byte_version = address_types::MAINNET_SCRIPT; hrp = "bc"; break; case (network::testnet): + byte_version = address_types::TESTNET_SCRIPT; hrp = "tb"; break; case (network::regtest): + byte_version = address_types::TESTNET_SCRIPT; hrp = "bcrt"; break; } - fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); - std::vector hash_data(sh.data(), sh.data() + sh.data_size()); - address = segwit_addr::encode(hrp, 0, hash_data); + + if (type == payment_type::P2WSH) { + fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); + std::vector hash_data(sh.data(), sh.data() + sh.data_size()); + address = segwit_addr::encode(hrp, 0, hash_data); + } else if (type == payment_type::P2SH_WSH) { + fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size()); + fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size()); + raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size()); + + bytes address_bytes(1, byte_version); // 1 byte version test net + address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end()); + fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size())); + address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum + address = fc::to_base58(address_bytes); + } else { + elog("Unsupported payment type of address"); + } } btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector> &keys_data, bitcoin_address::network ntype) : diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/estimate_fee_external.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/estimate_fee_external.cpp new file mode 100644 index 00000000..7ae350e7 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/estimate_fee_external.cpp @@ -0,0 +1,157 @@ + +#include + +#include +#include + +#include + +#include + +namespace graphene { +namespace peerplays_sidechain { + +static size_t writeFunction(void *ptr, size_t size, size_t nmemb, std::string *data) { + data->append((char *)ptr, size * nmemb); + return size * nmemb; +} + +estimate_fee_external::estimate_fee_external() { + curl = curl_easy_init(); +} + +estimate_fee_external::~estimate_fee_external() { + curl_easy_cleanup(curl); +} + +std::vector> estimate_fee_external::get_fee_external(uint16_t target_block) { + + std::vector> estimate_fee_external_collection; + this->target_block = target_block; + + for (auto &url_fee_parser : url_get_fee_parsers) { + response = get_response(url_fee_parser.first); + uint64_t fee = url_fee_parser.second(); + std::string url_str = url_fee_parser.first; + if (fee != 0) { + estimate_fee_external_collection.emplace_back(std::make_pair(url_fee_parser.first, fee)); + } + } + + return estimate_fee_external_collection; +} + +std::string estimate_fee_external::get_response(std::string url) { + + std::string response; + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_USERPWD, "user:pass"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.42.0"); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + curl_easy_perform(curl); + } + return response; +} + +uint64_t estimate_fee_external::parse_and_get_fee_1() { + //"https://www.bitgo.com/api/v2/btc/tx/fee" + + uint64_t founded_fee = 0; + + if (response.empty()) { + return founded_fee; + } + + std::stringstream response_ss(response); + boost::property_tree::ptree response_pt; + boost::property_tree::read_json(response_ss, response_pt); + + for (const auto &tx_child : response_pt.get_child("feeByBlockTarget")) { + const auto &block_num = tx_child.first.data(); + const auto &fee = tx_child.second.data(); + + founded_fee = std::stoi(fee); + + if (std::stoi(block_num) >= target_block) { + return founded_fee; + } + } + + return founded_fee; +} + +uint64_t estimate_fee_external::parse_and_get_fee_2() { + // https://bitcoiner.live/api/fees/estimates/latest + uint64_t founded_fee = 0; + + if (response.empty()) { + return founded_fee; + } + + std::stringstream response_ss(response); + boost::property_tree::ptree response_pt; + boost::property_tree::read_json(response_ss, response_pt); + + for (const auto &tx_child : response_pt.get_child("estimates")) { + const auto &time_str = tx_child.first.data(); + + auto time = std::stoi(time_str); + auto block_num = time / 30; + + if (tx_child.second.count("sat_per_vbyte")) { + auto founded_fee_str = tx_child.second.get_child("sat_per_vbyte").data(); + founded_fee = std::stoi(founded_fee_str) * 1000; + } + + if (block_num >= target_block) { + return founded_fee; + } + } + + return founded_fee; +} + +uint64_t estimate_fee_external::parse_and_get_fee_3() { + // https://api.blockchain.info/mempool/fees + + if (response.empty()) { + return 0; + } + + std::stringstream response_ss(response); + boost::property_tree::ptree response_pt; + boost::property_tree::read_json(response_ss, response_pt); + + if (response_pt.get_child("limits").count("min") && response_pt.get_child("limits").count("max")) { + auto limits_min_str = response_pt.get_child("limits.min").data(); + auto limits_max_str = response_pt.get_child("limits.max").data(); + + auto limits_min = std::stoi(limits_min_str); + auto limits_max = std::stoi(limits_max_str); + + auto priority_max = (limits_max - (limits_min - 1)) / 2; + + if (response_pt.count("regular") && response_pt.count("priority")) { + auto regular_str = response_pt.get_child("regular").data(); + auto priority_str = response_pt.get_child("priority").data(); + + auto regular = std::stoi(regular_str); + auto priority = std::stoi(priority_str); + + if (target_block >= priority_max) { + return regular * 1000; + } else { + return priority * 1000; + } + } + } + + return 0; +} + +}} // namespace graphene::peerplays_sidechain \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp new file mode 100644 index 00000000..fdcaa98e --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp @@ -0,0 +1,227 @@ + +#include + +#include + +#include +#include + +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +libbitcoin_client::libbitcoin_client(std::string url) : + obelisk_client(LIBBITCOIN_SERVER_TIMEOUT, LIBBITCOIN_SERVER_RETRIES) { + + std::string reg_expr = "^((?Phttps|http|tcp):\\/\\/)?(?P[a-zA-Z0-9\\-\\.]+)(:(?P\\d{1,5}))?(?P\\/.+)?"; + boost::xpressive::sregex sr = boost::xpressive::sregex::compile(reg_expr); + + boost::xpressive::smatch sm; + + if (boost::xpressive::regex_search(url, sm, sr)) { + protocol = sm["Protocol"]; + if (protocol.empty()) { + protocol = "tcp"; + } + + host = sm["Host"]; + if (host.empty()) { + host + "localhost"; + } + + port = sm["Port"]; + if (port.empty()) { + port = "9091"; + } + } + + uint16_t port_num = std::stoi(port); + std::string final_url = protocol + "://" + host; + + libbitcoin::config::endpoint address(final_url, port_num); + + libbitcoin::client::connection_type connection; + connection.retries = LIBBITCOIN_SERVER_RETRIES; + connection.server = address; + + if (!obelisk_client.connect(connection)) { + elog("Can't connect libbitcoin for url: ${url}", ("url", final_url)); + } + + is_connected = true; +} + +std::string libbitcoin_client::send_transaction(std::string tx) { + + std::string res; + + auto error_handler = [&](const std::error_code &ec) { + elog("error on sending bitcoin transaction ${error_code}", ("error_code", ec.message())); + }; + + auto result_handler = [&](libbitcoin::code result_code) { + ilog("result code on sending transaction ${result_code}", ("result_code", result_code.message())); + res = std::to_string(result_code.value()); + }; + + libbitcoin::explorer::config::transaction transaction(tx); + + libbitcoin::chain::transaction trx; + + // This validates the tx, submits it to local tx pool, and notifies peers. + obelisk_client.transaction_pool_broadcast(error_handler, result_handler, transaction); + obelisk_client.wait(); + + return res; +} + +libbitcoin::chain::output::list libbitcoin_client::get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions) { + + libbitcoin::chain::output::list outs; + + auto error_handler = [&](const std::error_code &ec) { + elog("error on fetch_trx_by_hash: ${hash} ${error_code}", ("hash", tx_id)("error_code", ec.message())); + }; + + auto transaction_handler = [&](const libbitcoin::chain::transaction &tx_handler) { + tx_hash = libbitcoin::config::hash256(tx_handler.hash(false)).to_string(); + // TODO try to find this value (confirmitions) + confirmitions = 1; + outs = tx_handler.outputs(); + }; + + libbitcoin::hash_digest hash = libbitcoin::config::hash256(tx_id); + + // obelisk_client.blockchain_fetch_transaction (error_handler, transaction_handler,hash); + obelisk_client.blockchain_fetch_transaction2(error_handler, transaction_handler, hash); + + obelisk_client.wait(); + + return outs; +} + +std::vector libbitcoin_client::listunspent(std::string address, double amount) { + std::vector result; + + auto error_handler = [&](const std::error_code &ec) { + elog("error on list_unspent ${error_code}", ("error_code", ec.message())); + }; + + auto replay_handler = [&](const libbitcoin::chain::points_value &points) { + for (auto &point : points.points) { + list_unspent_replay output; + output.hash = libbitcoin::config::hash256(point.hash()).to_string(); + output.value = point.value(); + output.index = point.index(); + result.emplace_back(output); + } + }; + + libbitcoin::wallet::payment_address payment_address(address); + uint64_t satoshi = 100000000 * amount; + + obelisk_client.blockchain_fetch_unspent_outputs(error_handler, + replay_handler, payment_address, satoshi, libbitcoin::wallet::select_outputs::algorithm::individual); + + obelisk_client.wait(); + + return result; +} + +bool libbitcoin_client::get_is_test_net() { + + bool result = false; + + auto error_handler = [&](const std::error_code &ec) { + elog("error on fetching genesis block ${error_code}", ("error_code", ec.message())); + }; + + auto block_header_handler = [&](const libbitcoin::chain::header &block_header) { + std::string hash_str = libbitcoin::config::hash256(block_header.hash()).to_string(); + if (hash_str == GENESIS_TESTNET_HASH || hash_str == GENESIS_REGTEST_HASH) { + result = true; + } + }; + + obelisk_client.blockchain_fetch_block_header(error_handler, block_header_handler, 0); + + obelisk_client.wait(); + return result; +} + +uint64_t libbitcoin_client::get_fee_from_trx(libbitcoin::chain::transaction trx) { + bool general_fee_est_error = false; + + if (trx.is_coinbase()) { + return 0; + } + + const auto total_output_value = trx.total_output_value(); + // get the inputs and from inputs previous outputs + std::map> prev_out_trxs; + for (auto &ins : trx.inputs()) { + const auto &prev_out = ins.previous_output(); + prev_out_trxs[prev_out.hash()].emplace_back(prev_out.index()); + } + + // fetch the trx to get total input value + uint64_t total_input_value = 0; + auto transaction_handler = [&](const libbitcoin::chain::transaction &tx_handler) { + std::vector indexes = prev_out_trxs[tx_handler.hash()]; + + for (auto &index : indexes) { + total_input_value += tx_handler.outputs()[index].value(); + } + }; + + auto error_handler = [&](const std::error_code &ec) { + elog("error on fetching trx ${error_code}", ("error_code", ec.message())); + general_fee_est_error = true; + }; + + for (const auto &iter : prev_out_trxs) { + if (general_fee_est_error) { + break; + } + + obelisk_client.blockchain_fetch_transaction2(error_handler, transaction_handler, iter.first); + obelisk_client.wait(); + } + + if (total_input_value >= total_output_value) { + return total_input_value - total_output_value; + } else { + // something is really wrong if this happens,so we are going to mark as an error + elog("On fee estimation something is wrong in total inputs and total outputs for trx hash: ${hash}", + ("hash", libbitcoin::config::hash256(trx.hash()).to_string())); + return 0; + } +} + +uint64_t libbitcoin_client::get_average_fee_from_trxs(std::vector trx_list) { + std::vector fee_per_trxs; + + for (auto &trx : trx_list) { + + uint64_t fee = get_fee_from_trx(trx); + if (fee > 0) { + fee_per_trxs.emplace_back(fee); + } + } + + uint64_t average_estimated_fee = 0; + + if (fee_per_trxs.size()) { + for (const auto &fee : fee_per_trxs) { + average_estimated_fee += fee; + } + + average_estimated_fee /= fee_per_trxs.size(); + } + + return average_estimated_fee; +} +}} // namespace graphene::peerplays_sidechain \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp index b9467e2f..04762352 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp @@ -8,6 +8,14 @@ using namespace graphene::chain; namespace graphene { namespace peerplays_sidechain { namespace bitcoin { const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15 +enum address_types { MAINNET_SCRIPT = 5, + TESTNET_SCRIPT = 196 }; + +enum script_op { + OP_0 = 0x00, + OP_PUSH = 0x20, + OP_SIZE_34 = 0x22 +}; class bitcoin_address { @@ -96,9 +104,6 @@ private: void create_address(); public: - enum address_types { MAINNET_SCRIPT = 5, - TESTNET_SCRIPT = 196 }; - enum { OP_0 = 0x00, OP_EQUAL = 0x87, OP_HASH160 = 0xa9, @@ -145,7 +150,7 @@ public: btc_weighted_multisig_address() = default; btc_weighted_multisig_address(const std::vector> &keys_data, - network network_type = network::regtest); + network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH); bytes get_redeem_script() const { return redeem_script_; @@ -190,7 +195,7 @@ class btc_one_or_weighted_multisig_address : public bitcoin_address { public: btc_one_or_weighted_multisig_address() = default; btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data, - network network_type = network::regtest); + network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH); bytes get_redeem_script() const { return redeem_script_; } diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp new file mode 100644 index 00000000..10be2868 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include +#include +#include + +typedef std::function get_fee_func_type; + +namespace graphene { namespace peerplays_sidechain { + +class estimate_fee_external { +public: + estimate_fee_external(); + ~estimate_fee_external(); + std::vector> get_fee_external(uint16_t target_block); + +private: + std::string get_response(std::string url); + // Here add your custom parser for external url. Take care of incremental name + // and populate the list of url_parsers bellow paired with the function + uint64_t parse_and_get_fee_1(); + uint64_t parse_and_get_fee_2(); + uint64_t parse_and_get_fee_3(); + + const std::map url_get_fee_parsers{ + {"https://www.bitgo.com/api/v2/btc/tx/fee", std::bind(&estimate_fee_external::parse_and_get_fee_1, this)}, + {"https://bitcoiner.live/api/fees/estimates/latest", std::bind(&estimate_fee_external::parse_and_get_fee_2, this)}, + {"https://api.blockchain.info/mempool/fees", std::bind(&estimate_fee_external::parse_and_get_fee_3, this)}}; + + std::string response; + uint16_t target_block; + CURL *curl{nullptr}; +}; + +}} // namespace graphene::peerplays_sidechain \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp new file mode 100644 index 00000000..4426983a --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#define LIBBITCOIN_SERVER_TIMEOUT (10) +#define LIBBITCOIN_SERVER_RETRIES (100) +#define DEAFULT_LIBBITCOIN_TRX_FEE (20000) +#define MAX_TRXS_IN_MEMORY_POOL (30000) +#define MIN_TRXS_IN_BUCKET (100) + +#define GENESIS_MAINNET_HASH "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" +#define GENESIS_TESTNET_HASH "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" +#define GENESIS_REGTEST_HASH "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" + +namespace graphene { namespace peerplays_sidechain { + +typedef std::function + block_update_handler; + +struct list_unspent_replay { + std::string hash; + uint64_t value; + uint32_t index; +}; + +class libbitcoin_client { +public: + libbitcoin_client(std::string url); + std::string send_transaction(const std::string tx); + libbitcoin::chain::output::list get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions); + std::vector listunspent(std::string address, double amount); + uint64_t get_average_fee_from_trxs(std::vector trx_list); + uint64_t get_fee_from_trx(libbitcoin::chain::transaction trx); + bool get_is_test_net(); + +private: + libbitcoin::client::obelisk_client obelisk_client; + libbitcoin::protocol::zmq::identifier id; + + std::string protocol; + std::string host; + std::string port; + std::string url; + + bool is_connected = false; +}; + +}} // namespace graphene::peerplays_sidechain \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp index 4e1c4b58..5f582eb1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/utils.hpp @@ -1,4 +1,5 @@ #pragma once + #include #include #include diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index 0e7e2fc1..836a1f05 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -59,6 +59,7 @@ protected: sidechain_type sidechain; bool debug_rpc_calls; + bool use_bitcoind_client; std::map private_keys; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index e880a7ae..bfb93805 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -1,18 +1,20 @@ #pragma once -#include #include +#include #include #include #include #include -#include - #include + #include +#include +#include +#include namespace graphene { namespace peerplays_sidechain { @@ -23,7 +25,27 @@ public: uint64_t amount_; }; -class bitcoin_rpc_client : public rpc_client { +class btc_txin { +public: + std::vector tx_address; + uint64_t tx_amount; + uint64_t tx_vout; +}; + +class btc_tx { +public: + std::string tx_txid; + uint32_t tx_confirmations; + std::vector tx_in_list; +}; + +class block_data { +public: + std::string block_hash; + libbitcoin::chain::block block; +}; + +class bitcoin_client_base { public: enum class multi_type { script, @@ -41,14 +63,47 @@ public: std::string label; }; + virtual uint64_t estimatesmartfee(uint16_t conf_target = 1) = 0; + virtual std::vector getblock(const block_data &block, int32_t verbosity = 2) = 0; + virtual btc_tx getrawtransaction(const std::string &txid, const bool verbose = false) = 0; + virtual void getnetworkinfo() = 0; + virtual std::string getblockchaininfo() = 0; + virtual std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999) = 0; + virtual std::string sendrawtransaction(const std::string &tx_hex) = 0; + virtual void importmulti(const std::vector &address_or_script_array, const bool rescan = true) { + ; + }; + virtual std::string loadwallet(const std::string &filename) { + return ""; + }; + virtual std::string walletlock() { + return ""; + }; + virtual bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60) { + return false; + }; + + void import_trx_to_memory_pool(const libbitcoin::chain::transaction &trx) { + std::unique_lock lck(libbitcoin_event_mutex); + if (trx_memory_pool.size() < MAX_TRXS_IN_MEMORY_POOL) { + trx_memory_pool.emplace_back(trx); + } + } + +protected: + std::vector trx_memory_pool; + std::mutex libbitcoin_event_mutex; +}; + +class bitcoin_rpc_client : public bitcoin_client_base, public rpc_client { +public: public: bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); - std::string createwallet(const std::string &wallet_name); - uint64_t estimatesmartfee(uint16_t conf_target = 128); - std::string getblock(const std::string &block_hash, int32_t verbosity = 2); - std::string getrawtransaction(const std::string &txid, const bool verbose = false); - std::string getnetworkinfo(); + uint64_t estimatesmartfee(uint16_t conf_target = 1); + std::vector getblock(const block_data &block, int32_t verbosity = 2); + btc_tx getrawtransaction(const std::string &txid, const bool verbose = false); + void getnetworkinfo(); std::string getblockchaininfo(); void importmulti(const std::vector &address_or_script_array, const bool rescan = true); std::vector listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999); @@ -60,35 +115,84 @@ public: private: std::string ip; - uint32_t rpc_port; std::string user; std::string password; std::string wallet_name; std::string wallet_password; + uint32_t bitcoin_major_version; +}; + +class bitcoin_libbitcoin_client : public bitcoin_client_base, public libbitcoin_client { +public: + bitcoin_libbitcoin_client(std::string url); + uint64_t estimatesmartfee(uint16_t conf_target = 1); + std::vector getblock(const block_data &block, int32_t verbosity = 2); + btc_tx getrawtransaction(const std::string &txid, const bool verbose = false); + void getnetworkinfo(); + std::string getblockchaininfo(); + std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999); + std::string sendrawtransaction(const std::string &tx_hex); + +private: + bool is_test_net = false; + std::unique_ptr estimate_fee_ext; + uint64_t current_internal_fee = DEAFULT_LIBBITCOIN_TRX_FEE; }; // ============================================================================= -class zmq_listener { +class zmq_listener_base { public: - zmq_listener(std::string _ip, uint32_t _zmq); - virtual ~zmq_listener(); + virtual ~zmq_listener_base(){}; + zmq_listener_base(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port = 0) { + ip = _ip; + block_zmq_port = _block_zmq_port; + trx_zmq_port = _trx_zmq_port; + stopped = false; + }; + virtual void start() = 0; + boost::signals2::signal block_event_received; + boost::signals2::signal trx_event_received; +protected: + std::string ip; + uint32_t block_zmq_port; + uint32_t trx_zmq_port; + std::atomic_bool stopped; + std::thread block_thr; + std::thread trx_thr; +}; + +class zmq_listener : public zmq_listener_base { +public: + zmq_listener(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port = 0); + virtual ~zmq_listener(); void start(); - boost::signals2::signal event_received; private: void handle_zmq(); std::vector receive_multipart(); - std::string ip; - uint32_t zmq_port; - zmq::context_t ctx; zmq::socket_t socket; +}; - std::atomic_bool stopped; - std::thread thr; +class zmq_listener_libbitcoin : public zmq_listener_base { +public: + zmq_listener_libbitcoin(std::string _ip, uint32_t _block_zmq_port = 9093, uint32_t _trx_zmq_port = 9094); + virtual ~zmq_listener_libbitcoin(); + void start(); + +private: + void handle_block(); + void handle_trx(); + + libbitcoin::protocol::zmq::context block_context; + libbitcoin::protocol::zmq::socket block_socket; + libbitcoin::protocol::zmq::poller block_poller; + libbitcoin::protocol::zmq::context trx_context; + libbitcoin::protocol::zmq::socket trx_socket; + libbitcoin::protocol::zmq::poller trx_poller; }; // ============================================================================= @@ -109,16 +213,19 @@ public: virtual optional estimate_withdrawal_transaction_fee() const override; private: - std::string ip; - uint32_t zmq_port; + std::string bitcoin_node_ip; + std::string libbitcoin_server_ip; + uint32_t libbitcoin_block_zmq_port; + uint32_t libbitcoin_trx_zmq_port; + uint32_t bitcoin_node_zmq_port; uint32_t rpc_port; std::string rpc_user; std::string rpc_password; std::string wallet_name; std::string wallet_password; - std::unique_ptr bitcoin_client; - std::unique_ptr listener; + std::unique_ptr bitcoin_client; + std::unique_ptr listener; fc::future on_changed_objects_task; @@ -138,9 +245,9 @@ private: std::string sign_transaction(const sidechain_transaction_object &sto); std::string send_transaction(const sidechain_transaction_object &sto); - void handle_event(const std::string &event_data); + void block_handle_event(const block_data &event_data); + void trx_handle_event(const libbitcoin::chain::transaction &event_data); std::string get_redeemscript_for_userdeposit(const std::string &user_address); - std::vector extract_info_from_block(const std::string &_block); void on_changed_objects(const vector &ids, const flat_set &accounts); void on_changed_objects_cb(const vector &ids, const flat_set &accounts); }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index bd5bb504..e4a2b8ea 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -177,6 +177,10 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("debug-rpc-calls", bpo::value()->default_value(false), "Outputs RPC calls to console"); cli.add_options()("bitcoin-sidechain-enabled", bpo::value()->default_value(false), "Bitcoin sidechain handler enabled"); + cli.add_options()("use-bitcoind-client", bpo::value()->default_value(false), "Use bitcoind client instead of libbitcoin client"); + cli.add_options()("libbitcoin-server-ip", bpo::value()->default_value("127.0.0.1"), "Libbitcoin server IP address"); + cli.add_options()("libbitcoin-server-block-zmq-port", bpo::value()->default_value(9093), "Block ZMQ port of libbitcoin server"); + cli.add_options()("libbitcoin-server-trx-zmq-port", bpo::value()->default_value(9094), "Trx ZMQ port of libbitcoin server"); cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); @@ -192,7 +196,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("ethereum-node-rpc-user", bpo::value(), "Ethereum RPC user"); cli.add_options()("ethereum-node-rpc-password", bpo::value(), "Ethereum RPC password"); cli.add_options()("ethereum-wallet-contract-address", bpo::value(), "Ethereum wallet contract address"); - cli.add_options()("erc-20-address", bpo::value>()->composing()->multitoken(), + cli.add_options()("ethereum-erc-20-address", bpo::value>()->composing()->multitoken(), "Tuple of [ERC-20 symbol, ERC-20 address] (may specify multiple times)"); cli.add_options()("ethereum-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")), "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); @@ -249,12 +253,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt } sidechain_enabled_bitcoin = options.at("bitcoin-sidechain-enabled").as(); - config_ready_bitcoin = options.count("bitcoin-node-ip") && - options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && - options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") && - options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") && - options.count("bitcoin-private-key"); - if (sidechain_enabled_bitcoin && !config_ready_bitcoin) { + + config_ready_bitcoin = (((options.count("libbitcoin-server-ip") && options.count("libbitcoin-server-zmq-port")) || + (options.count("bitcoin-node-ip") && options.count("bitcoin-node-zmq-port") && + options.count("bitcoin-node-rpc-port") && options.count("bitcoin-node-rpc-user") && + options.count("bitcoin-node-rpc-password") && options.count("bitcoin-wallet-name") && + options.count("bitcoin-wallet-password"))) && + options.count("bitcoin-private-key")); + if (!config_ready_bitcoin) { wlog("Haven't set up Bitcoin sidechain parameters"); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 3f469cc8..f37352bb 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -29,11 +29,6 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _url, std::string _user, std: rpc_client(_url, _user, _password, _debug_rpc_calls) { } -std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { - const std::string params = std::string("[\"") + wallet_name + std::string("\"]"); - return send_post_request("createwallet", params, debug_rpc_calls); -} - uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { const std::string params = std::string("[") + std::to_string(conf_target) + std::string("]"); const std::string str = send_post_request("estimatesmartfee", params, debug_rpc_calls); @@ -58,36 +53,130 @@ uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { return 20000; } -std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { - const std::string params = std::string("[\"") + block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); - return send_post_request("getblock", params, debug_rpc_calls); +std::vector bitcoin_rpc_client::getblock(const block_data &block, int32_t verbosity) { + std::string params = std::string("[\"") + block.block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); + std::string str = send_post_request("getblock", params, debug_rpc_calls); + std::vector result; + + if (str.empty()) { + return result; + } + + std::stringstream ss(str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + auto json_result = json.get_child_optional("result"); + + for (const auto &tx_child : json_result.get().get_child("tx")) { + const auto &tx = tx_child.second; + + for (const auto &o : tx.get_child("vout")) { + const auto script = o.second.get_child("scriptPubKey"); + std::vector address_list; + + if (script.count("address")) { + address_list.emplace_back(script.get("address")); + } else if (script.count("addresses")) { + for (const auto &addr : script.get_child("addresses")) { + address_list.emplace_back(addr.second.get_value()); + } + } else { + continue; + } + + for (auto &address : address_list) { + const auto address_base58 = address; + info_for_vin vin; + vin.out.hash_tx = tx.get_child("txid").get_value(); + string amount = o.second.get_child("value").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + vin.out.amount = std::stoll(amount); + vin.out.n_vout = o.second.get_child("n").get_value(); + vin.address = address_base58; + result.push_back(vin); + } + } + } + + return result; } -std::string bitcoin_rpc_client::getnetworkinfo() { - static const std::string params = std::string("[]"); - return send_post_request("getnetworkinfo", params, debug_rpc_calls); +void bitcoin_rpc_client::getnetworkinfo() { + std::string params = std::string("[]"); + std::string str = send_post_request("getnetworkinfo", params, debug_rpc_calls); + + std::stringstream network_info_ss(str); + boost::property_tree::ptree network_info_json; + boost::property_tree::read_json(network_info_ss, network_info_json); + + bitcoin_major_version = network_info_json.get("result.version") / 10000; + ilog("Bitcoin major version is: '${version}'", ("version", bitcoin_major_version)); } -std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) { - const std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]"); - return send_post_request("getrawtransaction", params, debug_rpc_calls); +btc_tx bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) { + std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]"); + std::string str = send_post_request("getrawtransaction", params, debug_rpc_calls); + + btc_tx tx; + + std::stringstream tx_ss(str); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(tx_ss, tx_json); + + if (tx_json.count("error") && tx_json.get_child("error").empty()) { + + std::string tx_txid = tx_json.get("result.txid"); + uint32_t tx_confirmations = tx_json.get("result.confirmations"); + + tx.tx_txid = tx_txid; + tx.tx_confirmations = tx_confirmations; + + for (auto &input : tx_json.get_child("result.vout")) { + btc_txin tx_in; + std::string tx_vout_s = input.second.get("n"); + tx_in.tx_vout = std::stoll(tx_vout_s); + if (bitcoin_major_version > 21) { + std::string address = input.second.get("scriptPubKey.address"); + tx_in.tx_address.emplace_back(address); + } else { + for (auto &address : input.second.get_child("scriptPubKey.addresses")) { + tx_in.tx_address.emplace_back(address.second.data()); + } + } + + std::string tx_amount_s = input.second.get("value"); + tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + tx_in.tx_amount = std::stoll(tx_amount_s); + + tx.tx_in_list.emplace_back(tx_in); + } + } + + return tx; } std::string bitcoin_rpc_client::getblockchaininfo() { static const std::string params = std::string("[]"); const std::string str = send_post_request("getblockchaininfo", params, debug_rpc_calls); + std::string result; + if (str.length() > 0) { std::stringstream ss(str); boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); + if (json.find("result") != json.not_found()) { + auto json_result = json.get_child("result"); + if (json_result.count("chain")) { + result = json_result.get("chain"); + } + } } - return str; + return result; } void bitcoin_rpc_client::importmulti(const std::vector &address_or_script_array, const bool rescan) { @@ -106,11 +195,11 @@ void bitcoin_rpc_client::importmulti(const std::vector &address_or //! Note /* Creation time of the key expressed in UNIX epoch time, - or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest - key will determine how far back blockchain rescans need to begin for missing wallet transactions. - "now" can be specified to bypass scanning, for keys which are known to never have been used, and - 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key - creation time of all keys being imported by the importmulti call will be scanned.*/ + or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest + key will determine how far back blockchain rescans need to begin for missing wallet transactions. + "now" can be specified to bypass scanning, for keys which are known to never have been used, and + 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key + creation time of all keys being imported by the importmulti call will be scanned.*/ if (¶m != &address_or_script_array.back()) { argument_1 += ", "; @@ -243,15 +332,178 @@ bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_ else return true; } +bitcoin_libbitcoin_client::bitcoin_libbitcoin_client(std::string url) : + libbitcoin_client(url) { + + estimate_fee_ext = std::unique_ptr(new estimate_fee_external()); +} + +uint64_t bitcoin_libbitcoin_client::estimatesmartfee(uint16_t conf_target) { + std::vector> fees = estimate_fee_ext->get_fee_external(conf_target); + std::vector accumulated_fees; + for (auto &external_fees : fees) { + if (external_fees.second != 0) { + accumulated_fees.emplace_back(external_fees.second); + } + } + + // we will rather pick minimal fee from external sources than internal fee calculation + if (accumulated_fees.empty()) { + accumulated_fees.emplace_back(current_internal_fee); + } + + return *std::min_element(accumulated_fees.begin(), accumulated_fees.end()); +} + +std::vector bitcoin_libbitcoin_client::getblock(const block_data &block, int32_t verbosity) { + + std::unique_lock lck(libbitcoin_event_mutex); + + // estimate fee + const auto &block_trxs = block.block.transactions(); + std::vector bucket_trxs; + for (auto &mem_pool_trx : trx_memory_pool) { + for (auto &trx : block_trxs) { + if (mem_pool_trx.hash() == trx.hash()) { + bucket_trxs.emplace_back(mem_pool_trx); + break; + } + } + } + + uint64_t average_fee = get_average_fee_from_trxs(bucket_trxs); + if (average_fee > 0 && bucket_trxs.size() >= MIN_TRXS_IN_BUCKET) { + current_internal_fee = average_fee; + } + + // We could consider accumulation which could spread to multiple blocks for better metric + // for now we only keep tracking for not confirmed transaction until we get next block + trx_memory_pool.clear(); + + std::vector result; + + const libbitcoin::chain::transaction::list trx_list = block.block.transactions(); + + for (const auto &tx : trx_list) { + uint32_t vout_seq = 0; + for (const auto &o : tx.outputs()) { + std::vector address_list; + + libbitcoin::wallet::payment_address::list addresses; + if (is_test_net) { + addresses = o.addresses(libbitcoin::wallet::payment_address::testnet_p2kh, + libbitcoin::wallet::payment_address::testnet_p2sh); + } else { + addresses = o.addresses(); + } + + for (auto &payment_address : addresses) { + std::stringstream ss; + ss << payment_address; + address_list.emplace_back(ss.str()); + } + + // addres list consists usual of one element + for (auto &address : address_list) { + const auto address_base58 = address; + info_for_vin vin; + vin.out.hash_tx = libbitcoin::config::hash256(tx.hash()).to_string(); + vin.out.amount = std::floor(o.value()); + vin.out.n_vout = vout_seq; + vin.address = address_base58; + result.push_back(vin); + } + vout_seq++; + } + } + + return result; +} + +btc_tx bitcoin_libbitcoin_client::getrawtransaction(const std::string &txid, const bool verbose) { + btc_tx tx; + + std::string tx_hash; + uint32_t confirmitions; + + libbitcoin::chain::output::list outs = get_transaction(txid, tx_hash, confirmitions); + + if (tx_hash.empty()) { + return tx; + } + + tx.tx_txid = tx_hash; + tx.tx_confirmations = confirmitions; + + uint64_t tx_vout_sequence = 0; + + for (auto &out : outs) { + btc_txin tx_in; + tx_in.tx_vout = tx_vout_sequence++; + + libbitcoin::wallet::payment_address::list addresses; + if (is_test_net) { + addresses = out.addresses(libbitcoin::wallet::payment_address::testnet_p2kh, + libbitcoin::wallet::payment_address::testnet_p2sh); + } else { + addresses = out.addresses(); + } + + for (auto &address : addresses) { + + std::stringstream ss; + ss << address; + tx_in.tx_address.emplace_back(ss.str()); + } + + tx_in.tx_amount = std::floor(out.value()); + tx.tx_in_list.emplace_back(tx_in); + } + + return tx; +} + +void bitcoin_libbitcoin_client::getnetworkinfo() { + // This function is only used for bitcoind client in order of getting + // version of bitcoin client. Version is used for extracting addresses or address + // which is not important for libbitcoin client +} + +std::string bitcoin_libbitcoin_client::getblockchaininfo() { + if (get_is_test_net()) { + is_test_net = true; + return "regtest"; + } + + return ""; +} + +std::vector bitcoin_libbitcoin_client::listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf, const uint32_t maxconf) { + + std::vector result; + std::vector outputs = listunspent(address, transfer_amount); + for (auto &output : outputs) { + btc_txout txo; + txo.txid_ = output.hash; + txo.out_num_ = output.index; + txo.amount_ = output.value; + result.push_back(txo); + } + + return result; +} + +std::string bitcoin_libbitcoin_client::sendrawtransaction(const std::string &tx_hex) { + std::string res = send_transaction(tx_hex); + return res; +} // ============================================================================= -zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) : - ip(_ip), - zmq_port(_zmq), +zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq_block_port, uint32_t _zmq_trx_port) : + zmq_listener_base(_ip, _zmq_block_port, _zmq_trx_port), ctx(1), - socket(ctx, ZMQ_SUB), - stopped(false) { + socket(ctx, ZMQ_SUB) { } void zmq_listener::start() { @@ -265,16 +517,16 @@ void zmq_listener::start() { // socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); // socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); // socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); - socket.connect("tcp://" + ip + ":" + std::to_string(zmq_port)); + socket.connect("tcp://" + ip + ":" + std::to_string(block_zmq_port)); - thr = std::thread(&zmq_listener::handle_zmq, this); + block_thr = std::thread(&zmq_listener::handle_zmq, this); ilog("zmq_listener thread started"); } zmq_listener::~zmq_listener() { stopped = true; - thr.join(); + block_thr.join(); } std::vector zmq_listener::receive_multipart() { @@ -302,7 +554,9 @@ void zmq_listener::handle_zmq() { } const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); - event_received(block_hash); + block_data event_data; + event_data.block_hash = block_hash; + block_event_received(event_data); } } catch (zmq::error_t &e) { elog("handle_zmq recv_multipart exception ${str}", ("str", e.what())); @@ -314,6 +568,92 @@ void zmq_listener::handle_zmq() { // ============================================================================= +// ============================================================================= + +zmq_listener_libbitcoin::zmq_listener_libbitcoin(std::string _ip, uint32_t _block_zmq_port, uint32_t _trx_zmq_port) : + zmq_listener_base(_ip, _block_zmq_port, _trx_zmq_port), + block_socket(block_context, libbitcoin::protocol::zmq::socket::role::subscriber), + trx_socket(trx_context, libbitcoin::protocol::zmq::socket::role::subscriber) { +} + +zmq_listener_libbitcoin::~zmq_listener_libbitcoin() { + stopped.store(true); + block_thr.join(); + trx_thr.join(); +} + +void zmq_listener_libbitcoin::start() { + std::string endpoint_address = "tcp://" + ip; + + libbitcoin::config::endpoint block_address(endpoint_address, block_zmq_port); + libbitcoin::config::endpoint trx_address(endpoint_address, trx_zmq_port); + + block_socket.connect(block_address); + trx_socket.connect(trx_address); + + block_thr = std::thread(&zmq_listener_libbitcoin::handle_block, this); + trx_thr = std::thread(&zmq_listener_libbitcoin::handle_trx, this); +} + +void zmq_listener_libbitcoin::handle_trx() { + trx_poller.add(trx_socket); + + while (!stopped.load()) { + const auto identifiers = trx_poller.wait(500); + + if (identifiers.contains(trx_socket.id())) { + libbitcoin::protocol::zmq::message message; + + trx_socket.receive(message); + + std::vector data; + for (int i = 0; i < 2; i++) { + data.clear(); + message.dequeue(data); + } + + libbitcoin::chain::transaction trx; + trx.from_data(data, true); + + trx_event_received(trx); + } + } + + ilog("zmq_listener_libbitcoin trx thread finished"); +} + +void zmq_listener_libbitcoin::handle_block() { + block_poller.add(block_socket); + + while (!stopped.load()) { + const auto identifiers = block_poller.wait(500); + + if (identifiers.contains(block_socket.id())) { + libbitcoin::protocol::zmq::message message; + + block_socket.receive(message); + + std::vector data; + for (int i = 0; i < 3; i++) { + data.clear(); + message.dequeue(data); + } + + libbitcoin::chain::block block; + block.from_data(data, true); + + block_data event_data; + event_data.block_hash = libbitcoin::config::hash256(block.hash()).to_string(); + event_data.block = std::move(block); + block_event_received(event_data); + } + } + + ilog("zmq_listener_libbitcoin block thread finished"); +} + +// ============================================================================= + sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::bitcoin; @@ -322,8 +662,16 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain debug_rpc_calls = options.at("debug-rpc-calls").as(); } - ip = options.at("bitcoin-node-ip").as(); - zmq_port = options.at("bitcoin-node-zmq-port").as(); + if (options.count("use-bitcoind-client")) { + use_bitcoind_client = options.at("use-bitcoind-client").as(); + } + + libbitcoin_server_ip = options.at("libbitcoin-server-ip").as(); + libbitcoin_block_zmq_port = options.at("libbitcoin-server-block-zmq-port").as(); + libbitcoin_trx_zmq_port = options.at("libbitcoin-server-trx-zmq-port").as(); + + bitcoin_node_ip = options.at("bitcoin-node-ip").as(); + bitcoin_node_zmq_port = options.at("bitcoin-node-zmq-port").as(); rpc_port = options.at("bitcoin-node-rpc-port").as(); rpc_user = options.at("bitcoin-node-rpc-user").as(); rpc_password = options.at("bitcoin-node-rpc-password").as(); @@ -348,55 +696,44 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } } - std::string url = ip + ":" + std::to_string(rpc_port); - if (!wallet_name.empty()) { - url = url + "/wallet/" + wallet_name; + if (use_bitcoind_client) { + std::string url = bitcoin_node_ip + ":" + std::to_string(rpc_port); + if (!wallet_name.empty()) { + url = url + "/wallet/" + wallet_name; + } + bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); + if (!wallet_name.empty()) { + bitcoin_client->loadwallet(wallet_name); + } + + listener = std::unique_ptr(new zmq_listener(bitcoin_node_ip, bitcoin_node_zmq_port)); + + } else { + bitcoin_client = std::unique_ptr(new bitcoin_libbitcoin_client(libbitcoin_server_ip)); + + listener = std::unique_ptr(new zmq_listener_libbitcoin(libbitcoin_server_ip, libbitcoin_block_zmq_port, libbitcoin_trx_zmq_port)); } - bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); - if (!wallet_name.empty()) { - bitcoin_client->loadwallet(wallet_name); - } - - std::string blockchain_info = bitcoin_client->getblockchaininfo(); - if (blockchain_info.empty()) { - elog("No Bitcoin node running at ${url}", ("url", url)); - FC_ASSERT(false); - } - std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end())); - boost::property_tree::ptree bci_json; - boost::property_tree::read_json(bci_ss, bci_json); + std::string chain_info = bitcoin_client->getblockchaininfo(); using namespace bitcoin; network_type = bitcoin_address::network::mainnet; - if (bci_json.count("chain")) { - std::string chain = bci_json.get("chain"); - if (chain.length() > 0) { - if (chain == "test") { - network_type = bitcoin_address::network::testnet; - } else if (chain == "regtest") { - network_type = bitcoin_address::network::regtest; - } - } + if (chain_info == "test") { + network_type = bitcoin_address::network::testnet; + } else if (chain_info == "regtest") { + network_type = bitcoin_address::network::regtest; } - std::string network_info_str = bitcoin_client->getnetworkinfo(); - if (network_info_str.empty()) { - elog("No Bitcoin node running at ${url}", ("url", url)); - FC_ASSERT(false); - } - std::stringstream network_info_ss(network_info_str); - boost::property_tree::ptree network_info_json; - boost::property_tree::read_json(network_info_ss, network_info_json); + bitcoin_client->getnetworkinfo(); - bitcoin_major_version = network_info_json.get("result.version") / 10000; - ilog("Bitcoin major version is: '${version}'", ("version", bitcoin_major_version)); - - listener = std::unique_ptr(new zmq_listener(ip, zmq_port)); listener->start(); - listener->event_received.connect([this](const std::string &event_data) { - std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); + listener->block_event_received.connect([this](const block_data &block_event_data) { + std::thread(&sidechain_net_handler_bitcoin::block_handle_event, this, block_event_data).detach(); + }); + + listener->trx_event_received.connect([this](const libbitcoin::chain::transaction &trx_event_data) { + std::thread(&sidechain_net_handler_bitcoin::trx_handle_event, this, trx_event_data).detach(); }); database.changed_objects.connect([this](const vector &ids, const flat_set &accounts) { @@ -418,7 +755,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) { - //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); + // ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -450,7 +787,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; const auto ast = active_sidechain_types(database.head_block_time()); const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); - const son_wallet_id_type op_id{ id }; + const son_wallet_id_type op_id{id}; const auto &idx = database.get_index_type().indices().get(); const auto swo = idx.find(op_id); if (swo != idx.end()) { @@ -491,7 +828,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) const object_id_type object_id = op_obj_idx_1.get().object_id; const auto ast = active_sidechain_types(database.head_block_time()); const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); - const object_id_type obj_id{ object_id.space(), object_id.type(), id }; + const object_id_type obj_id{object_id.space(), object_id.type(), id}; std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); @@ -533,50 +870,35 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) uint64_t swdo_amount = swdo->sidechain_amount.value; uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1)); - const std::string tx_str = bitcoin_client->getrawtransaction(swdo_txid, true); - if (tx_str != "") { - std::stringstream tx_ss(tx_str); - boost::property_tree::ptree tx_json; - boost::property_tree::read_json(tx_ss, tx_json); + btc_tx tx = bitcoin_client->getrawtransaction(swdo_txid, true); - if (tx_json.count("error") && tx_json.get_child("error").empty()) { + if (!tx.tx_in_list.empty()) { - std::string tx_txid = tx_json.get("result.txid"); - uint32_t tx_confirmations = tx_json.get("result.confirmations"); - std::string tx_address = ""; - uint64_t tx_amount = -1; - uint64_t tx_vout = -1; + std::string tx_txid = tx.tx_txid; + uint32_t tx_confirmations = tx.tx_confirmations; + std::string tx_address = ""; + uint64_t tx_amount = -1; + uint64_t tx_vout = -1; - for (auto &input : tx_json.get_child("result.vout")) { - std::string tx_vout_s = input.second.get("n"); - tx_vout = std::stoll(tx_vout_s); - if (tx_vout == swdo_vout) { - if (bitcoin_major_version > 21) { - std::string address = input.second.get("scriptPubKey.address"); - if (address == swdo_address) { - tx_address = address; - } - } else { - for (auto &address : input.second.get_child("scriptPubKey.addresses")) { - if (address.second.data() == swdo_address) { - tx_address = address.second.data(); - break; - } - } + for (auto &input : tx.tx_in_list) { + tx_vout = input.tx_vout; + if (tx_vout == swdo_vout) { + for (auto &address : input.tx_address) { + if (address == swdo_address) { + tx_address = address; + break; } - std::string tx_amount_s = input.second.get("value"); - tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); - tx_amount = std::stoll(tx_amount_s); - break; } + tx_amount = input.tx_amount; + break; } - - process_ok = (swdo_txid == tx_txid) && - (swdo_address == tx_address) && - (swdo_amount == tx_amount) && - (swdo_vout == tx_vout) && - (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); } + + process_ok = (swdo_txid == tx_txid) && + (swdo_address == tx_address) && + (swdo_amount == tx_amount) && + (swdo_vout == tx_vout) && + (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); } object_id_type object_id = op_obj_idx_1.get().object_id; @@ -724,7 +1046,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { const auto ast = active_sidechain_types(database.head_block_time()); const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); - const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; + const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id}; if (proposal_exists(chain::operation::tag::value, op_id)) { return; @@ -765,7 +1087,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { if (!tx_str.empty()) { const auto ast = active_sidechain_types(database.head_block_time()); const auto prev_id = prev_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); - const object_id_type prev_op_id{ prev_sw->id.space(), prev_sw->id.type(), prev_id }; + const object_id_type prev_op_id{prev_sw->id.space(), prev_sw->id.type(), prev_id}; sidechain_transaction_create_operation stc_op; stc_op.payer = gpo.parameters.son_account(); @@ -820,7 +1142,12 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { if (sao.expires == time_point_sec::maximum()) { auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key))); - btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type); + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_bitcoind_client) { + payment_type_address = payment_type::P2WSH; + } + + btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type, payment_type_address); std::string address_data = "{ \"redeemScript\": \"" + fc::to_hex(addr.get_redeem_script()) + "\", \"witnessScript\": \"" + fc::to_hex(addr.get_witness_script()) + "\" }"; @@ -977,61 +1304,53 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain return false; } - const std::string tx_str = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true); - if (tx_str != "") { - std::stringstream tx_ss(tx_str); - boost::property_tree::ptree tx_json; - boost::property_tree::read_json(tx_ss, tx_json); + btc_tx tx = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true); - if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) { - return false; - } + if (tx.tx_in_list.empty()) { + // This case will result with segmentation fault. + // FIXME check if that happened before introducing libbitcoin + return false; + } - const chain::global_property_object &gpo = database.get_global_properties(); + const chain::global_property_object &gpo = database.get_global_properties(); - using namespace bitcoin; - std::vector> pubkey_weights; - for (auto si : sto.signers) { - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); - pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); - } - btc_weighted_multisig_address addr(pubkey_weights, network_type); + using namespace bitcoin; + std::vector> pubkey_weights; + for (auto si : sto.signers) { + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); + pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); + } - std::string tx_txid = tx_json.get("result.txid"); - uint32_t tx_confirmations = tx_json.get("result.confirmations"); - std::string tx_address = addr.get_address(); - int64_t tx_amount = -1; + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_bitcoind_client) { + payment_type_address = payment_type::P2WSH; + } - if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { - if (sto.object_id.is()) { - for (auto &input : tx_json.get_child("result.vout")) { - if (bitcoin_major_version > 21) { - std::string address = input.second.get("scriptPubKey.address"); - if (address == tx_address) { - std::string tx_amount_s = input.second.get("value"); - tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); - tx_amount = std::stoll(tx_amount_s); - } - } else { - for (auto &address : input.second.get_child("scriptPubKey.addresses")) { - if (address.second.data() == tx_address) { - std::string tx_amount_s = input.second.get("value"); - tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); - tx_amount = std::stoll(tx_amount_s); - break; - } - } + btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address); + + std::string tx_txid = tx.tx_txid; + uint32_t tx_confirmations = tx.tx_confirmations; + std::string tx_address = addr.get_address(); + int64_t tx_amount = -1; + + if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { + if (sto.object_id.is()) { + for (auto &input : tx.tx_in_list) { + for (auto &address : input.tx_address) { + if (address == tx_address) { + tx_amount = input.tx_amount; + break; } } settle_amount = asset(tx_amount, database.get_global_properties().parameters.btc_asset()); return true; } + } - if (sto.object_id.is()) { - auto swwo = database.get(sto.object_id); - settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset()); - return true; - } + if (sto.object_id.is()) { + auto swwo = database.get(sto.object_id); + settle_amount = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset()); + return true; } } return false; @@ -1051,7 +1370,11 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } - btc_weighted_multisig_address addr(pubkey_weights, network_type); + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_bitcoind_client) { + payment_type_address = payment_type::P2WSH; + } + btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address); std::stringstream ss; @@ -1287,18 +1610,34 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran } // Add redeemscripts to vins and make tx ready for sending sign_witness_transaction_finalize(tx, redeem_scripts, false); + + if (!use_bitcoind_client) { + // get witness script from redeem script + bitcoin::bytes redeem_bytes = parse_hex(redeem_script); + fc::sha256 sha = fc::sha256::hash(&redeem_bytes[0], redeem_bytes.size()); + std::string witness_script(sha.str()); + witness_script.insert(0, std::string("220020")); + for (size_t i = 0; i < tx.vin.size(); i++) { + tx.vin[i].scriptSig = parse_hex(witness_script); + } + } + std::string final_tx_hex = fc::to_hex(pack(tx)); - return bitcoin_client->sendrawtransaction(final_tx_hex); + std::string res = bitcoin_client->sendrawtransaction(final_tx_hex); + + if (res.empty()) { + return res; + } + + return tx.get_txid().str(); } -void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { - const std::string block = bitcoin_client->getblock(event_data); - if (block.empty()) - return; +void sidechain_net_handler_bitcoin::block_handle_event(const block_data &event_data) { - add_to_son_listener_log("BLOCK : " + event_data); + auto vins = bitcoin_client->getblock(event_data); + + add_to_son_listener_log("BLOCK : " + event_data.block_hash); - auto vins = extract_info_from_block(block); scoped_lock interlock(event_handler_mutex); const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); @@ -1335,6 +1674,10 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) } } +void sidechain_net_handler_bitcoin::trx_handle_event(const libbitcoin::chain::transaction &trx_data) { + bitcoin_client->import_trx_to_memory_pool(trx_data); +} + std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(const std::string &user_address) { using namespace bitcoin; const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); @@ -1355,56 +1698,14 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key))); - btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type); - return fc::to_hex(deposit_addr.get_redeem_script()); -} -std::vector sidechain_net_handler_bitcoin::extract_info_from_block(const std::string &_block) { - std::stringstream ss(_block); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - auto json_result = json.get_child_optional("result"); - - std::vector result; - - for (const auto &tx_child : json_result.get().get_child("tx")) { - const auto &tx = tx_child.second; - - for (const auto &o : tx.get_child("vout")) { - const auto script = o.second.get_child("scriptPubKey"); - - if (bitcoin_major_version > 21) { - if (!script.count("address")) - continue; - } else { - if (!script.count("addresses")) - continue; - } - - auto sort_out_vin = [&](std::string address) { - const auto address_base58 = address; - info_for_vin vin; - vin.out.hash_tx = tx.get_child("txid").get_value(); - string amount = o.second.get_child("value").get_value(); - amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); - vin.out.amount = std::stoll(amount); - vin.out.n_vout = o.second.get_child("n").get_value(); - vin.address = address_base58; - result.push_back(vin); - }; - - if (bitcoin_major_version > 21) { - std::string address = script.get("address"); - sort_out_vin(address); - } else { - for (const auto &addr : script.get_child("addresses")) // in which cases there can be more addresses? - sort_out_vin(addr.second.get_value()); - } - } + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_bitcoind_client) { + payment_type_address = payment_type::P2WSH; } + btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type, payment_type_address); - return result; + return fc::to_hex(deposit_addr.get_redeem_script()); } void sidechain_net_handler_bitcoin::on_changed_objects(const vector &ids, const flat_set &accounts) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index d0051714..40bf8481 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -148,8 +148,8 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha wallet_contract_address = options.at("ethereum-wallet-contract-address").as(); - if (options.count("erc-20-address")) { - const std::vector symbol_addresses = options["erc-20-address"].as>(); + if (options.count("ethereum-erc-20-address")) { + const std::vector symbol_addresses = options["ethereum-erc-20-address"].as>(); for (const std::string &itr : symbol_addresses) { auto itr_pair = graphene::app::dejsonify>(itr, 5); ilog("ERC-20 symbol: ${symbol}, address: ${address}", ("symbol", itr_pair.first)("address", itr_pair.second)); @@ -236,7 +236,7 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) const son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; const auto ast = active_sidechain_types(database.head_block_time()); const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); - const son_wallet_id_type op_id{ id }; + const son_wallet_id_type op_id{id}; const auto &idx = database.get_index_type().indices().get(); const auto swo = idx.find(op_id); if (swo != idx.end()) { @@ -259,7 +259,7 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) if (po.proposed_transaction.operations.size() >= 2) { const object_id_type object_id = op_obj_idx_1.get().object_id; const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); - const object_id_type obj_id{ object_id.space(), object_id.type(), id }; + const object_id_type obj_id{object_id.space(), object_id.type(), id}; const std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); @@ -459,7 +459,7 @@ void sidechain_net_handler_ethereum::process_primary_wallet() { const auto ast = active_sidechain_types(database.head_block_time()); const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); - const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; + const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id}; if (proposal_exists(chain::operation::tag::value, op_id)) { return; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 3f1378e7..6ad95850 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -211,7 +211,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; const auto ast = active_sidechain_types(database.head_block_time()); const auto id = (swo_id.instance.value - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); - const son_wallet_id_type op_id{ id }; + const son_wallet_id_type op_id{id}; const auto &idx = database.get_index_type().indices().get(); const auto swo = idx.find(op_id); if (swo != idx.end()) { @@ -234,7 +234,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { if (po.proposed_transaction.operations.size() >= 2) { const object_id_type object_id = op_obj_idx_1.get().object_id; const auto id = (object_id.instance() - std::distance(ast.begin(), ast.find(sidechain))) / ast.size(); - const object_id_type obj_id{ object_id.space(), object_id.type(), id }; + const object_id_type obj_id{object_id.space(), object_id.type(), id}; std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); @@ -493,7 +493,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { const auto ast = active_sidechain_types(database.head_block_time()); const auto id = active_sw->id.instance() * ast.size() + std::distance(ast.begin(), ast.find(sidechain)); - const object_id_type op_id{ active_sw->id.space(), active_sw->id.type(), id }; + const object_id_type op_id{active_sw->id.space(), active_sw->id.type(), id}; if (proposal_exists(chain::operation::tag::value, op_id)) { return; From 19e0911d64ca2fa4fdbff3fa71598e8788a5f019 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Fri, 10 Feb 2023 13:34:53 +0200 Subject: [PATCH 086/106] #492 - fix withdrawal encoders for big numbers --- .../peerplays_sidechain/bitcoin/estimate_fee_external.hpp | 2 ++ .../peerplays_sidechain/sidechain_net_handler_ethereum.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp index 10be2868..fdfcc73f 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp @@ -2,8 +2,10 @@ #include +#include #include #include +#include #include typedef std::function get_fee_func_type; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 40bf8481..a3be8649 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -785,7 +785,7 @@ optional sidechain_net_handler_ethereum::estimate_withdrawal_transaction_ } const auto &public_key = son->sidechain_public_keys.at(sidechain); - const auto data = ethereum::withdrawal_encoder::encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); + const auto data = ethereum::withdrawal_encoder::encode(public_key, boost::multiprecision::uint256_t{1} * boost::multiprecision::uint256_t{10000000000}, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; const auto estimate_gas = ethereum::from_hex(rpc_client->get_estimate_gas(params)); @@ -808,14 +808,14 @@ std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(co std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { if (swwo.withdraw_currency == "ETH") { - return ethereum::withdrawal_encoder::encode(ethereum::remove_0x(swwo.withdraw_address), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); + return ethereum::withdrawal_encoder::encode(ethereum::remove_0x(swwo.withdraw_address), boost::multiprecision::uint256_t{swwo.withdraw_amount.value} * boost::multiprecision::uint256_t{10000000000}, swwo.id.operator std::string()); } else { const auto it = erc20_addresses.left.find(swwo.withdraw_currency); if (it == erc20_addresses.left.end()) { elog("No erc-20 token: ${symbol}", ("symbol", swwo.withdraw_currency)); return ""; } - return ethereum::withdrawal_erc20_encoder::encode(ethereum::remove_0x(it->second), ethereum::remove_0x(swwo.withdraw_address), swwo.withdraw_amount.value, swwo.id.operator std::string()); + return ethereum::withdrawal_erc20_encoder::encode(ethereum::remove_0x(it->second), ethereum::remove_0x(swwo.withdraw_address), boost::multiprecision::uint256_t{swwo.withdraw_amount.value}, swwo.id.operator std::string()); } return ""; From e44ed0cfe5df825f2fe264b68be382b559ebb609 Mon Sep 17 00:00:00 2001 From: Milos Milosevic Date: Fri, 10 Feb 2023 14:50:23 +0000 Subject: [PATCH 087/106] #421 fix double in consensus of nft_lottery_token_purchase --- .../chain/include/graphene/chain/protocol/nft_lottery.hpp | 4 ++-- libraries/chain/nft_lottery_evaluator.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/nft_lottery.hpp b/libraries/chain/include/graphene/chain/protocol/nft_lottery.hpp index 0c8ea855..00ea31b0 100644 --- a/libraries/chain/include/graphene/chain/protocol/nft_lottery.hpp +++ b/libraries/chain/include/graphene/chain/protocol/nft_lottery.hpp @@ -18,7 +18,7 @@ namespace graphene // Buyer purchasing lottery tickets account_id_type buyer; // count of tickets to buy - uint64_t tickets_to_buy; + share_type tickets_to_buy; // amount that can spent asset amount; @@ -83,4 +83,4 @@ FC_REFLECT(graphene::chain::nft_lottery_reward_operation::fee_parameters_type, ( FC_REFLECT(graphene::chain::nft_lottery_end_operation::fee_parameters_type, (fee)) FC_REFLECT(graphene::chain::nft_lottery_token_purchase_operation, (fee)(lottery_id)(buyer)(tickets_to_buy)(amount)(extensions)) FC_REFLECT(graphene::chain::nft_lottery_reward_operation, (fee)(lottery_id)(winner)(amount)(win_percentage)(is_benefactor_reward)(winner_ticket_id)(extensions)) -FC_REFLECT(graphene::chain::nft_lottery_end_operation, (fee)(lottery_id)(extensions)) \ No newline at end of file +FC_REFLECT(graphene::chain::nft_lottery_end_operation, (fee)(lottery_id)(extensions)) diff --git a/libraries/chain/nft_lottery_evaluator.cpp b/libraries/chain/nft_lottery_evaluator.cpp index 794616cf..2d9fe7f0 100644 --- a/libraries/chain/nft_lottery_evaluator.cpp +++ b/libraries/chain/nft_lottery_evaluator.cpp @@ -30,7 +30,7 @@ namespace graphene auto lottery_options = lottery_md_obj.lottery_data->lottery_options; FC_ASSERT(lottery_options.ticket_price.asset_id == op.amount.asset_id); - FC_ASSERT((double)op.amount.amount.value / lottery_options.ticket_price.amount.value == (double)op.tickets_to_buy); + FC_ASSERT(op.tickets_to_buy * lottery_options.ticket_price.amount.value == op.amount.amount.value); return void_result(); } FC_CAPTURE_AND_RETHROW((op)) @@ -142,4 +142,4 @@ namespace graphene FC_CAPTURE_AND_RETHROW((op)) } } // namespace chain -} // namespace graphene \ No newline at end of file +} // namespace graphene From f477af67714c79b044580008ca4d7e2d2537c41a Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 16 Feb 2023 12:45:03 +0200 Subject: [PATCH 088/106] #509 - fix hive withdrawal processing --- .../plugins/peerplays_sidechain/sidechain_net_handler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 490cfe0b..a4fe1252 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -679,7 +679,8 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { const bool is_tracked_asset = ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || - (sidechain == sidechain_type::ethereum) || + ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id != gpo.parameters.btc_asset()) + && (transfer_op.amount.asset_id != gpo.parameters.hbd_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hive_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); From 4e2850f82625b910a9d46f58a86d16aa00a274b2 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Fri, 17 Feb 2023 05:45:22 +0100 Subject: [PATCH 089/106] Fix libbitcoin build in docker and related README instructions --- Dockerfile | 1 + Dockerfile.18.04 | 1 + README.md | 1 + 3 files changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index d49bbe08..7a229aa7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -136,6 +136,7 @@ RUN \ RUN \ git clone https://github.com/libbitcoin/libbitcoin-build.git && \ cd libbitcoin-build && \ + git reset --hard 92c215fc1ffa272bab4d485d369d0306db52d69d && \ ./generate3.sh && \ cd ../libbitcoin-explorer && \ ./install.sh && \ diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index d09e01b2..9531d986 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -136,6 +136,7 @@ RUN \ RUN \ git clone https://github.com/libbitcoin/libbitcoin-build.git && \ cd libbitcoin-build && \ + git reset --hard 92c215fc1ffa272bab4d485d369d0306db52d69d && \ ./generate3.sh && \ cd ../libbitcoin-explorer && \ ./install.sh && \ diff --git a/README.md b/README.md index c3533d29..00b2fff3 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ libbitcoin-explorer setup: ``` git clone https://github.com/libbitcoin/libbitcoin-build.git cd libbitcoin-build +git reset --hard 92c215fc1ffa272bab4d485d369d0306db52d69d ./generate3.sh cd ../libbitcoin-explorer sudo ./install.sh From 2788281062205a0bc9904b31aeae4ef30f7e7643 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 23 Feb 2023 17:55:49 +0200 Subject: [PATCH 090/106] #501 - concurrent_unordered_set for connection --- libraries/net/node.cpp | 824 ++++++++++++++++++++++++++--------------- 1 file changed, 526 insertions(+), 298 deletions(-) diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 85e8c676..c3198d1d 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -128,6 +128,124 @@ namespace graphene { namespace net { namespace detail { namespace bmi = boost::multi_index; + + /******* + * A class to wrap std::unordered_set for multithreading + */ + template , class Pred = std::equal_to > + class concurrent_unordered_set : private std::unordered_set + { + private: + mutable fc::mutex mux; + + public: + /// Iterations require a lock. This exposes the mutex. Use with care (i.e. lock_guard) + fc::mutex& get_mutex()const { return mux; } + + /// Insertion + /// @{ + std::pair< typename std::unordered_set::iterator, bool> emplace( Key key) + { + fc::scoped_lock lock(mux); + return std::unordered_set::emplace( key ); + } + std::pair< typename std::unordered_set::iterator, bool> insert (const Key& val) + { + fc::scoped_lock lock(mux); + return std::unordered_set::insert( val ); + } + /// @} + /// Size + /// @{ + size_t size() const + { + fc::scoped_lock lock(mux); + return std::unordered_set::size(); + } + bool empty() const noexcept + { + fc::scoped_lock lock(mux); + return std::unordered_set::empty(); + } + /// @} + /// Removal + /// @{ + void clear() noexcept + { + fc::scoped_lock lock(mux); + std::unordered_set::clear(); + } + typename std::unordered_set::iterator erase( + typename std::unordered_set::const_iterator itr) + { + fc::scoped_lock lock(mux); + return std::unordered_set::erase( itr); + } + size_t erase( const Key& key) + { + fc::scoped_lock lock(mux); + return std::unordered_set::erase( key ); + } + /// @} + /// Swap + /// @{ + void swap( typename std::unordered_set& other ) noexcept + { + fc::scoped_lock lock(mux); + std::unordered_set::swap( other ); + } + /// @} + /// Iteration + /// @{ + typename std::unordered_set::iterator begin() noexcept + { + fc::scoped_lock lock(mux); + return std::unordered_set::begin(); + } + typename std::unordered_set::const_iterator begin() const noexcept + { + fc::scoped_lock lock(mux); + return std::unordered_set::begin(); + } + typename std::unordered_set::local_iterator begin(size_t n) + { + fc::scoped_lock lock(mux); + return std::unordered_set::begin(n); + } + typename std::unordered_set::const_local_iterator begin(size_t n) const + { + fc::scoped_lock lock(mux); + return std::unordered_set::begin(n); + } + typename std::unordered_set::iterator end() noexcept + { + fc::scoped_lock lock(mux); + return std::unordered_set::end(); + } + typename std::unordered_set::const_iterator end() const noexcept + { + fc::scoped_lock lock(mux); + return std::unordered_set::end(); + } + typename std::unordered_set::local_iterator end(size_t n) + { + fc::scoped_lock lock(mux); + return std::unordered_set::end(n); + } + typename std::unordered_set::const_local_iterator end(size_t n) const + { + fc::scoped_lock lock(mux); + return std::unordered_set::end(n); + } + /// @} + /// Search + typename std::unordered_set::const_iterator find(Key key) + { + fc::scoped_lock lock(mux); + return std::unordered_set::find(key); + } + }; + class blockchain_tied_message_cache { private: @@ -481,9 +599,9 @@ namespace graphene { namespace net { namespace detail { /// used by the task that advertises inventory during normal operation // @{ - fc::promise::ptr _retrigger_advertise_inventory_loop_promise; - fc::future _advertise_inventory_loop_done; - std::unordered_set _new_inventory; /// list of items we have received but not yet advertised to our peers + fc::promise::ptr _retrigger_advertise_inventory_loop_promise; + fc::future _advertise_inventory_loop_done; + concurrent_unordered_set _new_inventory; /// list of items we have received but not yet advertised to our peers // @} fc::future _terminate_inactive_connections_loop_done; @@ -519,13 +637,13 @@ namespace graphene { namespace net { namespace detail { /** Stores all connections which have not yet finished key exchange or are still sending initial handshaking messages * back and forth (not yet ready to initiate syncing) */ - std::unordered_set _handshaking_connections; + concurrent_unordered_set _handshaking_connections; /** stores fully established connections we're either syncing with or in normal operation with */ - std::unordered_set _active_connections; + concurrent_unordered_set _active_connections; /** stores connections we've closed (sent closing message, not actually closed), but are still waiting for the remote end to close before we delete them */ - std::unordered_set _closing_connections; + concurrent_unordered_set _closing_connections; /** stores connections we've closed, but are still waiting for the OS to notify us that the socket is really closed */ - std::unordered_set _terminating_connections; + concurrent_unordered_set _terminating_connections; boost::circular_buffer _most_recent_blocks_accepted; // the /n/ most recent blocks we've accepted (currently tuned to the max number of connections) @@ -854,16 +972,19 @@ namespace graphene { namespace net { namespace detail { ilog( "cleaning up node" ); _node_is_shutting_down.store(true); - for (const peer_connection_ptr& active_peer : _active_connections) { - fc::optional inbound_endpoint = active_peer->get_endpoint_for_connecting(); - if (inbound_endpoint) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& active_peer : _active_connections) { - fc::optional updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*inbound_endpoint); - if (updated_peer_record) + fc::optional inbound_endpoint = active_peer->get_endpoint_for_connecting(); + if (inbound_endpoint) { - updated_peer_record->last_seen_time = fc::time_point::now(); - _potential_peer_db.update_entry(*updated_peer_record); + fc::optional updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*inbound_endpoint); + if (updated_peer_record) + { + updated_peer_record->last_seen_time = fc::time_point::now(); + _potential_peer_db.update_entry(*updated_peer_record); + } } } } @@ -1061,6 +1182,7 @@ namespace graphene { namespace net { namespace detail { std::set sync_items_to_request; // for each idle peer that we're syncing with + fc::scoped_lock lock(_active_connections.get_mutex()); for( const peer_connection_ptr& peer : _active_connections ) { if( peer->we_need_sync_items_from_peer && @@ -1119,6 +1241,7 @@ namespace graphene { namespace net { namespace detail { bool node_impl::is_item_in_any_peers_inventory(const item_id& item) const { + fc::scoped_lock lock(_active_connections.get_mutex()); for( const peer_connection_ptr& peer : _active_connections ) { if (peer->inventory_peer_advertised_to_us.find(item) != peer->inventory_peer_advertised_to_us.end() ) @@ -1158,9 +1281,13 @@ namespace graphene { namespace net { namespace detail { fetch_messages_to_send_set items_by_peer; // initialize the fetch_messages_to_send with an empty set of items for all idle peers - for (const peer_connection_ptr& peer : _active_connections) - if (peer->idle()) - items_by_peer.insert(peer_and_items_to_fetch(peer)); + { + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { + if (peer->idle()) + items_by_peer.insert(peer_and_items_to_fetch(peer)); + } + } // now loop over all items we want to fetch for (auto item_iter = _items_to_fetch.begin(); item_iter != _items_to_fetch.end();) @@ -1262,56 +1389,59 @@ namespace graphene { namespace net { namespace detail { dlog("beginning an iteration of advertise inventory"); // swap inventory into local variable, clearing the node's copy std::unordered_set inventory_to_advertise; - inventory_to_advertise.swap(_new_inventory); + _new_inventory.swap(inventory_to_advertise); // process all inventory to advertise and construct the inventory messages we'll send // first, then send them all in a batch (to avoid any fiber interruption points while // we're computing the messages) std::list > inventory_messages_to_send; - for (const peer_connection_ptr& peer : _active_connections) { - // only advertise to peers who are in sync with us - idump((peer->peer_needs_sync_items_from_us)); - if( !peer->peer_needs_sync_items_from_us ) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { - std::map > items_to_advertise_by_type; - // don't send the peer anything we've already advertised to it - // or anything it has advertised to us - // group the items we need to send by type, because we'll need to send one inventory message per type - unsigned total_items_to_send_to_this_peer = 0; - idump((inventory_to_advertise)); - for (const item_id& item_to_advertise : inventory_to_advertise) + // only advertise to peers who are in sync with us + idump((peer->peer_needs_sync_items_from_us)); + if( !peer->peer_needs_sync_items_from_us ) { - auto adv_to_peer = peer->inventory_advertised_to_peer.find(item_to_advertise); - auto adv_to_us = peer->inventory_peer_advertised_to_us.find(item_to_advertise); + std::map > items_to_advertise_by_type; + // don't send the peer anything we've already advertised to it + // or anything it has advertised to us + // group the items we need to send by type, because we'll need to send one inventory message per type + unsigned total_items_to_send_to_this_peer = 0; + idump((inventory_to_advertise)); + for (const item_id& item_to_advertise : inventory_to_advertise) + { + auto adv_to_peer = peer->inventory_advertised_to_peer.find(item_to_advertise); + auto adv_to_us = peer->inventory_peer_advertised_to_us.find(item_to_advertise); - if (adv_to_peer == peer->inventory_advertised_to_peer.end() && - adv_to_us == peer->inventory_peer_advertised_to_us.end()) - { - items_to_advertise_by_type[item_to_advertise.item_type].push_back(item_to_advertise.item_hash); - peer->inventory_advertised_to_peer.insert(peer_connection::timestamped_item_id(item_to_advertise, fc::time_point::now())); - ++total_items_to_send_to_this_peer; - if (item_to_advertise.item_type == trx_message_type) - testnetlog("advertising transaction ${id} to peer ${endpoint}", ("id", item_to_advertise.item_hash)("endpoint", peer->get_remote_endpoint())); - dlog("advertising item ${id} to peer ${endpoint}", ("id", item_to_advertise.item_hash)("endpoint", peer->get_remote_endpoint())); - } - else - { - if (adv_to_peer != peer->inventory_advertised_to_peer.end() ) + if (adv_to_peer == peer->inventory_advertised_to_peer.end() && + adv_to_us == peer->inventory_peer_advertised_to_us.end()) + { + items_to_advertise_by_type[item_to_advertise.item_type].push_back(item_to_advertise.item_hash); + peer->inventory_advertised_to_peer.insert(peer_connection::timestamped_item_id(item_to_advertise, fc::time_point::now())); + ++total_items_to_send_to_this_peer; + if (item_to_advertise.item_type == trx_message_type) + testnetlog("advertising transaction ${id} to peer ${endpoint}", ("id", item_to_advertise.item_hash)("endpoint", peer->get_remote_endpoint())); + dlog("advertising item ${id} to peer ${endpoint}", ("id", item_to_advertise.item_hash)("endpoint", peer->get_remote_endpoint())); + } + else + { + if (adv_to_peer != peer->inventory_advertised_to_peer.end() ) idump( (*adv_to_peer) ); - if (adv_to_us != peer->inventory_peer_advertised_to_us.end() ) + if (adv_to_us != peer->inventory_peer_advertised_to_us.end() ) idump( (*adv_to_us) ); + } } - } dlog("advertising ${count} new item(s) of ${types} type(s) to peer ${endpoint}", ("count", total_items_to_send_to_this_peer) - ("types", items_to_advertise_by_type.size()) - ("endpoint", peer->get_remote_endpoint())); - for (auto items_group : items_to_advertise_by_type) - inventory_messages_to_send.push_back(std::make_pair(peer, item_ids_inventory_message(items_group.first, items_group.second))); + ("types", items_to_advertise_by_type.size()) + ("endpoint", peer->get_remote_endpoint())); + for (auto items_group : items_to_advertise_by_type) + inventory_messages_to_send.push_back(std::make_pair(peer, item_ids_inventory_message(items_group.first, items_group.second))); + } + peer->clear_old_inventory(); } - peer->clear_old_inventory(); } for (auto iter = inventory_messages_to_send.begin(); iter != inventory_messages_to_send.end(); ++iter) @@ -1360,25 +1490,30 @@ namespace graphene { namespace net { namespace detail { uint32_t handshaking_timeout = _peer_inactivity_timeout; fc::time_point handshaking_disconnect_threshold = fc::time_point::now() - fc::seconds(handshaking_timeout); - for( const peer_connection_ptr handshaking_peer : _handshaking_connections ) - if( handshaking_peer->connection_initiation_time < handshaking_disconnect_threshold && - handshaking_peer->get_last_message_received_time() < handshaking_disconnect_threshold && - handshaking_peer->get_last_message_sent_time() < handshaking_disconnect_threshold ) + { + fc::scoped_lock lock(_handshaking_connections.get_mutex()); + for( const peer_connection_ptr handshaking_peer : _handshaking_connections ) { - wlog( "Forcibly disconnecting from handshaking peer ${peer} due to inactivity of at least ${timeout} seconds", - ( "peer", handshaking_peer->get_remote_endpoint() )("timeout", handshaking_timeout ) ); - wlog("Peer's negotiating status: ${status}, bytes sent: ${sent}, bytes received: ${received}", - ("status", handshaking_peer->negotiation_status) - ("sent", handshaking_peer->get_total_bytes_sent()) - ("received", handshaking_peer->get_total_bytes_received())); - handshaking_peer->connection_closed_error = fc::exception(FC_LOG_MESSAGE(warn, "Terminating handshaking connection due to inactivity of ${timeout} seconds. Negotiating status: ${status}, bytes sent: ${sent}, bytes received: ${received}", - ("peer", handshaking_peer->get_remote_endpoint()) - ("timeout", handshaking_timeout) - ("status", handshaking_peer->negotiation_status) - ("sent", handshaking_peer->get_total_bytes_sent()) - ("received", handshaking_peer->get_total_bytes_received()))); - peers_to_disconnect_forcibly.push_back( handshaking_peer ); + if( handshaking_peer->connection_initiation_time < handshaking_disconnect_threshold && + handshaking_peer->get_last_message_received_time() < handshaking_disconnect_threshold && + handshaking_peer->get_last_message_sent_time() < handshaking_disconnect_threshold ) + { + wlog( "Forcibly disconnecting from handshaking peer ${peer} due to inactivity of at least ${timeout} seconds", + ( "peer", handshaking_peer->get_remote_endpoint() )("timeout", handshaking_timeout ) ); + wlog("Peer's negotiating status: ${status}, bytes sent: ${sent}, bytes received: ${received}", + ("status", handshaking_peer->negotiation_status) + ("sent", handshaking_peer->get_total_bytes_sent()) + ("received", handshaking_peer->get_total_bytes_received())); + handshaking_peer->connection_closed_error = fc::exception(FC_LOG_MESSAGE(warn, "Terminating handshaking connection due to inactivity of ${timeout} seconds. Negotiating status: ${status}, bytes sent: ${sent}, bytes received: ${received}", + ("peer", handshaking_peer->get_remote_endpoint()) + ("timeout", handshaking_timeout) + ("status", handshaking_peer->negotiation_status) + ("sent", handshaking_peer->get_total_bytes_sent()) + ("received", handshaking_peer->get_total_bytes_received()))); + peers_to_disconnect_forcibly.push_back( handshaking_peer ); + } } + } // timeout for any active peers is two block intervals uint32_t active_disconnect_timeout = 10 * _recent_block_interval_in_seconds; @@ -1398,94 +1533,103 @@ namespace graphene { namespace net { namespace detail { fc::time_point active_disconnect_threshold = fc::time_point::now() - fc::seconds(active_disconnect_timeout); fc::time_point active_send_keepalive_threshold = fc::time_point::now() - fc::seconds(active_send_keepalive_timeout); fc::time_point active_ignored_request_threshold = fc::time_point::now() - active_ignored_request_timeout; - for( const peer_connection_ptr& active_peer : _active_connections ) { - if( active_peer->connection_initiation_time < active_disconnect_threshold && - active_peer->get_last_message_received_time() < active_disconnect_threshold ) + fc::scoped_lock lock(_active_connections.get_mutex()); + for( const peer_connection_ptr& active_peer : _active_connections ) { - wlog( "Closing connection with peer ${peer} due to inactivity of at least ${timeout} seconds", - ( "peer", active_peer->get_remote_endpoint() )("timeout", active_disconnect_timeout ) ); - peers_to_disconnect_gently.push_back( active_peer ); - } - else - { - bool disconnect_due_to_request_timeout = false; - for (const peer_connection::item_to_time_map_type::value_type& item_and_time : active_peer->sync_items_requested_from_peer) - if (item_and_time.second < active_ignored_request_threshold) - { - wlog("Disconnecting peer ${peer} because they didn't respond to my request for sync item ${id}", - ("peer", active_peer->get_remote_endpoint())("id", item_and_time.first.item_hash)); - disconnect_due_to_request_timeout = true; - break; - } - if (!disconnect_due_to_request_timeout && - active_peer->item_ids_requested_from_peer && - active_peer->item_ids_requested_from_peer->get<1>() < active_ignored_request_threshold) - { - wlog("Disconnecting peer ${peer} because they didn't respond to my request for sync item ids after ${synopsis}", - ("peer", active_peer->get_remote_endpoint()) - ("synopsis", active_peer->item_ids_requested_from_peer->get<0>())); - disconnect_due_to_request_timeout = true; - } - if (!disconnect_due_to_request_timeout) - for (const peer_connection::item_to_time_map_type::value_type& item_and_time : active_peer->items_requested_from_peer) + if( active_peer->connection_initiation_time < active_disconnect_threshold && + active_peer->get_last_message_received_time() < active_disconnect_threshold ) + { + wlog( "Closing connection with peer ${peer} due to inactivity of at least ${timeout} seconds", + ( "peer", active_peer->get_remote_endpoint() )("timeout", active_disconnect_timeout ) ); + peers_to_disconnect_gently.push_back( active_peer ); + } + else + { + bool disconnect_due_to_request_timeout = false; + for (const peer_connection::item_to_time_map_type::value_type& item_and_time : active_peer->sync_items_requested_from_peer) if (item_and_time.second < active_ignored_request_threshold) { - wlog("Disconnecting peer ${peer} because they didn't respond to my request for item ${id}", - ("peer", active_peer->get_remote_endpoint())("id", item_and_time.first.item_hash)); + wlog("Disconnecting peer ${peer} because they didn't respond to my request for sync item ${id}", + ("peer", active_peer->get_remote_endpoint())("id", item_and_time.first.item_hash)); disconnect_due_to_request_timeout = true; break; } - if (disconnect_due_to_request_timeout) - { - // we should probably disconnect nicely and give them a reason, but right now the logic - // for rescheduling the requests only executes when the connection is fully closed, - // and we want to get those requests rescheduled as soon as possible - peers_to_disconnect_forcibly.push_back(active_peer); - } - else if (active_peer->connection_initiation_time < active_send_keepalive_threshold && - active_peer->get_last_message_received_time() < active_send_keepalive_threshold) - { - wlog( "Sending a keepalive message to peer ${peer} who hasn't sent us any messages in the last ${timeout} seconds", - ( "peer", active_peer->get_remote_endpoint() )("timeout", active_send_keepalive_timeout ) ); - peers_to_send_keep_alive.push_back(active_peer); - } - else if (active_peer->we_need_sync_items_from_peer && - !active_peer->is_currently_handling_message() && - !active_peer->item_ids_requested_from_peer && - active_peer->ids_of_items_to_get.empty()) - { - // This is a state we should never get into in the first place, but if we do, we should disconnect the peer - // to re-establish the connection. - fc_wlog(fc::logger::get("sync"), "Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.", - ("peer", active_peer->get_remote_endpoint())); - wlog("Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.", - ("peer", active_peer->get_remote_endpoint())); - peers_to_disconnect_forcibly.push_back(active_peer); + if (!disconnect_due_to_request_timeout && + active_peer->item_ids_requested_from_peer && + active_peer->item_ids_requested_from_peer->get<1>() < active_ignored_request_threshold) + { + wlog("Disconnecting peer ${peer} because they didn't respond to my request for sync item ids after ${synopsis}", + ("peer", active_peer->get_remote_endpoint()) + ("synopsis", active_peer->item_ids_requested_from_peer->get<0>())); + disconnect_due_to_request_timeout = true; + } + if (!disconnect_due_to_request_timeout) + for (const peer_connection::item_to_time_map_type::value_type& item_and_time : active_peer->items_requested_from_peer) + if (item_and_time.second < active_ignored_request_threshold) + { + wlog("Disconnecting peer ${peer} because they didn't respond to my request for item ${id}", + ("peer", active_peer->get_remote_endpoint())("id", item_and_time.first.item_hash)); + disconnect_due_to_request_timeout = true; + break; + } + if (disconnect_due_to_request_timeout) + { + // we should probably disconnect nicely and give them a reason, but right now the logic + // for rescheduling the requests only executes when the connection is fully closed, + // and we want to get those requests rescheduled as soon as possible + peers_to_disconnect_forcibly.push_back(active_peer); + } + else if (active_peer->connection_initiation_time < active_send_keepalive_threshold && + active_peer->get_last_message_received_time() < active_send_keepalive_threshold) + { + wlog( "Sending a keepalive message to peer ${peer} who hasn't sent us any messages in the last ${timeout} seconds", + ( "peer", active_peer->get_remote_endpoint() )("timeout", active_send_keepalive_timeout ) ); + peers_to_send_keep_alive.push_back(active_peer); + } + else if (active_peer->we_need_sync_items_from_peer && + !active_peer->is_currently_handling_message() && + !active_peer->item_ids_requested_from_peer && + active_peer->ids_of_items_to_get.empty()) + { + // This is a state we should never get into in the first place, but if we do, we should disconnect the peer + // to re-establish the connection. + fc_wlog(fc::logger::get("sync"), "Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.", + ("peer", active_peer->get_remote_endpoint())); + wlog("Disconnecting peer ${peer} because we think we need blocks from them but sync has stalled.", + ("peer", active_peer->get_remote_endpoint())); + peers_to_disconnect_forcibly.push_back(active_peer); + } } } } fc::time_point closing_disconnect_threshold = fc::time_point::now() - fc::seconds(GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT); - for( const peer_connection_ptr& closing_peer : _closing_connections ) - if( closing_peer->connection_closed_time < closing_disconnect_threshold ) - { - // we asked this peer to close their connectoin to us at least GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT - // seconds ago, but they haven't done it yet. Terminate the connection now - wlog( "Forcibly disconnecting peer ${peer} who failed to close their connection in a timely manner", - ( "peer", closing_peer->get_remote_endpoint() ) ); - peers_to_disconnect_forcibly.push_back( closing_peer ); + { + fc::scoped_lock lock(_closing_connections.get_mutex()); + for( const peer_connection_ptr& closing_peer : _closing_connections ) { + if (closing_peer->connection_closed_time < closing_disconnect_threshold) { + // we asked this peer to close their connectoin to us at least GRAPHENE_NET_PEER_DISCONNECT_TIMEOUT + // seconds ago, but they haven't done it yet. Terminate the connection now + wlog("Forcibly disconnecting peer ${peer} who failed to close their connection in a timely manner", + ("peer", closing_peer->get_remote_endpoint())); + peers_to_disconnect_forcibly.push_back(closing_peer); + } } + } uint32_t failed_terminate_timeout_seconds = 120; fc::time_point failed_terminate_threshold = fc::time_point::now() - fc::seconds(failed_terminate_timeout_seconds); - for (const peer_connection_ptr& peer : _terminating_connections ) - if (peer->get_connection_terminated_time() != fc::time_point::min() && - peer->get_connection_terminated_time() < failed_terminate_threshold) - { - wlog("Terminating connection with peer ${peer}, closing the connection didn't work", ("peer", peer->get_remote_endpoint())); - peers_to_terminate.push_back(peer); + { + fc::scoped_lock lock(_terminating_connections.get_mutex()); + for (const peer_connection_ptr& peer : _terminating_connections ) { + if (peer->get_connection_terminated_time() != fc::time_point::min() && + peer->get_connection_terminated_time() < failed_terminate_threshold) { + wlog("Terminating connection with peer ${peer}, closing the connection didn't work", ("peer", peer->get_remote_endpoint())); + peers_to_terminate.push_back(peer); + } } + } // That's the end of the sorting step; now all peers that require further processing are now in one of the // lists peers_to_disconnect_gently, peers_to_disconnect_forcibly, peers_to_send_keep_alive, or peers_to_terminate @@ -1493,11 +1637,14 @@ namespace graphene { namespace net { namespace detail { // if we've decided to delete any peers, do it now; in its current implementation this doesn't yield, // and once we start yielding, we may find that we've moved that peer to another list (closed or active) // and that triggers assertions, maybe even errors - for (const peer_connection_ptr& peer : peers_to_terminate ) { - assert(_terminating_connections.find(peer) != _terminating_connections.end()); - _terminating_connections.erase(peer); - schedule_peer_for_deletion(peer); + fc::scoped_lock lock(_terminating_connections.get_mutex()); + for (const peer_connection_ptr& peer : peers_to_terminate ) + { + assert(_terminating_connections.find(peer) != _terminating_connections.end()); + _terminating_connections.erase(peer); + schedule_peer_for_deletion(peer); + } } peers_to_terminate.clear(); @@ -1516,6 +1663,7 @@ namespace graphene { namespace net { namespace detail { // disconnect reason, so it may yield) for( const peer_connection_ptr& peer : peers_to_disconnect_gently ) { + fc::scoped_lock lock(_active_connections.get_mutex()); fc::exception detailed_error( FC_LOG_MESSAGE(warn, "Disconnecting due to inactivity", ( "last_message_received_seconds_ago", (peer->get_last_message_received_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() ) ( "last_message_sent_seconds_ago", (peer->get_last_message_sent_time() - fc::time_point::now() ).count() / fc::seconds(1 ).count() ) @@ -1539,6 +1687,7 @@ namespace graphene { namespace net { namespace detail { { VERIFY_CORRECT_THREAD(); + fc::scoped_lock lock(_active_connections.get_mutex()); std::list original_active_peers(_active_connections.begin(), _active_connections.end()); for( const peer_connection_ptr& active_peer : original_active_peers ) { @@ -1710,12 +1859,19 @@ namespace graphene { namespace net { namespace detail { peer_connection_ptr node_impl::get_peer_by_node_id(const node_id_t& node_id) { - for (const peer_connection_ptr& active_peer : _active_connections) - if (node_id == active_peer->node_id) - return active_peer; - for (const peer_connection_ptr& handshaking_peer : _handshaking_connections) - if (node_id == handshaking_peer->node_id) - return handshaking_peer; + { + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& active_peer : _active_connections) + if (node_id == active_peer->node_id) + return active_peer; + } + { + fc::scoped_lock lock(_handshaking_connections.get_mutex()); + for (const peer_connection_ptr& handshaking_peer : _handshaking_connections) + if (node_id == handshaking_peer->node_id) + return handshaking_peer; + } + return peer_connection_ptr(); } @@ -1727,18 +1883,25 @@ namespace graphene { namespace net { namespace detail { dlog("is_already_connected_to_id returning true because the peer is us"); return true; } - for (const peer_connection_ptr active_peer : _active_connections) - if (node_id == active_peer->node_id) - { - dlog("is_already_connected_to_id returning true because the peer is already in our active list"); - return true; + { + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr active_peer : _active_connections) { + if (node_id == active_peer->node_id) { + dlog("is_already_connected_to_id returning true because the peer is already in our active list"); + return true; + } } - for (const peer_connection_ptr handshaking_peer : _handshaking_connections) - if (node_id == handshaking_peer->node_id) - { - dlog("is_already_connected_to_id returning true because the peer is already in our handshaking list"); - return true; + } + { + fc::scoped_lock lock(_handshaking_connections.get_mutex()); + for (const peer_connection_ptr handshaking_peer : _handshaking_connections) { + if (node_id == handshaking_peer->node_id) { + dlog("is_already_connected_to_id returning true because the peer is already in our handshaking list"); + return true; + } } + } + return false; } @@ -1770,19 +1933,25 @@ namespace graphene { namespace net { namespace detail { ("max", _maximum_number_of_connections)); dlog(" my id is ${id}", ("id", _node_id)); - for (const peer_connection_ptr& active_connection : _active_connections) { - dlog(" active: ${endpoint} with ${id} [${direction}]", - ("endpoint", active_connection->get_remote_endpoint()) - ("id", active_connection->node_id) - ("direction", active_connection->direction)); + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& active_connection : _active_connections) + { + dlog(" active: ${endpoint} with ${id} [${direction}]", + ("endpoint", active_connection->get_remote_endpoint()) + ("id", active_connection->node_id) + ("direction", active_connection->direction)); + } } - for (const peer_connection_ptr& handshaking_connection : _handshaking_connections) { - dlog(" handshaking: ${endpoint} with ${id} [${direction}]", - ("endpoint", handshaking_connection->get_remote_endpoint()) - ("id", handshaking_connection->node_id) - ("direction", handshaking_connection->direction)); + fc::scoped_lock lock(_handshaking_connections.get_mutex()); + for (const peer_connection_ptr& handshaking_connection : _handshaking_connections) + { + dlog(" handshaking: ${endpoint} with ${id} [${direction}]", + ("endpoint", handshaking_connection->get_remote_endpoint()) + ("id", handshaking_connection->node_id) + ("direction", handshaking_connection->direction)); + } } } @@ -2229,6 +2398,7 @@ namespace graphene { namespace net { namespace detail { if (!_peer_advertising_disabled) { reply.addresses.reserve(_active_connections.size()); + fc::scoped_lock lock(_active_connections.get_mutex()); for (const peer_connection_ptr& active_peer : _active_connections) { fc::optional updated_peer_record = _potential_peer_db.lookup_entry_for_endpoint(*active_peer->get_remote_endpoint()); @@ -2414,11 +2584,14 @@ namespace graphene { namespace net { namespace detail { { VERIFY_CORRECT_THREAD(); uint32_t max_number_of_unfetched_items = 0; - for( const peer_connection_ptr& peer : _active_connections ) { - uint32_t this_peer_number_of_unfetched_items = (uint32_t)peer->ids_of_items_to_get.size() + peer->number_of_unfetched_item_ids; - max_number_of_unfetched_items = std::max(max_number_of_unfetched_items, - this_peer_number_of_unfetched_items); + fc::scoped_lock lock(_active_connections.get_mutex()); + for( const peer_connection_ptr& peer : _active_connections ) + { + uint32_t this_peer_number_of_unfetched_items = (uint32_t)peer->ids_of_items_to_get.size() + peer->number_of_unfetched_item_ids; + max_number_of_unfetched_items = std::max(max_number_of_unfetched_items, + this_peer_number_of_unfetched_items); + } } return max_number_of_unfetched_items; } @@ -2633,17 +2806,19 @@ namespace graphene { namespace net { namespace detail { originating_peer->ids_of_items_to_get.empty()) { bool is_first_item_for_other_peer = false; - for (const peer_connection_ptr& peer : _active_connections) - if (peer != originating_peer->shared_from_this() && - !peer->ids_of_items_to_get.empty() && - peer->ids_of_items_to_get.front() == blockchain_item_ids_inventory_message_received.item_hashes_available.front()) - { - dlog("The item ${newitem} is the first item for peer ${peer}", - ("newitem", blockchain_item_ids_inventory_message_received.item_hashes_available.front()) - ("peer", peer->get_remote_endpoint())); - is_first_item_for_other_peer = true; - break; + { + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { + if (peer != originating_peer->shared_from_this() && + !peer->ids_of_items_to_get.empty() && + peer->ids_of_items_to_get.front() == blockchain_item_ids_inventory_message_received.item_hashes_available.front()) { + dlog("The item ${newitem} is the first item for peer ${peer}", + ("newitem", blockchain_item_ids_inventory_message_received.item_hashes_available.front())("peer", peer->get_remote_endpoint())); + is_first_item_for_other_peer = true; + break; + } } + } dlog("is_first_item_for_other_peer: ${is_first}. item_hashes_received.size() = ${size}", ("is_first", is_first_item_for_other_peer)("size", item_hashes_received.size())); if (!is_first_item_for_other_peer) @@ -2933,15 +3108,18 @@ namespace graphene { namespace net { namespace detail { item_id advertised_item_id(item_ids_inventory_message_received.item_type, item_hash); bool we_advertised_this_item_to_a_peer = false; bool we_requested_this_item_from_a_peer = false; - for (const peer_connection_ptr peer : _active_connections) { - if (peer->inventory_advertised_to_peer.find(advertised_item_id) != peer->inventory_advertised_to_peer.end()) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr peer : _active_connections) { - we_advertised_this_item_to_a_peer = true; - break; + if (peer->inventory_advertised_to_peer.find(advertised_item_id) != peer->inventory_advertised_to_peer.end()) + { + we_advertised_this_item_to_a_peer = true; + break; + } + if (peer->items_requested_from_peer.find(advertised_item_id) != peer->items_requested_from_peer.end()) + we_requested_this_item_from_a_peer = true; } - if (peer->items_requested_from_peer.find(advertised_item_id) != peer->items_requested_from_peer.end()) - we_requested_this_item_from_a_peer = true; } // if we have already advertised it to a peer, we must have it, no need to do anything else @@ -3172,6 +3350,7 @@ namespace graphene { namespace net { namespace detail { }; bool is_fork_block = is_hard_fork_block(block_message_to_send.block.block_num()); + fc::scoped_lock lock(_active_connections.get_mutex()); for (const peer_connection_ptr& peer : _active_connections) { ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections @@ -3254,6 +3433,7 @@ namespace graphene { namespace net { namespace detail { else { // invalid message received + fc::scoped_lock lock(_active_connections.get_mutex()); for (const peer_connection_ptr& peer : _active_connections) { ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections @@ -3356,15 +3536,18 @@ namespace graphene { namespace net { namespace detail { // find out if this block is the next block on the active chain or one of the forks bool potential_first_block = false; - for (const peer_connection_ptr& peer : _active_connections) { - ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections - if (!peer->ids_of_items_to_get.empty() && - peer->ids_of_items_to_get.front() == received_block_iter->block_id) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { - potential_first_block = true; - peer->ids_of_items_to_get.pop_front(); - peer->ids_of_items_being_processed.insert(received_block_iter->block_id); + ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections + if (!peer->ids_of_items_to_get.empty() && + peer->ids_of_items_to_get.front() == received_block_iter->block_id) + { + potential_first_block = true; + peer->ids_of_items_to_get.pop_front(); + peer->ids_of_items_being_processed.insert(received_block_iter->block_id); + } } } @@ -3392,6 +3575,7 @@ namespace graphene { namespace net { namespace detail { { dlog("Already received and accepted this block (presumably through normal inventory mechanism), treating it as accepted"); std::vector< peer_connection_ptr > peers_needing_next_batch; + fc::scoped_lock lock(_active_connections.get_mutex()); for (const peer_connection_ptr& peer : _active_connections) { auto items_being_processed_iter = peer->ids_of_items_being_processed.find(received_block_iter->block_id); @@ -3527,55 +3711,62 @@ namespace graphene { namespace net { namespace detail { fc::time_point_sec block_time = block_message_to_process.block.timestamp; bool disconnect_this_peer = false; - for (const peer_connection_ptr& peer : _active_connections) { - ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections - - auto iter = peer->inventory_peer_advertised_to_us.find(block_message_item_id); - if (iter != peer->inventory_peer_advertised_to_us.end()) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { - // this peer offered us the item. It will eventually expire from the peer's - // inventory_peer_advertised_to_us list after some time has passed (currently 2 minutes). - // For now, it will remain there, which will prevent us from offering the peer this - // block back when we rebroadcast the block below - peer->last_block_delegate_has_seen = block_message_to_process.block_id; - peer->last_block_time_delegate_has_seen = block_time; + ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections + + auto iter = peer->inventory_peer_advertised_to_us.find(block_message_item_id); + if (iter != peer->inventory_peer_advertised_to_us.end()) + { + // this peer offered us the item. It will eventually expire from the peer's + // inventory_peer_advertised_to_us list after some time has passed (currently 2 minutes). + // For now, it will remain there, which will prevent us from offering the peer this + // block back when we rebroadcast the block below + peer->last_block_delegate_has_seen = block_message_to_process.block_id; + peer->last_block_time_delegate_has_seen = block_time; + } + peer->clear_old_inventory(); } - peer->clear_old_inventory(); } + message_propagation_data propagation_data{message_receive_time, message_validated_time, originating_peer->node_id}; broadcast( block_message_to_process, propagation_data ); _message_cache.block_accepted(); - for (const peer_connection_ptr& peer : _active_connections) { - if (is_hard_fork_block(block_number) ) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { - if (peer->last_known_fork_block_number != 0) + if (is_hard_fork_block(block_number) ) { - uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(peer->last_known_fork_block_number); - if (next_fork_block_number != 0 && - next_fork_block_number <= block_number) + if (peer->last_known_fork_block_number != 0) + { + uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(peer->last_known_fork_block_number); + if (next_fork_block_number != 0 && + next_fork_block_number <= block_number) + { + disconnect_this_peer = true; + } + } + } + + if(peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time()) + { + if(block_message_to_process.block.timestamp.sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch()) { disconnect_this_peer = true; } } - } - if(peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time()) - { - if(block_message_to_process.block.timestamp.sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch()) + if( disconnect_this_peer ) { - disconnect_this_peer = true; - } - } - - if( disconnect_this_peer ) - { - peers_to_disconnect.insert(peer); + peers_to_disconnect.insert(peer); #ifdef ENABLE_DEBUG_ULOGS - ulog("Disconnecting from peer because their version is too old. Their version date: ${date}", ("date", peer->graphene_git_revision_unix_timestamp)); + ulog("Disconnecting from peer because their version is too old. Their version date: ${date}", ("date", peer->graphene_git_revision_unix_timestamp)); #endif + } } } @@ -3614,9 +3805,11 @@ namespace graphene { namespace net { namespace detail { disconnect_reason = "You offered me a block that I have deemed to be invalid"; peers_to_disconnect.insert( originating_peer->shared_from_this() ); - for (const peer_connection_ptr& peer : _active_connections) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { if (!peer->ids_of_items_to_get.empty() && peer->ids_of_items_to_get.front() == block_message_to_process.block_id) peers_to_disconnect.insert(peer); + } } if (restart_sync_exception) @@ -3737,25 +3930,29 @@ namespace graphene { namespace net { namespace detail { void node_impl::forward_firewall_check_to_next_available_peer(firewall_check_state_data* firewall_check_state) { - for (const peer_connection_ptr& peer : _active_connections) { - if (firewall_check_state->expected_node_id != peer->node_id && // it's not the node who is asking us to test - !peer->firewall_check_state && // the peer isn't already performing a check for another node - firewall_check_state->nodes_already_tested.find(peer->node_id) == firewall_check_state->nodes_already_tested.end() && - peer->core_protocol_version >= 106) + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) { - wlog("forwarding firewall check for node ${to_check} to peer ${checker}", - ("to_check", firewall_check_state->endpoint_to_test) - ("checker", peer->get_remote_endpoint())); - firewall_check_state->nodes_already_tested.insert(peer->node_id); - peer->firewall_check_state = firewall_check_state; - check_firewall_message check_request; - check_request.endpoint_to_check = firewall_check_state->endpoint_to_test; - check_request.node_id = firewall_check_state->expected_node_id; - peer->send_message(check_request); - return; + if (firewall_check_state->expected_node_id != peer->node_id && // it's not the node who is asking us to test + !peer->firewall_check_state && // the peer isn't already performing a check for another node + firewall_check_state->nodes_already_tested.find(peer->node_id) == firewall_check_state->nodes_already_tested.end() && + peer->core_protocol_version >= 106) + { + wlog("forwarding firewall check for node ${to_check} to peer ${checker}", + ("to_check", firewall_check_state->endpoint_to_test) + ("checker", peer->get_remote_endpoint())); + firewall_check_state->nodes_already_tested.insert(peer->node_id); + peer->firewall_check_state = firewall_check_state; + check_firewall_message check_request; + check_request.endpoint_to_check = firewall_check_state->endpoint_to_test; + check_request.node_id = firewall_check_state->expected_node_id; + peer->send_message(check_request); + return; + } } } + wlog("Unable to forward firewall check for node ${to_check} to any other peers, returning 'unable'", ("to_check", firewall_check_state->endpoint_to_test)); @@ -3928,41 +4125,45 @@ namespace graphene { namespace net { namespace detail { } fc::time_point now = fc::time_point::now(); - for (const peer_connection_ptr& peer : _active_connections) { - ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr& peer : _active_connections) + { + ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections - current_connection_data data_for_this_peer; - data_for_this_peer.connection_duration = now.sec_since_epoch() - peer->connection_initiation_time.sec_since_epoch(); - if (peer->get_remote_endpoint()) // should always be set for anyone we're actively connected to - data_for_this_peer.remote_endpoint = *peer->get_remote_endpoint(); - data_for_this_peer.clock_offset = peer->clock_offset; - data_for_this_peer.round_trip_delay = peer->round_trip_delay; - data_for_this_peer.node_id = peer->node_id; - data_for_this_peer.connection_direction = peer->direction; - data_for_this_peer.firewalled = peer->is_firewalled; - fc::mutable_variant_object user_data; - if (peer->graphene_git_revision_sha) - user_data["graphene_git_revision_sha"] = *peer->graphene_git_revision_sha; - if (peer->graphene_git_revision_unix_timestamp) - user_data["graphene_git_revision_unix_timestamp"] = *peer->graphene_git_revision_unix_timestamp; - if (peer->fc_git_revision_sha) - user_data["fc_git_revision_sha"] = *peer->fc_git_revision_sha; - if (peer->fc_git_revision_unix_timestamp) - user_data["fc_git_revision_unix_timestamp"] = *peer->fc_git_revision_unix_timestamp; - if (peer->platform) - user_data["platform"] = *peer->platform; - if (peer->bitness) - user_data["bitness"] = *peer->bitness; - user_data["user_agent"] = peer->user_agent; + current_connection_data data_for_this_peer; + data_for_this_peer.connection_duration = now.sec_since_epoch() - peer->connection_initiation_time.sec_since_epoch(); + if (peer->get_remote_endpoint()) // should always be set for anyone we're actively connected to + data_for_this_peer.remote_endpoint = *peer->get_remote_endpoint(); + data_for_this_peer.clock_offset = peer->clock_offset; + data_for_this_peer.round_trip_delay = peer->round_trip_delay; + data_for_this_peer.node_id = peer->node_id; + data_for_this_peer.connection_direction = peer->direction; + data_for_this_peer.firewalled = peer->is_firewalled; + fc::mutable_variant_object user_data; + if (peer->graphene_git_revision_sha) + user_data["graphene_git_revision_sha"] = *peer->graphene_git_revision_sha; + if (peer->graphene_git_revision_unix_timestamp) + user_data["graphene_git_revision_unix_timestamp"] = *peer->graphene_git_revision_unix_timestamp; + if (peer->fc_git_revision_sha) + user_data["fc_git_revision_sha"] = *peer->fc_git_revision_sha; + if (peer->fc_git_revision_unix_timestamp) + user_data["fc_git_revision_unix_timestamp"] = *peer->fc_git_revision_unix_timestamp; + if (peer->platform) + user_data["platform"] = *peer->platform; + if (peer->bitness) + user_data["bitness"] = *peer->bitness; + user_data["user_agent"] = peer->user_agent; - user_data["last_known_block_hash"] = fc::variant( peer->last_block_delegate_has_seen, 1 ); - user_data["last_known_block_number"] = _delegate->get_block_number(peer->last_block_delegate_has_seen); - user_data["last_known_block_time"] = peer->last_block_time_delegate_has_seen; + user_data["last_known_block_hash"] = fc::variant( peer->last_block_delegate_has_seen, 1 ); + user_data["last_known_block_number"] = _delegate->get_block_number(peer->last_block_delegate_has_seen); + user_data["last_known_block_time"] = peer->last_block_time_delegate_has_seen; - data_for_this_peer.user_data = user_data; - reply.current_connections.emplace_back(data_for_this_peer); + data_for_this_peer.user_data = user_data; + reply.current_connections.emplace_back(data_for_this_peer); + } } + originating_peer->send_message(reply); } @@ -4047,6 +4248,7 @@ namespace graphene { namespace net { namespace detail { void node_impl::start_synchronizing() { + fc::scoped_lock lock(_active_connections.get_mutex()); for( const peer_connection_ptr& peer : _active_connections ) start_synchronizing_with_peer( peer ); } @@ -4253,9 +4455,19 @@ namespace graphene { namespace net { namespace detail { // the read loop before it gets an EOF). // operate off copies of the lists in case they change during iteration std::list all_peers; - boost::push_back(all_peers, _active_connections); - boost::push_back(all_peers, _handshaking_connections); - boost::push_back(all_peers, _closing_connections); + auto p_back = [&all_peers](const peer_connection_ptr& conn) { all_peers.push_back(conn); }; + { + fc::scoped_lock lock(_active_connections.get_mutex()); + std::for_each(_active_connections.begin(), _active_connections.end(), p_back); + } + { + fc::scoped_lock lock(_handshaking_connections.get_mutex()); + std::for_each(_handshaking_connections.begin(), _handshaking_connections.end(), p_back); + } + { + fc::scoped_lock lock(_closing_connections.get_mutex()); + std::for_each(_closing_connections.begin(), _closing_connections.end(), p_back); + } for (const peer_connection_ptr& peer : all_peers) { @@ -4521,9 +4733,7 @@ namespace graphene { namespace net { namespace detail { // whether the peer is firewalled, we want to disconnect now. _handshaking_connections.erase(new_peer); _terminating_connections.erase(new_peer); - assert(_active_connections.find(new_peer) == _active_connections.end()); _active_connections.erase(new_peer); - assert(_closing_connections.find(new_peer) == _closing_connections.end()); _closing_connections.erase(new_peer); display_current_connections(); @@ -4867,18 +5077,25 @@ namespace graphene { namespace net { namespace detail { peer_connection_ptr node_impl::get_connection_to_endpoint( const fc::ip::endpoint& remote_endpoint ) { VERIFY_CORRECT_THREAD(); - for( const peer_connection_ptr& active_peer : _active_connections ) { - fc::optional endpoint_for_this_peer( active_peer->get_remote_endpoint() ); - if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint ) - return active_peer; + fc::scoped_lock lock(_active_connections.get_mutex()); + for( const peer_connection_ptr& active_peer : _active_connections ) + { + fc::optional endpoint_for_this_peer( active_peer->get_remote_endpoint() ); + if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint ) + return active_peer; + } } - for( const peer_connection_ptr& handshaking_peer : _handshaking_connections ) { - fc::optional endpoint_for_this_peer( handshaking_peer->get_remote_endpoint() ); - if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint ) - return handshaking_peer; + fc::scoped_lock lock(_handshaking_connections.get_mutex()); + for( const peer_connection_ptr& handshaking_peer : _handshaking_connections ) + { + fc::optional endpoint_for_this_peer( handshaking_peer->get_remote_endpoint() ); + if( endpoint_for_this_peer && *endpoint_for_this_peer == remote_endpoint ) + return handshaking_peer; + } } + return peer_connection_ptr(); } @@ -4922,21 +5139,27 @@ namespace graphene { namespace net { namespace detail { ilog( " number of peers: ${active} active, ${handshaking}, ${closing} closing. attempting to maintain ${desired} - ${maximum} peers", ( "active", _active_connections.size() )("handshaking", _handshaking_connections.size() )("closing",_closing_connections.size() ) ( "desired", _desired_number_of_connections )("maximum", _maximum_number_of_connections ) ); - for( const peer_connection_ptr& peer : _active_connections ) { - ilog( " active peer ${endpoint} peer_is_in_sync_with_us:${in_sync_with_us} we_are_in_sync_with_peer:${in_sync_with_them}", - ( "endpoint", peer->get_remote_endpoint() ) - ( "in_sync_with_us", !peer->peer_needs_sync_items_from_us )("in_sync_with_them", !peer->we_need_sync_items_from_peer ) ); - if( peer->we_need_sync_items_from_peer ) - ilog( " above peer has ${count} sync items we might need", ("count", peer->ids_of_items_to_get.size() ) ); - if (peer->inhibit_fetching_sync_blocks) - ilog( " we are not fetching sync blocks from the above peer (inhibit_fetching_sync_blocks == true)" ); + fc::scoped_lock lock(_active_connections.get_mutex()); + for( const peer_connection_ptr& peer : _active_connections ) + { + ilog( " active peer ${endpoint} peer_is_in_sync_with_us:${in_sync_with_us} we_are_in_sync_with_peer:${in_sync_with_them}", + ( "endpoint", peer->get_remote_endpoint() ) + ( "in_sync_with_us", !peer->peer_needs_sync_items_from_us )("in_sync_with_them", !peer->we_need_sync_items_from_peer ) ); + if( peer->we_need_sync_items_from_peer ) + ilog( " above peer has ${count} sync items we might need", ("count", peer->ids_of_items_to_get.size() ) ); + if (peer->inhibit_fetching_sync_blocks) + ilog( " we are not fetching sync blocks from the above peer (inhibit_fetching_sync_blocks == true)" ); + } } - for( const peer_connection_ptr& peer : _handshaking_connections ) { - ilog( " handshaking peer ${endpoint} in state ours(${our_state}) theirs(${their_state})", - ( "endpoint", peer->get_remote_endpoint() )("our_state", peer->our_state )("their_state", peer->their_state ) ); + fc::scoped_lock lock(_handshaking_connections.get_mutex()); + for( const peer_connection_ptr& peer : _handshaking_connections ) + { + ilog( " handshaking peer ${endpoint} in state ours(${our_state}) theirs(${their_state})", + ( "endpoint", peer->get_remote_endpoint() )("our_state", peer->our_state )("their_state", peer->their_state ) ); + } } ilog( "--------- MEMORY USAGE ------------" ); @@ -4946,6 +5169,7 @@ namespace graphene { namespace net { namespace detail { ilog( "node._items_to_fetch size: ${size}", ("size", _items_to_fetch.size() ) ); ilog( "node._new_inventory size: ${size}", ("size", _new_inventory.size() ) ); ilog( "node._message_cache size: ${size}", ("size", _message_cache.size() ) ); + fc::scoped_lock lock(_active_connections.get_mutex()); for( const peer_connection_ptr& peer : _active_connections ) { ilog( " peer ${endpoint}", ("endpoint", peer->get_remote_endpoint() ) ); @@ -5043,6 +5267,7 @@ namespace graphene { namespace net { namespace detail { { VERIFY_CORRECT_THREAD(); std::vector statuses; + fc::scoped_lock lock(_active_connections.get_mutex()); for (const peer_connection_ptr& peer : _active_connections) { ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections @@ -5233,10 +5458,13 @@ namespace graphene { namespace net { namespace detail { _allowed_peers.clear(); _allowed_peers.insert(allowed_peers.begin(), allowed_peers.end()); std::list peers_to_disconnect; - if (!_allowed_peers.empty()) - for (const peer_connection_ptr& peer : _active_connections) + if (!_allowed_peers.empty()) { + fc::scoped_lock lock(_active_connections.get_mutex()); + for (const peer_connection_ptr &peer : _active_connections) { if (_allowed_peers.find(peer->node_id) == _allowed_peers.end()) peers_to_disconnect.push_back(peer); + } + } for (const peer_connection_ptr& peer : peers_to_disconnect) disconnect_from_peer(peer.get(), "My allowed_peers list has changed, and you're no longer allowed. Bye."); #endif // ENABLE_P2P_DEBUGGING_API From 7af3d037b520aafde294e0353c366da2e28bf68f Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Fri, 24 Feb 2023 09:31:51 +0000 Subject: [PATCH 091/106] #495 hive wallet update --- .../sidechain_net_handler_bitcoin.cpp | 8 +++++-- .../sidechain_net_handler_ethereum.cpp | 3 ++- .../sidechain_net_handler_hive.cpp | 21 ++++++++++++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index f37352bb..c528d8a4 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -755,7 +755,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) { - // ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -832,7 +832,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); - const auto st = st_idx.find(obj_id); + const auto st = st_idx.find(object_id); if (st == st_idx.end()) { std::string tx_str = ""; @@ -1052,6 +1052,10 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { return; } + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, op_id)) { + return; + } + const chain::global_property_object &gpo = database.get_global_properties(); const auto &active_sons = gpo.active_sons.at(sidechain); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index a3be8649..bb6a9821 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -205,6 +205,7 @@ sidechain_net_handler_ethereum::~sidechain_net_handler_ethereum() { } bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) { + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -263,7 +264,7 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) const std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); - const auto st = st_idx.find(obj_id); + const auto st = st_idx.find(object_id); if (st == st_idx.end()) { std::string tx_str = ""; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 6ad95850..6a54b7f5 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -180,7 +180,8 @@ sidechain_net_handler_hive::~sidechain_net_handler_hive() { } bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { - //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); + + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -238,7 +239,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { std::string op_tx_str = op_obj_idx_1.get().transaction; const auto &st_idx = database.get_index_type().indices().get(); - const auto st = st_idx.find(obj_id); + const auto st = st_idx.find(object_id); if (st == st_idx.end()) { std::string tx_str = ""; @@ -499,6 +500,10 @@ void sidechain_net_handler_hive::process_primary_wallet() { return; } + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, op_id)) { + return; + } + const chain::global_property_object &gpo = database.get_global_properties(); const auto &active_sons = gpo.active_sons.at(sidechain); @@ -577,7 +582,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { stc_op.object_id = op_id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - for (const auto &signer : gpo.active_sons.at(sidechain)) { + for (const auto &signer : signers) { son_info si; si.son_id = signer.son_id; si.weight = signer.weight; @@ -639,6 +644,11 @@ void sidechain_net_handler_hive::process_sidechain_addresses() { } bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object &swdo) { + + if (proposal_exists(chain::operation::tag::value, swdo.id)) { + return false; + } + const chain::global_property_object &gpo = database.get_global_properties(); price asset_price; @@ -685,6 +695,11 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object } bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_object &swwo) { + + if (proposal_exists(chain::operation::tag::value, swwo.id)) { + return false; + } + const chain::global_property_object &gpo = database.get_global_properties(); //===== From bfb961c7be078abbf61c7dff047f5b55dfaa9ee4 Mon Sep 17 00:00:00 2001 From: Milo M Date: Fri, 24 Feb 2023 09:36:10 +0000 Subject: [PATCH 092/106] automated test for nft_lottery --- tests/tests/nft_lottery_tests.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/tests/nft_lottery_tests.cpp b/tests/tests/nft_lottery_tests.cpp index 02d8bdc1..e0ecf5bb 100644 --- a/tests/tests/nft_lottery_tests.cpp +++ b/tests/tests/nft_lottery_tests.cpp @@ -193,6 +193,34 @@ BOOST_AUTO_TEST_CASE(tickets_purchase_fail_test) } } +BOOST_AUTO_TEST_CASE(tickets_purchase_overflow) +{ + try + { + nft_metadata_id_type test_nft_md_id = db.get_index().get_next_id(); + INVOKE(create_lottery_nft_md_test); + auto &test_nft_md_obj = test_nft_md_id(db); + + nft_lottery_token_purchase_operation tpo; + tpo.fee = asset(); + tpo.buyer = account_id_type(); + tpo.lottery_id = test_nft_md_obj.id; + tpo.tickets_to_buy = 9223372036854775800; // Large number so that the overall amount overflows + trx.operations.push_back(tpo); + BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::overflow_exception); + trx.operations.clear(); + + tpo.tickets_to_buy = -2; // Negative value should also be rejected + trx.operations.push_back(tpo); + BOOST_REQUIRE_THROW(PUSH_TX(db, trx, ~0), fc::exception); + } + catch (fc::exception &e) + { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE(lottery_end_by_stage_test) { try From 79974280c0a627e82fd6d07b55ed4b01ac533f1f Mon Sep 17 00:00:00 2001 From: timur <12267899-timur.5@users.noreply.gitlab.com> Date: Wed, 1 Mar 2023 06:01:52 +0000 Subject: [PATCH 093/106] SON connection pool --- .../peerplays_sidechain/common/rpc_client.cpp | 163 ++++++++++++++++-- .../peerplays_sidechain/common/rpc_client.hpp | 50 +++--- .../sidechain_net_handler.hpp | 6 +- .../sidechain_net_handler_bitcoin.hpp | 9 +- .../sidechain_net_handler_ethereum.hpp | 12 +- .../sidechain_net_handler_hive.hpp | 9 +- .../peerplays_sidechain_plugin.cpp | 10 +- .../sidechain_net_handler.cpp | 3 +- .../sidechain_net_handler_bitcoin.cpp | 51 ++++-- .../sidechain_net_handler_ethereum.cpp | 34 +++- .../sidechain_net_handler_hive.cpp | 37 +++- .../sidechain_net_handler_peerplays.cpp | 3 +- 12 files changed, 301 insertions(+), 86 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index ac526715..be050544 100644 --- a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp @@ -18,10 +18,40 @@ namespace graphene { namespace peerplays_sidechain { -rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : - url(_url), - user(_user), - password(_password), +struct rpc_reply { + uint16_t status; + std::string body; +}; + +class rpc_connection { +public: + rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls); + + std::string send_post_request(std::string method, std::string params, bool show_log); + std::string get_url() const; + +protected: + rpc_credentials credentials; + bool debug_rpc_calls; + + std::string protocol; + std::string host; + std::string port; + std::string target; + std::string authorization; + + uint32_t request_id; + +private: + rpc_reply send_post_request(std::string body, bool show_log); + + boost::beast::net::io_context ioc; + boost::beast::net::ip::tcp::resolver resolver; + boost::asio::ip::basic_resolver_results results; +}; + +rpc_connection::rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls) : + credentials(_credentials), debug_rpc_calls(_debug_rpc_calls), request_id(0), resolver(ioc) { @@ -31,7 +61,7 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor boost::xpressive::smatch sm; - if (boost::xpressive::regex_search(url, sm, sr)) { + if (boost::xpressive::regex_search(credentials.url, sm, sr)) { protocol = sm["Protocol"]; if (protocol.empty()) { protocol = "http"; @@ -52,15 +82,19 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor target = "/"; } - authorization = "Basic " + base64_encode(user + ":" + password); + authorization = "Basic " + base64_encode(credentials.user + ":" + credentials.password); results = resolver.resolve(host, port); } else { - elog("Invalid URL: ${url}", ("url", url)); + elog("Invalid URL: ${url}", ("url", credentials.url)); } } +std::string rpc_connection::get_url() const { + return credentials.url; +} + std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx) { if (reply_str.empty()) { wlog("RPC call ${function}, empty reply string", ("function", __FUNCTION__)); @@ -125,7 +159,7 @@ std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::st return ""; } -std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) { +std::string rpc_connection::send_post_request(std::string method, std::string params, bool show_log) { std::stringstream body; request_id = request_id + 1; @@ -164,7 +198,7 @@ std::string rpc_client::send_post_request(std::string method, std::string params return ""; } -rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { +rpc_reply rpc_connection::send_post_request(std::string body, bool show_log) { // These object is used as a context for ssl connection boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client); @@ -239,7 +273,7 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { reply.body = rbody; if (show_log) { - ilog("### Request URL: ${url}", ("url", url)); + ilog("### Request URL: ${url}", ("url", credentials.url)); ilog("### Request: ${body}", ("body", body)); ilog("### Response: ${rbody}", ("rbody", rbody)); } @@ -247,4 +281,113 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { return reply; } +rpc_client::rpc_client(sidechain_type _sidechain, const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection) : + sidechain(_sidechain), + debug_rpc_calls(_debug_rpc_calls), + simulate_connection_reselection(_simulate_connection_reselection) { + FC_ASSERT(_credentials.size()); + for (size_t i = 0; i < _credentials.size(); i++) + connections.push_back(new rpc_connection(_credentials[i], _debug_rpc_calls)); + n_active_conn = 0; + if (connections.size() > 1) + schedule_connection_selection(); +} + +void rpc_client::schedule_connection_selection() { + fc::time_point now = fc::time_point::now(); + static const int64_t time_to_next_conn_selection = 10 * 1000 * 1000; // 10 sec + fc::time_point next_wakeup = now + fc::microseconds(time_to_next_conn_selection); + connection_selection_task = fc::schedule([this] { + select_connection(); + }, + next_wakeup, "SON RPC connection selection"); +} + +void rpc_client::select_connection() { + FC_ASSERT(connections.size() > 1); + + const std::lock_guard lock(conn_mutex); + + static const int t_limit = 5 * 1000 * 1000, // 5 sec + quality_diff_threshold = 10 * 1000; // 10 ms + + int best_n = -1; + int best_quality = -1; + + std::vector head_block_numbers; + head_block_numbers.resize(connections.size()); + + std::vector qualities; + qualities.resize(connections.size()); + + for (size_t n = 0; n < connections.size(); n++) { + rpc_connection &conn = *connections[n]; + int quality = 0; + head_block_numbers[n] = std::numeric_limits::max(); + + // ping n'th node + if (debug_rpc_calls) + ilog("### Ping ${sidechain} node #${n}, ${url}", ("sidechain", fc::reflector::to_string(sidechain))("n", n)("url", conn.get_url())); + fc::time_point t_sent = fc::time_point::now(); + uint64_t head_block_number = ping(conn); + fc::time_point t_received = fc::time_point::now(); + int t = (t_received - t_sent).count(); + + // evaluate n'th node reply quality and switch to it if it's better + if (head_block_number != std::numeric_limits::max()) { + if (simulate_connection_reselection) + t += rand() % 10; + FC_ASSERT(t != -1); + head_block_numbers[n] = head_block_number; + if (t < t_limit) + quality = t_limit - t; // the less time, the higher quality + + // look for the best quality + if (quality > best_quality) { + best_n = n; + best_quality = quality; + } + } + qualities[n] = quality; + } + + FC_ASSERT(best_n != -1 && best_quality != -1); + if (best_n != n_active_conn) { // if the best client is not the current one, ... + uint64_t active_head_block_number = head_block_numbers[n_active_conn]; + if ((active_head_block_number == std::numeric_limits::max() // ...and the current one has no known head block... + || head_block_numbers[best_n] >= active_head_block_number) // ...or the best client's head is more recent than the current, ... + && best_quality > qualities[n_active_conn] + quality_diff_threshold) { // ...and the new client's quality exceeds current more than by threshold + n_active_conn = best_n; // ...then select new one + if (debug_rpc_calls) + ilog("### Reselected ${sidechain} node to #${n}, ${url}", ("sidechain", fc::reflector::to_string(sidechain))("n", n_active_conn)("url", connections[n_active_conn]->get_url())); + } + } + + schedule_connection_selection(); +} + +rpc_connection &rpc_client::get_active_connection() const { + return *connections[n_active_conn]; +} + +std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) { + const std::lock_guard lock(conn_mutex); + return send_post_request(get_active_connection(), method, params, show_log); +} + +std::string rpc_client::send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log) { + return conn.send_post_request(method, params, show_log); +} + +rpc_client::~rpc_client() { + try { + if (connection_selection_task.valid()) + connection_selection_task.cancel_and_wait(__FUNCTION__); + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp index eb8eac0c..bdec60d1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp @@ -3,44 +3,52 @@ #include #include +#include +#include + #include #include +#include + namespace graphene { namespace peerplays_sidechain { -struct rpc_reply { - uint16_t status; - std::string body; +class rpc_connection; + +struct rpc_credentials { + std::string url; + std::string user; + std::string password; }; class rpc_client { public: - rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); + const sidechain_type sidechain; + + rpc_client(sidechain_type _sidechain, const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection); + ~rpc_client(); protected: - std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx); - std::string retrieve_value_from_reply(std::string reply_str, std::string value_path); + bool debug_rpc_calls; + bool simulate_connection_reselection; std::string send_post_request(std::string method, std::string params, bool show_log); - std::string url; - std::string user; - std::string password; - bool debug_rpc_calls; + static std::string send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log); - std::string protocol; - std::string host; - std::string port; - std::string target; - std::string authorization; - - uint32_t request_id; + static std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx); + static std::string retrieve_value_from_reply(std::string reply_str, std::string value_path); private: - rpc_reply send_post_request(std::string body, bool show_log); + std::vector connections; + int n_active_conn; + fc::future connection_selection_task; + std::mutex conn_mutex; - boost::beast::net::io_context ioc; - boost::beast::net::ip::tcp::resolver resolver; - boost::asio::ip::basic_resolver_results results; + rpc_connection &get_active_connection() const; + + void select_connection(); + void schedule_connection_selection(); + virtual uint64_t ping(rpc_connection &conn) const = 0; }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index 836a1f05..85dbc1f6 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -16,8 +16,10 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_handler { +protected: + sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + public: - sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); virtual ~sidechain_net_handler(); sidechain_type get_sidechain() const; @@ -54,9 +56,9 @@ public: virtual optional estimate_withdrawal_transaction_fee() const = 0; protected: + const sidechain_type sidechain; peerplays_sidechain_plugin &plugin; graphene::chain::database &database; - sidechain_type sidechain; bool debug_rpc_calls; bool use_bitcoind_client; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index bfb93805..96e8ec9c 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -98,7 +98,7 @@ protected: class bitcoin_rpc_client : public bitcoin_client_base, public rpc_client { public: public: - bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); + bitcoin_rpc_client(const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection); uint64_t estimatesmartfee(uint16_t conf_target = 1); std::vector getblock(const block_data &block, int32_t verbosity = 2); @@ -113,6 +113,8 @@ public: std::string walletlock(); bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); + virtual uint64_t ping(rpc_connection &conn) const override; + private: std::string ip; std::string user; @@ -213,14 +215,11 @@ public: virtual optional estimate_withdrawal_transaction_fee() const override; private: - std::string bitcoin_node_ip; + std::vector _rpc_credentials; std::string libbitcoin_server_ip; uint32_t libbitcoin_block_zmq_port; uint32_t libbitcoin_trx_zmq_port; uint32_t bitcoin_node_zmq_port; - uint32_t rpc_port; - std::string rpc_user; - std::string rpc_password; std::string wallet_name; std::string wallet_password; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index 0cd22f96..fcabccf5 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -14,7 +14,7 @@ namespace graphene { namespace peerplays_sidechain { class ethereum_rpc_client : public rpc_client { public: - ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + ethereum_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection); std::string eth_blockNumber(); std::string eth_get_block_by_number(std::string block_number, bool full_block); @@ -36,6 +36,8 @@ public: std::string eth_send_raw_transaction(const std::string ¶ms); std::string eth_get_transaction_receipt(const std::string ¶ms); std::string eth_get_transaction_by_hash(const std::string ¶ms); + + virtual uint64_t ping(rpc_connection &conn) const override; }; class sidechain_net_handler_ethereum : public sidechain_net_handler { @@ -54,13 +56,9 @@ public: virtual optional estimate_withdrawal_transaction_fee() const override; private: - using bimap_type = boost::bimap; - -private: - std::string rpc_url; - std::string rpc_user; - std::string rpc_password; + std::vector _rpc_credentials; std::string wallet_contract_address; + using bimap_type = boost::bimap; bimap_type erc20_addresses; ethereum_rpc_client *rpc_client; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp index 440a2520..baa95bbf 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp @@ -13,7 +13,7 @@ namespace graphene { namespace peerplays_sidechain { class hive_rpc_client : public rpc_client { public: - hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + hive_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection); std::string account_history_api_get_transaction(std::string transaction_id); std::string block_api_get_block(uint32_t block_number); @@ -30,6 +30,8 @@ public: std::string get_head_block_time(); std::string get_is_test_net(); std::string get_last_irreversible_block_num(); + + virtual uint64_t ping(rpc_connection &conn) const override; }; class sidechain_net_handler_hive : public sidechain_net_handler { @@ -48,9 +50,8 @@ public: virtual optional estimate_withdrawal_transaction_fee() const override; private: - std::string rpc_url; - std::string rpc_user; - std::string rpc_password; + std::vector _rpc_credentials; + std::string wallet_account_name; hive_rpc_client *rpc_client; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index e4a2b8ea..6f6747db 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -175,13 +175,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("sidechain-retry-threshold", bpo::value()->default_value(150), "Sidechain retry throttling threshold"); cli.add_options()("debug-rpc-calls", bpo::value()->default_value(false), "Outputs RPC calls to console"); + cli.add_options()("simulate-rpc-connection-reselection", bpo::value()->default_value(false), "Simulate RPC connection reselection by altering their response times by a random value"); cli.add_options()("bitcoin-sidechain-enabled", bpo::value()->default_value(false), "Bitcoin sidechain handler enabled"); + cli.add_options()("bitcoin-node-ip", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("use-bitcoind-client", bpo::value()->default_value(false), "Use bitcoind client instead of libbitcoin client"); cli.add_options()("libbitcoin-server-ip", bpo::value()->default_value("127.0.0.1"), "Libbitcoin server IP address"); cli.add_options()("libbitcoin-server-block-zmq-port", bpo::value()->default_value(9093), "Block ZMQ port of libbitcoin server"); cli.add_options()("libbitcoin-server-trx-zmq-port", bpo::value()->default_value(9094), "Trx ZMQ port of libbitcoin server"); - cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); @@ -192,7 +193,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); cli.add_options()("ethereum-sidechain-enabled", bpo::value()->default_value(false), "Ethereum sidechain handler enabled"); - cli.add_options()("ethereum-node-rpc-url", bpo::value()->default_value("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]"); + cli.add_options()("ethereum-node-rpc-url", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]"); cli.add_options()("ethereum-node-rpc-user", bpo::value(), "Ethereum RPC user"); cli.add_options()("ethereum-node-rpc-password", bpo::value(), "Ethereum RPC password"); cli.add_options()("ethereum-wallet-contract-address", bpo::value(), "Ethereum wallet contract address"); @@ -202,7 +203,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); cli.add_options()("hive-sidechain-enabled", bpo::value()->default_value(false), "Hive sidechain handler enabled"); - cli.add_options()("hive-node-rpc-url", bpo::value()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); + cli.add_options()("hive-node-rpc-url", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-user", bpo::value(), "Hive node RPC user"); cli.add_options()("hive-node-rpc-password", bpo::value(), "Hive node RPC password"); cli.add_options()("hive-wallet-account-name", bpo::value(), "Hive wallet account name"); @@ -291,6 +292,9 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt if (sidechain_enabled_peerplays && !config_ready_peerplays) { wlog("Haven't set up Peerplays sidechain parameters"); } + + if (options.at("simulate-rpc-connection-reselection").as()) + ilog("### RPC connection reselection will be simulated"); } void peerplays_sidechain_plugin_impl::plugin_startup() { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index a4fe1252..f762da49 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -9,7 +9,8 @@ namespace graphene { namespace peerplays_sidechain { -sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : +sidechain_net_handler::sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + sidechain(_sidechain), plugin(_plugin), database(_plugin.database()) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index c528d8a4..76e23ab0 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -25,8 +25,8 @@ namespace graphene { namespace peerplays_sidechain { // ============================================================================= -bitcoin_rpc_client::bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : - rpc_client(_url, _user, _password, _debug_rpc_calls) { +bitcoin_rpc_client::bitcoin_rpc_client(const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection) : + rpc_client(sidechain_type::bitcoin, _credentials, _debug_rpc_calls, _simulate_connection_reselection) { } uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { @@ -498,6 +498,13 @@ std::string bitcoin_libbitcoin_client::sendrawtransaction(const std::string &tx_ return res; } +uint64_t bitcoin_rpc_client::ping(rpc_connection &conn) const { + std::string str = send_post_request(conn, "getblockcount", "[]", debug_rpc_calls); + if (str.length() > 0) + return std::stoll(str); + return std::numeric_limits::max(); +} + // ============================================================================= zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq_block_port, uint32_t _zmq_trx_port) : @@ -655,13 +662,19 @@ void zmq_listener_libbitcoin::handle_block() { // ============================================================================= sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : - sidechain_net_handler(_plugin, options) { - sidechain = sidechain_type::bitcoin; + sidechain_net_handler(sidechain_type::bitcoin, _plugin, options) { if (options.count("debug-rpc-calls")) { debug_rpc_calls = options.at("debug-rpc-calls").as(); } + bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as(); + std::vector ips = options.at("bitcoin-node-ip").as>(); + bitcoin_node_zmq_port = options.at("bitcoin-node-zmq-port").as(); + uint32_t rpc_port = options.at("bitcoin-node-rpc-port").as(); + std::string rpc_user = options.at("bitcoin-node-rpc-user").as(); + std::string rpc_password = options.at("bitcoin-node-rpc-password").as(); + if (options.count("use-bitcoind-client")) { use_bitcoind_client = options.at("use-bitcoind-client").as(); } @@ -670,11 +683,6 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain libbitcoin_block_zmq_port = options.at("libbitcoin-server-block-zmq-port").as(); libbitcoin_trx_zmq_port = options.at("libbitcoin-server-trx-zmq-port").as(); - bitcoin_node_ip = options.at("bitcoin-node-ip").as(); - bitcoin_node_zmq_port = options.at("bitcoin-node-zmq-port").as(); - rpc_port = options.at("bitcoin-node-rpc-port").as(); - rpc_user = options.at("bitcoin-node-rpc-user").as(); - rpc_password = options.at("bitcoin-node-rpc-password").as(); wallet_name = ""; if (options.count("bitcoin-wallet-name")) { wallet_name = options.at("bitcoin-wallet-name").as(); @@ -697,17 +705,27 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } if (use_bitcoind_client) { - std::string url = bitcoin_node_ip + ":" + std::to_string(rpc_port); - if (!wallet_name.empty()) { - url = url + "/wallet/" + wallet_name; + + for (size_t i = 0; i < ips.size(); i++) { + std::string ip = ips[i]; + std::string url = ip + ":" + std::to_string(rpc_port); + if (!wallet_name.empty()) { + url = url + "/wallet/" + wallet_name; + } + rpc_credentials creds; + creds.url = url; + creds.user = rpc_user; + creds.password = rpc_password; + _rpc_credentials.push_back(creds); } - bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); + FC_ASSERT(!_rpc_credentials.empty()); + + bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection)); if (!wallet_name.empty()) { bitcoin_client->loadwallet(wallet_name); } - listener = std::unique_ptr(new zmq_listener(bitcoin_node_ip, bitcoin_node_zmq_port)); - + listener = std::unique_ptr(new zmq_listener(ips[0], bitcoin_node_zmq_port)); } else { bitcoin_client = std::unique_ptr(new bitcoin_libbitcoin_client(libbitcoin_server_ip)); @@ -727,7 +745,6 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain bitcoin_client->getnetworkinfo(); - listener->start(); listener->block_event_received.connect([this](const block_data &block_event_data) { std::thread(&sidechain_net_handler_bitcoin::block_handle_event, this, block_event_data).detach(); }); @@ -736,6 +753,8 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain std::thread(&sidechain_net_handler_bitcoin::trx_handle_event, this, trx_event_data).detach(); }); + listener->start(); + database.changed_objects.connect([this](const vector &ids, const flat_set &accounts) { on_changed_objects(ids, accounts); }); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index bb6a9821..c4db3266 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -25,8 +25,8 @@ namespace graphene { namespace peerplays_sidechain { -ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : - rpc_client(url, user_name, password, debug_rpc_calls) { +ethereum_rpc_client::ethereum_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection) : + rpc_client(sidechain_type::ethereum, credentials, debug_rpc_calls, simulate_connection_reselection) { } std::string ethereum_rpc_client::eth_blockNumber() { @@ -126,20 +126,29 @@ std::string ethereum_rpc_client::eth_get_transaction_by_hash(const std::string & return send_post_request("eth_getTransactionByHash", "[\"" + params + "\"]", debug_rpc_calls); } +uint64_t ethereum_rpc_client::ping(rpc_connection &conn) const { + std::string reply = send_post_request(conn, "eth_blockNumber", "", debug_rpc_calls); + if (!reply.empty()) + return ethereum::from_hex(retrieve_value_from_reply(reply, "")); + return std::numeric_limits::max(); +} + sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : - sidechain_net_handler(_plugin, options) { - sidechain = sidechain_type::ethereum; + sidechain_net_handler(sidechain_type::ethereum, _plugin, options) { if (options.count("debug-rpc-calls")) { debug_rpc_calls = options.at("debug-rpc-calls").as(); } + bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as(); - rpc_url = options.at("ethereum-node-rpc-url").as(); + std::vector rpc_urls = options.at("ethereum-node-rpc-url").as>(); + std::string rpc_user; if (options.count("ethereum-node-rpc-user")) { rpc_user = options.at("ethereum-node-rpc-user").as(); } else { rpc_user = ""; } + std::string rpc_password; if (options.count("ethereum-node-rpc-password")) { rpc_password = options.at("ethereum-node-rpc-password").as(); } else { @@ -175,18 +184,27 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha } } - rpc_client = new ethereum_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); + for (size_t i = 0; i < rpc_urls.size(); i++) { + rpc_credentials creds; + creds.url = rpc_urls[i]; + creds.user = rpc_user; + creds.password = rpc_password; + _rpc_credentials.push_back(creds); + } + FC_ASSERT(!_rpc_credentials.empty()); + + rpc_client = new ethereum_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection); const std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { - elog("No Ethereum node running at ${url}", ("url", rpc_url)); + elog("No Ethereum node running at ${url}", ("url", _rpc_credentials[0].url)); FC_ASSERT(false); } chain_id = std::stoll(chain_id_str); const std::string network_id_str = rpc_client->get_network_id(); if (network_id_str.empty()) { - elog("No Ethereum node running at ${url}", ("url", rpc_url)); + elog("No Ethereum node running at ${url}", ("url", _rpc_credentials[0].url)); FC_ASSERT(false); } network_id = std::stoll(network_id_str); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 6a54b7f5..eb2f332f 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -30,8 +30,8 @@ namespace graphene { namespace peerplays_sidechain { -hive_rpc_client::hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : - rpc_client(url, user_name, password, debug_rpc_calls) { +hive_rpc_client::hive_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection) : + rpc_client(sidechain_type::hive, credentials, debug_rpc_calls, simulate_connection_reselection) { } std::string hive_rpc_client::account_history_api_get_transaction(std::string transaction_id) { @@ -112,20 +112,34 @@ std::string hive_rpc_client::get_last_irreversible_block_num() { return retrieve_value_from_reply(reply_str, "last_irreversible_block_num"); } +uint64_t hive_rpc_client::ping(rpc_connection &conn) const { + const std::string reply = send_post_request(conn, "database_api.get_dynamic_global_properties", "", debug_rpc_calls); + if (!reply.empty()) { + std::stringstream ss(reply); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.count("result")) + return json.get("result.head_block_number"); + } + return std::numeric_limits::max(); +} + sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : - sidechain_net_handler(_plugin, options) { - sidechain = sidechain_type::hive; + sidechain_net_handler(sidechain_type::hive, _plugin, options) { if (options.count("debug-rpc-calls")) { debug_rpc_calls = options.at("debug-rpc-calls").as(); } + bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as(); - rpc_url = options.at("hive-node-rpc-url").as(); + std::vector rpc_urls = options.at("hive-node-rpc-url").as>(); + std::string rpc_user; if (options.count("hive-rpc-user")) { rpc_user = options.at("hive-rpc-user").as(); } else { rpc_user = ""; } + std::string rpc_password; if (options.count("hive-rpc-password")) { rpc_password = options.at("hive-rpc-password").as(); } else { @@ -146,11 +160,20 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi } } - rpc_client = new hive_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); + for (size_t i = 0; i < rpc_urls.size(); i++) { + rpc_credentials creds; + creds.url = rpc_urls[i]; + creds.user = rpc_user; + creds.password = rpc_password; + _rpc_credentials.push_back(creds); + } + FC_ASSERT(!_rpc_credentials.empty()); + + rpc_client = new hive_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection); const std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { - elog("No Hive node running at ${url}", ("url", rpc_url)); + elog("No Hive node running at ${url}", ("url", _rpc_credentials[0].url)); FC_ASSERT(false); } chain_id = chain_id_type(chain_id_str); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index 0f6d06eb..edf92e80 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -23,8 +23,7 @@ namespace graphene { namespace peerplays_sidechain { sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : - sidechain_net_handler(_plugin, options) { - sidechain = sidechain_type::peerplays; + sidechain_net_handler(sidechain_type::peerplays, _plugin, options) { //const auto &assets_by_symbol = database.get_index_type().indices().get(); //const auto get_asset_id = [&assets_by_symbol](const string &symbol) { // auto asset_itr = assets_by_symbol.find(symbol); From 54ff842db10f667f91e81fa17b1e66f44e6d8b6c Mon Sep 17 00:00:00 2001 From: Milo M Date: Mon, 6 Mar 2023 15:50:17 +0000 Subject: [PATCH 094/106] num_son no overwriting --- libraries/chain/account_evaluator.cpp | 118 ++++++++++++++++-- .../graphene/chain/protocol/account.hpp | 15 ++- tests/cli/son.cpp | 13 ++ 3 files changed, 132 insertions(+), 14 deletions(-) diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 4ecb3307..884bba2d 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -53,7 +53,54 @@ void verify_authority_accounts( const database& db, const authority& a ) } } -void verify_account_votes( const database& db, const account_options& options ) +// Overwrites the num_son values from the origin to the destination for those sidechains which are found in the origin. +// Keeps the values of num_son for the sidechains which are found in the destination, but not in the origin. +// Returns false if an error is detected. +bool merge_num_sons( flat_map& destination, + const flat_map& origin, + fc::optional head_block_time = {}) +{ + const auto active_sidechains = head_block_time.valid() ? active_sidechain_types(*head_block_time) : all_sidechain_types; + bool success = true; + + for (const auto &ns : origin) + { + destination[ns.first] = ns.second; + if (active_sidechains.find(ns.first) == active_sidechains.end()) + { + success = false; + } + } + + return success; +} + +flat_map count_SON_votes_per_sidechain( const flat_set& votes ) +{ + flat_map SON_votes_per_sidechain = account_options::ext::empty_num_son(); + + for (const auto &vote : votes) + { + switch (vote.type()) + { + case vote_id_type::son_bitcoin: + SON_votes_per_sidechain[sidechain_type::bitcoin]++; + break; + case vote_id_type::son_hive: + SON_votes_per_sidechain[sidechain_type::hive]++; + break; + case vote_id_type::son_ethereum: + SON_votes_per_sidechain[sidechain_type::ethereum]++; + break; + default: + break; + } + } + + return SON_votes_per_sidechain; +} + +void verify_account_votes( const database& db, const account_options& options, fc::optional account = {} ) { // ensure account's votes satisfy requirements // NB only the part of vote checking that requires chain state is here, @@ -69,14 +116,40 @@ void verify_account_votes( const database& db, const account_options& options ) FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" ); + + flat_map merged_num_sons = account_options::ext::empty_num_son(); + + // Merge with existing account if exists + if ( account.valid() && account->options.extensions.value.num_son.valid()) + { + merge_num_sons( merged_num_sons, *account->options.extensions.value.num_son, db.head_block_time() ); + } + + // Apply update operation on top if ( options.extensions.value.num_son.valid() ) { - for(const auto& num_sons : *options.extensions.value.num_son) - { - FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count, - "Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) ); - } + merge_num_sons( merged_num_sons, *options.extensions.value.num_son, db.head_block_time() ); } + + for(const auto& num_sons : merged_num_sons) + { + FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count, + "Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) ); + } + + // Count the votes for SONs and confirm that the account did not vote for less SONs than num_son + flat_map SON_votes_per_sidechain = count_SON_votes_per_sidechain(options.votes); + + for (const auto& number_of_votes : SON_votes_per_sidechain) + { + // Number of votes of account_options are also checked in account_options::do_evaluate, + // but there we are checking the value before merging num_sons, so the values should be checked again + const auto sidechain = number_of_votes.first; + FC_ASSERT( number_of_votes.second >= merged_num_sons[sidechain], + "Voted for less sons than specified in num_son (votes ${v} < num_son ${ns}) for sidechain ${s}", + ("v", number_of_votes.second) ("ns", merged_num_sons[sidechain]) ("s", sidechain) ); + } + FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." ); uint32_t max_vote_id = gpo.next_available_vote_id; @@ -191,9 +264,10 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio obj.active = o.active; obj.options = o.options; - if (!obj.options.extensions.value.num_son.valid()) + obj.options.extensions.value.num_son = account_options::ext::empty_num_son(); + if ( o.options.extensions.value.num_son.valid() ) { - obj.options.extensions.value = account_options::ext(); + merge_num_sons( *obj.options.extensions.value.num_son, *o.options.extensions.value.num_son ); } obj.statistics = d.create([&obj](account_statistics_object& s){ @@ -295,7 +369,7 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio acnt = &o.account(d); if( o.new_options.valid() ) - verify_account_votes( d, *o.new_options ); + verify_account_votes( d, *o.new_options, *acnt ); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } @@ -334,7 +408,31 @@ void_result account_update_evaluator::do_apply( const account_update_operation& a.active = *o.active; a.top_n_control_flags = 0; } - if( o.new_options ) a.options = *o.new_options; + + // New num_son structure initialized to 0 + flat_map new_num_son = account_options::ext::empty_num_son(); + + // If num_son of existing object is valid, we should merge the existing data + if ( a.options.extensions.value.num_son.valid() ) + { + merge_num_sons( new_num_son, *a.options.extensions.value.num_son ); + } + + // If num_son of the operation are valid, they should merge the existing data + if ( o.new_options ) + { + const auto new_options = *o.new_options; + + if ( new_options.extensions.value.num_son.valid() ) + { + merge_num_sons( new_num_son, *new_options.extensions.value.num_son ); + } + + a.options = *o.new_options; + } + + a.options.extensions.value.num_son = new_num_son; + if( o.extensions.value.owner_special_authority.valid() ) { a.owner_special_authority = *(o.extensions.value.owner_special_authority); diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index c8074885..c46caac5 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -36,21 +36,28 @@ namespace graphene { namespace chain { bool is_cheap_name( const string& n ); /// These are the fields which can be updated by the active authority. - struct account_options + struct account_options { struct ext { /// The number of active son members this account votes the blockchain should appoint /// Must not exceed the actual number of son members voted for in @ref votes - optional< flat_map > num_son = []{ + optional< flat_map > num_son; + + /// Returns and empty num_son map with all sidechains + static flat_map empty_num_son() + { flat_map num_son; - for(const auto& active_sidechain_type : all_sidechain_types){ + for(const auto& active_sidechain_type : all_sidechain_types) + { num_son[active_sidechain_type] = 0; } + return num_son; - }(); + } }; + /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- /// validated account activities. This field is here to prevent confusion if the active authority has zero or /// multiple keys in it. diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 4662c57c..e3758557 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -740,6 +740,19 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::ethereum, 0, true); BOOST_CHECK(generate_maintenance_block()); + // Vote for less SONs than num_son (2 votes, but num_son is 3) + accepted.clear(); + rejected.clear(); + accepted.push_back("son1account"); + accepted.push_back("son2account"); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 3, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 3, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 3, true), fc::exception); + generate_block(); + // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; From 7516126d016db97635cc13d27ad1ba77d9535fd0 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 16 May 2023 11:46:25 +0000 Subject: [PATCH 095/106] New set of functionality --- .gitlab-ci.yml | 59 +++++++++++++++++-- Dockerfile | 1 - .../chain/sidechain_address_object.hpp | 11 +++- .../peerplays_sidechain/ethereum/encoders.cpp | 5 +- .../sidechain_net_handler_ethereum.cpp | 14 +++-- libraries/wallet/wallet.cpp | 15 ++++- 6 files changed, 89 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 61afce6e..ecc5a72a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,6 +9,8 @@ stages: - build - test - dockerize + - python-test + - deploy build-mainnet: stage: build @@ -48,6 +50,7 @@ dockerize-mainnet: IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA before_script: - docker info + - docker builder prune -a -f - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - docker build --no-cache -t $IMAGE . @@ -56,8 +59,6 @@ dockerize-mainnet: - docker rmi $IMAGE tags: - builder - when: - manual timeout: 3h @@ -78,12 +79,29 @@ build-testnet: - build/libraries/ - build/programs/ - build/tests/ + rules: + - if: $CI_COMMIT_BRANCH == "master" + when: always tags: - builder - when: - manual - timeout: - 3h + +deploy-testnet: + stage: deploy + dependencies: + - build-testnet + script: + - sudo systemctl stop witness + - rm $WORK_DIR/peerplays/witness_node || true + - cp build/programs/witness_node/witness_node $WORK_DIR/peerplays/ + - sudo systemctl restart witness + rules: + - if: $CI_COMMIT_BRANCH == "master" + when: always + environment: + name: devnet + url: $DEVNET_URL + tags: + - devnet test-testnet: stage: test @@ -119,3 +137,32 @@ dockerize-testnet: manual timeout: 3h + +test-e2e: + stage: python-test + variables: + IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA + before_script: + - docker info + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + script: + - git clone https://gitlab.com/PBSA/tools-libs/peerplays-utils.git + - cd peerplays-utils/peerplays-qa-environment + - git checkout origin/feature/python-e2e-tests-for-CI + - cd e2e-tests/ + - python3 -m venv venv + - source venv/bin/activate + - pip3 install -r requirements.txt + - python3 main.py --stop + - docker ps -a + - docker pull $IMAGE + - docker tag $IMAGE peerplays-base:latest + - docker image ls -a + - python3 main.py --start all + - docker ps -a + - python3 -m pytest test_btc_init_state.py test_hive_inital_state.py test_pp_inital_state.py + - python3 main.py --stop + - deactivate + - docker ps -a + tags: + - python-tests diff --git a/Dockerfile b/Dockerfile index 7a229aa7..8e2c76f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -190,7 +190,6 @@ ADD . peerplays RUN \ cd peerplays && \ git submodule update --init --recursive && \ - git symbolic-ref --short HEAD && \ git log --oneline -n 5 && \ mkdir build && \ cd build && \ diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp index 73be44d5..b808a23c 100644 --- a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -36,6 +36,15 @@ namespace graphene { namespace chain { deposit_address(""), withdraw_public_key(""), withdraw_address("") {} + + inline string get_deposit_address() const { + if(sidechain_type::ethereum != sidechain) + return deposit_address; + + auto deposit_address_lower = deposit_address; + std::transform(deposit_address_lower.begin(), deposit_address_lower.end(), deposit_address_lower.begin(), ::tolower); + return deposit_address_lower; + } }; struct by_account; @@ -76,7 +85,7 @@ namespace graphene { namespace chain { ordered_non_unique< tag, composite_key, - member, + const_mem_fun, member > > diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp index 872a5720..19a373de 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -137,8 +137,9 @@ std::string rlp_encoder::encode_length(int len, int offset) { std::string rlp_encoder::hex2bytes(const std::string &s) { std::string dest; - dest.resize(s.size() / 2); - hex2bin(s.c_str(), &dest[0]); + const auto s_final = s.size() % 2 == 0 ? s : "0" + s; + dest.resize(s_final.size() / 2); + hex2bin(s_final.c_str(), &dest[0]); return dest; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index c4db3266..fb21a87c 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -684,13 +684,18 @@ std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sid const ethereum::signature_encoder encoder{function_signature}; #ifdef SEND_RAW_TRANSACTION + const auto data = encoder.encode(transactions); + const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; + ethereum::raw_transaction raw_tr; raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key)); raw_tr.gas_price = rpc_client->get_gas_price(); - raw_tr.gas_limit = rpc_client->get_gas_limit(); + raw_tr.gas_limit = rpc_client->get_estimate_gas(params); + if (raw_tr.gas_limit.empty()) + raw_tr.gas_limit = rpc_client->get_gas_limit(); raw_tr.to = wallet_contract_address; raw_tr.value = ""; - raw_tr.data = encoder.encode(transactions); + raw_tr.data = data; raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id)); const auto sign_tr = raw_tr.sign(get_private_key(public_key)); @@ -804,7 +809,7 @@ optional sidechain_net_handler_ethereum::estimate_withdrawal_transaction_ } const auto &public_key = son->sidechain_public_keys.at(sidechain); - const auto data = ethereum::withdrawal_encoder::encode(public_key, boost::multiprecision::uint256_t{1} * boost::multiprecision::uint256_t{10000000000}, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); + const auto data = ethereum::withdrawal_encoder::encode(public_key, boost::multiprecision::uint256_t{1} * boost::multiprecision::uint256_t{10000000000}, "0"); const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; const auto estimate_gas = ethereum::from_hex(rpc_client->get_estimate_gas(params)); @@ -909,8 +914,9 @@ void sidechain_net_handler_ethereum::handle_event(const std::string &block_numbe const boost::property_tree::ptree tx = tx_child.second; tx_idx = tx_idx + 1; - const std::string from = tx.get("from"); const std::string to = tx.get("to"); + std::string from = tx.get("from"); + std::transform(from.begin(), from.end(), from.begin(), ::tolower); std::string cmp_to = to; std::transform(cmp_to.begin(), cmp_to.end(), cmp_to.begin(), ::toupper); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index e1363b0b..f01e2605 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2773,12 +2773,21 @@ public: FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); + bool update_vote_time = false; if (approve) { FC_ASSERT(son_obj->get_sidechain_vote_id(sidechain).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", sidechain)("son", *son_obj)); + account_id_type stake_account = get_account_id(voting_account); + const auto gpos_info = _remote_db->get_gpos_info(stake_account); + const auto vesting_subperiod = _remote_db->get_global_properties().parameters.gpos_subperiod(); + const auto gpos_start_time = fc::time_point_sec(_remote_db->get_global_properties().parameters.gpos_period_start()); + const auto subperiod_start_time = gpos_start_time.sec_since_epoch() + (gpos_info.current_subperiod - 1) * vesting_subperiod; + auto insert_result = voting_account_object.options.votes.insert(*son_obj->get_sidechain_vote_id(sidechain)); - if (!insert_result.second) - FC_THROW("Account ${account} has already voted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); + if (!insert_result.second && (gpos_info.last_voted_time.sec_since_epoch() >= subperiod_start_time)) + FC_THROW("Account ${account} was already voting for son ${son} in the current GPOS sub-period", ("account", voting_account)("son", son)); + else + update_vote_time = true; //Allow user to vote in each sub-period(Update voting time, which is reference in calculating VF) } else { @@ -2787,9 +2796,11 @@ public: if (!votes_removed) FC_THROW("Account ${account} has already unvoted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); } + account_update_operation account_update_op; account_update_op.account = voting_account_object.id; account_update_op.new_options = voting_account_object.options; + account_update_op.extensions.value.update_last_voting_time = update_vote_time; signed_transaction tx; tx.operations.push_back( account_update_op ); From f5c6a6310b00b4e0a14f9af95a2756c6cd951f29 Mon Sep 17 00:00:00 2001 From: Rily Dunlap Date: Wed, 17 May 2023 14:50:10 +0000 Subject: [PATCH 096/106] Updated build rules for mainnet and testnet --- .gitlab-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ecc5a72a..c443fe98 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,6 +29,8 @@ build-mainnet: - build/libraries/ - build/programs/ - build/tests/ + rules: + - if: $CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" tags: - builder @@ -80,8 +82,8 @@ build-testnet: - build/programs/ - build/tests/ rules: - - if: $CI_COMMIT_BRANCH == "master" - when: always + - if: $CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" + when: manual tags: - builder From 0b37a48b02951f949da04b838fb5a2c5e093b8cd Mon Sep 17 00:00:00 2001 From: Christopher Sanborn Date: Thu, 25 May 2023 13:23:05 -0400 Subject: [PATCH 097/106] Set Hard Fork dates for testnet and main net. --- libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf index 0eb8cc04..e6d8a9cb 100644 --- a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf +++ b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf @@ -1,7 +1,7 @@ #ifndef HARDFORK_SON_FOR_ETHEREUM_TIME #ifdef BUILD_PEERPLAYS_TESTNET -#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-01-24T00:00:00")) +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-06-05T12:00:00")) #else -#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-03-24T00:00:00")) +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-05T12:00:00")) #endif #endif From c34415b403871f1042fee7d093465d4945b3db12 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Fri, 9 Jun 2023 08:10:26 +0000 Subject: [PATCH 098/106] Fixes for public testnet --- .gitlab-ci.yml | 6 +- libraries/chain/db_block.cpp | 18 +-- libraries/chain/db_witness_schedule.cpp | 140 ++++++++---------- .../chain/include/graphene/chain/database.hpp | 4 +- libraries/chain/son_wallet_evaluator.cpp | 28 +++- 5 files changed, 94 insertions(+), 102 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c443fe98..f300e82b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,8 +29,6 @@ build-mainnet: - build/libraries/ - build/programs/ - build/tests/ - rules: - - if: $CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" tags: - builder @@ -81,9 +79,7 @@ build-testnet: - build/libraries/ - build/programs/ - build/tests/ - rules: - - if: $CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "beatrice" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" - when: manual + when: manual tags: - builder diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index adabcffe..79b24464 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -739,13 +739,11 @@ void database::_apply_block( const signed_block& next_block ) if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { update_witness_schedule(next_block); - bool need_to_update_son_schedule = false; - for(const auto& active_sons : global_props.active_sons){ - if(!active_sons.second.empty()) - need_to_update_son_schedule = true; - } - if(need_to_update_son_schedule) { - update_son_schedule(next_block); + + for(const auto& active_sons : global_props.active_sons) { + if(!active_sons.second.empty()) { + update_son_schedule(active_sons.first, next_block); + } } } @@ -783,15 +781,11 @@ void database::_apply_block( const signed_block& next_block ) if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { update_witness_schedule(); - bool need_update_son_schedule = false; for(const auto& active_sidechain_type : active_sidechain_types(dynamic_global_props.time)) { if(global_props.active_sons.at(active_sidechain_type).size() > 0) { - need_update_son_schedule = true; + update_son_schedule(active_sidechain_type); } } - if(need_update_son_schedule) { - update_son_schedule(); - } } if( !_node_property_object.debug_updates.empty() ) diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 12d4a6dd..d4874b29 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -200,44 +200,41 @@ void database::update_witness_schedule() } } -void database::update_son_schedule() +void database::update_son_schedule(sidechain_type type) { const global_property_object& gpo = get_global_properties(); - for(const auto& active_sidechain_type : active_sidechain_types(head_block_time())) + const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(type))); + if( gpo.active_sons.at(type).size() != 0 && + head_block_num() % gpo.active_sons.at(type).size() == 0) { - const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); - if( gpo.active_sons.at(active_sidechain_type).size() != 0 && - head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0) + modify( sidechain_sso, [&]( son_schedule_object& _sso ) { - modify( sidechain_sso, [&]( son_schedule_object& _sso ) + _sso.current_shuffled_sons.clear(); + _sso.current_shuffled_sons.reserve( gpo.active_sons.at(type).size() ); + + for ( const auto &w : gpo.active_sons.at(type) ) { + _sso.current_shuffled_sons.push_back(w.son_id); + } + + auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; + + for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i) { - _sso.current_shuffled_sons.clear(); - _sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() ); + /// High performance random generator + /// http://xorshift.di.unimi.it/ + uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL; + k ^= (k >> 12); + k ^= (k << 25); + k ^= (k >> 27); + k *= 2685821657736338717ULL; - for ( const auto &w : gpo.active_sons.at(active_sidechain_type) ) { - _sso.current_shuffled_sons.push_back(w.son_id); - } - - auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; - - for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i) - { - /// High performance random generator - /// http://xorshift.di.unimi.it/ - uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL; - k ^= (k >> 12); - k ^= (k << 25); - k ^= (k >> 27); - k *= 2685821657736338717ULL; - - uint32_t jmax = _sso.current_shuffled_sons.size() - i; - uint32_t j = i + k % jmax; - std::swap(_sso.current_shuffled_sons[i], - _sso.current_shuffled_sons[j]); - } - }); - } + uint32_t jmax = _sso.current_shuffled_sons.size() - i; + uint32_t j = i + k % jmax; + std::swap(_sso.current_shuffled_sons[i], + _sso.current_shuffled_sons[j]); + } + }); } } @@ -321,23 +318,15 @@ void database::update_witness_schedule(const signed_block& next_block) idump( ( double(total_time/1000000.0)/calls) ); } -void database::update_son_schedule(const signed_block& next_block) +void database::update_son_schedule(sidechain_type type, const signed_block& next_block) { auto start = fc::time_point::now(); #ifndef NDEBUG const son_schedule_object& sso = get(son_schedule_id_type()); #endif const global_property_object& gpo = get_global_properties(); - const flat_map schedule_needs_filled = [&gpo]() - { - flat_map schedule_needs_filled; - for(const auto& sidechain_active_sons : gpo.active_sons) - { - schedule_needs_filled[sidechain_active_sons.first] = sidechain_active_sons.second.size(); - } - return schedule_needs_filled; - }(); - uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); + const uint32_t schedule_needs_filled = gpo.active_sons.at(type).size(); + const uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); // We shouldn't be able to generate _pending_block with timestamp // in the past, and incoming blocks from the network with timestamp @@ -351,46 +340,43 @@ void database::update_son_schedule(const signed_block& next_block) assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); - for(const auto& active_sidechain_type : active_sidechain_types(head_block_time())) + const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(type))); + son_id_type first_son; + bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son ); + son_id_type son_id; + + modify(sidechain_sso, [&](son_schedule_object& _sso) { - const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); - son_id_type first_son; - bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son ); - son_id_type son_id; + _sso.slots_since_genesis += schedule_slot; + witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis); - modify(sidechain_sso, [&](son_schedule_object& _sso) - { - _sso.slots_since_genesis += schedule_slot; - witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis); + _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(type).size()) / 2, 1); - _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(active_sidechain_type).size()) / 2, 1); - - if( slot_is_near ) + if( slot_is_near ) + { + uint32_t drain = schedule_slot; + while( drain > 0 ) { - uint32_t drain = schedule_slot; - while( drain > 0 ) - { - if( _sso.scheduler.size() == 0 ) - break; - _sso.scheduler.consume_schedule(); - --drain; - } + if( _sso.scheduler.size() == 0 ) + break; + _sso.scheduler.consume_schedule(); + --drain; } - else - { - _sso.scheduler.reset_schedule( first_son ); - } - while( !_sso.scheduler.get_slot(schedule_needs_filled.at(active_sidechain_type), son_id) ) - { - if( _sso.scheduler.produce_schedule(rng) & emit_turn ) - memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); - } - _sso.last_scheduling_block = next_block.block_num(); - _sso.recent_slots_filled = ( - (_sso.recent_slots_filled << 1) - + 1) << (schedule_slot - 1); - }); - } + } + else + { + _sso.scheduler.reset_schedule( first_son ); + } + while( !_sso.scheduler.get_slot(schedule_needs_filled, son_id) ) + { + if( _sso.scheduler.produce_schedule(rng) & emit_turn ) + memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); + } + _sso.last_scheduling_block = next_block.block_num(); + _sso.recent_slots_filled = ( + (_sso.recent_slots_filled << 1) + + 1) << (schedule_slot - 1); + }); auto end = fc::time_point::now(); static uint64_t total_time = 0; diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index c8e1f214..48be59f0 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -292,8 +292,8 @@ namespace graphene { namespace chain { vector get_near_witness_schedule()const; void update_witness_schedule(); void update_witness_schedule(const signed_block& next_block); - void update_son_schedule(); - void update_son_schedule(const signed_block& next_block); + void update_son_schedule(sidechain_type type); + void update_son_schedule(sidechain_type type, const signed_block& next_block); void check_lottery_end_by_participants( asset_id_type asset_id ); void check_ending_lotteries(); diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 138f8e57..03e2edc1 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -97,10 +97,18 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); + const son_wallet_id_type son_wallet_id = [&]{ + if(db().head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) + { + const auto ast = active_sidechain_types(db().head_block_time()); + const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size(); + return son_wallet_id_type{ id }; + } + + return op.son_wallet_id; + }(); + const auto& idx = db().get_index_type().indices().get(); - const auto ast = active_sidechain_types(db().head_block_time()); - const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size(); - const son_wallet_id_type son_wallet_id{ id }; FC_ASSERT( idx.find(son_wallet_id) != idx.end() ); //auto itr = idx.find(op.son_wallet_id); //FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() || @@ -110,10 +118,18 @@ void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_ope object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) { try { + const son_wallet_id_type son_wallet_id = [&]{ + if(db().head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) + { + const auto ast = active_sidechain_types(db().head_block_time()); + const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size(); + return son_wallet_id_type{ id }; + } + + return op.son_wallet_id; + }(); + const auto& idx = db().get_index_type().indices().get(); - const auto ast = active_sidechain_types(db().head_block_time()); - const auto id = (op.son_wallet_id.instance.value - std::distance(ast.begin(), ast.find(op.sidechain))) / ast.size(); - const son_wallet_id_type son_wallet_id{ id }; auto itr = idx.find(son_wallet_id); if (itr != idx.end()) { From 435c1f8e96de6b4671f998bdb8f7611bb9c1cfea Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 20 Jun 2023 07:37:24 +0000 Subject: [PATCH 099/106] Fix issue with balance discrepancies in 1.5.23-beta --- libraries/chain/db_maint.cpp | 228 ++++++++++++------ .../chain/include/graphene/chain/database.hpp | 3 +- .../sidechain_net_handler.cpp | 3 +- .../sidechain_net_handler_bitcoin.cpp | 2 +- tests/tests/son_operations_tests.cpp | 2 +- 5 files changed, 158 insertions(+), 80 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index e4ccc45d..7df4f403 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -185,13 +185,109 @@ void database::update_worker_votes() } } -void database::pay_sons() +void database::pay_sons_before_hf_ethereum() +{ + const auto now = head_block_time(); + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + // Current requirement is that we have to pay every 24 hours, so the following check + if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) + { + const sidechain_type st = sidechain_type::bitcoin; + const auto sons = sort_votable_objects(st, get_global_properties().parameters.maximum_son_count()); + + // After SON2 HF + uint64_t total_votes = 0; + for( const son_object& son : sons ) + { + FC_ASSERT(son.get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", son)); + total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(st)]; + } + const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { + const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); + return weight; + }; + + // Before SON2 HF + auto get_weight_before_son2_hf = []( uint64_t son_votes ) { + const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0); + const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); + return weight; + }; + + uint64_t weighted_total_txs_signed = 0; + const share_type son_budget = dpo.son_budget; + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &st](const object& o) { + const son_statistics_object& s = static_cast(o); + const auto& idx = get_index_type().indices().get(); + const auto son_obj = idx.find( s.owner ); + uint16_t son_weight = 0; + FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj)); + if( now >= HARDFORK_SON2_TIME ) { + son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); + } + else { + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); + } + const uint64_t txs_signed_bitcoin = s.txs_signed.contains(sidechain_type::bitcoin) ? s.txs_signed.at(sidechain_type::bitcoin) : 0; + const uint64_t txs_signed_hive = s.txs_signed.contains(sidechain_type::hive) ? s.txs_signed.at(sidechain_type::hive) : 0; + weighted_total_txs_signed += ((txs_signed_bitcoin + txs_signed_hive) * son_weight); + }); + + // Now pay off each SON proportional to the number of transactions signed. + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &st](const object& o) { + const son_statistics_object& s = static_cast(o); + const uint64_t txs_signed_bitcoin = s.txs_signed.contains(sidechain_type::bitcoin) ? s.txs_signed.at(sidechain_type::bitcoin) : 0; + const uint64_t txs_signed_hive = s.txs_signed.contains(sidechain_type::hive) ? s.txs_signed.at(sidechain_type::hive) : 0; + + if(txs_signed_bitcoin > 0 || txs_signed_hive > 0) { + const auto& idx = get_index_type().indices().get(); + auto son_obj = idx.find( s.owner ); + uint16_t son_weight = 0; + FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj)); + if( now >= HARDFORK_SON2_TIME ) { + son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); + } + else { + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); + } + const share_type pay = ((txs_signed_bitcoin + txs_signed_hive) * son_weight * son_budget.value)/weighted_total_txs_signed; + modify( *son_obj, [&]( son_object& _son_obj) + { + _son_obj.pay_son_fee(pay, *this); + }); + //Remove the amount paid out to SON from global SON Budget + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.son_budget -= pay; + } ); + //Reset the tx counter in each son statistics object + modify( s, [&]( son_statistics_object& _s) + { + if(_s.txs_signed.contains(sidechain_type::bitcoin)) + _s.txs_signed.at(sidechain_type::bitcoin) = 0; + if(_s.txs_signed.contains(sidechain_type::hive)) + _s.txs_signed.at(sidechain_type::hive) = 0; + }); + } + }); + + //Note the last son pay out time + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.last_son_payout_time = now; + }); + } +} + +void database::pay_sons_after_hf_ethereum() { const time_point_sec now = head_block_time(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); // Current requirement is that we have to pay every 24 hours, so the following check if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { + flat_map bits_to_drop; for(const auto& active_sidechain_type : active_sidechain_types(now)) { assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 ); @@ -209,93 +305,72 @@ void database::pay_sons() } } - const sidechain_type st = [&now, &active_sidechain_type]{ - if( now < HARDFORK_SON_FOR_ETHEREUM_TIME ) - return sidechain_type::bitcoin; - else - return active_sidechain_type; - }(); - - const auto sons = sort_votable_objects(st, - (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)) - ); + const auto sons = sort_votable_objects(active_sidechain_type, (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count))); // After SON2 HF uint64_t total_votes = 0; for( const son_object& son : sons ) { - FC_ASSERT(son.get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", son)); - total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(st)]; + FC_ASSERT(son.get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", son)); + total_votes += _vote_tally_buffer[*son.get_sidechain_vote_id(active_sidechain_type)]; } - const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { - const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); - return weight; - }; - // Before SON2 HF - auto get_weight_before_son2_hf = []( uint64_t son_votes ) { - const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0); - const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); - return weight; - }; - uint64_t weighted_total_txs_signed = 0; - const share_type son_budget = dpo.son_budget; - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type, &st](const object& o) { - const son_statistics_object& s = static_cast(o); - const auto& idx = get_index_type().indices().get(); - const auto son_obj = idx.find( s.owner ); - uint16_t son_weight = 0; - FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj)); - if( now >= HARDFORK_SON2_TIME ) { - son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); - } - else { - son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); - } + bits_to_drop[active_sidechain_type] = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + } + + auto get_weight = [&bits_to_drop]( sidechain_type sidechain, uint64_t son_votes ) { + const uint16_t weight = std::max((son_votes >> bits_to_drop.at(sidechain)), uint64_t(1) ); + return weight; + }; + + // Calculate weighted_total_txs_signed + uint64_t weighted_total_txs_signed = 0; + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now](const object& o) { + for(const auto& active_sidechain_type : active_sidechain_types(now)) { + const son_statistics_object &s = static_cast(o); + const auto &idx = get_index_type().indices().get(); + const auto son_obj = idx.find(s.owner); + FC_ASSERT(son_obj->get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", *son_obj)); + const uint16_t son_weight = get_weight(active_sidechain_type, _vote_tally_buffer[*son_obj->get_sidechain_vote_id(active_sidechain_type)]); const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; weighted_total_txs_signed += (txs_signed * son_weight); - }); + } + }); + + // Now pay off each SON proportional to the number of transactions signed + const share_type son_budget = dpo.son_budget; + get_index_type().inspect_all_objects([this, &now, &get_weight, &weighted_total_txs_signed, &dpo, &son_budget](const object& o) { + for(const auto& active_sidechain_type : active_sidechain_types(now)) { + const son_statistics_object &s = static_cast(o); - // Now pay off each SON proportional to the number of transactions signed. - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type, &st](const object& o) { - const son_statistics_object& s = static_cast(o); const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; - - if(txs_signed > 0) { - const auto& idx = get_index_type().indices().get(); - auto son_obj = idx.find( s.owner ); + if (txs_signed > 0) { + const auto &idx = get_index_type().indices().get(); + auto son_obj = idx.find(s.owner); uint16_t son_weight = 0; - FC_ASSERT(son_obj->get_sidechain_vote_id(st).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", st)("son", *son_obj)); - if( now >= HARDFORK_SON2_TIME ) { - son_weight += get_weight(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); - } - else { - son_weight += get_weight_before_son2_hf(_vote_tally_buffer[*son_obj->get_sidechain_vote_id(st)]); - } - const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; - modify( *son_obj, [&]( son_object& _son_obj) - { - _son_obj.pay_son_fee(pay, *this); + FC_ASSERT(son_obj->get_sidechain_vote_id(active_sidechain_type).valid(), "Invalid vote id, sidechain: ${sidechain}, son: ${son}", ("sidechain", active_sidechain_type)("son", *son_obj)); + son_weight += get_weight(active_sidechain_type, _vote_tally_buffer[*son_obj->get_sidechain_vote_id(active_sidechain_type)]); + const share_type pay = (txs_signed * son_weight * son_budget.value) / weighted_total_txs_signed; + modify(*son_obj, [&](son_object &_son_obj) { + _son_obj.pay_son_fee(pay, *this); }); - //Remove the amount paid out to SON from global SON Budget - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.son_budget -= pay; - } ); - //Reset the tx counter in each son statistics object - modify( s, [&]( son_statistics_object& _s) - { - if(_s.txs_signed.contains(active_sidechain_type)) + // Remove the amount paid out to SON from global SON Budget + modify(dpo, [&](dynamic_global_property_object &_dpo) { + _dpo.son_budget -= pay; + }); + // Reset the tx counter in each son statistics object + modify(s, [&](son_statistics_object &_s) { + if (_s.txs_signed.contains(active_sidechain_type)) _s.txs_signed.at(active_sidechain_type) = 0; }); } - }); - //Note the last son pay out time - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.last_son_payout_time = now; - }); - } + } + }); + + //Note the last son pay out time + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.last_son_payout_time = now; + }); } } @@ -2152,7 +2227,7 @@ void database::perform_son_tasks() }); } // create HIVE asset here because son_account is the issuer of the HIVE - if (gpo.parameters.hive_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME) + if (gpo.parameters.hive_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME) { const asset_dynamic_data_object& dyn_asset = create([](asset_dynamic_data_object& a) { @@ -2192,7 +2267,10 @@ void database::perform_son_tasks() // Before making a budget we should pay out SONs // This function should check if its time to pay sons // and modify the global son funds accordingly, whatever is left is passed on to next budget - pay_sons(); + if(head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME) + pay_sons_before_hf_ethereum(); + else + pay_sons_after_hf_ethereum(); } // Split vote_ids diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 48be59f0..24fb32d4 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -579,7 +579,8 @@ namespace graphene { namespace chain { void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const; void process_budget(); void pay_workers( share_type& budget ); - void pay_sons(); + void pay_sons_before_hf_ethereum(); + void pay_sons_after_hf_ethereum(); void perform_son_tasks(); void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void update_active_witnesses(); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index f762da49..0d19b179 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -680,8 +680,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { const bool is_tracked_asset = ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || - ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id != gpo.parameters.btc_asset()) - && (transfer_op.amount.asset_id != gpo.parameters.hbd_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hive_asset())) || + ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id != gpo.parameters.btc_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hbd_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hive_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 76e23ab0..974670ea 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -674,7 +674,7 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain uint32_t rpc_port = options.at("bitcoin-node-rpc-port").as(); std::string rpc_user = options.at("bitcoin-node-rpc-user").as(); std::string rpc_password = options.at("bitcoin-node-rpc-password").as(); - + if (options.count("use-bitcoind-client")) { use_bitcoind_client = options.at("use-bitcoind-client").as(); } diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 0da34705..0d671a62 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -574,7 +574,7 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::hive), 12); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::ethereum), 18); // Check that Alice and Bob are paid for signing the transactions in the previous day/cycle - BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance); + BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 79+obj1_balance); BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance); // Check the SON Budget is again allocated after maintenance BOOST_CHECK( dpo.son_budget.value == 200); From 93fb57c080c4d7ae3dd0f9beef52b3ff0f27a0cd Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 6 Jul 2023 05:31:28 +0000 Subject: [PATCH 100/106] Fix balance discrepancies in 1.5.23-beta --- .gitlab-ci.yml | 5 +- libraries/chain/confidential_evaluator.cpp | 136 ++++++++++++++++-- libraries/chain/db_getter.cpp | 10 +- .../include/graphene/chain/exceptions.hpp | 3 + .../graphene/chain/protocol/confidential.hpp | 12 +- 5 files changed, 141 insertions(+), 25 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f300e82b..3ec040bc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -151,16 +151,19 @@ test-e2e: - python3 -m venv venv - source venv/bin/activate - pip3 install -r requirements.txt - - python3 main.py --stop + - docker-compose down --remove-orphans - docker ps -a - docker pull $IMAGE - docker tag $IMAGE peerplays-base:latest - docker image ls -a + - docker-compose build - python3 main.py --start all - docker ps -a - python3 -m pytest test_btc_init_state.py test_hive_inital_state.py test_pp_inital_state.py - python3 main.py --stop - deactivate - docker ps -a + after_script: + - docker rmi $(docker images -a | grep -v 'hive-for-peerplays\|ethereum-for-peerplays\|bitcoin-for-peerplays\|ubuntu-for-peerplays' | awk '{print $3}') tags: - python-tests diff --git a/libraries/chain/confidential_evaluator.cpp b/libraries/chain/confidential_evaluator.cpp index fa4ac515..0526c116 100644 --- a/libraries/chain/confidential_evaluator.cpp +++ b/libraries/chain/confidential_evaluator.cpp @@ -33,45 +33,163 @@ namespace graphene { namespace chain { void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o ) { try { - return void_result(); + const auto& d = db(); + if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) + { + const auto& atype = o.amount.asset_id(d); + FC_ASSERT( atype.allow_confidential() ); + FC_ASSERT( !atype.is_transfer_restricted() ); + FC_ASSERT( !(atype.options.flags & white_list) ); + + for( const auto& out : o.outputs ) + { + for( const auto& a : out.owner.account_auths ) + a.first(d); // verify all accounts exist and are valid + } + } + + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } -void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o ) +void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o ) { try { - return void_result(); + if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + db().adjust_balance(o.from, -o.amount); + + const auto &add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset + db().modify(add, [&](asset_dynamic_data_object &obj) { + obj.confidential_supply += o.amount.amount; + FC_ASSERT(obj.confidential_supply >= 0); + }); + for (const auto &out : o.outputs) { + db().create([&](blinded_balance_object &obj) { + obj.asset_id = o.amount.asset_id; + obj.owner = out.owner; + obj.commitment = out.commitment; + }); + } + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void transfer_to_blind_evaluator::pay_fee() { + const auto& d = db(); + if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + if (d.head_block_time() >= HARDFORK_563_TIME) + pay_fba_fee(fba_accumulator_id_transfer_to_blind); + else + generic_evaluator::pay_fee(); + } } void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o ) { try { - return void_result(); + const auto& d = db(); + if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + o.fee.asset_id(d); // verify fee is a legit asset + const auto &bbi = d.get_index_type(); + const auto &cidx = bbi.indices().get(); + for (const auto &in : o.inputs) { + auto itr = cidx.find(in.commitment); + FC_ASSERT(itr != cidx.end()); + FC_ASSERT(itr->asset_id == o.fee.asset_id); + FC_ASSERT(itr->owner == in.owner); + } + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } -void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o ) +void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o ) { try { - return void_result(); + if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + db().adjust_balance(o.fee_payer(), o.fee); + db().adjust_balance(o.to, o.amount); + const auto &bbi = db().get_index_type(); + const auto &cidx = bbi.indices().get(); + for (const auto &in : o.inputs) { + auto itr = cidx.find(in.commitment); + FC_ASSERT(itr != cidx.end()); + db().remove(*itr); + } + const auto &add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset + db().modify(add, [&](asset_dynamic_data_object &obj) { + obj.confidential_supply -= o.amount.amount + o.fee.amount; + FC_ASSERT(obj.confidential_supply >= 0); + }); + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void transfer_from_blind_evaluator::pay_fee() { + const auto& d = db(); + if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + if (d.head_block_time() >= HARDFORK_563_TIME) + pay_fba_fee(fba_accumulator_id_transfer_from_blind); + else + generic_evaluator::pay_fee(); + } } void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o ) { try { - return void_result(); + const auto& d = db(); + if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + o.fee.asset_id(d); // verify fee is a legit asset + const auto &bbi = d.get_index_type(); + const auto &cidx = bbi.indices().get(); + for (const auto &out : o.outputs) { + for (const auto &a : out.owner.account_auths) + a.first(d); // verify all accounts exist and are valid + } + for (const auto &in : o.inputs) { + auto itr = cidx.find(in.commitment); + GRAPHENE_ASSERT(itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment", in.commitment)); + FC_ASSERT(itr->asset_id == o.fee.asset_id); + FC_ASSERT(itr->owner == in.owner); + } + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } -void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o ) +void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o ) { try { - return void_result(); + if( db().head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + db().adjust_balance(o.fee_payer(), o.fee); // deposit the fee to the temp account + const auto &bbi = db().get_index_type(); + const auto &cidx = bbi.indices().get(); + for (const auto &in : o.inputs) { + auto itr = cidx.find(in.commitment); + GRAPHENE_ASSERT(itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment", in.commitment)); + db().remove(*itr); + } + for (const auto &out : o.outputs) { + db().create([&](blinded_balance_object &obj) { + obj.asset_id = o.fee.asset_id; + obj.owner = out.owner; + obj.commitment = out.commitment; + }); + } + const auto &add = o.fee.asset_id(db()).dynamic_asset_data_id(db()); + db().modify(add, [&](asset_dynamic_data_object &obj) { + obj.confidential_supply -= o.fee.amount; + FC_ASSERT(obj.confidential_supply >= 0); + }); + } + return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } void blind_transfer_evaluator::pay_fee() { + const auto& d = db(); + if( d.head_block_time() < HARDFORK_SON_FOR_ETHEREUM_TIME ) { + if (d.head_block_time() >= HARDFORK_563_TIME) + pay_fba_fee(fba_accumulator_id_blind_transfer); + else + generic_evaluator::pay_fee(); + } } } } // graphene::chain diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 9510692c..af3635c0 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -305,16 +305,14 @@ bool database::is_son_dereg_valid( son_id_type son_id ) } bool status_son_dereg_valid = true; - for(const auto& status : son->statuses) - { - const auto& sidechain = status.first; - if(status.second != son_status::in_maintenance) + for (const auto &active_sidechain_type : active_sidechain_types(head_block_time())) { + if(son->statuses.at(active_sidechain_type) != son_status::in_maintenance) status_son_dereg_valid = false; if(status_son_dereg_valid) { - if(son->statistics(*this).last_active_timestamp.contains(sidechain)) { - if (head_block_time() - son->statistics(*this).last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { + if(son->statistics(*this).last_active_timestamp.contains(active_sidechain_type)) { + if (head_block_time() - son->statistics(*this).last_active_timestamp.at(active_sidechain_type) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { status_son_dereg_valid = false; } } diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index 92c7c5dd..406a235e 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -182,6 +182,9 @@ namespace graphene { namespace chain { GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer ); GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" ) + GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer ); + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" ); + /* FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" ) FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" ) diff --git a/libraries/chain/include/graphene/chain/protocol/confidential.hpp b/libraries/chain/include/graphene/chain/protocol/confidential.hpp index d1a28da3..86628f3b 100644 --- a/libraries/chain/include/graphene/chain/protocol/confidential.hpp +++ b/libraries/chain/include/graphene/chain/protocol/confidential.hpp @@ -158,9 +158,7 @@ struct transfer_to_blind_operation : public base_operation blind_factor_type blinding_factor; vector outputs; - account_id_type fee_payer()const { return account_id_type{}; } - - //account_id_type fee_payer()const { return from; } + account_id_type fee_payer()const { return from; } //void validate()const; //share_type calculate_fee(const fee_parameters_type& )const; }; @@ -181,9 +179,7 @@ struct transfer_from_blind_operation : public base_operation blind_factor_type blinding_factor; vector inputs; - account_id_type fee_payer()const { return account_id_type{}; } - - //account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } + account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } //void validate()const; //void get_required_authorities( vector& a )const //{ @@ -246,10 +242,8 @@ struct blind_transfer_operation : public base_operation vector inputs; vector outputs; - account_id_type fee_payer()const { return account_id_type{}; } - /** graphene TEMP account */ - //account_id_type fee_payer()const; + account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; } //void validate()const; //share_type calculate_fee( const fee_parameters_type& k )const; //void get_required_authorities( vector& a )const From 84a66c67229e53b78350e7473ac8675219e275be Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Fri, 7 Jul 2023 12:10:44 +0000 Subject: [PATCH 101/106] NEW HARDFORK TIME FOR SON ETH --- libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf index e6d8a9cb..1caf70c2 100644 --- a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf +++ b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf @@ -1,7 +1,7 @@ #ifndef HARDFORK_SON_FOR_ETHEREUM_TIME #ifdef BUILD_PEERPLAYS_TESTNET -#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-06-05T12:00:00")) +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-17T12:00:00")) #else -#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-05T12:00:00")) +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-31T12:00:00")) #endif #endif From bc0fbeb70730b61140a714dd3af3f612887d2d1f Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 13 Jul 2023 16:44:03 +0000 Subject: [PATCH 102/106] Change DB_VERSION to PPY2.5 --- libraries/chain/include/graphene/chain/config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index ef9a0c80..9b55b7b5 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -158,7 +158,7 @@ #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 #define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3 -#define GRAPHENE_CURRENT_DB_VERSION "PPY2.4" +#define GRAPHENE_CURRENT_DB_VERSION "PPY2.5" #define GRAPHENE_IRREVERSIBLE_THRESHOLD (70 * GRAPHENE_1_PERCENT) From 5fd79c3e78c1a98fe6f333ca13e63340fd4b91f6 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Mon, 17 Jul 2023 12:49:53 +0000 Subject: [PATCH 103/106] Set test-e2e as manual --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3ec040bc..ca0c83a6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -167,3 +167,5 @@ test-e2e: - docker rmi $(docker images -a | grep -v 'hive-for-peerplays\|ethereum-for-peerplays\|bitcoin-for-peerplays\|ubuntu-for-peerplays' | awk '{print $3}') tags: - python-tests + when: + manual From f0654e5ffd627fb5e21f892fd14a96b388323bba Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Wed, 23 Aug 2023 14:31:38 +0000 Subject: [PATCH 104/106] Fixes for 1.5.24-beta --- libraries/chain/db_maint.cpp | 4 ++-- .../peerplays_sidechain_plugin.cpp | 18 ++++++++++++++++-- .../sidechain_net_handler.cpp | 2 +- tests/cli/son.cpp | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7df4f403..eaa50d51 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -2283,7 +2283,7 @@ void database::perform_son_tasks() // Add vote_ids for HIVE and ETHEREUM to all existing SONs const auto &all_sons = get_index_type().indices().get(); for (const son_object &son : all_sons) { - vote_id_type existing_vote_id_bitcoin; + const auto existing_vote_id_bitcoin = son.get_bitcoin_vote_id(); vote_id_type new_vote_id_hive; vote_id_type new_vote_id_eth; @@ -2300,7 +2300,7 @@ void database::perform_son_tasks() // Duplicate all votes from bitcoin to hive const auto &all_accounts = get_index_type().indices().get(); for (const auto &account : all_accounts) { - if (account.options.votes.count(existing_vote_id_bitcoin) != 0) { + if (existing_vote_id_bitcoin.valid() && account.options.votes.count(*existing_vote_id_bitcoin) != 0) { modify(account, [new_vote_id_hive](account_object &a) { a.options.votes.insert(new_vote_id_hive); }); diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 6f6747db..98ebbbf2 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -100,6 +100,7 @@ private: uint16_t retries_threshold = 150; bool first_block_skipped; + bool son_processing_enabled; void on_applied_block(const signed_block &b); }; @@ -135,7 +136,8 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec } return net_handlers; }()), - first_block_skipped(false) { + first_block_skipped(false), + son_processing_enabled(false) { } peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { @@ -871,7 +873,19 @@ void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_ty void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { if (first_block_skipped) { - schedule_son_processing(); + if(son_processing_enabled) { + schedule_son_processing(); + } + else + { + const fc::time_point now_fine = fc::time_point::now(); + const fc::time_point_sec now = now_fine + fc::microseconds( 500000 ); + if( plugin.database().get_slot_time(1) >= now ) + { + son_processing_enabled = true; + schedule_son_processing(); + } + } } else { first_block_skipped = true; } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 0d19b179..667f1edc 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -680,7 +680,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { const bool is_tracked_asset = ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || - ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id != gpo.parameters.btc_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hbd_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hive_asset())) || + ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id != gpo.parameters.btc_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hbd_asset()) && (transfer_op.amount.asset_id != gpo.parameters.hive_asset()) && (transfer_op.amount.asset_id != asset_id_type())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index e3758557..ed01be2a 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -40,7 +40,7 @@ public: { fixture_.init_nathan(); fixture_.generate_blocks(HARDFORK_SON_FOR_ETHEREUM_TIME); - fixture_.generate_block(); + fixture_.generate_maintenance_block(); } void create_son(const std::string& account_name, const std::string& son_url, From a641b8e93f3a309edf0fe2772627d8cb755f4d30 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 3 Oct 2023 16:23:59 +0000 Subject: [PATCH 105/106] Fixes for 1.5.25-beta --- Dockerfile | 7 ++----- Dockerfile.18.04 | 7 ++----- README.md | 7 ++----- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8e2c76f3..b9068197 100644 --- a/Dockerfile +++ b/Dockerfile @@ -134,11 +134,8 @@ RUN \ libsodium-dev RUN \ - git clone https://github.com/libbitcoin/libbitcoin-build.git && \ - cd libbitcoin-build && \ - git reset --hard 92c215fc1ffa272bab4d485d369d0306db52d69d && \ - ./generate3.sh && \ - cd ../libbitcoin-explorer && \ + git clone --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git && \ + cd libbitcoin-explorer && \ ./install.sh && \ ldconfig && \ rm -rf /home/peerplays/src/* diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index 9531d986..1fe40a55 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -134,11 +134,8 @@ RUN \ libsodium-dev RUN \ - git clone https://github.com/libbitcoin/libbitcoin-build.git && \ - cd libbitcoin-build && \ - git reset --hard 92c215fc1ffa272bab4d485d369d0306db52d69d && \ - ./generate3.sh && \ - cd ../libbitcoin-explorer && \ + git clone --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git && \ + cd libbitcoin-explorer && \ ./install.sh && \ ldconfig && \ rm -rf /home/peerplays/src/* diff --git a/README.md b/README.md index 00b2fff3..cff50dc3 100644 --- a/README.md +++ b/README.md @@ -77,11 +77,8 @@ sudo ldconfig libbitcoin-explorer setup: ``` -git clone https://github.com/libbitcoin/libbitcoin-build.git -cd libbitcoin-build -git reset --hard 92c215fc1ffa272bab4d485d369d0306db52d69d -./generate3.sh -cd ../libbitcoin-explorer +git clone --branch version3.8.0 --depth 1 https://gitlab.com/PBSA/peerplays-1.0/libbitcoin-explorer.git +cd libbitcoin-explorer sudo ./install.sh sudo ldconfig ``` From dc4cdd6e4bb23f968e19b3427eadae77277afe41 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Wed, 4 Oct 2023 16:51:45 +0000 Subject: [PATCH 106/106] Set HARDFORK_SON_FOR_ETHEREUM_TIME to 24 of October --- libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf index 1caf70c2..05ae0708 100644 --- a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf +++ b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf @@ -2,6 +2,6 @@ #ifdef BUILD_PEERPLAYS_TESTNET #define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-17T12:00:00")) #else -#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-07-31T12:00:00")) +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2023-10-24T12:00:00")) #endif #endif