#include #include #include #include #include #include #include #include #include namespace sidechain { sidechain_net_manager::sidechain_net_manager( graphene::chain::database* _db, std::string _ip, uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password ) { initialize_manager(_db, _ip, _zmq, _rpc, _user, _password ); } void sidechain_net_manager::initialize_manager( graphene::chain::database* _db, std::string _ip, uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password ) { listener = std::unique_ptr( new zmq_listener( _ip, _zmq ) ); bitcoin_client = std::unique_ptr( new bitcoin_rpc_client( _ip, _rpc, _user, _password ) ); db = std::unique_ptr( _db ); fc::http::connection conn; try { conn.connect_to( fc::ip::endpoint( fc::ip::address( _ip ), _rpc ) ); } catch ( fc::exception e ) { elog( "No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", _ip) ("port", _rpc) ); FC_ASSERT( false ); } listener->block_received.connect([this]( const std::string& block_hash ) { std::thread( &sidechain_net_manager::handle_block, this, block_hash ).detach(); } ); db->send_btc_tx.connect([this]( const sidechain::bitcoin_transaction& trx ) { std::thread( &sidechain_net_manager::send_btc_tx, this, trx ).detach(); } ); } void sidechain_net_manager::update_tx_infos( const std::string& block_hash ) { std::string block = bitcoin_client->receive_full_block( block_hash ); if( block != "" ) { const auto& vins = extract_info_from_block( block ); for( const auto& v : vins ) { db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address ); } } } void sidechain_net_manager::update_tx_approvals() { std::vector trx_for_check; const auto& confirmations_num = db->get_sidechain_params().confirmations_num; db->bitcoin_confirmations.safe_for([&]( btc_tx_confirmations_index::iterator itr_b, btc_tx_confirmations_index::iterator itr_e ){ for(auto iter = itr_b; iter != itr_e; iter++) { db->bitcoin_confirmations.modify( iter->transaction_id, [&]( bitcoin_transaction_confirmations& obj ) { obj.count_block++; }); if( iter->count_block == confirmations_num ) { trx_for_check.push_back( iter->transaction_id ); } } }); update_transaction_status( trx_for_check ); } void sidechain_net_manager::update_estimated_fee() { db->estimated_feerate = bitcoin_client->receive_estimated_fee(); } void sidechain_net_manager::send_btc_tx( const sidechain::bitcoin_transaction& trx ) { db->bitcoin_confirmations.insert( bitcoin_transaction_confirmations( trx.get_txid() ) ); FC_ASSERT( !bitcoin_client->connection_is_not_defined() ); const auto tx_hex = fc::to_hex( pack( trx ) ); bitcoin_client->send_btc_tx( tx_hex ); } bool sidechain_net_manager::connection_is_not_defined() const { return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined(); } void sidechain_net_manager::handle_block( const std::string& block_hash ) { update_tx_approvals(); update_estimated_fee(); update_tx_infos( block_hash ); } std::vector sidechain_net_manager::extract_info_from_block( const std::string& _block ) { std::stringstream ss( _block ); boost::property_tree::ptree block; boost::property_tree::read_json( ss, block ); std::vector result; const auto& addr_idx = db->get_index_type().indices().get(); for (const auto& tx_child : block.get_child("tx")) { const auto& tx = tx_child.second; for ( const auto& o : tx.get_child("vout") ) { const auto script = o.second.get_child("scriptPubKey"); if( !script.count("addresses") ) continue; for (const auto& addr : script.get_child("addresses")) { // in which cases there can be more addresses? const auto address_base58 = addr.second.get_value(); if( !addr_idx.count( address_base58 ) ) continue; info_for_vin vin; vin.out.hash_tx = tx.get_child("txid").get_value(); vin.out.amount = parse_amount( o.second.get_child( "value" ).get_value() ); vin.out.n_vout = o.second.get_child( "n" ).get_value(); vin.address = address_base58; result.push_back( vin ); } } } return result; } void sidechain_net_manager::update_transaction_status( std::vector trx_for_check ) { const auto& confirmations_num = db->get_sidechain_params().confirmations_num; for( const auto& trx : trx_for_check ) { auto confirmations = bitcoin_client->receive_confirmations_tx( trx.str() ); db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { obj.count_block = confirmations; }); if( confirmations >= confirmations_num ) { db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { obj.confirmed = true; }); } else if( confirmations == 0 ) { auto is_in_mempool = bitcoin_client->receive_mempool_entry_tx( trx.str() ); db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { obj.missing = !is_in_mempool; }); } } } // Removes dot from amount output: "50.00000000" inline uint64_t sidechain_net_manager::parse_amount(std::string raw) { raw.erase(std::remove(raw.begin(), raw.end(), '.'), raw.end()); return std::stoll(raw); } }