From 157e6c2fd82298662f7acb1bf890c5ab54c8ba9a Mon Sep 17 00:00:00 2001 From: serkixenos Date: Wed, 20 Apr 2022 23:24:06 +0200 Subject: [PATCH 01/66] 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 38c96a3c4..a04948135 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 000000000..ecf668e3e --- /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 02/66] 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 391218b77..6883659d4 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 03/66] #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 3aa4465cf..a9257e547 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 @@ class sidechain_net_handler { 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 45fab4eea..7c87a9ef9 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 04/66] 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 | 145 +++++++++++++----- .../account_history_plugin.cpp | 6 +- .../elasticsearch/elasticsearch_plugin.cpp | 6 +- 14 files changed, 176 insertions(+), 66 deletions(-) delete mode 100644 libraries/chain/hardfork.d/CORE_210.hf diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 19f1ed103..56878eafb 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -795,6 +795,10 @@ class application_impl : public net::node_delegate { 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 67601c2e9..6c58e58b3 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 724cad856..c4aabfa87 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 2ca49c4c2..4a3b519f2 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 c5986fad0..62d6d2bf4 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 cf3bdade0..000000000 --- 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 50975174a..f62df9386 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 ac8ef6013..285003041 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 662c700a8..67eef6c7e 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 e17af148c..adbaf2628 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 61f1cef56..0cd0288fa 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 3222da08a..3fab0db30 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()<graphene_git_revision_unix_timestamp)); + #endif + connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version, + originating_peer->get_socket().remote_endpoint(), + rejection_reason_code::unspecified, + rejection_message.str() ); + + originating_peer->their_state = peer_connection::their_connection_state::connection_rejected; + originating_peer->send_message(message(connection_rejected)); + // for this type of message, we're immediately disconnecting this peer, instead of trying to + // 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); @@ -1979,28 +2008,30 @@ namespace graphene { namespace net { namespace detail { 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 - 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; - connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version, - originating_peer->get_socket().remote_endpoint(), - rejection_reason_code::unspecified, - rejection_message.str() ); - - originating_peer->their_state = peer_connection::their_connection_state::connection_rejected; - originating_peer->send_message(message(connection_rejected)); - // for this type of message, we're immediately disconnecting this peer, instead of trying to - // 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"); + 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,22 +3523,37 @@ 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 db3fa464c..60ce64f83 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 491c011d4..e306054a8 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 05/66] 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 e65c81c8e..83fa21f4c 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 06/66] 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 27c298619..695881be4 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 a04948135..ffe89d9fd 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 ecf668e3e..7a82fbe7d 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 6883659d4..58f42cb9d 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 07/66] 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 583effdeb..bc7989f70 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 08/66] 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 3fab0db30..c9f741014 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 0232bc445..d2fac215f 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 4cb27b08d..b3172d8e9 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 09/66] Ubuntu 18.04 build support --- Dockerfile | 6 +- Dockerfile.18.04 | 7 +- README.md | 164 +++++++++--------- .../chain/son_wallet_deposit_object.hpp | 2 +- .../chain/son_wallet_withdraw_object.hpp | 2 +- 5 files changed, 94 insertions(+), 87 deletions(-) diff --git a/Dockerfile b/Dockerfile index ffe89d9fd..e90e5380d 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 7a82fbe7d..5e928263b 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 58f42cb9d..b1333d8ec 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: -``` -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 +Following dependencies are needed for a clean install of Ubuntu 20.04: ``` -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 && \ +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 ``` -# libzmq setup + +Install libzmq from source: ``` -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 +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 ``` -# cppzmq setup + +Install cppzmq from source: ``` -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 +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 +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 +``` + +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/ +./bootstrap.sh +sudo ./b2 install ``` -# libzmq setup +Install cmake ``` - 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 +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 ``` -# cppzmq setup +Install libzmq 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) install +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 ``` -## Building Peerplays +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 ae68a64f0..2a741743e 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 d65f5cab7..2e4380bab 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 10/66] 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 3fab0db30..61ee3befa 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 11/66] 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 bc7989f70..a9e59307b 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 12/66] 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 61ee3befa..221804ff6 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 13/66] Remove fc based RNG --- libraries/fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fc b/libraries/fc index 6171e973c..e7369949b 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 14/66] Update README instructions for docker build --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index fc96f9578..c626bf9d4 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 15/66] 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 c626bf9d4..12949dd54 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 16/66] Update GitLab CI file, more manual build options --- .gitlab-ci.yml | 52 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 83fa21f4c..44038b971 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,6 +29,35 @@ build: tags: - builder +test: + stage: test + dependencies: + - build + script: + - ./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 + +dockerize: + stage: build + variables: + IMAGE: $CI_REGISTRY_IMAGE/$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 push $IMAGE + tags: + - builder + when: + manual + timeout: + 3h + build-testnet: stage: build script: @@ -46,21 +75,6 @@ build-testnet: - build/libraries/ - build/programs/ - build/tests/ - tags: - - builder-testnet - when: - manual - -dockerize: - stage: build - variables: - IMAGE: $CI_REGISTRY_IMAGE/$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 push $IMAGE tags: - builder when: @@ -68,10 +82,10 @@ dockerize: timeout: 3h -test: +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 17/66] 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 a36cc856c..2103c636f 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 18/66] 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 44038b971..cda41654e 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 19/66] 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 d0524d00d..f70584ecc 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 20/66] 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 e90e5380d..7a76c1360 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 5e928263b..d3fa1d75b 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 12949dd54..a8d980216 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 21/66] 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 0f95a6e36..53bdec084 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 f70584ecc..0c95a4195 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 22/66] 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 56878eafb..d6324d7df 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -107,6 +108,7 @@ class application_impl : public net::node_delegate { 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 @@ class application_impl : public net::node_delegate { */ 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 0c95a4195..841705f00 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 23/66] #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 6c58e58b3..c475813bf 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 24/66] 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 9c067e0e1..9b3ec841e 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 @@ class bitcoin_rpc_client { 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 @@ class zmq_listener { 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 a9e59307b..83e6c9487 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 25/66] 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 56ac6fd05..0c6843d90 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 841705f00..c6bef00f3 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 26/66] 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 d6324d7df..6ff51c020 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -117,67 +117,29 @@ class application_impl : public net::node_delegate { _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 - -#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 - }; + all_seeds.insert(all_seeds.end(), seeds.begin(), seeds.end()); + } - 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 @@ class application_impl : public net::node_delegate { 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 af5086b20..c5eea447f 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 27/66] 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 9b3ec841e..6fd1fcfa5 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 83e6c9487..6203ff792 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 28/66] 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 6ff51c020..652cc08de 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 29/66] 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 a9ea97740..1095bbeb8 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 @@ class database_api_impl : public std::enable_shared_from_this 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 93a2b88cc..679d8a4c8 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 @@ class database_api { // 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 30/66] 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 652cc08de..afae81a7a 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 31/66] 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 36347c09c..d9556e304 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 32/66] #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 d6324d7df..841f6747b 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -454,7 +454,7 @@ class application_impl : public net::node_delegate { 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 aa199c84f..3026c2f0d 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 0c6843d90..c3f826d11 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 50ccb8aef..c6de2047c 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 2405369a8..b980998cc 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 d2fac215f..35b9ab1c5 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2158,8 +2158,8 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 491ec8f96..1f09de9f4 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 a802aac58..4fb3a24d8 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 33/66] 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 c6bef00f3..8b797d4a8 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 c5eea447f..e1c6c12ec 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 34/66] 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 1f09de9f4..38a3799b4 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -39,7 +39,7 @@ class son_test_helper 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 35/66] #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 e535465cb..d9c387a68 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 f6b084f12..8dd18915a 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 afae81a7a..1a3d3a8ad 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -362,7 +362,6 @@ class application_impl : public net::node_delegate { 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 1095bbeb8..250413ee3 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -237,9 +237,6 @@ class database_api_impl : public std::enable_shared_from_this // 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 98be16a6f..68f2514a3 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -289,33 +289,6 @@ class network_node_api { 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 @@ class login_api { 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 @@ class login_api { 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 679d8a4c8..abe65a2b8 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -877,15 +877,6 @@ class database_api { */ 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 9946b492d..fa4ac5153 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 62d6d2bf4..2360a3181 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 406a235eb..92c7c5dd4 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 697ef35b6..d1a28da35 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 83d347ab2..8cbde2ac8 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 2e8fbc68b..4bca850d5 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 e7369949b..156b0c4e4 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 afad20936..e40ea3294 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 418085624..34f8eb89c 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 02790e19d..185db897e 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 35b9ab1c5..09aa5bdca 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -134,8 +134,6 @@ struct operation_printer 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 @@ class wallet_api_impl 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 82a121c90..c2b5908a0 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 792a300ec..000000000 --- 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 86dd3d7fe..5faf4038b 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 36/66] #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 e40ea3294..893f82aac 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 37/66] 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 250413ee3..6ffee9117 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 38/66] Boost Beast based RPC client --- .../peerplays_sidechain/common/rpc_client.cpp | 1067 ++-------------- .../peerplays_sidechain/common/rpc_client.hpp | 129 +- .../sidechain_net_handler_bitcoin.hpp | 24 +- .../sidechain_net_handler_bitcoin.cpp | 1131 +++-------------- 4 files changed, 328 insertions(+), 2023 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index d08d337b5..173019ebd 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) { +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), + resolver(ioc) { - typedef std::string::size_type size_t; - constexpr auto npos = std::string::npos; + 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); - size_t schema_end = url.find("://"); - size_t host_begin; - std::string temp_schema; + boost::xpressive::smatch sm; - 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; + if (boost::xpressive::regex_search(url, sm, sr)) { + protocol = sm["Protocol"]; + if (protocol.empty()) { + protocol = "http"; } - 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; + host = sm["Host"]; + if (host.empty()) { + host + "localhost"; } - } - 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 + port = sm["Port"]; + if (port.empty()) { + port = "80"; } - } - // 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)"); + target = sm["Target"]; + if (target.empty()) { + target = "/"; } - 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 + authorization = "Basic " + fc::base64_encode(user + ":" + password); + results = resolver.resolve(host, port); -// 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); + elog("Invalid URL: ${url}", ("url", url)); } } -}} // 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), - request_id(0), - client(url) - -{ - - client.set_method("POST"); - client.set_headers("Authorization : Basic" + fc::base64_encode(user_name + ":" + password)); -} - 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; -//} - -//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; -//} - -//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; -//} - -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); +rpc_reply rpc_client::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); + + boost::beast::net::ssl::stream ssl_tcp_stream(ioc, ctx); + boost::beast::tcp_stream tcp_stream(ioc); + + // 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); + } + + // 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) { - 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())); + ilog("### Response: ${rbody}", ("rbody", rbody)); } - return response; + 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 63d218eec..1a7977827 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 { - - 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; +struct rpc_reply { + uint16_t status; 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 6fd1fcfa5..55af0c4ef 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 @@ class btc_txout { uint64_t amount_; }; -class bitcoin_rpc_client { +class bitcoin_rpc_client: public rpc_client { public: enum class multi_type { script, @@ -41,49 +42,30 @@ class bitcoin_rpc_client { }; 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 6203ff792..82511bed4 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); - - 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); + 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.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(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())); + 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); } - 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 += "]"; - - std::string argument_2 = std::string{"{\"rescan\": "} + (rescan ? "true" : "false") + "}"; - body += argument_1 + ", " + argument_2 + "]}"; + argument_1 += ", \"timestamp\": " + std::to_string(fc::time_point_sec::from_iso_string("2022-01-01T00:00:00").sec_since_epoch()) + "}"; - const auto reply = send_post_request(body, debug_rpc_calls); + //! 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 (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return; + if (¶m != &address_or_script_array.back()) { + argument_1 += ", "; } + } + 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); + std::string argument_2 = std::string{"{\"rescan\": "} + (rescan ? "true" : "false") + "}"; + params += argument_1 + "," + argument_2 + "]"; - 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))); - } + 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; - } + if (str.length() == 0) { + 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); + std::stringstream ss(str); + 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())); + 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; - } 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 result; } + + 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::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) + ","; + params += std::string("[\""); + params += address; + params += std::string("\"],true,{\"minimumAmount\":"); + params += std::to_string(minimum_amount); + params += std::string("} ]"); - 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); + std::vector result; + std::string str = send_post_request("listunspent", params, debug_rpc_calls); - 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::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); + 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 ""; + 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); } + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + return result; +} - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } +std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { - 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())); - } - 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::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.code")) { + if (json.get_child("error.code").get_value() == -27) { + return tx_hex; + } + } } - 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 json.get("result"); } -} -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); - - 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); + std::string params = std::string("[]"); + std::string str = send_post_request("walletlock", params, debug_rpc_calls); - 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); - - 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(); - } + std::stringstream ss(str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - 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())); - } - 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))); + 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; - } -} - -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 39/66] 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 | 654 +++++++++++------- libraries/chain/db_witness_schedule.cpp | 153 ++-- .../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 | 320 +++++---- .../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 | 176 +++-- 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, 1776 insertions(+), 1128 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 cda41654e..61afce6e4 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 7a76c1360..50c51c3a1 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 d3fa1d75b..49a06fa1c 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 a8d980216..f3d8c5b9d 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 6ffee9117..b10d6b998 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 3026c2f0d..5f1a44168 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 c475813bf..9606c59e8 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 78740c9b9..0bb9b10bc 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 c4aabfa87..cc862618b 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,14 +1121,30 @@ 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 c3f826d11..d18e0f26b 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; + 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]; + } } - 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; - } + 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)) + ); - if(txs_signed > 0){ + // 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(); - - 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; - }); + for(const auto& new_active_sidechain_sons : new_active_sons) { + const auto& sidechain = new_active_sidechain_sons.first; + + vector current_sons, new_sons; + vector sons_to_remove, sons_to_add; + const auto &idx = get_index_type().indices().get(); + + 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; + }); + } - 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; - }); + 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; + }); - // 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); - } - } - ); + // 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; - }); + 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->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); - } - } - ); + // 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->status == son_status::inactive) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::active; - }); + 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(size_t i = 0; i < new_sons.size(); i++) { - auto son = idx.find( new_sons[i] ); - if(son == idx.end()) // SON is deleted already + 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->status)("sv", son->total_votes) ); + 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; + + gp.active_sons[sidechain].reserve(_new_active_sidechain_sons.size()); + gp.active_sons[sidechain].insert(gp.active_sons[sidechain].end(), _new_active_sidechain_sons.cbegin(), _new_active_sidechain_sons.cend()); + } }); - const son_schedule_object& sso = son_schedule_id_type()(*this); - modify(sso, [&](son_schedule_object& _sso) + for(const auto& active_sidechain_type : active_sidechain_types) { - 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) + 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) { - 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 084c8e1d3..f4df5ac22 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; - - 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] ); - } - }); + _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) ) { + _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); - - _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1); + 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; - if( slot_is_near ) + modify(sidechain_sso, [&](son_schedule_object& _sso) { - 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); - }); + _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); + }); + } + 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 f62df9386..eeb251672 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 53bdec084..71aba1dd0 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 c6de2047c..9c4c16d54 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 5194bed2c..75c3db85b 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 8a46954d3..023bb5115 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 b8aa07c65..73be44d5b 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 7f986f961..1097323b0 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 2bfecac44..75e583198 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 d0b74e791..c9089bfb5 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 315def339..63b546ea7 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 53b0e74a4..86f3e9cc2 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 0a5155892..f405d83a3 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 2eff563f9..945b218df 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 42dfe149b..40e0e1d27 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 b980998cc..bee6b1be3 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 625ef2f0b..bd97fef56 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 776eb0651..8d24bad6f 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 f96202820..8d0199d46 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 0baed1cb0..9b1ff1b7b 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 2110e49db..ae0a867bd 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 9ea2ce347..d7337e42b 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 92591d0af..114f9bc77 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 @@ class peerplays_sidechain_plugin : public graphene::app::plugin { 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 000000000..3aa428941 --- /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 8bfda125a..000000000 --- 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 ed80fbfc2..461b1a925 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 @@ class peerplays_sidechain_plugin_impl { 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 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 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(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 @@ class peerplays_sidechain_plugin_impl { 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 7c87a9ef9..224144a0f 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 82511bed4..b48ccbb67 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 000000000..1c6632d8e --- /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 d63e6743e..c62911eca 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 d5a9ec32e..b2945251a 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 e2cb16085..000000000 --- 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 185db897e..0086c6545 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 09aa5bdca..43ca49fd2 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2230,69 +2230,99 @@ class wallet_api_impl 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); + 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; } - 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() } + FC_CAPTURE_AND_RETHROW() + } + //! Fixme - do we need to specify sidechain_type as params here? 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"; + { + 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"; } - } else { - status = "NOT OK, invalid SON id"; + result[son_obj->id] = status; } - result[son_obj->id] = status; + return result; } - return result; - } FC_CAPTURE_AND_RETHROW() } + FC_CAPTURE_AND_RETHROW() + } optional get_active_son_wallet() { try { @@ -2800,6 +2830,7 @@ class wallet_api_impl signed_transaction vote_for_son(string voting_account, string son, + sidechain_type sidechain, bool approve, bool broadcast /* = false */) { try { @@ -2813,19 +2844,20 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 @@ class wallet_api_impl { 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 @@ class wallet_api_impl { 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 38a3799b4..872577356 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 4fb3a24d8..807c495a2 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 755ad37f9..72f50b9db 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 1e3bb7e4f..5bb003aa1 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 cef29b546..4b1e4fe28 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 40/66] Fix unit test failing on develop branch, #418 --- tests/cli/son.cpp | 442 +++++++++++++++++++++++----------------------- 1 file changed, 223 insertions(+), 219 deletions(-) diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 872577356..67c5019a6 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"); - - 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 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"); - son1_start_votes = son1_obj.total_votes; - son2_obj = con.wallet_api_ptr->get_son("son2account"); - son2_start_votes = son2_obj.total_votes; - - std::vector accepted; - std::vector rejected; - signed_transaction update_votes_tx; - - // 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()); - - // 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 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, - 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[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 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, - 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[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); - 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, - 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[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 - 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, - 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[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); - - // 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()); - - // 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); - - } 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_CHECK(generate_maintenance_block()); + + BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes"); + + 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 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"); + son1_start_votes = son1_obj.total_votes; + son2_obj = con.wallet_api_ptr->get_son("son2account"); + son2_start_votes = son2_obj.total_votes; + + std::vector accepted; + std::vector rejected; + signed_transaction update_votes_tx; + + // 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()); + + // 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 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, + 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[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 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, + 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[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); + 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, + 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[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 + 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, + 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[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); + + // 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()); + + // 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); + + } 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 41/66] 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 893f82aac..033cafe0d 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 55af0c4ef..9a858cb74 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 @@ class btc_txout { 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 @@ class bitcoin_rpc_client: public rpc_client { 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 42/66] 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 efff812d3..3a4b842c8 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 9edca51ce..894850d6b 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 adbaf2628..cbf5c594c 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 0cd0288fa..58c467a69 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 ff7f40368..3aadf5996 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 8b797d4a8..85e8c6762 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 76ae9c8c1..20bc5da94 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 43/66] 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 1a3d3a8ad..b29cd87b9 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 71de7db5a..99b023784 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 d9c823467..7b9b99185 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 7e610ace6..000000000 --- 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 3e058b64c..000000000 --- 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 806330d60..d22350346 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 e1c6c12ec..b3c0aa376 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 44/66] 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 b29cd87b9..db9ef028e 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 173019ebd..4c1365f38 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 4491487f4..5bd1dfd71 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 1a7977827..eb8eac0c1 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 @@ class rpc_client { 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 99c590195..066a36fe5 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 9a858cb74..62eeeab63 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 @@ class bitcoin_rpc_client : public rpc_client { 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 @@ class sidechain_net_handler_bitcoin : public sidechain_net_handler { 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 47e6bf905..72539db2e 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 @@ class sidechain_net_handler_hive : public sidechain_net_handler { 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 461b1a925..12de3dfd1 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 b48ccbb67..cace6fb38 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 c62911eca..7d3c4de92 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 45/66] 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 | 27 ++++++- libraries/wallet/wallet.cpp | 81 ++++++++----------- 4 files changed, 166 insertions(+), 50 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b10d6b998..3158ba499 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -184,6 +184,10 @@ class database_api_impl : public std::enable_shared_from_this 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 abe65a2b8..b53c8eeb1 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -675,6 +675,32 @@ class database_api { */ 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 0086c6545..9850a72b4 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1376,11 +1376,31 @@ class wallet_api */ map list_active_sons(); + /** + * @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 - * @return SON network status description + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return SON network status description for a given sidechain type */ - map get_son_network_status(); + 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 43ca49fd2..49825790f 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2274,55 +2274,25 @@ class wallet_api_impl 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 46/66] 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 a3f333db8..3e1b2e067 100755 --- a/libraries/wallet/generate_api_documentation.pl +++ b/libraries/wallet/generate_api_documentation.pl @@ -44,13 +44,37 @@ END 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 9850a72b4..305526c7e 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 47/66] SON for Ethereum --- libraries/CMakeLists.txt | 1 + libraries/app/database_api.cpp | 12 + libraries/chain/db_init.cpp | 39 +- 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, 2845 insertions(+), 48 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 cf2355f13..f28a6ceee 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 b10d6b998..0a96bc267 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 cc862618b..e9f3b9f5e 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -1101,8 +1101,8 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Initialize witness schedule - #ifndef NDEBUG - const son_schedule_object& ssohive = +#ifndef NDEBUG + const son_schedule_object& ssobitcoin = #endif create([&](son_schedule_object& _sso) { @@ -1111,20 +1111,19 @@ 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; _sso.recent_slots_filled = fc::uint128::max_value(); }); - assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); + assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) ); #ifndef NDEBUG - const son_schedule_object& ssobitcoin = + const son_schedule_object& ssoethereum = #endif create([&](son_schedule_object& _sso) { @@ -1133,17 +1132,37 @@ 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_ethereum_sons = get_global_properties().active_sons.at(sidechain_type::ethereum); _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_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( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) ); + 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 d18e0f26b..7474edac6 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 f4df5ac22..b4c4bb6a2 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 000000000..72e929fed --- /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 3a11e99f0..94493f301 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 023bb5115..913f6c5b9 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 1097323b0..a717f7782 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 c9089bfb5..afe7230f5 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 bee6b1be3..a1b7994e2 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 8d24bad6f..5016ea0f9 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 c94a62501..e607c1038 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 d7337e42b..4f7246029 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 000000000..02f334f5d --- /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 000000000..ad42088b1 --- /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 000000000..a75327c34 --- /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 000000000..f85d0e615 --- /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 000000000..ce64e1ae6 --- /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 000000000..9edb6ae3b --- /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 000000000..1ff97978b --- /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 000000000..693c72845 --- /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 000000000..963244fa8 --- /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 000000000..b44617132 --- /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 000000000..3944d0a2e --- /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 12de3dfd1..e2a8d9b89 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 224144a0f..971df7c7a 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 000000000..066c9dd86 --- /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 1c6632d8e..cd6b50be4 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 000000000..c790323e0 --- /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 000000000..1e744c129 --- /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 000000000..881e806ce --- /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 000000000..32aa140f4 --- /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 000000000..172728eb4 --- /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 43ca49fd2..c334ebc20 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2845,7 +2845,7 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 67c5019a6..0e5d67cfd 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 000000000..769c35b19 --- /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 72f50b9db..818c1743d 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 5bb003aa1..a128b4740 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 48/66] 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 9606c59e8..65f10cc7c 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 49/66] 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 7474edac6..f4a1bca58 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 50/66] 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 db9ef028e..4f5f97c38 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 098e5dd15..33c7df663 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 ac7d7fc95..b9582548c 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 43ffd6cd3..7fd8792af 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 7268006d3..1a9533166 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 907d26ae9..8b7cfd087 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 @@ class debug_witness_plugin : public graphene::app::plugin { 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 628c8866e..105135868 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -4114,7 +4114,7 @@ class wallet_api_impl "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 7b9b99185..0c3bb1352 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 e39ea9293..000000000 --- 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 53d56733b..000000000 --- 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 c94d3fd2a..000000000 --- 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 51/66] 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 e2a8d9b89..b52f99068 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -88,6 +88,7 @@ class peerplays_sidechain_plugin_impl { 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 52/66] 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 3e1b2e067..c1a8bb40a 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 53/66] bug/457-multithread-son-processing --- libraries/chain/db_block.cpp | 105 +++++++++++------- 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, 99 insertions(+), 52 deletions(-) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 65f10cc7c..d49f1dc5a 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 ); - - // postpone transaction if it would make block too big - if( new_total_size >= maximum_block_size ) - { - postponed_tx_count++; - continue; - } + 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; + } - 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) ); + 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)); + } } } + 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 e9f3b9f5e..82d7fde18 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 4a3b519f2..dea75bc67 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 eeb251672..2a432732e 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 fa2109aab..e76e5a837 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 60ce64f83..a47496f4e 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 e306054a8..fae54da20 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 b52f99068..277847201 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -87,6 +87,7 @@ class peerplays_sidechain_plugin_impl { 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 54/66] 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 305526c7e..f17239a4b 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 105135868..fd7bcc2b9 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 55/66] 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 | 680 +++++++++++++++++- 6 files changed, 662 insertions(+), 86 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 4bf0756ae..a385ba472 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 f17239a4b..cedaa21d0 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 fd7bcc2b9..7185b7d33 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2230,50 +2230,6 @@ class wallet_api_impl 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 70bdfb7c2..461923501 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 79004f3aa..f0486af8b 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 0e5d67cfd..7858664c4 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,138 @@ 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(); + 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); + + 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); + 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); + 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()); + + } 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 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(); @@ -895,50 +1021,552 @@ 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); + + 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++) { - 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); + 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()); - for(unsigned int i = 1; i < son_number; i++) + 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) { - 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); + 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(); - 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()); + 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_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); + BOOST_TEST_MESSAGE("Sending Heartbeat for sonaccount4"); - map active_sons = con.wallet_api_ptr->list_active_sons(); - BOOST_CHECK(active_sons.size() == son_number); + 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); - BOOST_CHECK(active_sons.find(name) != active_sons.end()); + 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"); + } + } } - BOOST_CHECK_NO_THROW(con.wallet_api_ptr->list_active_sons()); + 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 cli wallet tests for list_active_sons end"); + 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 56/66] #461 - handle exception when execute POST request --- .../peerplays_sidechain/common/rpc_client.cpp | 98 +++++--- .../sidechain_net_handler_bitcoin.cpp | 238 +++++++++--------- .../sidechain_net_handler_ethereum.cpp | 37 +-- .../sidechain_net_handler_hive.cpp | 46 ++-- 4 files changed, 227 insertions(+), 192 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index 4c1365f38..ac5267156 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 cace6fb38..713c5ca37 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); - - 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; - - 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; + 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()) { + + 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; + } } } + 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; - - 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_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); - 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 066c9dd86..ce75ec5c8 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 7d3c4de92..3980f516d 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 57/66] #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 f4a1bca58..8673362d3 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 58/66] #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 cedaa21d0..aac79252a 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 7185b7d33..d8d79c4b8 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 59/66] 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 bd97fef56..0efd54cf3 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 818c1743d..5f4885e69 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 60/66] #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 cace6fb38..a2dfa750e 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 066c9dd86..22b933ca3 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 7d3c4de92..a5d266fdc 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 61/66] #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 3944d0a2e..322cf5f9f 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 02ec5bce0..c71c47481 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 62/66] #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 a75327c34..e243670d8 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 63/66] #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 b44617132..13a89278a 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 322cf5f9f..ccc1aad4d 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 @@ class sidechain_net_handler_ethereum : public sidechain_net_handler { 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 c71c47481..ce0e29e92 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 64/66] #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 a385ba472..64fb82c20 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 0bb9b10bc..1b61ee79c 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 7858664c4..83db8d480 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 a128b4740..23ddfd1c3 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 65/66] 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 000000000..3a0a39906 --- /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 0efd54cf3..3cffc61ed 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 5f4885e69..8c3229d34 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 66/66] #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 114f9bc77..d890ae687 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 @@ class peerplays_sidechain_plugin : public graphene::app::plugin { 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 b4636537f..5d6df6af8 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 @@ class sidechain_api { 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 a9257e547..fc295b4c1 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 @@ class sidechain_net_handler { 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 62eeeab63..eaa485264 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 @@ class sidechain_net_handler_bitcoin : public sidechain_net_handler { 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 ccc1aad4d..e6e8f298a 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 @@ class ethereum_rpc_client : public rpc_client { 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 @@ class sidechain_net_handler_ethereum : public sidechain_net_handler { 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 72539db2e..440a2520f 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 @@ class sidechain_net_handler_hive : public sidechain_net_handler { 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 69eea1a90..139db7bfc 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 @@ class sidechain_net_handler_peerplays : public sidechain_net_handler { 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 277847201..c8da6ce6e 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -48,6 +48,7 @@ class peerplays_sidechain_plugin_impl { 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 2a85d0347..833d91ffb 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_api.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_api.cpp @@ -12,6 +12,7 @@ class sidechain_api_impl { 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 ea4621c01..862f991a7 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 ce0e29e92..c827a721c 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 a4c061755..63ae22c63 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 b2945251a..76fbbb3e2 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 aac79252a..6a0b5ee54 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 d8d79c4b8..6b1b2eb14 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3291,6 +3291,25 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 @@ class wallet_api_impl 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 )