From 5c416e3a5b39316eb034f3170bc007036e419fe2 Mon Sep 17 00:00:00 2001 From: Meheboob Khan Date: Wed, 7 Sep 2022 13:57:00 +0000 Subject: [PATCH 1/6] Port net library --- libraries/net/core_messages.cpp | 1 - libraries/net/include/graphene/net/config.hpp | 5 + libraries/net/include/graphene/net/node.hpp | 29 ++++- .../include/graphene/net/peer_connection.hpp | 3 +- .../include/graphene/net/peer_database.hpp | 2 +- libraries/net/node.cpp | 117 ++++++++++++++++++ libraries/net/peer_database.cpp | 3 +- 7 files changed, 154 insertions(+), 6 deletions(-) diff --git a/libraries/net/core_messages.cpp b/libraries/net/core_messages.cpp index efff812d..3a4b842c 100644 --- a/libraries/net/core_messages.cpp +++ b/libraries/net/core_messages.cpp @@ -47,4 +47,3 @@ namespace graphene { namespace net { const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type; } } // graphene::net - diff --git a/libraries/net/include/graphene/net/config.hpp b/libraries/net/include/graphene/net/config.hpp index 9edca51c..894850d6 100644 --- a/libraries/net/include/graphene/net/config.hpp +++ b/libraries/net/include/graphene/net/config.hpp @@ -23,6 +23,8 @@ */ #pragma once +#include + #define GRAPHENE_NET_PROTOCOL_VERSION 106 /** @@ -110,3 +112,6 @@ #define GRAPHENE_NET_MAX_NESTED_OBJECTS (250) #define MAXIMUM_PEERDB_SIZE 1000 + +constexpr size_t MAX_BLOCKS_TO_HANDLE_AT_ONCE = 200; +constexpr size_t MAX_SYNC_BLOCKS_TO_PREFETCH = 10 * MAX_BLOCKS_TO_HANDLE_AT_ONCE; diff --git a/libraries/net/include/graphene/net/node.hpp b/libraries/net/include/graphene/net/node.hpp index adbaf262..cbf5c594 100644 --- a/libraries/net/include/graphene/net/node.hpp +++ b/libraries/net/include/graphene/net/node.hpp @@ -61,7 +61,7 @@ namespace graphene { namespace net { class node_delegate { public: - virtual ~node_delegate(){} + virtual ~node_delegate() = default; /** * If delegate has the item, the network has no need to fetch it. @@ -71,7 +71,9 @@ namespace graphene { namespace net { /** * @brief Called when a new block comes in from the network * + * @param blk_msg the message which contains the block * @param sync_mode true if the message was fetched through the sync process, false during normal operation + * @param contained_transaction_msg_ids container for the transactions to write back into * @returns true if this message caused the blockchain to switch forks, false if it did not * * @throws exception if error validating the item, otherwise the item is @@ -195,7 +197,7 @@ namespace graphene { namespace net { { public: node(const std::string& user_agent); - ~node(); + virtual ~node(); void close(); @@ -213,11 +215,34 @@ namespace graphene { namespace net { */ void add_node( const fc::ip::endpoint& ep ); + /***** + * @brief add a list of nodes to seed the p2p network + * @param seeds a vector of url strings + */ + void add_seed_nodes( std::vector seeds ); + + /**** + * @brief add a node to seed the p2p network + * @param in the url as a string + */ + void add_seed_node( const std::string& in); + /** * Attempt to connect to the specified endpoint immediately. */ virtual void connect_to_endpoint( const fc::ip::endpoint& ep ); + /** + * @brief Helper to convert a string to a collection of endpoints + * + * This converts a string (i.e. "bitshares.eu:665535" to a collection of endpoints. + * NOTE: Throws an exception if not in correct format or was unable to resolve URL. + * + * @param in the incoming string + * @returns a vector of endpoints + */ + static std::vector resolve_string_to_ip_endpoints( const std::string& in ); + /** * Specifies the network interface and port upon which incoming * connections should be accepted. diff --git a/libraries/net/include/graphene/net/peer_connection.hpp b/libraries/net/include/graphene/net/peer_connection.hpp index 0cd0288f..58c467a6 100644 --- a/libraries/net/include/graphene/net/peer_connection.hpp +++ b/libraries/net/include/graphene/net/peer_connection.hpp @@ -62,6 +62,7 @@ namespace graphene { namespace net class peer_connection_delegate { public: + virtual ~peer_connection_delegate() = default; virtual void on_message(peer_connection* originating_peer, const message& received_message) = 0; virtual void on_connection_closed(peer_connection* originating_peer) = 0; @@ -125,7 +126,7 @@ namespace graphene { namespace net * it is sitting on the queue */ virtual size_t get_size_in_queue() = 0; - virtual ~queued_message() {} + virtual ~queued_message() = default; }; /* when you queue up a 'real_queued_message', a full copy of the message is diff --git a/libraries/net/include/graphene/net/peer_database.hpp b/libraries/net/include/graphene/net/peer_database.hpp index ff7f4036..3aadf599 100644 --- a/libraries/net/include/graphene/net/peer_database.hpp +++ b/libraries/net/include/graphene/net/peer_database.hpp @@ -97,7 +97,7 @@ namespace graphene { namespace net { { public: peer_database(); - ~peer_database(); + virtual ~peer_database(); void open(const fc::path& databaseFilename); void close(); diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 8b797d4a..85e8c676 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -72,6 +72,7 @@ #include #include #include +#include #include #include @@ -555,6 +556,10 @@ namespace graphene { namespace net { namespace detail { fc::future _bandwidth_monitor_loop_done; fc::future _dump_node_status_task_done; + /// Used by the task that checks whether addresses of seed nodes have been updated + /// @{ + boost::container::flat_set _seed_nodes; + fc::future _update_seed_nodes_loop_done; /* We have two alternate paths through the schedule_peer_for_deletion code -- one that * uses a mutex to prevent one fiber from adding items to the queue while another is deleting @@ -728,6 +733,11 @@ namespace graphene { namespace net { namespace detail { void listen_to_p2p_network(); void connect_to_p2p_network(); void add_node( const fc::ip::endpoint& ep ); + void add_seed_node( const std::string& in); + void add_seed_nodes( std::vector seeds ); + void resolve_seed_node_and_add( const std::string& seed_string ); + void update_seed_nodes_task(); + void schedule_next_update_seed_nodes_task(); void initiate_connect_to(const peer_connection_ptr& peer); void connect_to_endpoint(const fc::ip::endpoint& ep); void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available); @@ -4757,7 +4767,69 @@ namespace graphene { namespace net { namespace detail { _potential_peer_db.update_entry(updated_peer_record); trigger_p2p_network_connect_loop(); } + void node_impl::add_seed_node(const std::string& endpoint_string) + { + VERIFY_CORRECT_THREAD(); + _seed_nodes.insert( endpoint_string ); + resolve_seed_node_and_add( endpoint_string ); + } + void node_impl::resolve_seed_node_and_add(const std::string& endpoint_string) + { + VERIFY_CORRECT_THREAD(); + std::vector endpoints; + ilog("Resolving seed node ${endpoint}", ("endpoint", endpoint_string)); + try + { + endpoints = graphene::net::node::resolve_string_to_ip_endpoints(endpoint_string); + } + catch(...) + { + wlog( "Unable to resolve endpoint during attempt to add seed node ${ep}", ("ep", endpoint_string) ); + } + for (const fc::ip::endpoint& endpoint : endpoints) + { + ilog("Adding seed node ${endpoint}", ("endpoint", endpoint)); + add_node(endpoint); + } + } + void node_impl::update_seed_nodes_task() + { + VERIFY_CORRECT_THREAD(); + try + { + dlog("Starting an iteration of update_seed_nodes loop."); + for( const std::string& endpoint_string : _seed_nodes ) + { + resolve_seed_node_and_add( endpoint_string ); + } + dlog("Done an iteration of update_seed_nodes loop."); + } + catch (const fc::canceled_exception&) + { + ilog( "update_seed_nodes_task canceled" ); + throw; + } + FC_CAPTURE_AND_LOG( (_seed_nodes) ) + + schedule_next_update_seed_nodes_task(); + } + + void node_impl::schedule_next_update_seed_nodes_task() + { + VERIFY_CORRECT_THREAD(); + + if( _node_is_shutting_down ) + return; + + if( _update_seed_nodes_loop_done.valid() && _update_seed_nodes_loop_done.canceled() ) + return; + + _update_seed_nodes_loop_done = fc::schedule( [this]() { update_seed_nodes_task(); }, + fc::time_point::now() + fc::hours(3), + "update_seed_nodes_loop" ); + } + void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer) { new_peer->get_socket().open(); @@ -5296,6 +5368,11 @@ namespace graphene { namespace net { namespace detail { INVOKE_IN_IMPL(add_node, ep); } + void node::add_seed_node(const std::string& in) + { + INVOKE_IN_IMPL(add_seed_node, in); + } + void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint ) { INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint); @@ -5677,5 +5754,45 @@ namespace graphene { namespace net { namespace detail { #undef INVOKE_AND_COLLECT_STATISTICS } // end namespace detail + std::vector node::resolve_string_to_ip_endpoints(const std::string& in) + { + try + { + std::string::size_type colon_pos = in.find(':'); + if (colon_pos == std::string::npos) + FC_THROW("Missing required port number in endpoint string \"${endpoint_string}\"", + ("endpoint_string", in)); + std::string port_string = in.substr(colon_pos + 1); + try + { + uint16_t port = boost::lexical_cast(port_string); + + std::string hostname = in.substr(0, colon_pos); + std::vector endpoints = fc::resolve(hostname, port); + if (endpoints.empty()) + FC_THROW_EXCEPTION( fc::unknown_host_exception, + "The host name can not be resolved: ${hostname}", + ("hostname", hostname) ); + return endpoints; + } + catch (const boost::bad_lexical_cast&) + { + FC_THROW("Bad port: ${port}", ("port", port_string)); + } + } + FC_CAPTURE_AND_RETHROW((in)) + } + void node::add_seed_nodes(std::vector seeds) + { + for(const std::string& endpoint_string : seeds ) + { + try { + add_seed_node(endpoint_string); + } catch( const fc::exception& e ) { + wlog( "caught exception ${e} while adding seed node ${endpoint}", + ("e", e.to_detail_string())("endpoint", endpoint_string) ); + } + } + } } } // end namespace graphene::net diff --git a/libraries/net/peer_database.cpp b/libraries/net/peer_database.cpp index 76ae9c8c..20bc5da9 100644 --- a/libraries/net/peer_database.cpp +++ b/libraries/net/peer_database.cpp @@ -50,7 +50,8 @@ namespace graphene { namespace net { indexed_by, member >, + &potential_peer_record::last_seen_time>, + std::greater >, hashed_unique, member Date: Wed, 7 Sep 2022 13:57:17 +0000 Subject: [PATCH 2/6] Update delayed node feature --- libraries/app/application.cpp | 5 +- .../delayed_node/delayed_node_plugin.cpp | 39 ++- programs/CMakeLists.txt | 1 - programs/delayed_node/CMakeLists.txt | 21 -- programs/delayed_node/main.cpp | 305 ------------------ programs/witness_node/CMakeLists.txt | 2 +- programs/witness_node/main.cpp | 2 + 7 files changed, 30 insertions(+), 345 deletions(-) delete mode 100644 programs/delayed_node/CMakeLists.txt delete mode 100644 programs/delayed_node/main.cpp diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 1a3d3a8a..b29cd87b 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -917,7 +917,8 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti wanted.insert("accounts_list"); wanted.insert("affiliate_stats"); } - wanted.insert("witness"); + if (!wanted.count("delayed_node") && !wanted.count("witness")) // explicitly requested delayed_node functionality suppresses witness functions + wanted.insert("witness"); wanted.insert("bookie"); int es_ah_conflict_counter = 0; @@ -949,7 +950,7 @@ void application::startup() { } std::shared_ptr application::get_plugin(const string &name) const { - return my->_active_plugins[name]; + return is_plugin_enabled(name) ? my->_active_plugins[name] : nullptr; } bool application::is_plugin_enabled(const string &name) const { diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index 71de7db5..99b02378 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -63,8 +63,24 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c void delayed_node_plugin::connect() { - my->client_connection = std::make_shared(my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS); + fc::http::websocket_connection_ptr con; + try + { + con = my->client.connect(my->remote_endpoint); + } + catch( const fc::exception& e ) + { + wlog("Error while connecting: ${e}", ("e", e.to_detail_string())); + connection_failed(); + return; + } + my->client_connection = std::make_shared( + con, GRAPHENE_NET_MAX_NESTED_OBJECTS ); my->database_api = my->client_connection->get_remote_api(0); + my->database_api->set_block_applied_callback([this]( const fc::variant& block_id ) + { + fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS ); + } ); my->client_connection_closed = my->client_connection->closed.connect([this] { connection_failed(); }); @@ -73,7 +89,9 @@ void delayed_node_plugin::connect() void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options) { FC_ASSERT(options.count("trusted-node") > 0); + ilog("delayed_node_plugin: plugin_initialize() begin"); my->remote_endpoint = "ws://" + options.at("trusted-node").as(); + ilog("delayed_node_plugin: plugin_initialize() end"); } void delayed_node_plugin::sync_with_trusted_node() @@ -100,8 +118,11 @@ void delayed_node_plugin::sync_with_trusted_node() while( remote_dpo.last_irreversible_block_num > db.head_block_num() ) { fc::optional block = my->database_api->get_block( db.head_block_num()+1 ); + // TODO: during sync, decouple requesting blocks from preprocessing + applying them FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have."); ilog("Pushing block #${n}", ("n", block->block_num())); + // timur: failed to merge from bitshares, API n/a in peerplays + // db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait(); db.push_block(*block); synced_blocks++; } @@ -136,24 +157,12 @@ void delayed_node_plugin::plugin_startup() mainloop(); }); - try - { - connect(); - my->database_api->set_block_applied_callback([this]( const fc::variant& block_id ) - { - fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS ); - } ); - return; - } - catch (const fc::exception& e) - { - elog("Error during connection: ${e}", ("e", e.to_detail_string())); - } - fc::async([this]{connection_failed();}); + connect(); } void delayed_node_plugin::connection_failed() { + my->last_received_remote_head = my->last_processed_remote_head; elog("Connection to trusted node failed; retrying in 5 seconds..."); fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5)); } diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index d9c82346..7b9b9918 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -4,7 +4,6 @@ if( BUILD_PEERPLAYS_PROGRAMS ) add_subdirectory( genesis_util ) add_subdirectory( witness_node ) add_subdirectory( debug_node ) - add_subdirectory( delayed_node ) add_subdirectory( js_operation_serializer ) add_subdirectory( size_checker ) endif( BUILD_PEERPLAYS_PROGRAMS ) diff --git a/programs/delayed_node/CMakeLists.txt b/programs/delayed_node/CMakeLists.txt deleted file mode 100644 index 7e610ace..00000000 --- a/programs/delayed_node/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -add_executable( delayed_node main.cpp ) -if( UNIX AND NOT APPLE ) - set(rt_library rt ) -endif() - -find_package( Gperftools QUIET ) -if( GPERFTOOLS_FOUND ) - message( STATUS "Found gperftools; compiling delayed_node with TCMalloc") - list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc ) -endif() - -target_link_libraries( delayed_node - PRIVATE graphene_app graphene_egenesis_full graphene_delayed_node ${PLATFORM_SPECIFIC_LIBS} ) - -install( TARGETS - delayed_node - - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib -) diff --git a/programs/delayed_node/main.cpp b/programs/delayed_node/main.cpp deleted file mode 100644 index 3e058b64..00000000 --- a/programs/delayed_node/main.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#ifdef WIN32 -# include -#else -# include -#endif - -using namespace graphene; -namespace bpo = boost::program_options; - -void write_default_logging_config_to_stream(std::ostream& out); -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename); - -int main(int argc, char** argv) { - try { - app::application node; - bpo::options_description app_options("Graphene Delayed Node"); - bpo::options_description cfg_options("Graphene Delayed Node"); - app_options.add_options() - ("help,h", "Print this help message and exit.") - ("data-dir,d", bpo::value()->default_value("delayed_node_data_dir"), "Directory containing databases, configuration file, etc.") - ; - - bpo::variables_map options; - - bpo::options_description cli, cfg; - node.set_program_options(cli, cfg); - cfg_options.add(cfg); - - cfg_options.add_options() - ("plugins", bpo::value()->default_value("delayed_node account_history market_history"), - "Space-separated list of plugins to activate"); - - auto delayed_plug = node.register_plugin(); - auto history_plug = node.register_plugin(); - auto market_history_plug = node.register_plugin(); - - // add plugin options to config - try - { - bpo::options_description cli, cfg; - node.set_program_options(cli, cfg); - app_options.add(cli); - cfg_options.add(cfg); - bpo::store(bpo::parse_command_line(argc, argv, app_options), options); - } - catch (const boost::program_options::error& e) - { - std::cerr << "Error parsing command line: " << e.what() << "\n"; - return 1; - } - - if( options.count("help") ) - { - std::cout << app_options << "\n"; - return 0; - } - - fc::path data_dir; - if( options.count("data-dir") ) - { - data_dir = options["data-dir"].as(); - if( data_dir.is_relative() ) - data_dir = fc::current_path() / data_dir; - } - - fc::path config_ini_path = data_dir / "config.ini"; - // Create config file if not already present - if( !fc::exists(config_ini_path) ) - { - ilog("Writing new config file at ${path}", ("path", config_ini_path)); - if( !fc::exists(data_dir) ) - fc::create_directories(data_dir); - - std::ofstream out_cfg(config_ini_path.preferred_string()); - for( const boost::shared_ptr od : cfg_options.options() ) - { - if( !od->description().empty() ) - out_cfg << "# " << od->description() << "\n"; - boost::any store; - if( !od->semantic()->apply_default(store) ) - out_cfg << "# " << od->long_name() << " = \n"; - else { - auto example = od->format_parameter(); - if( example.empty() ) - // This is a boolean switch - out_cfg << od->long_name() << " = " << "false\n"; - else { - // The string is formatted "arg (=)" - example.erase(0, 6); - example.erase(example.length()-1); - out_cfg << od->long_name() << " = " << example << "\n"; - } - } - out_cfg << "\n"; - } - write_default_logging_config_to_stream(out_cfg); - out_cfg.close(); - // read the default logging config we just wrote out to the file and start using it - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } - - // Parse configuration file - try { - bpo::store(bpo::parse_config_file(config_ini_path.preferred_string().c_str(), cfg_options, true), options); - // try to get logging options from the config file. - try - { - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } - catch (const fc::exception&) - { - wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string())); - } - - bpo::notify(options); - } catch( const boost::program_options::error& e ) { - elog("Error parsing configuration file: ${e}", ("e", e.what())); - return 1; - } - - if( !options.count("plugins") ) - options.insert( std::make_pair( "plugins", bpo::variable_value(std::string("delayed_node account_history market_history"), true) ) ); - - node.initialize(data_dir, options); - node.initialize_plugins( options ); - - node.startup(); - node.startup_plugins(); - - fc::promise::ptr exit_promise = new fc::promise("UNIX Signal Handler"); - fc::set_signal_handler([&exit_promise](int signal) { - exit_promise->set_value(signal); - }, SIGINT); - - ilog("Started delayed node on a chain with ${h} blocks.", ("h", node.chain_database()->head_block_num())); - ilog("Chain ID is ${id}", ("id", node.chain_database()->get_chain_id()) ); - - int signal = exit_promise->wait(); - ilog("Exiting from signal ${n}", ("n", signal)); - node.shutdown_plugins(); - return 0; - } catch( const fc::exception& e ) { - elog("Exiting with error:\n${e}", ("e", e.to_detail_string())); - return 1; - } -} - -// logging config is too complicated to be parsed by boost::program_options, -// so we do it by hand -// -// Currently, you can only specify the filenames and logging levels, which -// are all most users would want to change. At a later time, options can -// be added to control rotation intervals, compression, and other seldom- -// used features -void write_default_logging_config_to_stream(std::ostream& out) -{ - out << "# declare an appender named \"stderr\" that writes messages to the console\n" - "[log.console_appender.stderr]\n" - "stream=std_error\n\n" - "# declare an appender named \"p2p\" that writes messages to p2p.log\n" - "[log.file_appender.p2p]\n" - "filename=logs/p2p/p2p.log\n" - "# filename can be absolute or relative to this config file\n\n" - "# route any messages logged to the default logger to the \"stderr\" logger we\n" - "# declared above, if they are info level are higher\n" - "[logger.default]\n" - "level=info\n" - "appenders=stderr\n\n" - "# route messages sent to the \"p2p\" logger to the p2p appender declared above\n" - "[logger.p2p]\n" - "level=info\n" - "appenders=p2p\n\n"; -} - -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename) -{ - try - { - fc::logging_config logging_config; - bool found_logging_config = false; - - boost::property_tree::ptree config_ini_tree; - boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree); - for (const auto& section : config_ini_tree) - { - const std::string& section_name = section.first; - const boost::property_tree::ptree& section_tree = section.second; - - const std::string console_appender_section_prefix = "log.console_appender."; - const std::string file_appender_section_prefix = "log.file_appender."; - const std::string logger_section_prefix = "logger."; - - if (boost::starts_with(section_name, console_appender_section_prefix)) - { - std::string console_appender_name = section_name.substr(console_appender_section_prefix.length()); - std::string stream_name = section_tree.get("stream"); - - // construct a default console appender config here - // stdout/stderr will be taken from ini file, everything else hard-coded here - fc::console_appender::config console_appender_config; - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::debug, - fc::console_appender::color::green)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::warn, - fc::console_appender::color::brown)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::error, - fc::console_appender::color::cyan)); - console_appender_config.stream = fc::variant(stream_name, 1).as(1); - logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config, GRAPHENE_MAX_NESTED_OBJECTS))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, file_appender_section_prefix)) - { - std::string file_appender_name = section_name.substr(file_appender_section_prefix.length()); - fc::path file_name = section_tree.get("filename"); - if (file_name.is_relative()) - file_name = fc::absolute(config_ini_filename).parent_path() / file_name; - - - // construct a default file appender config here - // filename will be taken from ini file, everything else hard-coded here - fc::file_appender::config file_appender_config; - file_appender_config.filename = file_name; - file_appender_config.flush = true; - file_appender_config.rotate = true; - file_appender_config.rotation_interval = fc::hours(1); - file_appender_config.rotation_limit = fc::days(1); - logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config, GRAPHENE_MAX_NESTED_OBJECTS))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, logger_section_prefix)) - { - std::string logger_name = section_name.substr(logger_section_prefix.length()); - std::string level_string = section_tree.get("level"); - std::string appenders_string = section_tree.get("appenders"); - fc::logger_config logger_config(logger_name); - logger_config.level = fc::variant(level_string, 1).as(1); - boost::split(logger_config.appenders, appenders_string, - boost::is_any_of(" ,"), - boost::token_compress_on); - logging_config.loggers.push_back(logger_config); - found_logging_config = true; - } - } - if (found_logging_config) - return logging_config; - else - return fc::optional(); - } - FC_RETHROW_EXCEPTIONS(warn, "") -} diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index 806330d6..d2235034 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -11,7 +11,7 @@ endif() # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 target_link_libraries( witness_node - PRIVATE graphene_app graphene_egenesis_full graphene_snapshot graphene_witness peerplays_sidechain ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_app graphene_egenesis_full graphene_snapshot graphene_delayed_node graphene_witness peerplays_sidechain ${PLATFORM_SPECIFIC_LIBS} ) # also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins install( TARGETS diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index e1c6c12e..b3c0aa37 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -90,6 +91,7 @@ int main(int argc, char** argv) { auto bookie_plug = node->register_plugin(); auto peerplays_sidechain = node->register_plugin(); auto snapshot_plug = node->register_plugin(); + auto delayed_plug = node->register_plugin(); // add plugin options to config try From b895b52b7bcbfea3fc7e4f33edf372053242aba3 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Thu, 8 Sep 2022 16:58:08 +0200 Subject: [PATCH 3/6] Cherrypick important fixes/cosmetics from feature/son-for-ethereum --- libraries/app/application.cpp | 4 +- .../peerplays_sidechain/common/rpc_client.cpp | 16 ++- .../peerplays_sidechain/common/utils.cpp | 42 ++++++++ .../peerplays_sidechain/common/rpc_client.hpp | 7 +- .../peerplays_sidechain/common/utils.hpp | 7 ++ .../sidechain_net_handler_bitcoin.hpp | 7 +- .../sidechain_net_handler_hive.hpp | 15 +-- .../peerplays_sidechain_plugin.cpp | 22 ++--- .../sidechain_net_handler_bitcoin.cpp | 14 +-- .../sidechain_net_handler_hive.cpp | 98 +++++++++---------- 10 files changed, 138 insertions(+), 94 deletions(-) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index b29cd87b..db9ef028 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -917,8 +917,8 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti wanted.insert("accounts_list"); wanted.insert("affiliate_stats"); } - if (!wanted.count("delayed_node") && !wanted.count("witness")) // explicitly requested delayed_node functionality suppresses witness functions - wanted.insert("witness"); + if (!wanted.count("delayed_node") && !wanted.count("witness")) // explicitly requested delayed_node functionality suppresses witness functions + wanted.insert("witness"); wanted.insert("bookie"); int es_ah_conflict_counter = 0; diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index 173019eb..4c1365f3 100644 --- a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp @@ -2,23 +2,20 @@ #include #include -#include - -#include -#include -#include #include #include #include #include #include +#include +#include +#include -#include - -#include #include +#include + namespace graphene { namespace peerplays_sidechain { rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : @@ -55,7 +52,8 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor target = "/"; } - authorization = "Basic " + fc::base64_encode(user + ":" + password); + authorization = "Basic " + base64_encode(user + ":" + password); + results = resolver.resolve(host, port); } else { diff --git a/libraries/plugins/peerplays_sidechain/common/utils.cpp b/libraries/plugins/peerplays_sidechain/common/utils.cpp index 4491487f..5bd1dfd7 100644 --- a/libraries/plugins/peerplays_sidechain/common/utils.cpp +++ b/libraries/plugins/peerplays_sidechain/common/utils.cpp @@ -1,8 +1,50 @@ #include +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +const std::string base64_padding[] = {"", "==", "="}; + +std::string base64_encode(const std::string &s) { + using namespace boost::archive::iterators; + + typedef base64_from_binary> base64_enc; + + std::stringstream os; + std::copy(base64_enc(s.c_str()), base64_enc(s.c_str() + s.size()), std::ostream_iterator(os)); + os << base64_padding[s.size() % 3]; + + return os.str(); +} + +std::string base64_decode(const std::string &s) { + using namespace boost::archive::iterators; + + typedef transform_width, 8, 6> base64_dec; + + std::stringstream os; + unsigned int size = s.size(); + if (size && s[size - 1] == '=') { + --size; + if (size && s[size - 1] == '=') + --size; + } + if (size == 0) + return std::string(); + + std::copy(base64_dec(s.data()), base64_dec(s.data() + size), std::ostream_iterator(os)); + + return os.str(); +} + std::string object_id_to_string(graphene::chain::object_id_type id) { std::string object_id = fc::to_string(id.space()) + "." + fc::to_string(id.type()) + "." + fc::to_string(id.instance()); return object_id; } + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp index 1a797782..eb8eac0c 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp @@ -23,15 +23,16 @@ protected: std::string send_post_request(std::string method, std::string params, bool show_log); std::string url; + std::string user; + std::string password; + bool debug_rpc_calls; + std::string protocol; std::string host; std::string port; std::string target; std::string authorization; - std::string user; - std::string password; - bool debug_rpc_calls; uint32_t request_id; private: diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp index 99c59019..066a36fe 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/utils.hpp @@ -2,4 +2,11 @@ #include +namespace graphene { namespace peerplays_sidechain { + +std::string base64_encode(const std::string &s); +std::string base64_decode(const std::string &s); + std::string object_id_to_string(graphene::chain::object_id_type id); + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 9a858cb7..62eeeab6 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -63,7 +63,7 @@ private: uint32_t rpc_port; std::string user; std::string password; - std::string wallet; + std::string wallet_name; std::string wallet_password; }; @@ -111,17 +111,18 @@ private: std::string ip; uint32_t zmq_port; uint32_t rpc_port; - uint32_t bitcoin_major_version; std::string rpc_user; std::string rpc_password; - std::string wallet; + std::string wallet_name; std::string wallet_password; std::unique_ptr bitcoin_client; std::unique_ptr listener; fc::future on_changed_objects_task; + bitcoin::bitcoin_address::network network_type; + uint32_t bitcoin_major_version; std::mutex event_handler_mutex; typedef std::lock_guard scoped_lock; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp index 47e6bf90..72539db2 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp @@ -6,15 +6,14 @@ #include -#include #include #include namespace graphene { namespace peerplays_sidechain { -class hive_node_rpc_client : public rpc_client { +class hive_rpc_client : public rpc_client { public: - hive_node_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); std::string account_history_api_get_transaction(std::string transaction_id); std::string block_api_get_block(uint32_t block_number); @@ -48,10 +47,12 @@ public: bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); private: - std::string node_rpc_url; - std::string node_rpc_user; - std::string node_rpc_password; - hive_node_rpc_client *node_rpc_client; + std::string rpc_url; + std::string rpc_user; + std::string rpc_password; + std::string wallet_account_name; + + hive_rpc_client *rpc_client; hive::chain_id_type chain_id; hive::network network_type; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 461b1a92..12de3dfd 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -172,7 +172,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); cli.add_options()("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password"); - cli.add_options()("bitcoin-wallet", bpo::value(), "Bitcoin wallet"); + cli.add_options()("bitcoin-wallet-name", bpo::value(), "Bitcoin wallet name"); cli.add_options()("bitcoin-wallet-password", bpo::value(), "Bitcoin wallet password"); cli.add_options()("bitcoin-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); @@ -181,6 +181,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("hive-node-rpc-url", bpo::value()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-user", bpo::value(), "Hive node RPC user"); cli.add_options()("hive-node-rpc-password", bpo::value(), "Hive node RPC password"); + cli.add_options()("hive-wallet-account-name", bpo::value(), "Hive wallet account name"); cli.add_options()("hive-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4", "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n")), "Tuple of [Hive public key, Hive private key] (may specify multiple times)"); @@ -231,9 +232,9 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt config_ready_bitcoin = options.count("bitcoin-node-ip") && options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") && - /*options.count("bitcoin-wallet") && options.count("bitcoin-wallet-password") &&*/ + options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") && options.count("bitcoin-private-key"); - if (!config_ready_bitcoin) { + if (sidechain_enabled_bitcoin && !config_ready_bitcoin) { wlog("Haven't set up Bitcoin sidechain parameters"); } @@ -248,28 +249,21 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt sidechain_enabled_hive = options.at("hive-sidechain-enabled").as(); config_ready_hive = options.count("hive-node-rpc-url") && /*options.count("hive-node-rpc-user") && options.count("hive-node-rpc-password") &&*/ + options.count("hive-wallet-account-name") && options.count("hive-private-key"); - if (!config_ready_hive) { + if (sidechain_enabled_hive && !config_ready_hive) { wlog("Haven't set up Hive sidechain parameters"); } #ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS - sidechain_enabled_peerplays = true; //options.at("peerplays-sidechain-enabled").as(); + sidechain_enabled_peerplays = true; #else sidechain_enabled_peerplays = false; #endif config_ready_peerplays = true; - if (!config_ready_peerplays) { + if (sidechain_enabled_peerplays && !config_ready_peerplays) { wlog("Haven't set up Peerplays sidechain parameters"); } - - if (!(config_ready_bitcoin && - /*config_ready_ethereum &&*/ - config_ready_hive && - config_ready_peerplays)) { - wlog("Haven't set up any sidechain parameters"); - throw; - } } void peerplays_sidechain_plugin_impl::plugin_startup() { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index b48ccbb6..cace6fb3 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -334,9 +334,9 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain rpc_port = options.at("bitcoin-node-rpc-port").as(); rpc_user = options.at("bitcoin-node-rpc-user").as(); rpc_password = options.at("bitcoin-node-rpc-password").as(); - wallet = ""; - if (options.count("bitcoin-wallet")) { - wallet = options.at("bitcoin-wallet").as(); + wallet_name = ""; + if (options.count("bitcoin-wallet-name")) { + wallet_name = options.at("bitcoin-wallet-name").as(); } wallet_password = ""; if (options.count("bitcoin-wallet-password")) { @@ -356,13 +356,13 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } std::string url = ip + ":" + std::to_string(rpc_port); - if (wallet.length() > 0) { - url = url + "/wallet/" + wallet; + if (!wallet_name.empty()) { + url = url + "/wallet/" + wallet_name; } bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); - if (!wallet.empty()) { - bitcoin_client->loadwallet(wallet); + if (!wallet_name.empty()) { + bitcoin_client->loadwallet(wallet_name); } std::string blockchain_info = bitcoin_client->getblockchaininfo(); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index c62911ec..7d3c4de9 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -28,25 +28,23 @@ #include #include -#include - namespace graphene { namespace peerplays_sidechain { -hive_node_rpc_client::hive_node_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : +hive_rpc_client::hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : rpc_client(url, user_name, password, debug_rpc_calls) { } -std::string hive_node_rpc_client::account_history_api_get_transaction(std::string transaction_id) { +std::string hive_rpc_client::account_history_api_get_transaction(std::string transaction_id) { std::string params = "{ \"id\": \"" + transaction_id + "\" }"; return send_post_request("account_history_api.get_transaction", params, debug_rpc_calls); } -std::string hive_node_rpc_client::block_api_get_block(uint32_t block_number) { +std::string hive_rpc_client::block_api_get_block(uint32_t block_number) { std::string params = "{ \"block_num\": " + std::to_string(block_number) + " }"; return send_post_request("block_api.get_block", params, debug_rpc_calls); } -std::string hive_node_rpc_client::condenser_api_get_accounts(std::vector accounts) { +std::string hive_rpc_client::condenser_api_get_accounts(std::vector accounts) { std::string params = ""; for (auto account : accounts) { if (!params.empty()) { @@ -58,58 +56,58 @@ std::string hive_node_rpc_client::condenser_api_get_accounts(std::vector accounts; accounts.push_back(account); std::string reply_str = condenser_api_get_accounts(accounts); return retrieve_array_value_from_reply(reply_str, "", 0); } -std::string hive_node_rpc_client::get_account_memo_key(std::string account) { +std::string hive_rpc_client::get_account_memo_key(std::string account) { std::string reply_str = get_account(account); reply_str = "{\"result\":" + reply_str + "}"; return retrieve_value_from_reply(reply_str, "memo_key"); } -std::string hive_node_rpc_client::get_chain_id() { +std::string hive_rpc_client::get_chain_id() { std::string reply_str = database_api_get_version(); return retrieve_value_from_reply(reply_str, "chain_id"); } -std::string hive_node_rpc_client::get_head_block_id() { +std::string hive_rpc_client::get_head_block_id() { std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "head_block_id"); } -std::string hive_node_rpc_client::get_head_block_time() { +std::string hive_rpc_client::get_head_block_time() { std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "time"); } -std::string hive_node_rpc_client::get_is_test_net() { +std::string hive_rpc_client::get_is_test_net() { std::string reply_str = condenser_api_get_config(); return retrieve_value_from_reply(reply_str, "IS_TEST_NET"); } -std::string hive_node_rpc_client::get_last_irreversible_block_num() { +std::string hive_rpc_client::get_last_irreversible_block_num() { std::string reply_str = database_api_get_dynamic_global_properties(); return retrieve_value_from_reply(reply_str, "last_irreversible_block_num"); } @@ -122,18 +120,20 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi debug_rpc_calls = options.at("debug-rpc-calls").as(); } - node_rpc_url = options.at("hive-node-rpc-url").as(); - if (options.count("hive-node-rpc-user")) { - node_rpc_user = options.at("hive-node-rpc-user").as(); + rpc_url = options.at("hive-node-rpc-url").as(); + if (options.count("hive-rpc-user")) { + rpc_user = options.at("hive-rpc-user").as(); } else { - node_rpc_user = ""; + rpc_user = ""; } - if (options.count("hive-node-rpc-password")) { - node_rpc_password = options.at("hive-node-rpc-password").as(); + if (options.count("hive-rpc-password")) { + rpc_password = options.at("hive-rpc-password").as(); } else { - node_rpc_password = ""; + rpc_password = ""; } + wallet_account_name = options.at("hive-wallet-account-name").as(); + if (options.count("hive-private-key")) { const std::vector pub_priv_keys = options["hive-private-key"].as>(); for (const std::string &itr_key_pair : pub_priv_keys) { @@ -146,16 +146,16 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi } } - node_rpc_client = new hive_node_rpc_client(node_rpc_url, node_rpc_user, node_rpc_password, debug_rpc_calls); + rpc_client = new hive_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); - std::string chain_id_str = node_rpc_client->get_chain_id(); + std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { - elog("No Hive node running at ${url}", ("url", node_rpc_url)); + elog("No Hive node running at ${url}", ("url", rpc_url)); FC_ASSERT(false); } chain_id = chain_id_type(chain_id_str); - std::string is_test_net = node_rpc_client->get_is_test_net(); + std::string is_test_net = rpc_client->get_is_test_net(); network_type = is_test_net.compare("true") == 0 ? hive::network::testnet : hive::network::mainnet; if (network_type == hive::network::mainnet) { ilog("Running on Hive mainnet, chain id ${chain_id_str}", ("chain_id_str", chain_id_str)); @@ -225,7 +225,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { } if (son_sets_equal) { - address_ok = (op_obj_idx_0.get().address == "son-account"); + address_ok = (op_obj_idx_0.get().address == wallet_account_name); } if (po.proposed_transaction.operations.size() >= 2) { @@ -254,14 +254,14 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { account_auths[wallet_son.public_key] = wallet_son.weight; } - std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); + std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); hive::authority active; active.weight_threshold = total_weight * 2 / 3 + 1; active.account_auths = account_auths; hive::account_update_operation auo; - auo.account = "son-account"; + auo.account = wallet_account_name; auo.active = active; auo.memo_key = op_trx.operations[0].get().memo_key; @@ -303,7 +303,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value; uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-"))); - std::string tx_str = node_rpc_client->account_history_api_get_transaction(swdo_txid); + std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid); if (tx_str != "") { std::stringstream ss_tx(tx_str); @@ -408,7 +408,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { } hive::transfer_operation t_op; - t_op.from = "son-account"; + t_op.from = wallet_account_name; t_op.to = swwo->withdraw_address; t_op.amount.amount = swwo->withdraw_amount; t_op.amount.symbol = symbol; @@ -495,7 +495,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { account_auths[active_son.public_key] = active_son.weight; } - std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); + std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); if (memo_key.empty()) { return; @@ -506,14 +506,14 @@ void sidechain_net_handler_hive::process_primary_wallet() { active.account_auths = account_auths; hive::account_update_operation auo; - auo.account = "son-account"; + auo.account = wallet_account_name; auo.active = active; auo.memo_key = hive::public_key_type(memo_key); - std::string block_id_str = node_rpc_client->get_head_block_id(); + std::string block_id_str = rpc_client->get_head_block_id(); hive::block_id_type head_block_id(block_id_str); - std::string head_block_time_str = node_rpc_client->get_head_block_time(); + std::string head_block_time_str = rpc_client->get_head_block_time(); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str); hive::signed_transaction htrx; @@ -538,7 +538,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { swu_op.payer = gpo.parameters.son_account(); swu_op.son_wallet_id = active_sw->id; swu_op.sidechain = sidechain; - swu_op.address = "son-account"; + swu_op.address = wallet_account_name; proposal_op.proposed_ops.emplace_back(swu_op); @@ -662,16 +662,16 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob } hive::transfer_operation t_op; - t_op.from = "son-account"; + t_op.from = wallet_account_name; t_op.to = swwo.withdraw_address; t_op.amount.amount = swwo.withdraw_amount; t_op.amount.symbol = symbol; t_op.memo = ""; - std::string block_id_str = node_rpc_client->get_head_block_id(); + std::string block_id_str = rpc_client->get_head_block_id(); hive::block_id_type head_block_id(block_id_str); - std::string head_block_time_str = node_rpc_client->get_head_block_time(); + std::string head_block_time_str = rpc_client->get_head_block_time(); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str); hive::signed_transaction htrx; @@ -727,7 +727,7 @@ std::string sidechain_net_handler_hive::process_sidechain_transaction(const side hive::signed_transaction htrx; fc::raw::unpack(ss_trx, htrx, 1000); - std::string chain_id_str = node_rpc_client->get_chain_id(); + std::string chain_id_str = rpc_client->get_chain_id(); const hive::chain_id_type chain_id(chain_id_str); fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain))); @@ -755,7 +755,7 @@ std::string sidechain_net_handler_hive::send_sidechain_transaction(const sidecha } std::string params = fc::json::to_string(htrx); - node_rpc_client->network_broadcast_api_broadcast_transaction(params); + rpc_client->network_broadcast_api_broadcast_transaction(params); return htrx.id().str(); } @@ -770,7 +770,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr return false; } - std::string tx_str = node_rpc_client->account_history_api_get_transaction(sto.sidechain_transaction); + std::string tx_str = rpc_client->account_history_api_get_transaction(sto.sidechain_transaction); if (tx_str != "") { std::stringstream ss_tx(tx_str); @@ -781,7 +781,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr std::string tx_txid = tx_json.get("result.transaction_id"); uint32_t tx_block_num = tx_json.get("result.block_num"); - uint32_t last_irreversible_block = std::stoul(node_rpc_client->get_last_irreversible_block_num()); + uint32_t last_irreversible_block = std::stoul(rpc_client->get_last_irreversible_block_num()); //std::string tx_address = addr.get_address(); //int64_t tx_amount = -1; @@ -817,7 +817,7 @@ void sidechain_net_handler_hive::schedule_hive_listener() { void sidechain_net_handler_hive::hive_listener_loop() { schedule_hive_listener(); - std::string reply = node_rpc_client->database_api_get_dynamic_global_properties(); + std::string reply = rpc_client->database_api_get_dynamic_global_properties(); if (!reply.empty()) { std::stringstream ss(reply); boost::property_tree::ptree json; @@ -832,7 +832,7 @@ void sidechain_net_handler_hive::hive_listener_loop() { } } - //std::string reply = node_rpc_client->get_last_irreversible_block_num(); + //std::string reply = rpc_client->get_last_irreversible_block_num(); //if (!reply.empty()) { // uint64_t last_irreversible_block = std::stoul(reply); // if (last_irreversible_block != last_block_received) { @@ -844,7 +844,7 @@ void sidechain_net_handler_hive::hive_listener_loop() { } void sidechain_net_handler_hive::handle_event(const std::string &event_data) { - std::string block = node_rpc_client->block_api_get_block(std::atoll(event_data.c_str())); + std::string block = rpc_client->block_api_get_block(std::atoll(event_data.c_str())); if (block != "") { add_to_son_listener_log("BLOCK : " + event_data); std::stringstream ss(block); @@ -869,7 +869,7 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) { std::string from = op_value.get("from"); std::string to = op_value.get("to"); - if (to == "son-account") { + if (to == wallet_account_name) { const auto &amount_child = op_value.get_child("amount"); From 0f64947f4a86c17c3ec4931716f0b447b2c3e76c Mon Sep 17 00:00:00 2001 From: Meheboob Khan Date: Wed, 14 Sep 2022 18:03:40 +0000 Subject: [PATCH 4/6] Improved get_active_sons and get_son_network_status API/CLI [issue 430] --- libraries/app/database_api.cpp | 78 ++++++++++++++++++ .../app/include/graphene/app/database_api.hpp | 30 +++++++ .../wallet/include/graphene/wallet/wallet.hpp | 29 ++++++- libraries/wallet/wallet.cpp | 81 ++++++++----------- 4 files changed, 167 insertions(+), 51 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b10d6b99..3158ba49 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -184,6 +184,10 @@ public: fc::optional get_son_by_account(const std::string account_id_or_name) const; map lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const; uint64_t get_son_count() const; + flat_map> get_active_sons(); + vector get_active_sons_by_sidechain(sidechain_type sidechain); + map> get_son_network_status(); + map get_son_network_status_by_sidechain(sidechain_type sidechain); // SON wallets optional get_active_son_wallet(); @@ -1848,6 +1852,80 @@ uint64_t database_api_impl::get_son_count() const { return _db.get_index_type().indices().size(); } +flat_map> database_api::get_active_sons() { + return my->get_active_sons(); +} + +flat_map> database_api_impl::get_active_sons() { + return get_global_properties().active_sons; +} + +vector database_api::get_active_sons_by_sidechain(sidechain_type sidechain) { + return my->get_active_sons_by_sidechain(sidechain); +} + +vector database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) { + const global_property_object &gpo = get_global_properties(); + + vector result; + + if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) { + result = gpo.active_sons.at(sidechain); + } + + return result; +} + +map> database_api::get_son_network_status() { + return my->get_son_network_status(); +} + +map> database_api_impl::get_son_network_status() { + map> result; + + for (auto active_sidechain_type : active_sidechain_types) { + result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type); + } + + return result; +} + +map database_api::get_son_network_status_by_sidechain(sidechain_type sidechain) { + return my->get_son_network_status_by_sidechain(sidechain); +} + +map database_api_impl::get_son_network_status_by_sidechain(sidechain_type sidechain) { + const global_property_object &gpo = get_global_properties(); + + map result; + + if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) { + for (const auto si : gpo.active_sons.at(sidechain)) { + const auto son_obj = si.son_id(_db); + const auto sso = son_obj.statistics(_db); + string status; + + if (sso.last_active_timestamp.find(sidechain) != sso.last_active_timestamp.end()) { + if (sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { + status = "OK, regular SON heartbeat"; + } else { + if (sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { + status = "OK, irregular SON heartbeat, but not triggering SON down proposal"; + } else { + status = "NOT OK, irregular SON heartbeat, triggering SON down proposal]"; + } + } + } else { + status = "No heartbeats sent"; + } + + result[si.son_id] = status; + } + } + + return result; +} + ////////////////////////////////////////////////////////////////////// // // // SON Wallets // diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index abe65a2b..b53c8eeb 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -675,6 +675,32 @@ public: */ uint64_t get_son_count() const; + /** + * @brief Get list of active sons + * @return List of active SONs + */ + flat_map> get_active_sons(); + + /** + * @brief Get list of active sons + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return List of active SONs + */ + vector get_active_sons_by_sidechain(sidechain_type sidechain); + + /** + * @brief Get SON network status + * @return SON network status description for a given sidechain type + */ + map> get_son_network_status(); + + /** + * @brief Get SON network status + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return SON network status description for a given sidechain type + */ + map get_son_network_status_by_sidechain(sidechain_type sidechain); + ///////////////////////// // SON Wallets // ///////////////////////// @@ -1149,6 +1175,10 @@ FC_API(graphene::app::database_api, (get_son_by_account) (lookup_son_accounts) (get_son_count) + (get_active_sons) + (get_active_sons_by_sidechain) + (get_son_network_status) + (get_son_network_status_by_sidechain) // SON wallets (get_active_son_wallet) diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 0086c654..9850a72b 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1377,10 +1377,30 @@ class wallet_api map list_active_sons(); /** - * @brief Get SON network status - * @return SON network status description + * @brief Get list of active sons + * @return List of active SONs */ - map get_son_network_status(); + flat_map> get_active_sons(); + + /** + * @brief Get list of active sons + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return List of active SONs + */ + vector get_active_sons_by_sidechain(sidechain_type sidechain); + + /** + * @brief Get SON network status + * @return SON network status description for a given sidechain type + */ + map> get_son_network_status(); + + /** + * @brief Get SON network status + * @param sidechain Sidechain type [bitcoin|ethereum|hive] + * @return SON network status description for a given sidechain type + */ + map get_son_network_status_by_sidechain(sidechain_type sidechain); /** * @brief Get active SON wallet @@ -2599,7 +2619,10 @@ FC_API( graphene::wallet::wallet_api, (activate_deregistered_son) (list_sons) (list_active_sons) + (get_active_sons) + (get_active_sons_by_sidechain) (get_son_network_status) + (get_son_network_status_by_sidechain) (request_son_maintenance) (cancel_request_son_maintenance) (get_active_son_wallet) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 43ca49fd..49825790 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2274,55 +2274,25 @@ public: FC_CAPTURE_AND_RETHROW() } - //! Fixme - do we need to specify sidechain_type as params here? - map get_son_network_status() - { - try - { - const global_property_object& gpo = get_global_properties(); + flat_map> get_active_sons() + { try { + return _remote_db->get_active_sons(); + } FC_CAPTURE_AND_RETHROW() } - set son_ids_set; - for(const auto& active_sidechain_type : active_sidechain_types) { - std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), - std::inserter(son_ids_set, son_ids_set.end()), - [](const son_info &swi) { - return swi.son_id; - }); - } - vector son_ids; - son_ids.reserve(son_ids_set.size()); - std::transform(son_ids_set.cbegin(), son_ids_set.cend(), - std::inserter(son_ids, son_ids.end()), - [](const son_id_type& sit) { - return sit; - }); + vector get_active_sons_by_sidechain(sidechain_type sidechain) + { try { + return _remote_db->get_active_sons_by_sidechain(sidechain); + } FC_CAPTURE_AND_RETHROW() } - map result; - std::vector> son_objects = _remote_db->get_sons(son_ids); - for(auto son_obj: son_objects) { - string status; - if (son_obj) { - son_statistics_object sso = get_object(son_obj->statistics); - for(const auto& active_sidechain_type : active_sidechain_types) { - if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { - status = "[OK, regular SON heartbeat for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; - } else { - if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { - status = "[OK, irregular SON heartbeat, but not triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; - } else { - status = "[NOT OK, irregular SON heartbeat, triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; - } - } - } - } else { - status = "NOT OK, invalid SON id"; - } - result[son_obj->id] = status; - } - return result; - } - FC_CAPTURE_AND_RETHROW() - } + map> get_son_network_status() + { try { + return _remote_db->get_son_network_status(); + } FC_CAPTURE_AND_RETHROW() } + + map get_son_network_status_by_sidechain(sidechain_type sidechain) + { try { + return _remote_db->get_son_network_status_by_sidechain(sidechain); + } FC_CAPTURE_AND_RETHROW() } optional get_active_son_wallet() { try { @@ -5310,11 +5280,26 @@ map wallet_api::list_active_sons() return my->list_active_sons(); } -map wallet_api::get_son_network_status() +flat_map> wallet_api::get_active_sons() +{ + return my->get_active_sons(); +} + +vector wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain) +{ + return my->get_active_sons_by_sidechain(sidechain); +} + +map> wallet_api::get_son_network_status() { return my->get_son_network_status(); } +map wallet_api::get_son_network_status_by_sidechain(sidechain_type sidechain) +{ + return my->get_son_network_status_by_sidechain(sidechain); +} + optional wallet_api::get_active_son_wallet() { return my->get_active_son_wallet(); From a9267544def377f8d277dd3a142d7d4560434487 Mon Sep 17 00:00:00 2001 From: timur <12267899-timur.5@users.noreply.gitlab.com> Date: Fri, 16 Sep 2022 02:42:31 +0000 Subject: [PATCH 5/6] Bug/fix wallet api doc generation --- .../wallet/generate_api_documentation.pl | 115 ++++++++++++------ .../wallet/include/graphene/wallet/wallet.hpp | 56 +++++++-- 2 files changed, 128 insertions(+), 43 deletions(-) diff --git a/libraries/wallet/generate_api_documentation.pl b/libraries/wallet/generate_api_documentation.pl index a3f333db..3e1b2e06 100755 --- a/libraries/wallet/generate_api_documentation.pl +++ b/libraries/wallet/generate_api_documentation.pl @@ -44,13 +44,37 @@ for my $class (@{$doxydocs->{classes}}) if ($member->{kind} eq 'function') { my @params = map { join(' ', cleanupDoxygenType($_->{type}), $_->{declaration_name}) } @{$member->{parameters}}; - my $briefDescription = sprintf("%40s %s(%s)\n", cleanupDoxygenType($member->{type}), $member->{name}, join(', ', @params)); - my $escapedBriefDescription = "\"" . escapeStringForC($briefDescription) . "\""; - my %paramInfo = map { $_->{declaration_name} => { type => $_->{type}} } @{$member->{parameters}}; + my $callDescription = sprintf("%40s %s(%s)\n", cleanupDoxygenType($member->{type}), $member->{name}, join(', ', @params)); + my $escapedBriefDescription = "\"" . escapeStringForC($callDescription) . "\""; + my %paramInfo = map { $_->{declaration_name} => { type => explainCType(cleanupDoxygenType($_->{type})) } } @{$member->{parameters}}; my $escapedDetailedDescription = "\"\"\n"; - if ($member->{detailed}->{doc}) + my $doc = $member->{detailed}->{doc}; + if ($doc) { - my $docString = formatDocComment($member->{detailed}->{doc}, \%paramInfo); + my $briefDescr = formatDocComment($member->{brief}->{doc}); # get from the proper place + unless ($briefDescr =~ /\w/) # if not provided (API author forgot to add '@brief' comment), + { + for (my $i = 0; $i < @{$doc}; ++$i) # then look inside 'detailed' section + { + my $docElement = $doc->[$i]; + if ($docElement->{type} eq 'text' and $docElement->{content} =~ /\w+/) # use first meaningful line as brief description + { + $briefDescr = $docElement->{content}; + $briefDescr =~ s/^\s+|\s+$//g; + splice @{$doc}, $i, 1; # this section shouldn't be used twice + last; + } + } + } + + my $cmdSyntax = $member->{name}; + my $cmdArgs = join(' ', map { $_->{declaration_name} } @{$member->{parameters}}); + $cmdSyntax .= " $cmdArgs" if $cmdArgs; + + my $docString; + $docString .= $briefDescr; + $docString .= "\n\n" . formatDocComment($doc, \%paramInfo, $cmdSyntax); + for my $line (split(/\n/, $docString)) { $escapedDetailedDescription .= " \"" . escapeStringForC($line . "\n") . "\"\n"; @@ -96,62 +120,85 @@ sub cleanupDoxygenType return $type; } +sub explainCType +{ + my($type) = @_; + $type =~ s/\b\w+:://g; # remove namespaces + $type =~ s/^(?:optional|api)<(.+)>$/$1/; # disregard optional<> and some other templates + $type =~ s/^const\s+(.+)/$1/; # strip const modifier + $type =~ s/^(.+)&/$1/; # strip references + $type =~ s/\s+$/$1/; + $type =~ s/\b(u?int(8|16|32|64)_t|int|unsigned)\b/integer/; # spare the user from width and signedness + $type =~ s/\bbool\b/boolean/; # they're not C++ people + $type =~ s/^(?:vector|set|flat_set)<(.+)>$/[$1, ...]/; # represent as JSon-like array notation + $type =~ s/^(?:map|flat_map)<(.+)\s*,\s*(.+)>$/{$1 => $2, ...}/; # same for map + $type =~ s/^time_point_sec$/time, e.g. 2021-12-25T14:30:05/; + return $type; +} + sub formatDocComment { - my($doc, $paramInfo) = @_; + my($doc, $paramInfo, $cmdSyntax) = @_; my $bodyDocs = ''; + my $notes = ''; + my $see = ''; my $paramDocs = ''; my $returnDocs = ''; for (my $i = 0; $i < @{$doc}; ++$i) { - if ($doc->[$i] eq 'params') + my $docElement = $doc->[$i]; + + if ($docElement->{params}) { $paramDocs .= "Parameters:\n"; - @parametersList = @{$doc->[$i + 1]}; - for my $parameter (@parametersList) + for my $parameter (@{$docElement->{params}}) { my $declname = $parameter->{parameters}->[0]->{name}; my $decltype = cleanupDoxygenType($paramInfo->{$declname}->{type}); - $paramDocs .= Text::Wrap::fill(' ', ' ', "$declname: " . formatDocComment($parameter->{doc}) . " (type: $decltype)") . "\n"; + $paramDocs .= Text::Wrap::fill(' ', ' ', "$declname ($decltype): " . formatDocComment($parameter->{doc})) . "\n"; } - ++$i; } - elsif ($doc->[$i]->{return}) + elsif ($docElement->{return}) { - $returnDocs .= "Returns\n"; - $returnDocs .= Text::Wrap::fill(' ',' ', formatDocComment($doc->[$i]->{return})) . "\n"; + $returnDocs .= "Returns:\n"; + $returnDocs .= Text::Wrap::fill(' ',' ', formatDocComment($docElement->{return})) . "\n"; } - else + elsif ($docElement->{note}) { - my $docElement = $doc->[$i]; - if ($docElement->{type} eq 'text' or $docElement->{type} eq 'url') - { - $bodyDocs .= $docElement->{content}; - } - elsif ($docElement->{type} eq 'parbreak') - { - $bodyDocs .= "\n\n"; - } - elsif ($docElement->{type} eq 'style' and $docElement->{style} eq 'code') - { - $bodyDocs .= "'"; - } + $notes .= Text::Wrap::fill(' ',' ', "Note: ".formatDocComment($docElement->{note})) . "\n"; + } + elsif ($docElement->{see}) + { + $see .= Text::Wrap::fill(' ',' ', "See: ".formatDocComment($docElement->{see})) . "\n"; + } + elsif ($docElement->{type} eq 'text' or $docElement->{type} eq 'url') + { + $bodyDocs .= $docElement->{content}; + } + elsif ($docElement->{type} eq 'parbreak') + { + $bodyDocs .= "\n\n"; + } + elsif ($docElement->{type} eq 'style' and $docElement->{style} eq 'code') + { + $bodyDocs .= "'"; } } $bodyDocs =~ s/^\s+|\s+$//g; $bodyDocs = Text::Wrap::fill('', '', $bodyDocs); - $paramDocs =~ s/^\s+|\s+$//g; + $notes =~ s/^\s+|\s+$//g; + $see =~ s/^\s+|\s+$//g; + $paramDocs =~ s/^\s+|\s+$//g; $returnDocs =~ s/^\s+|\s+$//g; - my $result = Text::Wrap::fill('', '', $bodyDocs); - $result .= "\n\n" . $paramDocs if $paramDocs; - $result .= "\n\n" . $returnDocs if $returnDocs; - - return $result; + my $cmdDocs; + $cmdDocs = "Command:\n" . Text::Wrap::fill(' ',' ', $cmdSyntax) if $cmdSyntax; + + return join "\n\n", grep {$_} ($bodyDocs, $notes, $see, $cmdDocs, $paramDocs, $returnDocs); } sub escapeCharForCString diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 9850a72b..305526c7 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -84,6 +84,32 @@ typedef multi_index_container< > > key_label_index_type; +/* How to write doxygen docs + * + * Good + * / ** Returns the block chain's rapidly-changing properties. + * * + * * The returned object contains information that changes every block interval + * * such as the head block number, the next witness, etc. + * * / + * + * Bad, no empty line + * / ** Returns the block chain's rapidly-changing properties. + * * The returned object contains information that changes every block interval + * * such as the head block number, the next witness, etc. + * * / + * + * Better, using @brief tag + * / ** + * * @brief Returns the block chain's rapidly-changing properties. + * * Long description text 1 + * * Long description text 2 + * * @param param1 param1 description + * * @param param2 param2 description + * * @returns return value description + * * / + * string get_rapidly_changing_properties(int32_t interval, string chain_id) + */ struct wallet_data { @@ -249,6 +275,7 @@ class wallet_api */ uint64_t get_account_count()const; /** Lists all accounts controlled by this wallet. + * * This returns a list of the full account objects for all accounts whose private keys * we possess. * @returns a list of account objects @@ -330,6 +357,7 @@ class wallet_api vector get_settle_orders(string a, uint32_t limit)const; /** Returns the block chain's slowly-changing settings. + * * This object contains all of the properties of the blockchain that are fixed * or that change only once per maintenance interval (daily) such as the * current list of witnesses, committee_members, block interval, etc. @@ -339,6 +367,7 @@ class wallet_api global_property_object get_global_properties() const; /** Returns the block chain's rapidly-changing properties. + * * The returned object contains information that changes every block interval * such as the head block number, the next witness, etc. * @see \c get_global_properties() for less-frequently changing properties @@ -354,6 +383,7 @@ class wallet_api account_object get_account(string account_name_or_id) const; /** Returns information about the given asset. + * * @param asset_name_or_id the symbol or id of the asset in question * @returns the information about the asset stored in the block chain */ @@ -368,6 +398,7 @@ class wallet_api asset_bitasset_data_object get_bitasset_data(string asset_name_or_id)const; /** Lookup the id of a named account. + * * @param account_name_or_id the name of the account to look up * @returns the id of the named account */ @@ -375,6 +406,7 @@ class wallet_api /** * Lookup the id of a named asset. + * * @param asset_name_or_id the symbol of an asset to look up * @returns the id of the given asset */ @@ -577,7 +609,7 @@ class wallet_api /** * Derive any number of *possible* owner keys from a given brain key. * - * NOTE: These keys may or may not match with the owner keys of any account. + * @note These keys may or may not match with the owner keys of any account. * This function is merely intended to assist with account or key recovery. * * @see suggest_brain_key() @@ -591,7 +623,8 @@ class wallet_api /** * Determine whether a textual representation of a public key * (in Base-58 format) is *currently* linked - * to any *registered* (i.e. non-stealth) account on the blockchain + * to any *registered* (i.e. non-stealth) account on the blockchain. + * * @param public_key Public key * @return Whether a public key is known */ @@ -682,7 +715,7 @@ class wallet_api uint32_t referrer_percent, bool broadcast = false); - /** Updates account public keys + /** Updates account public keys. * * @param name the name of the existing account * @param old_owner the owner key for the named account to be replaced @@ -700,7 +733,8 @@ class wallet_api bool broadcast = false); /** - * This method updates the key of an authority for an exisiting account. + * Updates the key of an authority for an exisiting account. + * Warning: You can create impossible authorities using this method. The method * will fail if you create an impossible owner authority, but will allow impossible * active and posting authorities. @@ -719,6 +753,7 @@ class wallet_api /** * Upgrades an account to prime status. + * * This makes the account holder a 'lifetime member'. * * @todo there is no option for annual membership @@ -786,7 +821,7 @@ class wallet_api /** - * This method is used to convert a JSON transaction to its transactin ID. + * Convert a JSON transaction to its transactin ID. */ transaction_id_type get_transaction_id( const signed_transaction& trx )const { return trx.id(); } @@ -794,7 +829,7 @@ class wallet_api /** These methods are used for stealth transfers */ ///@{ /** - * This method can be used to set the label for a public key + * Set the label for a public key * * @note No two keys can have the same label. * @@ -918,7 +953,7 @@ class wallet_api signed_transaction borrow_asset(string borrower_name, string amount_to_borrow, string asset_symbol, string amount_of_collateral, bool broadcast = false); - /** Cancel an existing order + /** Cancel an existing order. * * @param order_id the id of order to be cancelled * @param broadcast true to broadcast the transaction on the network @@ -978,6 +1013,7 @@ class wallet_api bool broadcast = false); /** Update the core options on an asset. + * * There are a number of options which all assets in the network use. These options are * enumerated in the asset_object::asset_options struct. This command is used to update * these options for an existing asset. @@ -1184,6 +1220,7 @@ class wallet_api bool broadcast = false); /** Lists all witnesses registered in the blockchain. + * * This returns a list of all account names that own witnesses, and the associated witness id, * sorted by name. This lists witnesses whether they are currently voted in or not. * @@ -1214,6 +1251,7 @@ class wallet_api map list_committee_members(const string& lowerbound, uint32_t limit); /** Lists all workers in the blockchain. + * * This returns a list of all account names that own worker, and the associated worker id, * sorted by name. This lists workers whether they are currently voted in or not. * @@ -1355,6 +1393,7 @@ class wallet_api bool broadcast = false); /** Lists all SONs in the blockchain. + * * This returns a list of all account names that own SON, and the associated SON id, * sorted by name. This lists SONs whether they are currently voted in or not. * @@ -1582,8 +1621,7 @@ class wallet_api string asset_symbol, bool broadcast = false); - /** - * Withdraw a GPOS vesting balance. + /** Withdraw a GPOS vesting balance. * * @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type. * @param amount The amount to withdraw. From 5f97eb7662f1dd8cdb48272abedeba4af1bf7d16 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Mon, 19 Sep 2022 19:23:39 +0000 Subject: [PATCH 6/6] SON for Ethereum --- libraries/CMakeLists.txt | 1 + libraries/app/database_api.cpp | 12 + libraries/chain/db_init.cpp | 69 +- libraries/chain/db_maint.cpp | 64 +- libraries/chain/db_witness_schedule.cpp | 9 +- .../chain/hardfork.d/SON_FOR_ETHEREUM.hf | 7 + .../chain/protocol/chain_parameters.hpp | 5 + .../include/graphene/chain/protocol/vote.hpp | 3 +- .../include/graphene/chain/sidechain_defs.hpp | 4 +- .../include/graphene/chain/son_object.hpp | 5 + libraries/chain/protocol/account.cpp | 4 + libraries/chain/son_evaluator.cpp | 5 +- libraries/chain/son_object.cpp | 4 + .../peerplays_sidechain/CMakeLists.txt | 8 +- .../peerplays_sidechain/ethereum/decoders.cpp | 224 ++++++ .../peerplays_sidechain/ethereum/encoders.cpp | 102 +++ .../ethereum/transaction.cpp | 229 ++++++ .../peerplays_sidechain/ethereum/types.cpp | 5 + .../peerplays_sidechain/ethereum/utils.cpp | 52 ++ .../peerplays_sidechain/ethereum/decoders.hpp | 28 + .../peerplays_sidechain/ethereum/encoders.hpp | 73 ++ .../ethereum/transaction.hpp | 163 ++++ .../peerplays_sidechain/ethereum/types.hpp | 12 + .../peerplays_sidechain/ethereum/utils.hpp | 37 + .../sidechain_net_handler_ethereum.hpp | 75 ++ .../peerplays_sidechain_plugin.cpp | 31 +- .../sidechain_net_handler.cpp | 8 + .../sidechain_net_handler_ethereum.cpp | 761 ++++++++++++++++++ .../sidechain_net_handler_factory.cpp | 4 + libraries/sha3/CMakeLists.txt | 16 + libraries/sha3/include/sha3/memzero.h | 16 + libraries/sha3/include/sha3/sha3.h | 88 ++ libraries/sha3/memzero.c | 75 ++ libraries/sha3/sha3.c | 397 +++++++++ libraries/wallet/wallet.cpp | 7 +- tests/cli/son.cpp | 119 ++- .../ethereum_transaction_tests.cpp | 147 ++++ tests/tests/sidechain_addresses_test.cpp | 1 + tests/tests/son_operations_tests.cpp | 53 +- 39 files changed, 2860 insertions(+), 63 deletions(-) create mode 100644 libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/types.cpp create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/utils.cpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp create mode 100644 libraries/sha3/CMakeLists.txt create mode 100644 libraries/sha3/include/sha3/memzero.h create mode 100644 libraries/sha3/include/sha3/sha3.h create mode 100644 libraries/sha3/memzero.c create mode 100644 libraries/sha3/sha3.c create mode 100644 tests/peerplays_sidechain/ethereum_transaction_tests.cpp diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index cf2355f1..f28a6cee 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory( egenesis ) add_subdirectory( fc ) add_subdirectory( net ) add_subdirectory( plugins ) +add_subdirectory( sha3 ) add_subdirectory( time ) add_subdirectory( utilities ) add_subdirectory( wallet ) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b10d6b99..0a96bc26 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2085,6 +2085,7 @@ vector database_api_impl::lookup_vote_ids(const vector &v const auto &against_worker_idx = _db.get_index_type().indices().get(); const auto &son_bictoin_idx = _db.get_index_type().indices().get(); const auto &son_hive_idx = _db.get_index_type().indices().get(); + const auto &son_ethereum_idx = _db.get_index_type().indices().get(); vector result; result.reserve(votes.size()); @@ -2136,6 +2137,14 @@ vector database_api_impl::lookup_vote_ids(const vector &v result.emplace_back(variant()); break; } + case vote_id_type::son_ethereum: { + auto itr = son_ethereum_idx.find(id); + if (itr != son_ethereum_idx.end()) + result.emplace_back(variant(*itr, 5)); + else + result.emplace_back(variant()); + break; + } case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings default: @@ -2173,6 +2182,9 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const const auto son_hive_ids = get_votes_objects(votes_ids, 5); if (!son_hive_ids.empty()) son_ids[sidechain_type::hive] = std::move(son_hive_ids); + const auto son_ethereum_ids = get_votes_objects(votes_ids, 5); + if (!son_ethereum_ids.empty()) + son_ids[sidechain_type::ethereum] = std::move(son_ethereum_ids); return son_ids; }(); diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index cc862618..e9f3b9f5 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -1101,28 +1101,6 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Initialize witness schedule - #ifndef NDEBUG - const son_schedule_object& ssohive = -#endif - create([&](son_schedule_object& _sso) - { - // for scheduled - memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); - - witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); - - auto init_witnesses = get_global_properties().active_witnesses; - - _sso.scheduler = son_scheduler(); - _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); - - - _sso.last_scheduling_block = 0; - - _sso.recent_slots_filled = fc::uint128::max_value(); - }); - assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); - #ifndef NDEBUG const son_schedule_object& ssobitcoin = #endif @@ -1133,11 +1111,10 @@ void database::init_genesis(const genesis_state_type& genesis_state) witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); - auto init_witnesses = get_global_properties().active_witnesses; + auto init_bitcoin_sons = get_global_properties().active_sons.at(sidechain_type::bitcoin); _sso.scheduler = son_scheduler(); - _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); - + _sso.scheduler._min_token_count = std::max(int(init_bitcoin_sons.size()) / 2, 1); _sso.last_scheduling_block = 0; @@ -1145,6 +1122,48 @@ void database::init_genesis(const genesis_state_type& genesis_state) }); assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) ); +#ifndef NDEBUG + const son_schedule_object& ssoethereum = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_ethereum_sons = get_global_properties().active_sons.at(sidechain_type::ethereum); + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_ethereum_sons.size()) / 2, 1); + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); + }); + assert( ssoethereum.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::ethereum)) ); + +#ifndef NDEBUG + const son_schedule_object& ssohive = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_hive_sons = get_global_properties().active_sons.at(sidechain_type::hive); + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_hive_sons.size()) / 2, 1); + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); + }); + assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); + // Create FBA counters create([&]( fba_accumulator_object& acc ) { diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index d18e0f26..7474edac 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -92,7 +92,10 @@ vector> database::sort_votable_objects< count = std::min(count, refs.size()); std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), [this, sidechain](const son_object& a, const son_object& b)->bool { - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || + sidechain == sidechain_type::ethereum || + sidechain == sidechain_type::hive, + "Unexpected sidechain type"); const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; @@ -718,7 +721,9 @@ void database::update_active_sons() assert( _son_count_histogram_buffer.size() > 0 ); for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ +#ifndef NDEBUG assert( son_count_histogram_buffer.second.size() > 0 ); +#endif } const flat_map stake_target = [this]{ @@ -2044,7 +2049,7 @@ void database::perform_son_tasks() }); } // create BTC asset here because son_account is the issuer of the BTC - if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME) + if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME) { const asset_dynamic_data_object& dyn_asset = create([](asset_dynamic_data_object& a) { @@ -2063,7 +2068,7 @@ void database::perform_son_tasks() asset_issuer_permission_flags::override_authority; a.options.core_exchange_rate.base.amount = 100000; a.options.core_exchange_rate.base.asset_id = asset_id_type(0); - a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value + a.options.core_exchange_rate.quote.amount = 2500; a.options.core_exchange_rate.quote.asset_id = a.id; a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set @@ -2077,8 +2082,42 @@ void database::perform_son_tasks() gpo.pending_parameters->extensions.value.btc_asset = btc_asset.get_id(); }); } + // create ETH asset here because son_account is the issuer of the ETH + if (gpo.parameters.eth_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME) + { + const asset_dynamic_data_object& dyn_asset = + create([](asset_dynamic_data_object& a) { + a.current_supply = 0; + }); + + const asset_object& eth_asset = + create( [&gpo, &dyn_asset]( asset_object& a ) { + a.symbol = "ETH"; + a.precision = 8; + a.issuer = gpo.parameters.son_account(); + a.options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY; + a.options.market_fee_percent = 500; // 5% + a.options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK; + a.options.flags = asset_issuer_permission_flags::charge_market_fee | + asset_issuer_permission_flags::override_authority; + a.options.core_exchange_rate.base.amount = 100000; + a.options.core_exchange_rate.base.asset_id = asset_id_type(0); + a.options.core_exchange_rate.quote.amount = 2500; + a.options.core_exchange_rate.quote.asset_id = a.id; + a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty + a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set + a.options.whitelist_markets.clear(); // might be traded with + a.options.blacklist_markets.clear(); // might not be traded with + a.dynamic_asset_data_id = dyn_asset.id; + }); + modify( gpo, [ð_asset]( global_property_object& gpo ) { + gpo.parameters.extensions.value.eth_asset = eth_asset.get_id(); + if( gpo.pending_parameters ) + gpo.pending_parameters->extensions.value.eth_asset = eth_asset.get_id(); + }); + } // create HBD asset here because son_account is the issuer of the HBD - if (gpo.parameters.hbd_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME) + if (gpo.parameters.hbd_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME) { const asset_dynamic_data_object& dyn_asset = create([](asset_dynamic_data_object& a) { @@ -2097,7 +2136,7 @@ void database::perform_son_tasks() asset_issuer_permission_flags::override_authority; a.options.core_exchange_rate.base.amount = 100000; a.options.core_exchange_rate.base.asset_id = asset_id_type(0); - a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value + a.options.core_exchange_rate.quote.amount = 2500; a.options.core_exchange_rate.quote.asset_id = a.id; a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set @@ -2131,7 +2170,7 @@ void database::perform_son_tasks() asset_issuer_permission_flags::override_authority; a.options.core_exchange_rate.base.amount = 100000; a.options.core_exchange_rate.base.asset_id = asset_id_type(0); - a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value + a.options.core_exchange_rate.quote.amount = 2500; a.options.core_exchange_rate.quote.asset_id = a.id; a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set @@ -2413,14 +2452,17 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; if( !p.pending_parameters->extensions.value.hive_asset.valid() ) p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; + if( !p.pending_parameters->extensions.value.eth_asset.valid() ) + p.pending_parameters->extensions.value.eth_asset = p.parameters.extensions.value.eth_asset; // the following parameters are not allowed to be changed. So take what is in global property - p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; - p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; - p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count; - p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset; - p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account; p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start; + p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account; + p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset; + p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count; + p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; + p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; + p.pending_parameters->extensions.value.eth_asset = p.parameters.extensions.value.eth_asset; p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index f4df5ac2..b4c4bb6a 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -77,8 +77,9 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const unsigned_int database::get_son_schedule_id( sidechain_type type )const { static const map schedule_map = { - { sidechain_type::hive, 0 }, - { sidechain_type::bitcoin, 1 } + { sidechain_type::bitcoin, 0 }, + { sidechain_type::ethereum, 1 }, + { sidechain_type::hive, 2 } }; return schedule_map.at(type); @@ -322,8 +323,10 @@ void database::update_witness_schedule(const signed_block& next_block) void database::update_son_schedule(const signed_block& next_block) { auto start = fc::time_point::now(); - const global_property_object& gpo = get_global_properties(); +#ifndef NDEBUG const son_schedule_object& sso = get(son_schedule_id_type()); +#endif + const global_property_object& gpo = get_global_properties(); const flat_map schedule_needs_filled = [&gpo]() { flat_map schedule_needs_filled; diff --git a/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf new file mode 100644 index 00000000..72e929fe --- /dev/null +++ b/libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf @@ -0,0 +1,7 @@ +#ifndef HARDFORK_SON_FOR_ETHEREUM_TIME +#ifdef BUILD_PEERPLAYS_TESTNET +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) +#else +#define HARDFORK_SON_FOR_ETHEREUM_TIME (fc::time_point_sec::from_iso_string("2022-07-01T00:00:00")) +#endif +#endif diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 3a11e99f..94493f30 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -70,6 +70,7 @@ namespace graphene { namespace chain { optional < uint16_t > maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS optional < asset_id_type > hbd_asset = asset_id_type(); optional < asset_id_type > hive_asset = asset_id_type(); + optional < asset_id_type > eth_asset = asset_id_type(); }; struct chain_parameters @@ -220,6 +221,9 @@ namespace graphene { namespace chain { inline asset_id_type hive_asset() const { return extensions.value.hive_asset.valid() ? *extensions.value.hive_asset : asset_id_type(); } + inline asset_id_type eth_asset() const { + return extensions.value.eth_asset.valid() ? *extensions.value.eth_asset : asset_id_type(); + } private: static void safe_copy(chain_parameters& to, const chain_parameters& from); }; @@ -257,6 +261,7 @@ FC_REFLECT( graphene::chain::parameter_extension, (maximum_son_count) (hbd_asset) (hive_asset) + (eth_asset) ) FC_REFLECT( graphene::chain::chain_parameters, diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 023bb511..913f6c5b 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -61,6 +61,7 @@ struct vote_id_type worker, son_bitcoin, son_hive, + son_ethereum, VOTE_TYPE_COUNT }; @@ -145,7 +146,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui FC_REFLECT_TYPENAME( fc::flat_set ) -FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(VOTE_TYPE_COUNT) ) +FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(son_ethereum)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp index 1097323b..a717f778 100644 --- a/libraries/chain/include/graphene/chain/sidechain_defs.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -14,7 +14,7 @@ enum class sidechain_type { hive }; -static const std::set active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::hive}; +static const std::set active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive}; } } @@ -24,4 +24,4 @@ FC_REFLECT_ENUM(graphene::chain::sidechain_type, (ethereum) (eos) (hive) - (peerplays) ) \ No newline at end of file + (peerplays) ) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index c9089bfb..afe7230f 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -89,11 +89,13 @@ namespace graphene { namespace chain { inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); } inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); } inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); } + inline vote_id_type get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); } }; struct by_account; struct by_vote_id_bitcoin; struct by_vote_id_hive; + struct by_vote_id_ethereum; using son_multi_index_type = multi_index_container< son_object, indexed_by< @@ -108,6 +110,9 @@ namespace graphene { namespace chain { >, ordered_unique< tag, const_mem_fun + >, + ordered_unique< tag, + const_mem_fun > > >; diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index bee6b1be..a1b7994e 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -186,6 +186,8 @@ void account_options::validate() const --needed_sons[sidechain_type::bitcoin]; else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] ) --needed_sons[sidechain_type::hive]; + else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] ) + --needed_sons[sidechain_type::ethereum]; FC_ASSERT( needed_witnesses == 0, "May not specify fewer witnesses than the number voted for."); @@ -195,6 +197,8 @@ void account_options::validate() const "May not specify fewer Bitcoin SONs than the number voted for."); FC_ASSERT( needed_sons[sidechain_type::hive] == 0, "May not specify fewer Hive SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0, + "May not specify fewer Ethereum SONs than the number voted for."); } void affiliate_reward_distribution::validate() const diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 8d24bad6..5016ea0f 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -40,15 +40,18 @@ object_id_type create_son_evaluator::do_apply(const son_create_operation& op) { try { vote_id_type vote_id_bitcoin; vote_id_type vote_id_hive; - db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive](global_property_object& p) { + vote_id_type vote_id_ethereum; + db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive, &vote_id_ethereum](global_property_object& p) { vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin); vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive); + vote_id_ethereum = get_next_vote_id(p, vote_id_type::son_ethereum); }); const auto& new_son_object = db().create( [&]( son_object& obj ){ obj.son_account = op.owner_account; obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin; obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive; + obj.sidechain_vote_ids[sidechain_type::ethereum] = vote_id_ethereum; obj.url = op.url; obj.deposit = op.deposit; obj.signing_key = op.signing_key; diff --git a/libraries/chain/son_object.cpp b/libraries/chain/son_object.cpp index c94a6250..e607c103 100644 --- a/libraries/chain/son_object.cpp +++ b/libraries/chain/son_object.cpp @@ -20,6 +20,10 @@ namespace graphene { namespace chain { retval = retval && (sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) && (sidechain_public_keys.at(sidechain_type::hive).length() > 0); + + retval = retval && + (sidechain_public_keys.find( sidechain_type::ethereum ) != sidechain_public_keys.end()) && + (sidechain_public_keys.at(sidechain_type::ethereum).length() > 0); } return retval; diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index d7337e42..4f724602 100755 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -6,6 +6,7 @@ add_library( peerplays_sidechain sidechain_net_handler_factory.cpp sidechain_net_handler.cpp sidechain_net_handler_bitcoin.cpp + sidechain_net_handler_ethereum.cpp sidechain_net_handler_hive.cpp sidechain_net_handler_peerplays.cpp bitcoin/bech32.cpp @@ -17,6 +18,11 @@ add_library( peerplays_sidechain bitcoin/sign_bitcoin_transaction.cpp common/rpc_client.cpp common/utils.cpp + ethereum/encoders.cpp + ethereum/decoders.cpp + ethereum/transaction.cpp + ethereum/types.cpp + ethereum/utils.cpp hive/asset.cpp hive/operations.cpp hive/transaction.cpp @@ -36,7 +42,7 @@ endif() unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) -target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin zmq ) +target_link_libraries( peerplays_sidechain PRIVATE graphene_plugin sha3 zmq ) target_include_directories( peerplays_sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp new file mode 100644 index 00000000..02f334f5 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp @@ -0,0 +1,224 @@ +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +//! rlp_decoder + +namespace { +const signed char p_util_hexdigit[256] = + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +} + +std::vector rlp_decoder::decode(const std::string &str) { + size_t consumed = 0; + const auto raw_vec = parse_hex(str); + const std::vector rlp_array = decode_rlp(raw_vec.data(), raw_vec.size(), consumed); + std::vector result_array; + for (const auto &rlp : decode_rlp(raw_vec.data(), raw_vec.size(), consumed)) { + result_array.emplace_back(bytes2hex(rlp)); + } + return result_array; +} + +std::vector rlp_decoder::decode_rlp(const unsigned char *raw, size_t len, size_t &consumed) { + std::vector rlp_result; + + consumed = 0; + + const unsigned char *end = raw + len; + const size_t prefixlen = 1; + unsigned char ch = *raw; + + if (len < 1) { + return rlp_result; + } + + // Case 1: [prefix is 1-byte data buffer] + if (ch <= 0x7f) { + const unsigned char *tok_start = raw; + const unsigned char *tok_end = tok_start + prefixlen; + FC_ASSERT(tok_end <= end); + + // parsing done; assign data buffer value. + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back(buf.cbegin(), buf.cend()); + + consumed = buf.size(); + } + // Case 2: [prefix, including buffer length][data] + else if ((ch >= 0x80) && (ch <= 0xb7)) { + const size_t blen = ch - 0x80; + const size_t expected = prefixlen + blen; + + if (len < expected) + return std::vector{}; + + const unsigned char *tok_start = raw + 1; + const unsigned char *tok_end = tok_start + blen; + FC_ASSERT(tok_end <= end); + + // require minimal encoding + if ((blen == 1) && (tok_start[0] <= 0x7f)) + return std::vector{}; + + // parsing done; assign data buffer value. + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back(buf.cbegin(), buf.cend()); + + consumed = expected; + } + // Case 3: [prefix][buffer length][data] + else if ((ch >= 0xb8) && (ch <= 0xbf)) { + const size_t uintlen = ch - 0xb7; + size_t expected = prefixlen + uintlen; + + if (len < expected) + return std::vector{}; + + FC_ASSERT(uintlen > 0 && uintlen <= RLP_maxUintLen); + + const unsigned char *tok_start = raw + prefixlen; + if ((uintlen > 1) && (tok_start[0] == 0)) // no leading zeroes + return std::vector{}; + + // read buffer length + const uint64_t slen = to_int(tok_start, uintlen); + + // validate buffer length, including possible addition overflows. + expected = prefixlen + uintlen + slen; + if ((slen < (RLP_listStart - RLP_bufferLenStart - RLP_maxUintLen)) || (expected > len) || (slen > len)) + return std::vector{}; + + // parsing done; assign data buffer value. + tok_start = raw + prefixlen + uintlen; + const unsigned char *tok_end = tok_start + slen; + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back(buf.cbegin(), buf.cend()); + + consumed = expected; + } + // Case 4: [prefix][list] + else if ((ch >= 0xc0) && (ch <= 0xf7)) { + const size_t payloadlen = ch - 0xc0; + const size_t expected = prefixlen + payloadlen; + + // read list payload + const auto array = decode_array(raw, len, 0, payloadlen); + rlp_result.insert(rlp_result.end(), array.cbegin(), array.cend()); + + consumed = expected; + } + // Case 5: [prefix][list length][list] + else { + FC_ASSERT((ch >= 0xf8) && (ch <= 0xff)); + + const size_t uintlen = ch - 0xf7; + const size_t expected = prefixlen + uintlen; + + if (len < expected) + return std::vector{}; + + FC_ASSERT(uintlen > 0 && uintlen <= RLP_maxUintLen); + + const unsigned char *tok_start = raw + prefixlen; + if ((uintlen > 1) && (tok_start[0] == 0)) // no leading zeroes + return std::vector{}; + + // read list length + const size_t payloadlen = to_int(tok_start, uintlen); + + // special requirement for non-immediate length + if (payloadlen < (0x100 - RLP_listStart - RLP_maxUintLen)) + return std::vector{}; + + // read list payload + const auto array = decode_array(raw, len, uintlen, payloadlen); + rlp_result.insert(rlp_result.end(), array.cbegin(), array.cend()); + + consumed = prefixlen + uintlen + payloadlen; + } + + return rlp_result; +} + +std::vector rlp_decoder::decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen) { + std::vector rlp_result; + const size_t prefixlen = 1; + + // validate list length, including possible addition overflows. + const size_t expected = prefixlen + uintlen + payloadlen; + if ((expected > len) || (payloadlen > len)) + return std::vector{}; + + size_t child_len = payloadlen; + const unsigned char *list_ent = raw + prefixlen + uintlen; + + // recursively read until payloadlen bytes parsed, or error + while (child_len > 0) { + size_t child_consumed = 0; + + const auto val = decode_rlp(list_ent, child_len, child_consumed); + rlp_result.insert(rlp_result.end(), val.cbegin(), val.cend()); + + list_ent += child_consumed; + child_len -= child_consumed; + } + + return rlp_result; +} + +uint64_t rlp_decoder::to_int(const unsigned char *raw, size_t len) { + if (len == 0) + return 0; + else if (len == 1) + return *raw; + else + return (raw[len - 1]) + (to_int(raw, len - 1) * 256); +} + +std::vector rlp_decoder::parse_hex(const std::string &str) { + return parse_hex(str.c_str()); +} + +std::vector rlp_decoder::parse_hex(const char *psz) { + // convert hex dump to vector + std::vector vch; + while (true) { + while (isspace(*psz)) + psz++; + signed char c = hex_digit(*psz++); + if (c == (signed char)-1) + break; + unsigned char n = (c << 4); + c = hex_digit(*psz++); + if (c == (signed char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +signed char rlp_decoder::hex_digit(char c) { + return p_util_hexdigit[(unsigned char)c]; +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp new file mode 100644 index 00000000..ad42088b --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -0,0 +1,102 @@ +#include + +#include +#include +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +//! base_encoder +std::string base_encoder::encode_uint256(boost::multiprecision::uint256_t value) { + return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str(); +} + +std::string base_encoder::encode_address(const std::string &value) { + return (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value)).str(); +} + +std::string base_encoder::encode_string(const std::string &value) { + std::string data = (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), value.size())).str(); + data += boost::algorithm::hex(value) + std::string((64 - value.size() * 2 % 64), '0'); + return data; +} + +//! update_owners_encoder +std::string update_owners_encoder::encode(const std::vector> &owners_weights, const std::string &object_id) const { + std::string data = "0x" + function_signature; + data += base_encoder::encode_uint256(64); + data += base_encoder::encode_uint256((owners_weights.size() * 2 + 3) * 32); + data += base_encoder::encode_uint256(owners_weights.size()); + for (const auto &owner : owners_weights) { + data += base_encoder::encode_address(owner.first); + data += base_encoder::encode_uint256(owner.second); + } + data += base_encoder::encode_string(object_id); + + return data; +} + +//! withdrawal_encoder +std::string withdrawal_encoder::encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) const { + std::string data = "0x" + function_signature; + data += base_encoder::encode_address(to); + data += base_encoder::encode_uint256(amount); + data += base_encoder::encode_uint256(32 * 3); + data += base_encoder::encode_string(object_id); + + return data; +} + +//! rlp_encoder +std::string rlp_encoder::encode(const std::string &s) { + return encode_rlp(hex2bytes(s)); +} + +std::string rlp_encoder::encode_length(int len, int offset) { + if (len < 56) { + std::string temp; + temp = (char)(len + offset); + return temp; + } else { + const std::string hexLength = to_hex(len); + const int lLength = hexLength.size() / 2; + const std::string fByte = to_hex(offset + 55 + lLength); + return hex2bytes(fByte + hexLength); + } +} + +std::string rlp_encoder::hex2bytes(const std::string &s) { + std::string dest; + dest.resize(s.size() / 2); + hex2bin(s.c_str(), &dest[0]); + return dest; +} + +std::string rlp_encoder::encode_rlp(const std::string &s) { + if (s.size() == 1 && (unsigned char)s[0] < 128) + return s; + else + return encode_length(s.size(), 128) + s; +} + +int rlp_encoder::char2int(char input) { + if (input >= '0' && input <= '9') + return input - '0'; + if (input >= 'A' && input <= 'F') + return input - 'A' + 10; + if (input >= 'a' && input <= 'f') + return input - 'a' + 10; + + return -1; +} + +void rlp_encoder::hex2bin(const char *src, char *target) { + while (*src && src[1]) { + *(target++) = char2int(*src) * 16 + char2int(src[1]); + src += 2; + } +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp new file mode 100644 index 00000000..a75327c3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp @@ -0,0 +1,229 @@ +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +const secp256k1_context *eth_context() { + static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + return ctx; +} + +//! transaction + +base_transaction::base_transaction(const std::string &raw_tx) { +} + +//! transaction + +transaction::transaction(const std::string &raw_tx) : + base_transaction{raw_tx} { + deserialize(raw_tx); +} + +const transaction &transaction::sign(const std::string &private_key) const { + return *this; +} + +std::string transaction::serialize() const { + boost::property_tree::ptree pt; + pt.put("from", from); + pt.put("to", to); + pt.put("data", data); + + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, pt); + return ss.str(); +} + +void transaction::deserialize(const std::string &raw_tx) { + std::stringstream ss_tx(raw_tx); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(ss_tx, tx_json); + + if (tx_json.count("from")) + from = tx_json.get("from"); + if (tx_json.count("to")) + to = tx_json.get("to"); + if (tx_json.count("data")) + data = tx_json.get("data"); +} + +//! raw_transaction + +raw_transaction::raw_transaction(const std::string &raw_tx) : + base_transaction{raw_tx} { + deserialize(raw_tx); +} + +bytes raw_transaction::hash() const { + bytes hash; + hash.resize(32); + const auto transaction_string = boost::algorithm::unhex(remove_0x(serialize())); + keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data()); + + return hash; +} + +signed_transaction raw_transaction::sign(const std::string &private_key) const { + //! Prepare signed transaction + signed_transaction tr; + tr.nonce = nonce; + tr.gas_price = gas_price; + tr.gas_limit = gas_limit; + tr.to = to; + tr.value = value; + tr.data = data; + + const bytes priv_key = parse_hex(private_key); + + int recid = 0; + secp256k1_ecdsa_recoverable_signature sig; + FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash().data(), (const unsigned char *)priv_key.data(), NULL, NULL)); + fc::ecc::compact_signature result; + FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig)); + + bytes r; + for (int i = 1; i < 33; i++) + r.emplace_back((char)result.at(i)); + + bytes v = bytes{char(recid + from_hex(chain_id) * 2 + 35)}; + + bytes s; + for (int i = 33; i < 65; i++) + s.emplace_back((char)result.at(i)); + + tr.r = fc::to_hex((char *)&r[0], r.size()); + tr.v = fc::to_hex((char *)&v[0], v.size()); + tr.s = fc::to_hex((char *)&s[0], s.size()); + + return tr; +} + +std::string raw_transaction::serialize() const { + const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) + + rlp_encoder::encode(remove_0x(gas_price)) + + rlp_encoder::encode(remove_0x(gas_limit)) + + rlp_encoder::encode(remove_0x(to)) + + rlp_encoder::encode(remove_0x(value)) + + rlp_encoder::encode(remove_0x(data)) + + rlp_encoder::encode(remove_0x(chain_id)) + + rlp_encoder::encode("") + + rlp_encoder::encode(""); + + return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized)); +} + +void raw_transaction::deserialize(const std::string &raw_tx) { + const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx)); + FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format"); + + nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0"); + boost::algorithm::to_lower(nonce); + gas_price = add_0x(rlp_array.at(1)); + boost::algorithm::to_lower(gas_price); + gas_limit = add_0x(rlp_array.at(2)); + boost::algorithm::to_lower(gas_limit); + to = add_0x(rlp_array.at(3)); + boost::algorithm::to_lower(to); + value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0"); + boost::algorithm::to_lower(value); + data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : ""; + boost::algorithm::to_lower(data); + chain_id = add_0x(rlp_array.at(6)); + boost::algorithm::to_lower(chain_id); +} + +//! signed_transaction + +signed_transaction::signed_transaction(const std::string &raw_tx) : + base_transaction{raw_tx} { + deserialize(raw_tx); +} + +std::string signed_transaction::recover(const std::string &chain_id) const { + fc::ecc::compact_signature input64; + fc::from_hex(r, (char *)&input64.at(1), 32); + fc::from_hex(v, (char *)&input64.at(0), 1); + int recid = input64.at(0) - from_hex(chain_id) * 2 - 35; + fc::from_hex(s, (char *)&input64.at(33), 32); + + secp256k1_ecdsa_recoverable_signature sig; + FC_ASSERT(secp256k1_ecdsa_recoverable_signature_parse_compact(eth_context(), &sig, (const unsigned char *)&input64.data[1], recid)); + + raw_transaction tr; + tr.nonce = nonce; + tr.gas_price = gas_price; + tr.gas_limit = gas_limit; + tr.to = to; + tr.value = value; + tr.data = data; + tr.chain_id = chain_id; + + secp256k1_pubkey rawPubkey; + FC_ASSERT(secp256k1_ecdsa_recover(eth_context(), &rawPubkey, &sig, (const unsigned char *)tr.hash().data())); + + std::array pubkey; + size_t biglen = 65; + FC_ASSERT(secp256k1_ec_pubkey_serialize(eth_context(), pubkey.data(), &biglen, &rawPubkey, SECP256K1_EC_UNCOMPRESSED)); + + const std::string out = std::string(pubkey.begin(), pubkey.end()).substr(1); + bytes hash; + hash.resize(32); + keccak_256((const unsigned char *)out.data(), out.size(), (unsigned char *)hash.data()); + + return add_0x(fc::to_hex((char *)&hash[0], hash.size()).substr(24)); +} + +std::string signed_transaction::serialize() const { + const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) + + rlp_encoder::encode(remove_0x(gas_price)) + + rlp_encoder::encode(remove_0x(gas_limit)) + + rlp_encoder::encode(remove_0x(to)) + + rlp_encoder::encode(remove_0x(value)) + + rlp_encoder::encode(remove_0x(data)) + + rlp_encoder::encode(remove_0x(v)) + + rlp_encoder::encode(remove_0x(r)) + + rlp_encoder::encode(remove_0x(s)); + + return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized)); +} + +void signed_transaction::deserialize(const std::string &raw_tx) { + const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx)); + FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format"); + + nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0"); + boost::algorithm::to_lower(nonce); + gas_price = add_0x(rlp_array.at(1)); + boost::algorithm::to_lower(gas_price); + gas_limit = add_0x(rlp_array.at(2)); + boost::algorithm::to_lower(gas_limit); + to = add_0x(rlp_array.at(3)); + boost::algorithm::to_lower(to); + value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0"); + boost::algorithm::to_lower(value); + data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : ""; + boost::algorithm::to_lower(data); + v = add_0x(rlp_array.at(6)); + boost::algorithm::to_lower(v); + r = add_0x(rlp_array.at(7)); + boost::algorithm::to_lower(r); + s = add_0x(rlp_array.at(8)); + boost::algorithm::to_lower(s); +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/types.cpp b/libraries/plugins/peerplays_sidechain/ethereum/types.cpp new file mode 100644 index 00000000..f85d0e61 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/types.cpp @@ -0,0 +1,5 @@ +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp new file mode 100644 index 00000000..ce64e1ae --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp @@ -0,0 +1,52 @@ +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +bytes parse_hex(const std::string &str) { + bytes vec(str.size() / 2); + fc::from_hex(str, vec.data(), vec.size()); + return vec; +} + +std::string bytes2hex(const std::string &s) { + std::string dest; + for (const auto &i : s) + dest += uchar2Hex((unsigned char)i); + + return dest; +} + +std::string uchar2Hex(unsigned char n) { + std::string dest; + dest.resize(2); + sprintf(&dest[0], "%X", n); + + if (n < (unsigned char)16) { + dest[1] = dest[0]; + dest[0] = '0'; + } + + return dest; +} + +std::string add_0x(const std::string &s) { + if (s.size() > 1) { + if (s.substr(0, 2) == "0x") + return s; + } + + return "0x" + s; +} + +std::string remove_0x(const std::string &s) { + if (s.size() > 1) { + if (s.substr(0, 2) == "0x") + return s.substr(2); + } + + return s; +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp new file mode 100644 index 00000000..9edb6ae3 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +class rlp_decoder { +private: + enum RLP_constants { + RLP_maxUintLen = 8, + RLP_bufferLenStart = 0x80, + RLP_listStart = 0xc0, + }; + +public: + static std::vector decode(const std::string &str); + +private: + static std::vector decode_rlp(const unsigned char *raw, size_t len, size_t &consumed); + static std::vector decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen); + static uint64_t to_int(const unsigned char *raw, size_t len); + static std::vector parse_hex(const std::string &str); + static std::vector parse_hex(const char *psz); + static signed char hex_digit(char c); +}; + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp new file mode 100644 index 00000000..1ff97978 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +class base_encoder { +public: + static std::string encode_uint256(boost::multiprecision::uint256_t value); + static std::string encode_address(const std::string &value); + static std::string encode_string(const std::string &value); +}; + +class update_owners_encoder { +public: + const std::string function_signature = "23ab6adf"; //! updateOwners((address,uint256)[],string) + + std::string encode(const std::vector> &owners_weights, const std::string &object_id) const; +}; + +class withdrawal_encoder { +public: + const std::string function_signature = "e088747b"; //! withdraw(address,uint256,string) + + std::string encode(const std::string &to, boost::multiprecision::uint256_t amount, const std::string &object_id) const; +}; + +class rlp_encoder { +public: + static std::string encode(const std::string &s); + static std::string encode_length(int len, int offset); + static std::string hex2bytes(const std::string &s); + +private: + static std::string encode_rlp(const std::string &s); + static int char2int(char input); + static void hex2bin(const char *src, char *target); +}; + +/*class ethereum_function_call_encoder { +public: + enum operation_t { + OPERATION_CALL, + OPERATION_DELEGATE_CALL + }; + + static constexpr const char *const default_prev_addr = "0000000000000000000000000000000000000001"; + + std::string encode_function_signature(const std::string &function_signature); + std::string encode_address(const std::string &addr); + std::string encode_uint256(const std::string &value); + std::string encode_uint8(uint8_t value); + std::string encode_bytes(const std::string &values); +}; + +class safe_transaction_encoder { +public: + static constexpr const char *const default_safe_tx_gas = "0"; + static constexpr const char *const default_data_gas = "0"; + static constexpr const char *const default_gas_price = "0"; + static constexpr const char *const default_gas_token = "0000000000000000000000000000000000000000"; + static constexpr const char *const default_refund_receiver = "0000000000000000000000000000000000000000"; + + std::string create_safe_address(const std::vector &owner_addresses, uint32_t threshold); + std::string build_transaction(const std::string &safe_account_addr, const std::string &value, const std::string &data, uint8_t operation, const std::string &safeTxGas, const std::string &dataGas, const std::string &gasPrice, const std::string &gasToken, const std::string &refundReceiver); + +private: + ethereum_function_call_encoder m_ethereum_function_call_encoder; +};*/ + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp new file mode 100644 index 00000000..693c7284 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +class base_transaction { +public: + base_transaction() = default; + base_transaction(const std::string &raw_tx); + + virtual std::string serialize() const = 0; + virtual void deserialize(const std::string &raw_tx) = 0; +}; + +class transaction : base_transaction { +public: + std::string from; + std::string to; + std::string data; + + transaction() = default; + transaction(const std::string &raw_tx); + + const transaction &sign(const std::string &private_key) const; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string &raw_tx) override; +}; + +class signed_transaction; +class raw_transaction : base_transaction { +public: + std::string nonce; + std::string gas_price; + std::string gas_limit; + std::string to; + std::string value; + std::string data; + std::string chain_id; + + raw_transaction() = default; + raw_transaction(const std::string &raw_tx); + + bytes hash() const; + signed_transaction sign(const std::string &private_key) const; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string &raw_tx) override; +}; + +class signed_transaction : base_transaction { +public: + std::string nonce; + std::string gas_price; + std::string gas_limit; + std::string to; + std::string value; + std::string data; + std::string v; + std::string r; + std::string s; + + signed_transaction() = default; + signed_transaction(const std::string &raw_tx); + + std::string recover(const std::string &chain_id) const; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string &raw_tx) override; +}; + +}}} // namespace graphene::peerplays_sidechain::ethereum + +// Example 1 +//{ +// "blockHash": "0x64a6706ecaf5a97b7f3e047abb20ff223ce82c6994d80e68fdb1fdfb38d0209c", +// "blockNumber": "0xe5827c", +// "from": "0x8614c67e085f2334010f2a28e806c6f1cc176d12", +// "gas": "0x38822", +// "gasPrice": "0xce42cba69", +// "maxFeePerGas": "0xddb4d8d16", +// "maxPriorityFeePerGas": "0x3b9aca00", +// "hash": "0xeac92ea09fa8eb3ca2fb0d156cceb38ae69d4345869d41e8e49d5ecbcbb622dc", +// "input": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000", +// "nonce": "0x32", +// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", +// "transactionIndex": "0xb6", +// "value": "0x2514d9d7d7d8000", +// "type": "0x2", +// "accessList": [], +// "chainId": "0x1", +// "v": "0x1", +// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c", +// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" +//} +// +//"0xf9021332850ce42cba69830388229468b3465833fb72a70ecdf485e0e4c7bd8665fc458802514d9d7d7d8000b901a45ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde0312150000000000000000000000000000000000000000000000000000000001a02f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28ca0782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" +// +//{ +// "nonce": 50, +// "gasPrice": { +// "_hex": "0x0ce42cba69" +// }, +// "gasLimit": { +// "_hex": "0x038822" +// }, +// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45", +// "value": { +// "_hex": "0x02514d9d7d7d8000" +// }, +// "data": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000", +// "v": 1, +// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c", +// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14" +//} + +// Example 2 +//{ +// "blockHash": "0xe2ae3afd86dc7343c7fb753441447a0a51bb19499325ad6e278256f0cd1b5894", +// "blockNumber": "0xe58271", +// "from": "0xb895ade6d337fbb8cb97f2ea7da43106c7f5cc26", +// "gas": "0x5208", +// "gasPrice": "0xe6f3b322e", +// "maxFeePerGas": "0x1322455fd3", +// "maxPriorityFeePerGas": "0x53724e00", +// "hash": "0xed29b56e52ad2d452e25b8ec70c37f59d935cd6d0f8fe8e83b256f3ffdfd3fce", +// "input": "0x", +// "nonce": "0x37", +// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373", +// "transactionIndex": "0x8a", +// "value": "0x4563918244f40000", +// "type": "0x2", +// "accessList": [], +// "chainId": "0x1", +// "v": "0x0", +// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c", +// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" +//} +// +//"0xf86c37850e6f3b322e82520894176386b6ffc469ac049f9ec1f6cc0efd1d09b373884563918244f400008000a0dcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4ca028c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" +// +//{ +// "nonce": 55, +// "gasPrice": { +// "_hex": "0x0e6f3b322e" +// }, +// "gasLimit": { +// "_hex": "0x5208" +// }, +// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373", +// "value": { +// "_hex": "0x4563918244f40000" +// }, +// "data": "0x", +// "v": 0, +// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c", +// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a" +//} diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp new file mode 100644 index 00000000..963244fa --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +typedef uint64_t chain_id_type; +typedef uint64_t network_id_type; + +using bytes = std::vector; + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp new file mode 100644 index 00000000..b4461713 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +bytes parse_hex(const std::string &str); + +std::string bytes2hex(const std::string &s); + +std::string uchar2Hex(unsigned char n); + +std::string add_0x(const std::string &s); + +std::string remove_0x(const std::string &s); + +template +std::string to_hex(const T &val) { + std::stringstream stream; + stream << std::hex << val; + std::string result(stream.str()); + if (result.size() % 2) + result = "0" + result; + return result; +} + +template +T from_hex(const std::string &s) { + T val; + std::stringstream stream; + stream << std::hex << s; + stream >> val; + + return val; +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp new file mode 100644 index 00000000..3944d0a2 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include + +#include + +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +class ethereum_rpc_client : public rpc_client { +public: + ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + + std::string admin_node_info(); + std::string eth_get_block_by_number(std::string block_number, bool full_block); + std::string eth_get_logs(std::string wallet_contract_address); + std::string net_version(); + std::string eth_get_transaction_count(const std::string ¶ms); + std::string eth_gas_price(); + + std::string get_chain_id(); + std::string get_network_id(); + std::string get_nonce(const std::string &address); + std::string get_gas_price(); + std::string get_gas_limit(); + + std::string eth_send_transaction(const std::string ¶ms); + std::string eth_send_raw_transaction(const std::string ¶ms); + std::string eth_get_transaction_receipt(const std::string ¶ms); +}; + +class sidechain_net_handler_ethereum : public sidechain_net_handler { +public: + sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + virtual ~sidechain_net_handler_ethereum(); + + bool process_proposal(const proposal_object &po); + void process_primary_wallet(); + void process_sidechain_addresses(); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto); + std::string send_sidechain_transaction(const sidechain_transaction_object &sto); + bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + +private: + std::string rpc_url; + std::string rpc_user; + std::string rpc_password; + std::string wallet_contract_address; + + ethereum_rpc_client *rpc_client; + + ethereum::chain_id_type chain_id; + ethereum::network_id_type network_id; + + std::string create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id); + std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); + std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); + + std::string sign_transaction(const sidechain_transaction_object &sto); + + uint64_t last_block_received; + fc::future _listener_task; + boost::signals2::signal event_received; + void schedule_ethereum_listener(); + void ethereum_listener_loop(); + void handle_event(const std::string &event_data); +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 12de3dfd..e2a8d9b8 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -177,6 +177,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("bitcoin-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); + cli.add_options()("ethereum-sidechain-enabled", bpo::value()->default_value(false), "Ethereum sidechain handler enabled"); + cli.add_options()("ethereum-node-rpc-url", bpo::value()->default_value("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]"); + cli.add_options()("ethereum-node-rpc-user", bpo::value(), "Ethereum RPC user"); + cli.add_options()("ethereum-node-rpc-password", bpo::value(), "Ethereum RPC password"); + cli.add_options()("ethereum-wallet-contract-address", bpo::value(), "Ethereum wallet contract address"); + cli.add_options()("ethereum-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", "9bedac2bd8fe2a6f6528e066c67fc8ac0622e96828d40c0e820d83c5bd2b0589")), + "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); + cli.add_options()("hive-sidechain-enabled", bpo::value()->default_value(false), "Hive sidechain handler enabled"); cli.add_options()("hive-node-rpc-url", bpo::value()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-user", bpo::value(), "Hive node RPC user"); @@ -238,13 +246,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt wlog("Haven't set up Bitcoin sidechain parameters"); } - //sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as(); - //config_ready_ethereum = options.count("ethereum-node-ip") && - // options.count("ethereum-address") && - // options.count("ethereum-public-key") && options.count("ethereum-private-key"); - //if (!config_ready_ethereum) { - // wlog("Haven't set up Ethereum sidechain parameters"); - //} + sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as(); + config_ready_ethereum = options.count("ethereum-node-rpc-url") && + /*options.count("ethereum-node-rpc-user") && options.count("ethereum-node-rpc-password") &&*/ + options.count("ethereum-wallet-contract-address") && + options.count("ethereum-private-key"); + if (sidechain_enabled_ethereum && !config_ready_ethereum) { + wlog("Haven't set up Ethereum sidechain parameters"); + } sidechain_enabled_hive = options.at("hive-sidechain-enabled").as(); config_ready_hive = options.count("hive-node-rpc-url") && @@ -283,10 +292,10 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { ilog("Bitcoin sidechain handler running"); } - //if (sidechain_enabled_ethereum && config_ready_ethereum) { - // net_manager->create_handler(sidechain_type::ethereum, options); - // ilog("Ethereum sidechain handler running"); - //} + if (sidechain_enabled_ethereum && config_ready_ethereum) { + net_handlers.at(sidechain_type::ethereum) = net_handler_factory.create_handler(sidechain_type::ethereum, options); + ilog("Ethereum sidechain handler running"); + } if (sidechain_enabled_hive && config_ready_hive) { net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 224144a0..971df7c7 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -172,18 +172,21 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ #ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS //enable_peerplays_asset_deposits = (sed.sidechain == sidechain_type::peerplays) && // (sed.sidechain_currency.compare("BTC") != 0) && + // (sed.sidechain_currency.compare("ETH") != 0) && // (sed.sidechain_currency.compare("HBD") != 0) && // (sed.sidechain_currency.compare("HIVE") != 0); #endif bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) || + ((sed.sidechain == sidechain_type::ethereum) && (sed.sidechain_currency.compare("ETH") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || enable_peerplays_asset_deposits); bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays ((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || + (sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))); @@ -240,6 +243,10 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ withdraw_currency = "BTC"; withdraw_currency_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; } + if (sed.sidechain_currency == object_id_to_string(gpo.parameters.eth_asset())) { + withdraw_currency = "ETH"; + withdraw_currency_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; + } if (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) { withdraw_currency = "HBD"; withdraw_currency_price = database.get(database.get_global_properties().parameters.hbd_asset()).options.core_exchange_rate; @@ -648,6 +655,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { bool is_tracked_asset = ((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) || + ((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) || ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp new file mode 100644 index 00000000..066c9dd8 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -0,0 +1,761 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SEND_RAW_TRANSACTION 1 + +namespace graphene { namespace peerplays_sidechain { + +ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : + rpc_client(url, user_name, password, debug_rpc_calls) { +} + +std::string ethereum_rpc_client::admin_node_info() { + return send_post_request("admin_nodeInfo", "", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_block_by_number(std::string block_number, bool full_block) { + std::string params = "[ \"" + block_number + "\", " + (full_block ? "true" : "false") + "]"; + return send_post_request("eth_getBlockByNumber", params, debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_logs(std::string wallet_contract_address) { + std::string params = "[{\"address\": \"" + wallet_contract_address + "\"}]"; + std::string reply_str = send_post_request("eth_getLogs", params, debug_rpc_calls); + return retrieve_value_from_reply(reply_str, ""); +} + +std::string ethereum_rpc_client::net_version() { + return send_post_request("net_version", "", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_transaction_count(const std::string ¶ms) { + return send_post_request("eth_getTransactionCount", params, debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_gas_price() { + return send_post_request("eth_gasPrice", "", debug_rpc_calls); +} + +std::string ethereum_rpc_client::get_chain_id() { + std::string reply_str = net_version(); + return retrieve_value_from_reply(reply_str, ""); +} + +std::string ethereum_rpc_client::get_network_id() { + std::string reply_str = admin_node_info(); + return retrieve_value_from_reply(reply_str, "protocols.eth.network"); +} + +std::string ethereum_rpc_client::get_nonce(const std::string &address) { + std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]"); + const auto nonce_val = ethereum::from_hex(retrieve_value_from_reply(reply_str, "")); + return nonce_val == 0 ? ethereum::add_0x("0") : ethereum::add_0x(ethereum::to_hex(nonce_val)); +} + +std::string ethereum_rpc_client::get_gas_price() { + std::string reply_str = eth_gas_price(); + return retrieve_value_from_reply(reply_str, ""); +} + +std::string ethereum_rpc_client::get_gas_limit() { + std::string reply_str = eth_get_block_by_number("latest", false); + if (!reply_str.empty()) { + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.count("result")) { + std::string gas_limit_s = json.get("result.gasLimit"); + return gas_limit_s; + } + } + return std::string{}; +} + +std::string ethereum_rpc_client::eth_send_transaction(const std::string ¶ms) { + return send_post_request("eth_sendTransaction", "[" + params + "]", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_send_raw_transaction(const std::string ¶ms) { + return send_post_request("eth_sendRawTransaction", "[ \"" + params + "\" ]", debug_rpc_calls); +} + +std::string ethereum_rpc_client::eth_get_transaction_receipt(const std::string ¶ms) { + return send_post_request("eth_getTransactionReceipt", "[\"" + params + "\"]", debug_rpc_calls); +} + +sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + sidechain_net_handler(_plugin, options) { + sidechain = sidechain_type::ethereum; + + if (options.count("debug-rpc-calls")) { + debug_rpc_calls = options.at("debug-rpc-calls").as(); + } + + rpc_url = options.at("ethereum-node-rpc-url").as(); + if (options.count("ethereum-node-rpc-user")) { + rpc_user = options.at("ethereum-node-rpc-user").as(); + } else { + rpc_user = ""; + } + if (options.count("ethereum-node-rpc-password")) { + rpc_password = options.at("ethereum-node-rpc-password").as(); + } else { + rpc_password = ""; + } + + wallet_contract_address = options.at("ethereum-wallet-contract-address").as(); + + if (options.count("ethereum-private-key")) { + const std::vector pub_priv_keys = options["ethereum-private-key"].as>(); + for (const std::string &itr_key_pair : pub_priv_keys) { + auto key_pair = graphene::app::dejsonify>(itr_key_pair, 5); + ilog("Ethereum Public Key: ${public}", ("public", key_pair.first)); + if (!key_pair.first.length() || !key_pair.second.length()) { + FC_THROW("Invalid public private key pair."); + } + private_keys[key_pair.first] = key_pair.second; + } + } + + rpc_client = new ethereum_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); + + std::string chain_id_str = rpc_client->get_chain_id(); + if (chain_id_str.empty()) { + elog("No Ethereum node running at ${url}", ("url", rpc_url)); + FC_ASSERT(false); + } + chain_id = std::stoll(chain_id_str); + std::string network_id_str = rpc_client->get_network_id(); + network_id = std::stoll(network_id_str); + + ilog("Running on Ethereum network, chain id ${chain_id_str}, network id ${network_id_str}", ("chain_id_str", chain_id_str)("network_id_str", network_id_str)); + + last_block_received = 0; + schedule_ethereum_listener(); + event_received.connect([this](const std::string &event_data) { + std::thread(&sidechain_net_handler_ethereum::handle_event, this, event_data).detach(); + }); +} + +sidechain_net_handler_ethereum::~sidechain_net_handler_ethereum() { +} + +bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) { + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); + + bool should_approve = false; + + const chain::global_property_object &gpo = database.get_global_properties(); + + int32_t op_idx_0 = -1; + chain::operation op_obj_idx_0; + + if (po.proposed_transaction.operations.size() >= 1) { + op_idx_0 = po.proposed_transaction.operations[0].which(); + op_obj_idx_0 = po.proposed_transaction.operations[0]; + } + + int32_t op_idx_1 = -1; + chain::operation op_obj_idx_1; + (void)op_idx_1; + + if (po.proposed_transaction.operations.size() >= 2) { + op_idx_1 = po.proposed_transaction.operations[1].which(); + op_obj_idx_1 = po.proposed_transaction.operations[1]; + } + + switch (op_idx_0) { + + case chain::operation::tag::value: { + bool address_ok = false; + bool transaction_ok = false; + son_wallet_id_type swo_id = op_obj_idx_0.get().son_wallet_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swo = idx.find(swo_id); + if (swo != idx.end()) { + + auto active_sons = gpo.active_sons.at(sidechain); + vector wallet_sons = swo->sons.at(sidechain); + + bool son_sets_equal = (active_sons.size() == wallet_sons.size()); + + if (son_sets_equal) { + for (size_t i = 0; i < active_sons.size(); i++) { + son_sets_equal = son_sets_equal && active_sons.at(i) == wallet_sons.at(i); + } + } + + if (son_sets_equal) { + address_ok = (op_obj_idx_0.get().address == wallet_contract_address); + } + + if (po.proposed_transaction.operations.size() >= 2) { + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swo = idx.find(object_id); + if (swo != idx.end()) { + tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), object_id.operator std::string()); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } else { + transaction_ok = true; + } + } + + should_approve = address_ok && + transaction_ok; + break; + } + + case chain::operation::tag::value: { + bool process_ok = false; + son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swdo = idx.find(swdo_id); + if (swdo != idx.end()) { + + //std::string swdo_txid = swdo->sidechain_transaction_id; + //std::string swdo_sidechain_from = swdo->sidechain_from; + //std::string swdo_sidechain_currency = swdo->sidechain_currency; + //uint64_t swdo_sidechain_amount = swdo->sidechain_amount.value; + //uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-"))); + // + //std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid); + //if (tx_str != "") { + // + // std::stringstream ss_tx(tx_str); + // boost::property_tree::ptree tx; + // boost::property_tree::read_json(ss_tx, tx); + // + // uint64_t op_idx = -1; + // for (const auto &ops : tx.get_child("result.operations")) { + // const auto &op = ops.second; + // op_idx = op_idx + 1; + // if (op_idx == swdo_op_idx) { + // std::string operation_type = op.get("type"); + // + // if (operation_type == "transfer_operation") { + // const auto &op_value = op.get_child("value"); + // + // std::string sidechain_from = op_value.get("from"); + // + // const auto &amount_child = op_value.get_child("amount"); + // + // uint64_t amount = amount_child.get("amount"); + // std::string nai = amount_child.get("nai"); + // std::string sidechain_currency = ""; + // if ((nai == "@@000000013" /*?? HBD*/) || (nai == "@@000000013" /*TBD*/)) { + // sidechain_currency = "HBD"; + // } + // if ((nai == "@@000000021") /*?? HIVE*/ || (nai == "@@000000021" /*TESTS*/)) { + // sidechain_currency = "HIVE"; + // } + // + // std::string memo = op_value.get("memo"); + // boost::trim(memo); + // if (!memo.empty()) { + // sidechain_from = memo; + // } + // + // process_ok = (swdo_sidechain_from == sidechain_from) && + // (swdo_sidechain_currency == sidechain_currency) && + // (swdo_sidechain_amount == amount); + // } + // } + // } + //} + } + + process_ok = true; + + should_approve = process_ok; + break; + } + + case chain::operation::tag::value: { + bool process_ok = false; + bool transaction_ok = false; + son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(swwo_id); + if (swwo != idx.end()) { + uint32_t swwo_block_num = swwo->block_num; + std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id; + uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1)); + + const auto &block = database.fetch_block_by_number(swwo_block_num); + + for (const auto &tx : block->transactions) { + if (tx.id().str() == swwo_peerplays_transaction_id) { + operation op = tx.operations[swwo_op_idx]; + transfer_operation t_op = op.get(); + + price asset_price = database.get(t_op.amount.asset_id).options.core_exchange_rate; + asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); + + process_ok = (t_op.to == gpo.parameters.son_account()) && + (swwo->peerplays_from == t_op.from) && + (swwo->peerplays_asset == peerplays_asset); + break; + } + } + + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(object_id); + if (swwo != idx.end()) { + tx_str = create_withdrawal_transaction(*swwo); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } + + should_approve = process_ok && + transaction_ok; + break; + } + + case chain::operation::tag::value: { + should_approve = true; + son_id_type signer = op_obj_idx_0.get().signer; + std::string signature = op_obj_idx_0.get().signature; + sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get().sidechain_transaction_id; + const auto &st_idx = database.get_index_type().indices().get(); + const auto sto = st_idx.find(sidechain_transaction_id); + if (sto == st_idx.end()) { + should_approve = false; + break; + } + + const auto &s_idx = database.get_index_type().indices().get(); + const auto son = s_idx.find(signer); + if (son == s_idx.end()) { + should_approve = false; + break; + } + + break; + } + + case chain::operation::tag::value: { + should_approve = true; + break; + } + + default: + should_approve = false; + elog("=================================================="); + elog("Proposal not considered for approval ${po}", ("po", po)); + elog("=================================================="); + } + + return should_approve; +} + +void sidechain_net_handler_ethereum::process_primary_wallet() { + const auto &swi = database.get_index_type().indices().get(); + const auto &active_sw = swi.rbegin(); + if (active_sw != swi.rend()) { + + if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || + (active_sw->addresses.at(sidechain).empty())) { + + if (proposal_exists(chain::operation::tag::value, active_sw->id)) { + return; + } + + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, active_sw->id)) { + return; + } + + const chain::global_property_object &gpo = database.get_global_properties(); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_update_operation swu_op; + swu_op.payer = gpo.parameters.son_account(); + swu_op.son_wallet_id = active_sw->id; + swu_op.sidechain = sidechain; + swu_op.address = wallet_contract_address; + proposal_op.proposed_ops.emplace_back(swu_op); + + std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), active_sw->id.operator std::string()); + if (!tx_str.empty()) { + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = active_sw->id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons.at(sidechain); + proposal_op.proposed_ops.emplace_back(stc_op); + } + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, active_sw->id); + } catch (fc::exception &e) { + elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); + return; + } + } + } +} + +void sidechain_net_handler_ethereum::process_sidechain_addresses() { +} + +bool sidechain_net_handler_ethereum::process_deposit(const son_wallet_deposit_object &swdo) { + const chain::global_property_object &gpo = database.get_global_properties(); + + price asset_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; + asset asset_to_issue = asset(swdo.peerplays_asset.amount * asset_price.quote.amount / asset_price.base.amount, database.get_global_properties().parameters.eth_asset()); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_deposit_process_operation swdp_op; + swdp_op.payer = gpo.parameters.son_account(); + swdp_op.son_wallet_deposit_id = swdo.id; + proposal_op.proposed_ops.emplace_back(swdp_op); + + asset_issue_operation ai_op; + ai_op.fee = database.current_fee_schedule().calculate_fee(ai_op); + ai_op.issuer = gpo.parameters.son_account(); + ai_op.asset_to_issue = asset_to_issue; + ai_op.issue_to_account = swdo.peerplays_from; + proposal_op.proposed_ops.emplace_back(ai_op); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception &e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + + return false; +} + +bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdraw_object &swwo) { + + if (proposal_exists(chain::operation::tag::value, swwo.id)) { + return false; + } + + std::string tx_str = create_withdrawal_transaction(swwo); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; + uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; + proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); + + son_wallet_withdraw_process_operation swwp_op; + swwp_op.payer = gpo.parameters.son_account(); + swwp_op.son_wallet_withdraw_id = swwo.id; + proposal_op.proposed_ops.emplace_back(swwp_op); + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swwo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons.at(sidechain); + proposal_op.proposed_ops.emplace_back(stc_op); + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return true; + } catch (fc::exception &e) { + elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; +} + +std::string sidechain_net_handler_ethereum::process_sidechain_transaction(const sidechain_transaction_object &sto) { + return sign_transaction(sto); +} + +std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sidechain_transaction_object &sto) { + boost::property_tree::ptree pt; + boost::property_tree::ptree pt_array; + for (const auto &signature : sto.signatures) { + const auto &transaction = signature.second; + + //! Check if we have this signed transaction, if not, don't send it + if (transaction.empty()) + continue; + +#ifdef SEND_RAW_TRANSACTION + const std::string sidechain_transaction = rpc_client->eth_send_raw_transaction(transaction); +#else + const std::string sidechain_transaction = rpc_client->eth_send_transaction(transaction); +#endif + + std::stringstream ss_tx(sidechain_transaction); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(ss_tx, tx_json); + if (tx_json.count("result") && !tx_json.count("error")) { + boost::property_tree::ptree node; + node.put("transaction", transaction); + node.put("transaction_receipt", tx_json.get("result")); + pt_array.push_back(std::make_pair("", node)); + } else { + //! Fixme + //! How should we proceed with error in eth_send_transaction + elog("Error in eth_send_transaction for transaction ${id}, transaction ${transaction}", ("id", sto.id)("transaction", transaction)); + return std::string{}; //! Return empty string, as we have error in sending + } + } + pt.add_child("result_array", pt_array); + + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, pt); + return ss.str(); +} + +bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount) { + std::stringstream ss(sto.sidechain_transaction); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (!json.count("result_array")) { + return false; + } + + size_t count = 0; + for (const auto &entry : json.get_child("result_array")) { + const std::string receipt = rpc_client->eth_get_transaction_receipt(entry.second.get("transaction_receipt")); + + std::stringstream ss_receipt(receipt); + boost::property_tree::ptree json_receipt; + boost::property_tree::read_json(ss_receipt, json_receipt); + + if (json_receipt.get("result") == "null") { + wlog("Block is not minted yet for transaction ${id}", ("id", sto.id)); + return false; + } + + if ("0x1" == json_receipt.get("result.status")) { + count += 1; + //! Fixme - compare data somehow? + //if( sto.transaction == entry_receipt.second.get("data") ) { + //} + } + } + + //! Check that we have all transactions + if (count != json.get_child("result_array").size()) { + wlog("Not all receipts received for transaction ${id}", ("id", sto.id)); + return false; + } else + return true; + + return false; +} + +std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string &object_id) { + std::vector> owners_weights; + for (auto &son : son_pubkeys) { + const std::string pub_key_str = son.public_key; + owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight)); + } + + const ethereum::update_owners_encoder encoder; + return encoder.encode(owners_weights, object_id); +} + +std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) { + return "Deposit-Transaction"; +} + +std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { + const ethereum::withdrawal_encoder encoder; + return encoder.encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string()); +} + +std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) { + const auto ¤t_son = plugin.get_current_son_object(sidechain); + FC_ASSERT(current_son.sidechain_public_keys.contains(sidechain), "No public keys for current son: ${account_id}", ("account_id", current_son.son_account)); + + const auto &public_key = current_son.sidechain_public_keys.at(sidechain); + +#ifdef SEND_RAW_TRANSACTION + ethereum::raw_transaction raw_tr; + raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key)); + raw_tr.gas_price = rpc_client->get_gas_price(); + raw_tr.gas_limit = rpc_client->get_gas_limit(); + raw_tr.to = wallet_contract_address; + raw_tr.value = ""; + raw_tr.data = sto.transaction; + raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id)); + + const auto sign_tr = raw_tr.sign(get_private_key(public_key)); + return sign_tr.serialize(); +#else + ethereum::transaction sign_transaction; + sign_transaction.data = sto.transaction; + sign_transaction.to = wallet_contract_address; + sign_transaction.from = "0x" + public_key; + return sign_transaction.sign(get_private_key(public_key)).serialize(); +#endif +} + +void sidechain_net_handler_ethereum::schedule_ethereum_listener() { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next = 5000; + + fc::time_point next_wakeup(now + fc::milliseconds(time_to_next)); + + _listener_task = fc::schedule([this] { + ethereum_listener_loop(); + }, + next_wakeup, "SON Ethereum listener task"); +} + +void sidechain_net_handler_ethereum::ethereum_listener_loop() { + schedule_ethereum_listener(); + + std::string reply = rpc_client->eth_get_block_by_number("latest", false); + //std::string reply = rpc_client->eth_get_logs(wallet_contract_address); + if (!reply.empty()) { + std::stringstream ss(reply); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.count("result")) { + std::string head_block_number_s = json.get("result.number"); + uint64_t head_block_number = std::strtoul(head_block_number_s.c_str(), nullptr, 16); + if (head_block_number != last_block_received) { + std::string event_data = std::to_string(head_block_number); + handle_event(event_data); + last_block_received = head_block_number; + } + } + } +} + +void sidechain_net_handler_ethereum::handle_event(const std::string &event_data) { + std::string block = rpc_client->eth_get_block_by_number("latest", true); + if (block != "") { + add_to_son_listener_log("BLOCK : " + event_data); + std::stringstream ss(block); + boost::property_tree::ptree block_json; + boost::property_tree::read_json(ss, block_json); + + size_t tx_idx = -1; + for (const auto &tx_child : block_json.get_child("result.transactions")) { + boost::property_tree::ptree tx = tx_child.second; + tx_idx = tx_idx + 1; + + std::string from = tx.get("from"); + std::string to = tx.get("to"); + + std::string cmp_to = to; + std::transform(cmp_to.begin(), cmp_to.end(), cmp_to.begin(), ::toupper); + std::string cmp_wallet_contract_address = wallet_contract_address; + std::transform(cmp_wallet_contract_address.begin(), cmp_wallet_contract_address.end(), cmp_wallet_contract_address.begin(), ::toupper); + + if (cmp_to == cmp_wallet_contract_address) { + + std::string value_s = tx.get("value"); + boost::multiprecision::uint256_t amount(value_s); + amount = amount / 100000; + amount = amount / 100000; + + const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); + const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, from, time_point_sec::maximum())); + if (addr_itr == sidechain_addresses_idx.end()) { + continue; + } + + std::stringstream ss; + ss << "ethereum" + << "-" << tx.get("hash") << "-" << tx_idx; + std::string sidechain_uid = ss.str(); + + sidechain_event_data sed; + sed.timestamp = database.head_block_time(); + sed.block_num = database.head_block_num(); + sed.sidechain = sidechain; + sed.sidechain_uid = sidechain_uid; + sed.sidechain_transaction_id = tx.get("hash"); + sed.sidechain_from = from; + sed.sidechain_to = to; + sed.sidechain_currency = "ETH"; + sed.sidechain_amount = amount; + sed.peerplays_from = addr_itr->sidechain_address_account; + sed.peerplays_to = database.get_global_properties().parameters.son_account(); + price eth_price = database.get(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate; + sed.peerplays_asset = asset(sed.sidechain_amount * eth_price.base.amount / eth_price.quote.amount); + + add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id); + + sidechain_event_data_received(sed); + } + } + } +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp index 1c6632d8..cd6b50be 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -18,6 +19,9 @@ std::unique_ptr sidechain_net_handler_factory::create_han case sidechain_type::hive: { return std::unique_ptr(new sidechain_net_handler_hive(plugin, options)); } + case sidechain_type::ethereum: { + return std::unique_ptr(new sidechain_net_handler_ethereum(plugin, options)); + } case sidechain_type::peerplays: { return std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); } diff --git a/libraries/sha3/CMakeLists.txt b/libraries/sha3/CMakeLists.txt new file mode 100644 index 00000000..c790323e --- /dev/null +++ b/libraries/sha3/CMakeLists.txt @@ -0,0 +1,16 @@ +file(GLOB HEADERS "include/sha3/*.h") + +add_library( sha3 + memzero.c + sha3.c +) + +target_include_directories( sha3 PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include ) +target_compile_definitions( sha3 PUBLIC USE_KECCAK=1 ) + +install( TARGETS + sha3 + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/libraries/sha3/include/sha3/memzero.h b/libraries/sha3/include/sha3/memzero.h new file mode 100644 index 00000000..1e744c12 --- /dev/null +++ b/libraries/sha3/include/sha3/memzero.h @@ -0,0 +1,16 @@ +#ifndef __MEMZERO_H__ +#define __MEMZERO_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif +void memzero(void* const pnt, const size_t len); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif diff --git a/libraries/sha3/include/sha3/sha3.h b/libraries/sha3/include/sha3/sha3.h new file mode 100644 index 00000000..881e806c --- /dev/null +++ b/libraries/sha3/include/sha3/sha3.h @@ -0,0 +1,88 @@ +/* sha3.h - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#ifndef __SHA3_H__ +#define __SHA3_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define sha3_224_hash_size 28 +#define sha3_256_hash_size 32 +#define sha3_384_hash_size 48 +#define sha3_512_hash_size 64 +#define sha3_max_permutation_size 25 +#define sha3_max_rate_in_qwords 24 + +#define SHA3_224_BLOCK_LENGTH 144 +#define SHA3_256_BLOCK_LENGTH 136 +#define SHA3_384_BLOCK_LENGTH 104 +#define SHA3_512_BLOCK_LENGTH 72 + +#define SHA3_224_DIGEST_LENGTH sha3_224_hash_size +#define SHA3_256_DIGEST_LENGTH sha3_256_hash_size +#define SHA3_384_DIGEST_LENGTH sha3_384_hash_size +#define SHA3_512_DIGEST_LENGTH sha3_512_hash_size + +/** + * SHA3 Algorithm context. + */ +typedef struct SHA3_CTX +{ + /* 1600 bits algorithm hashing state */ + uint64_t hash[sha3_max_permutation_size]; + /* 1536-bit buffer for leftovers */ + uint64_t message[sha3_max_rate_in_qwords]; + /* count of bytes in the message[] buffer */ + unsigned rest; + /* size of a message block processed at once */ + unsigned block_size; +} SHA3_CTX; + +/* methods for calculating the hash function */ + +void sha3_224_Init(SHA3_CTX *ctx); +void sha3_256_Init(SHA3_CTX *ctx); +void sha3_384_Init(SHA3_CTX *ctx); +void sha3_512_Init(SHA3_CTX *ctx); +void sha3_Update(SHA3_CTX *ctx, const unsigned char* msg, size_t size); +void sha3_Final(SHA3_CTX *ctx, unsigned char* result); + +#if USE_KECCAK +#define keccak_224_Init sha3_224_Init +#define keccak_256_Init sha3_256_Init +#define keccak_384_Init sha3_384_Init +#define keccak_512_Init sha3_512_Init +#define keccak_Update sha3_Update +void keccak_Final(SHA3_CTX *ctx, unsigned char* result); +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest); +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest); +#endif + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest); +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __SHA3_H__ */ diff --git a/libraries/sha3/memzero.c b/libraries/sha3/memzero.c new file mode 100644 index 00000000..32aa140f --- /dev/null +++ b/libraries/sha3/memzero.c @@ -0,0 +1,75 @@ +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 // C11's bounds-checking interface. +#endif +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef __unix__ +#include +#include +#endif + +// C11's bounds-checking interface. +#if defined(__STDC_LIB_EXT1__) +#define HAVE_MEMSET_S 1 +#endif + +// GNU C Library version 2.25 or later. +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// Newlib +#if defined(__NEWLIB__) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// FreeBSD version 11.0 or later. +#if defined(__FreeBSD__) && __FreeBSD_version >= 1100037 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// OpenBSD version 5.5 or later. +#if defined(__OpenBSD__) && OpenBSD >= 201405 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// NetBSD version 7.2 or later. +#if defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 +#define HAVE_EXPLICIT_MEMSET 1 +#endif + +// Adapted from +// https://github.com/jedisct1/libsodium/blob/1647f0d53ae0e370378a9195477e3df0a792408f/src/libsodium/sodium/utils.c#L102-L130 + +void memzero(void *const pnt, const size_t len) { +#ifdef _WIN32 + SecureZeroMemory(pnt, len); +#elif defined(HAVE_MEMSET_S) + memset_s(pnt, (rsize_t)len, 0, (rsize_t)len); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero(pnt, len); +#elif defined(HAVE_EXPLICIT_MEMSET) + explicit_memset(pnt, 0, len); +#else + volatile unsigned char *volatile pnt_ = (volatile unsigned char *volatile)pnt; + size_t i = (size_t)0U; + + while (i < len) { + pnt_[i++] = 0U; + } +#endif + + // explicitly mark the memory as overwritten for the Clang MemorySanitizer + // this is only included at compile time if MemorySanitizer is enabled and + // should not come with any downsides during regular builds +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + memset(pnt, 0, len); +#endif +#endif +} diff --git a/libraries/sha3/sha3.c b/libraries/sha3/sha3.c new file mode 100644 index 00000000..172728eb --- /dev/null +++ b/libraries/sha3/sha3.c @@ -0,0 +1,397 @@ +/* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#include +#include + +#include +#include + +#define I64(x) x##LL +#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) +#define le2me_64(x) (x) +#define IS_ALIGNED_64(p) (0 == (7 & ((long)(p)))) // [wallet-core] pointer/numerical type, for MacOS SDK 12.3 +# define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) + +/* constants */ +#define NumberOfRounds 24 + +/* SHA3 (Keccak) constants for 24 rounds */ +uint64_t keccak_round_constants[NumberOfRounds] = { + I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), I64(0x8000000080008000), + I64(0x000000000000808B), I64(0x0000000080000001), I64(0x8000000080008081), I64(0x8000000000008009), + I64(0x000000000000008A), I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A), + I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), I64(0x8000000000008003), + I64(0x8000000000008002), I64(0x8000000000000080), I64(0x000000000000800A), I64(0x800000008000000A), + I64(0x8000000080008081), I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008) +}; + +/* Initializing a sha3 context for given number of output bits */ +static void keccak_Init(SHA3_CTX *ctx, unsigned bits) +{ + /* NB: The Keccak capacity parameter = bits * 2 */ + unsigned rate = 1600 - bits * 2; + + memzero(ctx, sizeof(SHA3_CTX)); + ctx->block_size = rate / 8; + assert(rate <= 1600 && (rate % 64) == 0); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_224_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 224); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_256_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 256); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_384_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 384); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_512_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 512); +} + +/* Keccak theta() transformation */ +static void keccak_theta(uint64_t *A) +{ + unsigned int x = 0; + uint64_t C[5] = {0}, D[5] = {0}; + + for (x = 0; x < 5; x++) { + C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; + } + D[0] = ROTL64(C[1], 1) ^ C[4]; + D[1] = ROTL64(C[2], 1) ^ C[0]; + D[2] = ROTL64(C[3], 1) ^ C[1]; + D[3] = ROTL64(C[4], 1) ^ C[2]; + D[4] = ROTL64(C[0], 1) ^ C[3]; + + for (x = 0; x < 5; x++) { + A[x] ^= D[x]; + A[x + 5] ^= D[x]; + A[x + 10] ^= D[x]; + A[x + 15] ^= D[x]; + A[x + 20] ^= D[x]; + } +} + +/* Keccak pi() transformation */ +static void keccak_pi(uint64_t *A) +{ + uint64_t A1 = 0; + A1 = A[1]; + A[ 1] = A[ 6]; + A[ 6] = A[ 9]; + A[ 9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[ 2]; + A[ 2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[ 4]; + A[ 4] = A[24]; + A[24] = A[21]; + A[21] = A[ 8]; + A[ 8] = A[16]; + A[16] = A[ 5]; + A[ 5] = A[ 3]; + A[ 3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[ 7]; + A[ 7] = A[10]; + A[10] = A1; + /* note: A[ 0] is left as is */ +} + +/* Keccak chi() transformation */ +static void keccak_chi(uint64_t *A) +{ + int i = 0; + for (i = 0; i < 25; i += 5) { + uint64_t A0 = A[0 + i], A1 = A[1 + i]; + A[0 + i] ^= ~A1 & A[2 + i]; + A[1 + i] ^= ~A[2 + i] & A[3 + i]; + A[2 + i] ^= ~A[3 + i] & A[4 + i]; + A[3 + i] ^= ~A[4 + i] & A0; + A[4 + i] ^= ~A0 & A1; + } +} + +static void sha3_permutation(uint64_t *state) +{ + int round = 0; + for (round = 0; round < NumberOfRounds; round++) + { + keccak_theta(state); + + /* apply Keccak rho() transformation */ + state[ 1] = ROTL64(state[ 1], 1); + state[ 2] = ROTL64(state[ 2], 62); + state[ 3] = ROTL64(state[ 3], 28); + state[ 4] = ROTL64(state[ 4], 27); + state[ 5] = ROTL64(state[ 5], 36); + state[ 6] = ROTL64(state[ 6], 44); + state[ 7] = ROTL64(state[ 7], 6); + state[ 8] = ROTL64(state[ 8], 55); + state[ 9] = ROTL64(state[ 9], 20); + state[10] = ROTL64(state[10], 3); + state[11] = ROTL64(state[11], 10); + state[12] = ROTL64(state[12], 43); + state[13] = ROTL64(state[13], 25); + state[14] = ROTL64(state[14], 39); + state[15] = ROTL64(state[15], 41); + state[16] = ROTL64(state[16], 45); + state[17] = ROTL64(state[17], 15); + state[18] = ROTL64(state[18], 21); + state[19] = ROTL64(state[19], 8); + state[20] = ROTL64(state[20], 18); + state[21] = ROTL64(state[21], 2); + state[22] = ROTL64(state[22], 61); + state[23] = ROTL64(state[23], 56); + state[24] = ROTL64(state[24], 14); + + keccak_pi(state); + keccak_chi(state); + + /* apply iota(state, round) */ + *state ^= keccak_round_constants[round]; + } +} + +/** + * The core transformation. Process the specified block of data. + * + * @param hash the algorithm state + * @param block the message block to process + * @param block_size the size of the processed block in bytes + */ +static void sha3_process_block(uint64_t hash[25], const uint64_t *block, size_t block_size) +{ + /* expanded loop */ + hash[ 0] ^= le2me_64(block[ 0]); + hash[ 1] ^= le2me_64(block[ 1]); + hash[ 2] ^= le2me_64(block[ 2]); + hash[ 3] ^= le2me_64(block[ 3]); + hash[ 4] ^= le2me_64(block[ 4]); + hash[ 5] ^= le2me_64(block[ 5]); + hash[ 6] ^= le2me_64(block[ 6]); + hash[ 7] ^= le2me_64(block[ 7]); + hash[ 8] ^= le2me_64(block[ 8]); + /* if not sha3-512 */ + if (block_size > 72) { + hash[ 9] ^= le2me_64(block[ 9]); + hash[10] ^= le2me_64(block[10]); + hash[11] ^= le2me_64(block[11]); + hash[12] ^= le2me_64(block[12]); + /* if not sha3-384 */ + if (block_size > 104) { + hash[13] ^= le2me_64(block[13]); + hash[14] ^= le2me_64(block[14]); + hash[15] ^= le2me_64(block[15]); + hash[16] ^= le2me_64(block[16]); + /* if not sha3-256 */ + if (block_size > 136) { + hash[17] ^= le2me_64(block[17]); +#ifdef FULL_SHA3_FAMILY_SUPPORT + /* if not sha3-224 */ + if (block_size > 144) { + hash[18] ^= le2me_64(block[18]); + hash[19] ^= le2me_64(block[19]); + hash[20] ^= le2me_64(block[20]); + hash[21] ^= le2me_64(block[21]); + hash[22] ^= le2me_64(block[22]); + hash[23] ^= le2me_64(block[23]); + hash[24] ^= le2me_64(block[24]); + } +#endif + } + } + } + /* make a permutation of the hash */ + sha3_permutation(hash); +} + +#define SHA3_FINALIZED 0x80000000 + +/** + * Calculate message hash. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param ctx the algorithm context containing current hashing state + * @param msg message chunk + * @param size length of the message chunk + */ +void sha3_Update(SHA3_CTX *ctx, const unsigned char *msg, size_t size) +{ + size_t idx = (size_t)ctx->rest; + size_t block_size = (size_t)ctx->block_size; + + if (ctx->rest & SHA3_FINALIZED) return; /* too late for additional input */ + ctx->rest = (unsigned)((ctx->rest + size) % block_size); + + /* fill partial block */ + if (idx) { + size_t left = block_size - idx; + memcpy((char*)ctx->message + idx, msg, (size < left ? size : left)); + if (size < left) return; + + /* process partial block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + msg += left; + size -= left; + } + while (size >= block_size) { + uint64_t *aligned_message_block = NULL; + if (IS_ALIGNED_64(msg)) { + /* the most common case is processing of an already aligned message + without copying it */ + aligned_message_block = (uint64_t*)(void*)msg; + } else { + memcpy(ctx->message, msg, block_size); + aligned_message_block = ctx->message; + } + + sha3_process_block(ctx->hash, aligned_message_block, block_size); + msg += block_size; + size -= block_size; + } + if (size) { + memcpy(ctx->message, msg, size); /* save leftovers */ + } +} + +/** + * Store calculated hash into the given array. + * + * @param ctx the algorithm context containing current hashing state + * @param result calculated hash in binary form + */ +void sha3_Final(SHA3_CTX *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x06; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +#if USE_KECCAK +/** +* Store calculated hash into the given array. +* +* @param ctx the algorithm context containing current hashing state +* @param result calculated hash in binary form +*/ +void keccak_Final(SHA3_CTX *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x01; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} + +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + keccak_512_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} +#endif /* USE_KECCAK */ + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + sha3_256_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} + +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + sha3_512_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 43ca49fd..c334ebc2 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2845,7 +2845,7 @@ public: account_id_type son_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_account_id); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); if (approve) { @@ -2891,7 +2891,7 @@ public: account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) @@ -2902,7 +2902,7 @@ public: account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); - FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type"); unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) @@ -2943,6 +2943,7 @@ public: case sidechain_type::peerplays : return "peerplays"; case sidechain_type::bitcoin : return "bitcoin"; case sidechain_type::hive : return "hive"; + case sidechain_type::ethereum : return "ethereum"; default: FC_THROW("Wrong sidechain type: ${sidechain}", ("sidechain", sidechain)); } diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 67c5019a..0e5d67cf 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -128,11 +128,13 @@ BOOST_AUTO_TEST_CASE( create_sons ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); auto son1_obj = con.wallet_api_ptr->get_son("son1account"); @@ -140,16 +142,20 @@ BOOST_AUTO_TEST_CASE( create_sons ) BOOST_CHECK_EQUAL(son1_obj.url, "http://son1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); auto son2_obj = con.wallet_api_ptr->get_son("son2account"); BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account")); BOOST_CHECK_EQUAL(son2_obj.url, "http://son2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2"); - BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 24); - BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 25); + BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2"); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 25); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 26); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 27); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -170,6 +176,7 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; son_test_helper sth(*this); sth.create_son("sonmember", "http://sonmember", sidechain_public_keys); @@ -182,19 +189,23 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) BOOST_CHECK(son_data.son_account == sonmember_acct.get_id()); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 1"); BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::ethereum).instance(), 24); // update SON sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", sidechain_public_keys, true); son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember_updated"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 2"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::ethereum], "ethereum address 2"); // update SON signing key sidechain_public_keys.clear(); @@ -222,11 +233,13 @@ BOOST_AUTO_TEST_CASE( son_voting ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); BOOST_CHECK(generate_maintenance_block()); @@ -253,6 +266,7 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_TEST_MESSAGE("Voting for son1account"); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, true, true); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, true, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::ethereum, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there @@ -260,11 +274,13 @@ BOOST_AUTO_TEST_CASE( son_voting ) son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son1_end_votes[sidechain_type::hive] > son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] > son1_start_votes[sidechain_type::ethereum]); // Vote for a son2account BOOST_TEST_MESSAGE("Voting for son2account"); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, true, true); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, true, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::ethereum, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there @@ -272,6 +288,7 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] > son2_start_votes[sidechain_type::ethereum]); //! Check son1account voters auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; @@ -280,6 +297,8 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check son2account voters auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; @@ -288,6 +307,8 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check votes of nathan auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -298,11 +319,15 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son1account BOOST_TEST_MESSAGE("Withdraw vote for a son1account"); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, false, true); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, false, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::ethereum, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed @@ -310,12 +335,14 @@ BOOST_AUTO_TEST_CASE( son_voting ) son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); //! Check son1account voters voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; BOOST_REQUIRE(voters_for_son1account); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -324,11 +351,14 @@ BOOST_AUTO_TEST_CASE( son_voting ) BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son2account BOOST_TEST_MESSAGE("Withdraw vote for a son2account"); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, false, true); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, false, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::ethereum, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed @@ -336,12 +366,14 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); //! Check son2account voters voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; BOOST_REQUIRE(voters_for_son2account); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -383,6 +415,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), "http://son" + fc::to_pretty_string(i), sidechain_public_keys, @@ -400,6 +433,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -409,14 +443,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); for(unsigned int i = 0; i < son_number - 1; i++) { @@ -424,14 +461,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); for(unsigned int i = 0; i < son_number - 2; i++) { @@ -439,14 +479,17 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[ethereum]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(generate_maintenance_block()); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == son_number); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -468,11 +511,13 @@ BOOST_AUTO_TEST_CASE( list_son ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); auto res = con.wallet_api_ptr->list_sons("", 100); @@ -500,11 +545,13 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); BOOST_CHECK(generate_maintenance_block()); @@ -539,6 +586,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 2, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 2, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 2, true); generate_block(); BOOST_CHECK(generate_maintenance_block()); @@ -546,10 +595,14 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] > son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] > son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] > son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check son1account voters @@ -559,6 +612,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check son2account voters auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; @@ -567,6 +622,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::ethereum).voters[0].instance, nathan_account_object.id.instance()); //! Check votes of nathan auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -577,6 +634,9 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for SON 1 accepted.clear(); @@ -587,17 +647,23 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 1, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 1, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] < son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] < son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] < son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); // voice distribution changed, SON2 now has all voices son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); // nathan spent funds for vb, it has different voting power + BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] > son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check son1account voters @@ -605,6 +671,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_REQUIRE(voters_for_son1account); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -613,6 +680,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son2_obj.id.instance()); // Try to reject incorrect SON accepted.clear(); @@ -622,16 +691,22 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 1, true), fc::exception); BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 1, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true), fc::exception); generate_block(); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check votes of nathan @@ -641,6 +716,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::ethereum).at(0).id.instance(), son2_obj.id.instance()); // Reject SON2 accepted.clear(); @@ -650,16 +727,22 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 0, true); update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 0, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 0, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] < son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] < son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] < son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check son2account voters @@ -667,6 +750,7 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) BOOST_REQUIRE(voters_for_son2account); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::ethereum).voters.size(), 0); //! Check votes of nathan nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; @@ -681,16 +765,22 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) sidechain_type::bitcoin, 1, true), fc::exception); BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::hive, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::ethereum] == son1_start_votes[sidechain_type::ethereum]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check votes of nathan @@ -702,16 +792,24 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) rejected.clear(); BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::ethereum, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); + BOOST_CHECK(son2_end_votes[sidechain_type::ethereum] == son2_start_votes[sidechain_type::ethereum]); son2_start_votes = son2_end_votes; //! Check votes of nathan @@ -734,6 +832,7 @@ BOOST_AUTO_TEST_CASE( related_functions ) global_property_object gpo = con.wallet_api_ptr->get_global_properties(); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 0); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 0); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == 0); flat_map sidechain_public_keys; @@ -742,16 +841,19 @@ BOOST_AUTO_TEST_CASE( related_functions ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 1"; sth.create_son("son1account", "http://son1", sidechain_public_keys); sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address 2"; sth.create_son("son2account", "http://son2", sidechain_public_keys); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 2); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 2); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == 2); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -783,6 +885,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), "http://son" + fc::to_pretty_string(i), sidechain_public_keys, @@ -799,6 +902,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) std::string name = "sonaccount" + fc::to_pretty_string(i); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -808,14 +912,17 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::ethereum).size()); BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::ethereum).size() == son_number); map active_sons = con.wallet_api_ptr->list_active_sons(); BOOST_CHECK(active_sons.size() == son_number); @@ -855,6 +962,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) sidechain_public_keys.clear(); sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address " + fc::to_pretty_string(i); sidechain_public_keys[sidechain_type::hive] = "hive account " + fc::to_pretty_string(i); + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address " + fc::to_pretty_string(i); sth.create_son("sonaccount" + fc::to_pretty_string(i), "http://son" + fc::to_pretty_string(i), sidechain_public_keys, @@ -870,12 +978,14 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::bitcoin, true, true); con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::hive, true, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::ethereum, true, true); } BOOST_CHECK(generate_maintenance_block()); son_object son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -885,6 +995,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); // restore SON activity con.wallet_api_ptr->cancel_request_son_maintenance(name, true); @@ -894,6 +1005,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -903,6 +1015,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); // process maintenance BOOST_CHECK(generate_maintenance_block()); @@ -911,7 +1024,7 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) son_obj = con.wallet_api_ptr->get_son(name); BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::in_maintenance); - + BOOST_CHECK(son_obj.statuses.at(sidechain_type::ethereum) == son_status::in_maintenance); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); diff --git a/tests/peerplays_sidechain/ethereum_transaction_tests.cpp b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp new file mode 100644 index 00000000..769c35b1 --- /dev/null +++ b/tests/peerplays_sidechain/ethereum_transaction_tests.cpp @@ -0,0 +1,147 @@ +#include + +#include +#include +#include + +using namespace graphene::peerplays_sidechain::ethereum; + +BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests) + +BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) { + const withdrawal_encoder encoder; + const auto tx = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0"); + BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000"); + + const auto tx1 = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1"); + BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(update_owners_encoder_test) { + std::vector> owners_weights; + owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1); + owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1); + + const update_owners_encoder encoder; + const auto tx = encoder.encode(owners_weights, "1.35.0"); + BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000"); + + owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1); + const auto tx1 = encoder.encode(owners_weights, "1.36.1"); + BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000"); +} + +BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto tx = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"); + + //! Change value + raw_tr.value = "0x1BC16D674EC80000"; + const auto tx1 = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx1, "0xEC80843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000080218080"); + + //! Change data + raw_tr.data = "0x893d20e8"; + const auto tx2 = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx2, "0xF080843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000084893D20E8218080"); +} + +BOOST_AUTO_TEST_CASE(raw_transaction_deserialization_test) { + const raw_transaction raw_tr{"E480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"}; + + BOOST_CHECK_EQUAL(raw_tr.nonce, "0x0"); + BOOST_CHECK_EQUAL(raw_tr.gas_price, "0x3b9aca07"); + BOOST_CHECK_EQUAL(raw_tr.gas_limit, "0x7a1200"); + BOOST_CHECK_EQUAL(raw_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348"); + BOOST_CHECK_EQUAL(raw_tr.value, "0x0"); + BOOST_CHECK_EQUAL(raw_tr.data, ""); + BOOST_CHECK_EQUAL(raw_tr.chain_id, "0x21"); +} + +BOOST_AUTO_TEST_CASE(raw_transaction_hash_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto tx = raw_tr.serialize(); + BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"); + + const auto hash = raw_tr.hash(); + const auto hash_str = fc::to_hex((char *)&hash[0], hash.size()); + BOOST_CHECK_EQUAL(hash_str, "34934410cd305f4fa4e75a2c9294d625d6fbba729b5642ed2ca757ead50bb1fb"); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060"); + BOOST_CHECK_EQUAL(sign_tr.r, "5f09de6ac850b2a9e94acd709c12d4e9adbabc6b72281ec0bbe13bca7e57c7ce"); + BOOST_CHECK_EQUAL(sign_tr.v, "65"); + BOOST_CHECK_EQUAL(sign_tr.s, "7ca5f26c5b3e25f14a32b18ac9a2a41b7c68efd3b04b118e1b1f4bf1c4e299b0"); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_serialization_test) { + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = "0x21"; + + const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060"); + const auto tx = sign_tr.serialize(); + BOOST_CHECK_EQUAL(tx, "0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0"); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_deserialization_test) { + const signed_transaction sign_tr{"0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0"}; + + BOOST_CHECK_EQUAL(sign_tr.nonce, "0x0"); + BOOST_CHECK_EQUAL(sign_tr.gas_price, "0x3b9aca07"); + BOOST_CHECK_EQUAL(sign_tr.gas_limit, "0x7a1200"); + BOOST_CHECK_EQUAL(sign_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348"); + BOOST_CHECK_EQUAL(sign_tr.value, "0x0"); + BOOST_CHECK_EQUAL(sign_tr.data, ""); +} + +BOOST_AUTO_TEST_CASE(sign_transaction_recover_test) { + const std::string chain_id = "0x21"; + + raw_transaction raw_tr; + raw_tr.nonce = "0x0"; + raw_tr.gas_price = "0x3b9aca07"; + raw_tr.gas_limit = "0x7a1200"; + raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348"; + raw_tr.value = ""; + raw_tr.data = ""; + raw_tr.chain_id = chain_id; + + const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060"); + const auto from = sign_tr.recover(chain_id); + BOOST_CHECK_EQUAL(from, "0x5fbbb31be52608d2f52247e8400b7fcaa9e0bc12"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index 72f50b9d..818c1743 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -135,6 +135,7 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { flat_map sidechain_public_keys; sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; sidechain_public_keys[sidechain_type::hive] = "hive address"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address"; son_create_operation op; op.owner_account = bob_id; diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 5bb003aa..a128b474 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -87,6 +87,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { { flat_map sidechain_public_keys; sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + sidechain_public_keys[sidechain_type::hive] = "hive account"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address"; son_create_operation op; op.owner_account = alice_id; @@ -111,6 +113,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) { BOOST_CHECK( obj->url == test_url ); BOOST_CHECK( obj->signing_key == alice_public_key ); BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::hive) == "hive account" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::ethereum) == "ethereum address" ); BOOST_CHECK( obj->deposit.instance == deposit.instance.value ); BOOST_CHECK( obj->pay_vb.instance == payment.instance.value ); } @@ -124,7 +128,9 @@ BOOST_AUTO_TEST_CASE( update_son_test ) { { flat_map sidechain_public_keys; - sidechain_public_keys[sidechain_type::bitcoin] = "new bitcoin address"; + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + sidechain_public_keys[sidechain_type::hive] = "hive account"; + sidechain_public_keys[sidechain_type::ethereum] = "ethereum address"; son_update_operation op; op.son_id = son_id_type(0); @@ -143,7 +149,9 @@ BOOST_AUTO_TEST_CASE( update_son_test ) { auto obj = idx.find( alice_id ); BOOST_REQUIRE( obj != idx.end() ); BOOST_CHECK( obj->url == new_url ); - BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "new bitcoin address" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::hive) == "hive account" ); + BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::ethereum) == "ethereum address" ); } BOOST_AUTO_TEST_CASE( deregister_son_test ) { @@ -195,12 +203,14 @@ try { { _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; _s.statuses[sidechain_type::hive] = son_status::in_maintenance; + _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); }); auto deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -222,6 +232,7 @@ try { BOOST_REQUIRE( idx.size() == 1 ); BOOST_REQUIRE( obj->statuses.at(sidechain_type::bitcoin) == son_status::deregistered ); BOOST_REQUIRE( obj->statuses.at(sidechain_type::hive) == son_status::deregistered ); + BOOST_REQUIRE( obj->statuses.at(sidechain_type::ethereum) == son_status::deregistered ); BOOST_REQUIRE( son_stats_obj->deregistered_timestamp == now ); deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -496,30 +507,38 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) { _s.txs_signed[sidechain_type::bitcoin] = 2; _s.txs_signed[sidechain_type::hive] = 4; + _s.txs_signed[sidechain_type::ethereum] = 6; _s.total_txs_signed[sidechain_type::bitcoin] = 2; _s.total_txs_signed[sidechain_type::hive] = 4; + _s.total_txs_signed[sidechain_type::ethereum] = 6; _s.sidechain_txs_reported[sidechain_type::bitcoin] = 4; _s.sidechain_txs_reported[sidechain_type::hive] = 8; + _s.sidechain_txs_reported[sidechain_type::ethereum] = 12; _s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 4; _s.total_sidechain_txs_reported[sidechain_type::hive] = 8; + _s.total_sidechain_txs_reported[sidechain_type::ethereum] = 12; }); // Modify the transaction signed statistics of Bob's SON db.modify( *son_stats_obj2, [&]( son_statistics_object& _s) { _s.txs_signed[sidechain_type::bitcoin] = 3; _s.txs_signed[sidechain_type::hive] = 6; + _s.txs_signed[sidechain_type::ethereum] = 9; _s.total_txs_signed[sidechain_type::bitcoin] = 3; _s.total_txs_signed[sidechain_type::hive] = 6; + _s.total_txs_signed[sidechain_type::ethereum] = 9; _s.sidechain_txs_reported[sidechain_type::bitcoin] = 6; _s.sidechain_txs_reported[sidechain_type::hive] = 12; + _s.sidechain_txs_reported[sidechain_type::ethereum] = 18; _s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 6; _s.total_sidechain_txs_reported[sidechain_type::hive] = 12; + _s.total_sidechain_txs_reported[sidechain_type::ethereum] = 18; }); // Note the balances before the maintenance @@ -531,21 +550,29 @@ BOOST_AUTO_TEST_CASE( son_pay_test ) // Check if the signed transaction statistics are reset for both SONs BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::bitcoin), 0); BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::hive), 0); + BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::ethereum), 0); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::bitcoin), 2); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::hive), 4); + BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::ethereum), 6); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::bitcoin), 3); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::hive), 6); + BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::ethereum), 9); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 4); BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::hive), 8); + BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::ethereum), 12); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 6); BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::hive), 12); + BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::ethereum), 18); // Check that Alice and Bob are paid for signing the transactions in the previous day/cycle BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance); BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance); @@ -609,12 +636,14 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { _s.statuses[sidechain_type::bitcoin] = son_status::active; _s.statuses[sidechain_type::hive] = son_status::active; + _s.statuses[sidechain_type::ethereum] = son_status::active; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time()); _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time()); + _s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time()); }); { @@ -633,6 +662,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::request_maintenance); } { @@ -651,6 +681,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); } // Modify SON's status to in_maintenance @@ -658,11 +689,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; _s.statuses[sidechain_type::hive] = son_status::in_maintenance; + _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; }); flat_map downtime; downtime[sidechain_type::bitcoin] = 0; downtime[sidechain_type::hive] = 0; + downtime[sidechain_type::ethereum] = 0; { generate_block(); @@ -680,12 +713,16 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::inactive); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); } // Modify SON's status to in_maintenance @@ -693,6 +730,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; _s.statuses[sidechain_type::hive] = son_status::in_maintenance; + _s.statuses[sidechain_type::ethereum] = son_status::in_maintenance; }); // SON is selected as one of the active SONs @@ -702,6 +740,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { son_inf.son_id = son_id_type(0); _gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf); _gpo.active_sons[sidechain_type::hive].push_back(son_inf); + _gpo.active_sons[sidechain_type::ethereum].push_back(son_inf); }); { @@ -721,12 +760,16 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch()); downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch(); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); } { @@ -745,10 +788,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { trx.clear(); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin)); BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive)); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum)); BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts); } } FC_LOG_AND_RETHROW() } @@ -775,6 +821,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active); { // Check that transaction fails if down_ts < last_active_timestamp @@ -828,8 +875,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::in_maintenance); BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts); BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts); + BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum) == op.down_ts); } {