diff --git a/Dockerfile b/Dockerfile index 8a970e39..dc4caae4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ RUN \ libreadline-dev \ libssl-dev \ libtool \ + libzmq3-dev \ locales \ ntp \ pkg-config \ diff --git a/README.md b/README.md index 8207bb29..941afa68 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ This is a quick introduction to get new developers and witnesses up to speed on The following dependencies were necessary for a clean install of Ubuntu 18.04 LTS: ``` - sudo apt-get install gcc-5 g++-5 cmake make libbz2-dev\ - libdb++-dev libdb-dev libssl-dev openssl libreadline-dev\ - autoconf libtool git + sudo apt-get install autoconf bash build-essential ca-certificates cmake \ + doxygen git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ + libreadline-dev libssl-dev libtool libzmq3-dev locales ntp pkg-config \ + wget ``` ## Build Boost 1.67.0 diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index 931d4f45..4941ce51 100644 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -2,9 +2,12 @@ file(GLOB HEADERS "include/graphene/peerplays_sidechain/*.hpp") add_library( peerplays_sidechain peerplays_sidechain_plugin.cpp + sidechain_net_manager.cpp + sidechain_net_handler.cpp + sidechain_net_handler_bitcoin.cpp ) -target_link_libraries( peerplays_sidechain graphene_chain graphene_app ) +target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq ) target_include_directories( peerplays_sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/peerplays_sidechain/addresses.txt b/libraries/plugins/peerplays_sidechain/addresses.txt new file mode 100644 index 00000000..df57167d --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/addresses.txt @@ -0,0 +1,391 @@ +2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J +{ + "address": "2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J", + "scriptPubKey": "a914873aad1ecf7510c80b83d8ca94d21432dc71b88787", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/2']0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd))#7s0qfnvz", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00141d30ac0c47f7b32460265daf49c5925236f5882d", + "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "1d30ac0c47f7b32460265daf49c5925236f5882d", + "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", + "address": "bcrt1qr5c2crz877ejgcpxtkh5n3vj2gm0tzpdqrw3n0", + "scriptPubKey": "00141d30ac0c47f7b32460265daf49c5925236f5882d" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/2'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2MxAnE469fhhdvUqUB7daU997VSearb2mn7 +{ + "address": "2MxAnE469fhhdvUqUB7daU997VSearb2mn7", + "scriptPubKey": "a914360175a50918495a20573aed68d506a790420fe587", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/3']02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d))#p3st4e4e", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00146c1c0571f3132eb0702f487123d2026495592830", + "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "6c1c0571f3132eb0702f487123d2026495592830", + "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", + "address": "bcrt1qdswq2u0nzvhtqup0fpcj85szvj24j2psgfwy6w", + "scriptPubKey": "00146c1c0571f3132eb0702f487123d2026495592830" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/3'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL +{ + "address": "2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL", + "scriptPubKey": "a914bdce56e7f2fc04614c0f6f4d1d59fff63b0b73f187", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/4']020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462))#3r63c8fu", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00146abd0d5f055df80b41bc6449328eda09f61a2be3", + "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "6abd0d5f055df80b41bc6449328eda09f61a2be3", + "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", + "address": "bcrt1qd27s6hc9thuqksduv3yn9rk6p8mp52lr2e846e", + "scriptPubKey": "00146abd0d5f055df80b41bc6449328eda09f61a2be3" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/4'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR +{ + "address": "2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR", + "scriptPubKey": "a914b7abe6d957106da3a21782eea1164f4964b521ba87", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/5']03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce))#e3sfze3l", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8", + "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "7a4fd72ff8e192004c70d8139b4ca53e1467d8c8", + "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", + "address": "bcrt1q0f8awtlcuxfqqnrsmqfekn998c2x0kxgf4t7dm", + "scriptPubKey": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/5'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL +{ + "address": "2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL", + "scriptPubKey": "a914dcb019e8330b4fffc50ba22bbf90215922a1379787", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/6']028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013))#3326m2za", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652", + "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "ccd3dee026a1d641352f5b8c7d72805d75fd0652", + "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", + "address": "bcrt1qenfaacpx58tyzdf0twx86u5qt46l6pjjef5y7u", + "scriptPubKey": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/6'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG +{ + "address": "2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG", + "scriptPubKey": "a9144cb2b8f97d8e7ad5bfb81afd611394387f374ab887", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/7']02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b))#7gvhakzu", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", + "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", + "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", + "address": "bcrt1quds85rm5tchm3vz0u8a87puvx5qf5a7pqw8u7l", + "scriptPubKey": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/7'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg +{ + "address": "2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg", + "scriptPubKey": "a914dae51c6601ef4e05817f67d57d3ac0c8cb64948e87", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/8']03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b))#ckuy6v83", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7", + "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "60c1c7776b1f92cd36fe6138b99212ebe6381fe7", + "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", + "address": "bcrt1qvrquwamtr7fv6dh7vyutnysja0nrs8l8vzcnwj", + "scriptPubKey": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/8'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX +{ + "address": "2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX", + "scriptPubKey": "a91413930c6d40f5f01b169f2fc7884c2b3984ff8a3087", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/9']022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac))#f8cdpnn9", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4", + "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "35b75b7c34a7f908955b8b93aaa677aa47fab2c4", + "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", + "address": "bcrt1qxkm4klp55lus392m3wf64fnh4frl4vkyng4ffg", + "scriptPubKey": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/9'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM +{ + "address": "2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM", + "scriptPubKey": "a9145e91543069ae37d8bace2f59aade945f5916dc3287", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/10']03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af))#x7xpvc0y", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", + "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", + "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", + "address": "bcrt1qe6e4hqvckkx8dlx83fcdr5aza5w5xf0jd22xet", + "scriptPubKey": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/10'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + +2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy +{ + "address": "2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy", + "scriptPubKey": "a914e132c578bd294a01a472d42b376b29e5ef6678c987", + "ismine": true, + "solvable": true, + "desc": "sh(wpkh([153472fd/0'/0'/11']02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b))#2janhauz", + "iswatchonly": false, + "isscript": true, + "iswitness": false, + "script": "witness_v0_keyhash", + "hex": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762", + "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", + "embedded": { + "isscript": false, + "iswitness": true, + "witness_version": 0, + "witness_program": "831d63f8d99be71f9b385f2ccc92ab2783da3762", + "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", + "address": "bcrt1qsvwk87xen0n3lxectukvey4ty7pa5dmz0yanuv", + "scriptPubKey": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762" + }, + "label": "", + "ischange": false, + "timestamp": 1571845292, + "hdkeypath": "m/0'/0'/11'", + "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", + "hdmasterfingerprint": "153472fd", + "labels": [ + { + "name": "", + "purpose": "receive" + } + ] +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp new file mode 100644 index 00000000..1b6a9099 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +enum network { + bitcoin, + //ethereum +}; + +using bytes = std::vector; + +struct prev_out +{ + bool operator!=( const prev_out& obj ) const + { + if( this->hash_tx != obj.hash_tx || + this->n_vout != obj.n_vout || + this->amount != obj.amount ) + { + return true; + } + return false; + } + + std::string hash_tx; + uint32_t n_vout; + uint64_t amount; +}; + +struct info_for_vin +{ + info_for_vin() = default; + + info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes(), bool _resend = false ); + + bool operator!=( const info_for_vin& obj ) const; + + struct comparer { + bool operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const; + }; + + static uint64_t count_id_info_for_vin; + uint64_t id; + + fc::sha256 identifier; + + prev_out out; + std::string address; + bytes script; + + bool used = false; + bool resend = false; +}; + +struct sidechain_event_data { + network sidechain; + std::string transaction_id; + std::string from; + std::string to; + int64_t amount; +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index d32fb09d..45628223 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -1,10 +1,9 @@ #pragma once #include -#include -#include -#include +#include +#include namespace graphene { namespace peerplays_sidechain { using namespace chain; @@ -30,5 +29,5 @@ class peerplays_sidechain_plugin : public graphene::app::plugin std::unique_ptr my; }; -} } //graphene::peerplays_sidechain_plugin +} } //graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp new file mode 100644 index 00000000..fa4f0b50 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_handler { +public: + sidechain_net_handler(const boost::program_options::variables_map& options); + virtual ~sidechain_net_handler(); + + std::vector get_user_sidechain_address_mapping(); + +protected: + graphene::peerplays_sidechain::network network; + + virtual std::string create_multisignature_wallet( const std::vector public_keys ) = 0; + virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; + virtual std::string sign_transaction( const std::string& transaction ) = 0; + virtual std::string send_transaction( const std::string& transaction ) = 0; + + virtual void handle_event( const std::string& event_data ) = 0; + +private: + +}; + +} } // 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 new file mode 100644 index 00000000..792aaf45 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +class bitcoin_rpc_client { +public: + bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ; + std::string receive_full_block( const std::string& block_hash ); + int32_t receive_confirmations_tx( const std::string& tx_hash ); + bool receive_mempool_entry_tx( const std::string& tx_hash ); + uint64_t receive_estimated_fee(); + void send_btc_tx( const std::string& tx_hex ); + bool connection_is_not_defined() const; + +private: + + fc::http::reply send_post_request( std::string body ); + + std::string ip; + uint32_t rpc_port; + std::string user; + std::string password; + + fc::http::header authorization; +}; + +// ============================================================================= + +class zmq_listener { +public: + zmq_listener( std::string _ip, uint32_t _zmq ); + bool connection_is_not_defined() const { return zmq_port == 0; } + + fc::signal event_received; +private: + void handle_zmq(); + std::vector receive_multipart(); + + std::string ip; + uint32_t zmq_port; + + zmq::context_t ctx; + zmq::socket_t socket; +}; + +// ============================================================================= + +class sidechain_net_handler_bitcoin : public sidechain_net_handler { +public: + sidechain_net_handler_bitcoin(const boost::program_options::variables_map& options); + virtual ~sidechain_net_handler_bitcoin(); + + void update_tx_infos( const std::string& block_hash ); + + //void update_tx_approvals(); + + //void update_estimated_fee(); + + //void send_btc_tx( const sidechain::bitcoin_transaction& trx ); + + bool connection_is_not_defined() const; + + std::string create_multisignature_wallet( const std::vector public_keys ); + std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ); + std::string sign_transaction( const std::string& transaction ); + std::string send_transaction( const std::string& transaction ); + +private: + std::string ip; + uint32_t zmq_port; + uint32_t rpc_port; + std::string rpc_user; + std::string rpc_password; + + std::unique_ptr listener; + std::unique_ptr bitcoin_client; + graphene::chain::database* db; + + void handle_event( const std::string& event_data); + + std::vector extract_info_from_block( const std::string& _block ); + + void update_transaction_status( std::vector trx_for_check ); + + std::set get_valid_vins( const std::string tx_hash ); + + inline uint64_t parse_amount(std::string raw); + +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp new file mode 100644 index 00000000..49073314 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_manager { +public: + sidechain_net_manager(); + virtual ~sidechain_net_manager(); + + bool create_handler(peerplays_sidechain::network network, const boost::program_options::variables_map& options); +private: + + std::vector> net_handlers; + +}; + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 36d0b713..9b993614 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,11 +1,15 @@ #include +#include +#include + +namespace bpo = boost::program_options; + namespace graphene { namespace peerplays_sidechain { namespace detail { - class peerplays_sidechain_plugin_impl { public: @@ -16,8 +20,8 @@ class peerplays_sidechain_plugin_impl peerplays_sidechain_plugin& _self; - uint32_t parameter; - uint32_t optional_parameter; + peerplays_sidechain::sidechain_net_manager _net_manager; + }; peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() @@ -48,8 +52,22 @@ void peerplays_sidechain_plugin::plugin_set_program_options( ) { cli.add_options() - ("parameter", boost::program_options::value(), "Parameter") - ("optional-parameter", boost::program_options::value(), "Optional parameter") + //("bitcoin-node-ip", bpo::value()->implicit_value("127.0.0.1"), "IP address of Bitcoin node") + //("bitcoin-node-zmq-port", bpo::value()->implicit_value(28332), "ZMQ port of Bitcoin node") + //("bitcoin-node-rpc-port", bpo::value()->implicit_value(18332), "RPC port of Bitcoin node") + //("bitcoin-node-rpc-user", bpo::value(), "Bitcoin RPC user") + //("bitcoin-node-rpc-password", bpo::value(), "Bitcoin RPC password") + //("bitcoin-address", bpo::value(), "Bitcoin address") + //("bitcoin-public-key", bpo::value(), "Bitcoin public key") + //("bitcoin-private-key", bpo::value(), "Bitcoin private key") + ("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node") + ("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node") + ("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node") + ("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user") + ("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password") + ("bitcoin-address", bpo::value()->default_value("2N911a7smwDzUGARg8s7Q1ViizFCw6gWcbR"), "Bitcoin address") + ("bitcoin-public-key", bpo::value()->default_value("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772"), "Bitcoin public key") + ("bitcoin-private-key", bpo::value()->default_value("cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr"), "Bitcoin private key") ; cfg.add(cli); } @@ -58,11 +76,13 @@ void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options: { ilog("peerplays sidechain plugin: plugin_initialize()"); - if (options.count("parameter")) { - my->parameter = options["optional-parameter"].as(); - } - if (options.count("optional-parameter")) { - my->optional_parameter = options["optional-parameter"].as(); + if( 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-address" ) && options.count( "bitcoin-public-key" ) && options.count( "bitcoin-private-key" ) ) + { + my->_net_manager.create_handler(network::bitcoin, options); + } else { + wlog("Haven't set up bitcoin sidechain parameters"); } } @@ -71,4 +91,5 @@ void peerplays_sidechain_plugin::plugin_startup() ilog("peerplays sidechain plugin: plugin_startup()"); } -} } +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp new file mode 100644 index 00000000..fefeacc1 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -0,0 +1,35 @@ +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_handler::sidechain_net_handler(const boost::program_options::variables_map& options) { +} + +sidechain_net_handler::~sidechain_net_handler() { +} + +std::vector sidechain_net_handler::get_user_sidechain_address_mapping() { + std::vector result; + + switch (network) { + case network::bitcoin: + result.push_back("2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J"); + result.push_back("2MxAnE469fhhdvUqUB7daU997VSearb2mn7"); + result.push_back("2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL"); + result.push_back("2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR"); + result.push_back("2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL"); + //result.push_back("2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG"); + //result.push_back("2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg"); + //result.push_back("2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX"); + //result.push_back("2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM"); + //result.push_back("2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy"); + + default: + assert(false); + } + + return result; +} + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp new file mode 100644 index 00000000..1fce21ea --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -0,0 +1,403 @@ +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "graphene/peerplays_sidechain/sidechain_net_manager.hpp" + +namespace graphene { namespace peerplays_sidechain { + +// ============================================================================= + +bitcoin_rpc_client::bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password ): + ip( _ip ), rpc_port( _rpc ), user( _user ), password( _password ) +{ + authorization.key = "Authorization"; + authorization.val = "Basic " + fc::base64_encode( user + ":" + password ); +} + +std::string bitcoin_rpc_client::receive_full_block( const std::string& block_hash ) +{ + fc::http::connection conn; + conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); + + const auto url = "http://" + ip + ":" + std::to_string( rpc_port ) + "/rest/block/" + block_hash + ".json"; + + const auto reply = conn.request( "GET", url ); + if ( reply.status != 200 ) + return ""; + + ilog( "Receive Bitcoin block: ${hash}", ( "hash", block_hash ) ); + return std::string( reply.body.begin(), reply.body.end() ); +} + +//int32_t bitcoin_rpc_client::receive_confirmations_tx( const std::string& tx_hash ) +//{ +// const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getrawtransaction\", \"params\": [") + +// std::string("\"") + tx_hash + std::string("\"") + ", " + "true" + std::string("] }"); +// +// const auto reply = send_post_request( body ); +// +// if ( reply.status != 200 ) +// return 0; +// +// const auto result = std::string( reply.body.begin(), reply.body.end() ); +// +// std::stringstream ss( result ); +// boost::property_tree::ptree tx; +// boost::property_tree::read_json( ss, tx ); +// +// if( tx.count( "result" ) ) { +// if( tx.get_child( "result" ).count( "confirmations" ) ) { +// return tx.get_child( "result" ).get_child( "confirmations" ).get_value(); +// } +// } +// return 0; +//} + +bool bitcoin_rpc_client::receive_mempool_entry_tx( const std::string& tx_hash ) +{ + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getmempoolentry\", \"params\": [") + + std::string("\"") + tx_hash + std::string("\"") + std::string("] }"); + + const auto reply = send_post_request( body ); + + if ( reply.status != 200 ) + return false; + + return true; +} + +uint64_t bitcoin_rpc_client::receive_estimated_fee() +{ + static const auto confirmation_target_blocks = 6; + + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimated_feerate\", \"method\": \"estimatesmartfee\", \"params\": [") + + std::to_string(confirmation_target_blocks) + std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.status != 200 ) + return 0; + + std::stringstream ss( std::string( reply.body.begin(), reply.body.end() ) ); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( json.count( "result" ) ) + if ( json.get_child( "result" ).count( "feerate" ) ) { + auto feerate_str = json.get_child( "result" ).get_child( "feerate" ).get_value(); + feerate_str.erase( std::remove( feerate_str.begin(), feerate_str.end(), '.' ), feerate_str.end() ); + return std::stoll( feerate_str ); + } + return 0; +} + +void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex ) +{ + const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"send_tx\", \"method\": \"sendrawtransaction\", \"params\": [") + + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.body.empty() ) + return; + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + idump(( tx_hex )); + return; + } else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + const auto error_code = json.get_child( "error" ).get_child( "code" ).get_value(); + if( error_code == -27 ) // transaction already in block chain + return; + + wlog( "BTC tx is not sent! Reply: ${msg}", ("msg", reply_str) ); + } +} + +bool bitcoin_rpc_client::connection_is_not_defined() const +{ + return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); +} + +fc::http::reply bitcoin_rpc_client::send_post_request( std::string body ) +{ + fc::http::connection conn; + conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); + + const auto url = "http://" + ip + ":" + std::to_string( rpc_port ); + + return conn.request( "POST", url, body, fc::http::headers{authorization} ); +} + +// ============================================================================= + +zmq_listener::zmq_listener( std::string _ip, uint32_t _zmq ): ip( _ip ), zmq_port( _zmq ), ctx( 1 ), socket( ctx, ZMQ_SUB ) { + std::thread( &zmq_listener::handle_zmq, this ).detach(); +} + +std::vector zmq_listener::receive_multipart() { + std::vector msgs; + + int32_t more; + size_t more_size = sizeof( more ); + while ( true ) { + zmq::message_t msg; + socket.recv( &msg, 0 ); + socket.getsockopt( ZMQ_RCVMORE, &more, &more_size ); + + if ( !more ) + break; + msgs.push_back( std::move(msg) ); + } + + return msgs; +} + +void zmq_listener::handle_zmq() { + socket.setsockopt( ZMQ_SUBSCRIBE, "hashblock", 9 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "hashtx", 6 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawblock", 8 ); + //socket.setsockopt( ZMQ_SUBSCRIBE, "rawtx", 5 ); + socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) ); + + while ( true ) { + auto msg = receive_multipart(); + const auto header = std::string( static_cast( msg[0].data() ), msg[0].size() ); + const auto hash = boost::algorithm::hex( std::string( static_cast( msg[1].data() ), msg[1].size() ) ); + + event_received( hash ); + } +} + +// ============================================================================= + +sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(const boost::program_options::variables_map& options) : + sidechain_net_handler(options) { + network = peerplays_sidechain::network::bitcoin; + + ip = options.at("bitcoin-node-ip").as(); + zmq_port = options.at("bitcoin-node-zmq-port").as(); + rpc_port = options.at("bitcoin-node-rpc-port").as(); + rpc_user = options.at("bitcoin-node-rpc-user").as(); + rpc_password = options.at("bitcoin-node-rpc-password").as(); + + fc::http::connection conn; + try { + conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) ); + } catch ( fc::exception e ) { + elog( "No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", ip) ("port", rpc_port) ); + FC_ASSERT( false ); + } + + listener = std::unique_ptr( new zmq_listener( ip, zmq_port ) ); + bitcoin_client = std::unique_ptr( new bitcoin_rpc_client( ip, rpc_port, rpc_user, rpc_password ) ); + //db = _db; + + listener->event_received.connect([this]( const std::string& event_data ) { + std::thread( &sidechain_net_handler_bitcoin::handle_event, this, event_data ).detach(); + } ); + + //db->send_btc_tx.connect([this]( const sidechain::bitcoin_transaction& trx ) { + // std::thread( &sidechain_net_handler_bitcoin::send_btc_tx, this, trx ).detach(); + //} ); +} + +sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { +} + +void sidechain_net_handler_bitcoin::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 ); +// const auto& addr_idx = db->get_index_type().indices().get(); +// for( const auto& v : vins ) { +// const auto& addr_itr = addr_idx.find( v.address ); +// FC_ASSERT( addr_itr != addr_idx.end() ); +// db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address, addr_itr->address.get_witness_script() ); +// } + } +} + +//void sidechain_net_handler_bitcoin::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_handler_bitcoin::update_estimated_fee() +//{ +// db->estimated_feerate = bitcoin_client->receive_estimated_fee(); +//} + +//void sidechain_net_handler_bitcoin::send_btc_tx( const sidechain::bitcoin_transaction& trx ) +//{ +// std::set valid_vins; +// for( const auto& v : trx.vin ) { +// valid_vins.insert( v.prevout.hash ); +// } +// db->bitcoin_confirmations.insert( bitcoin_transaction_confirmations( trx.get_txid(), valid_vins ) ); +// +// 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_handler_bitcoin::connection_is_not_defined() const +{ + return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined(); +} + +std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) +{ + return ""; +} + +std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) +{ + return ""; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction( const std::string& transaction ) +{ + return ""; +} + +std::string sidechain_net_handler_bitcoin::send_transaction( const std::string& transaction ) +{ + return ""; +} + +void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) { + ilog("peerplays sidechain plugin: sidechain_net_handler_bitcoin::handle_event"); + ilog(" event_data: ${event_data}", ("event_data", event_data)); + //update_tx_approvals(); + //update_estimated_fee(); + //update_tx_infos( block_hash ); +} + +std::vector sidechain_net_handler_bitcoin::extract_info_from_block( const std::string& _block ) +{ + std::stringstream ss( _block ); + boost::property_tree::ptree block; + boost::property_tree::read_json( ss, block ); + + std::vector result; + + const auto& addr_idx = get_user_sidechain_address_mapping();// 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(); + + auto it = find(addr_idx.begin(), addr_idx.end(), address_base58); + if (it == addr_idx.end()) 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_handler_bitcoin::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() ); +// +// std::set valid_vins; +// if( !is_in_mempool ) { +// valid_vins = get_valid_vins( trx.str() ); +// } +// +// db->bitcoin_confirmations.modify( trx, [&]( bitcoin_transaction_confirmations& obj ) { +// obj.missing = !is_in_mempool; +// obj.valid_vins = valid_vins; +// }); +// } +// } +//} + +//std::set sidechain_net_handler_bitcoin::get_valid_vins( const std::string tx_hash ) +//{ +// const auto& confirmations_obj = db->bitcoin_confirmations.find( fc::sha256( tx_hash ) ); +// FC_ASSERT( confirmations_obj.valid() ); +// +// std::set valid_vins; +// for( const auto& v : confirmations_obj->valid_vins ) { +// auto confirmations = bitcoin_client->receive_confirmations_tx( v.str() ); +// if( confirmations == 0 ) { +// continue; +// } +// valid_vins.insert( v ); +// } +// return valid_vins; +//} + +// Removes dot from amount output: "50.00000000" +inline uint64_t sidechain_net_handler_bitcoin::parse_amount(std::string raw) { + raw.erase(std::remove(raw.begin(), raw.end(), '.'), raw.end()); + return std::stoll(raw); +} + +// ============================================================================= + +} } // graphene::peerplays_sidechain + diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp new file mode 100644 index 00000000..e1c0bce6 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_manager::sidechain_net_manager() { + ilog(__FUNCTION__); +} + +sidechain_net_manager::~sidechain_net_manager() { + ilog(__FUNCTION__); +} + +bool sidechain_net_manager::create_handler(peerplays_sidechain::network network, const boost::program_options::variables_map& options) { + ilog(__FUNCTION__); + + bool ret_val = false; + + switch (network) { + case network::bitcoin: { + std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(options)); + net_handlers.push_back(std::move(h)); + ret_val = true; + } + default: + assert(false); + } + + return ret_val; +} + +} } // graphene::peerplays_sidechain +