Compare commits

...

56 commits

Author SHA1 Message Date
Meheboob Khan
28df2e744d Fix for failing Unit tests 2022-08-24 19:55:08 +02:00
serkixenos
34df0c0754 Fix CI/CD build 2022-08-23 02:59:03 +02:00
Vlad Dobromyslov
6c2bf868c5 Fix signing of eth transaction. Calculate v value 2022-08-22 16:02:58 +03:00
Vlad Dobromyslov
adc6743ef0 1) get_nonce
2) get_gas_price
3) get_gas_limit
2022-08-22 10:43:53 +03:00
Vlad Dobromyslov
e3098c3fb6 rlp_decoder class 2022-08-19 17:34:57 +03:00
Vlad Dobromyslov
e6d980d092 1) raw_transaction
2) signed_transaction
3) rlp_encoder classes
2022-08-18 14:54:44 +03:00
Vlad Dobromyslov
b24b204e79 Add sha3 library for keccak 2022-08-17 07:48:59 +03:00
Vlad Dobromyslov
1c1ae88488 Delete submodule SHA3IUF 2022-08-16 10:30:14 +03:00
Vlad Dobromyslov
46ac4d79a3 withdraw transaction 2022-08-05 11:29:21 +03:00
Vlad Dobromyslov
212e5bc7ad Add ethereum::transaction 2022-08-04 13:06:53 +03:00
Vlad Dobromyslov
f6614ab122 base_encoder + update_owners_encoder 2022-08-04 10:27:09 +03:00
Vlad Dobromyslov
7f3e7e876a Replace string_to_hex with boost::algorithm::hex 2022-08-03 11:10:30 +03:00
Vlad Dobromyslov
776dc729b5 UpdateOwners:
1) process_proposal
2) sign_transaction
3) send_sidechain_transaction
4) settle_sidechain_transaction
2022-08-02 11:17:58 +03:00
serkixenos
7629ecc8b3 Improve SONs config parameter checks 2022-07-29 18:07:49 +02:00
Vlad Dobromyslov
e9dbafb56a Fix "hive-node-rpc-url" 2022-07-28 10:34:36 +03:00
serkixenos
6f2bc4584f Remove WS client 2022-07-27 01:42:01 +02:00
serkixenos
abda10d884 Remove WS client 2022-07-26 19:43:32 +02:00
serkixenos
35c2d4c63f Remove WS client 2022-07-26 19:34:13 +02:00
serkixenos
867f38796a Merge branch 'develop' into feature/son-for-ethereum 2022-07-26 17:11:16 +02:00
serkixenos
f12c9f3ef9 SON for Ethereum listener, WIP 2022-07-21 22:01:51 +02:00
serkixenos
51aa87130e Merge branch 'develop' into feature/son-for-ethereum 2022-07-21 20:55:13 +02:00
serkixenos
0f4ec86913 WEP 2022-07-16 00:01:59 +02:00
serkixenos
9de4a07ce2 Try to fix docker builds 2022-07-15 19:42:26 +02:00
serkixenos
b2eb4a8ff9 Boost Beast based Websocket client 2022-07-15 07:35:24 +02:00
serkixenos
28ac8214ca Update boost, remove curl from sidechain plugin 2022-07-13 04:18:25 +02:00
serkixenos
47023be7c5 Config file options cleanup 2022-07-11 18:30:39 +02:00
serkixenos
73163d2722 Merge branch 'feature/new-rpc-ws-clients' into feature/son-for-ethereum 2022-07-07 19:08:59 +02:00
serkixenos
bbef0062c1 Merge branch 'develop' into feature/son-for-ethereum 2022-07-07 19:07:16 +02:00
serkixenos
3545efbf94 Boost Beast based HTTP client for SONs 2022-07-07 18:27:47 +02:00
serkixenos
57ccbdffd4 Basic RPC communication with Ethereum node established 2022-06-30 00:21:04 +02:00
serkixenos
da46b16560 WIP: Major refactoring of Ethereum sidechain handler 2022-06-28 23:55:19 +02:00
serkixenos
cfaf31e705 Code formatting 2022-06-28 20:39:00 +02:00
serkixenos
7689d5adc0 SHA3IUF as git submodule 2022-06-28 20:35:50 +02:00
serkixenos
bac0642d4c Merge branch 'develop' into feature/son-for-ethereum 2022-06-28 16:49:53 +02:00
Vlad Dobromyslov
03b71bb2fe Move SHA3IUF from fc repo 2022-06-27 12:02:15 +03:00
Vlad Dobromyslov
e23ff2e5dc Updated submodule 2022-06-27 08:46:02 +03:00
Pavel Baykov
a2c0793c48 big multiprecision values and serialize an Ethereum transaction for RPC 2022-06-20 09:45:50 -03:00
Vlad Dobromyslov
cd235455cc Fix linking sha3 library 2022-06-16 09:56:42 +03:00
Pavel Baykov
3a142d9fd2 missing Makefile in sha3, create_safe_address 2022-06-15 10:17:15 -03:00
Pavel Baykov
6e213fcfad remove hardcodes, implementation build_transaction safe_transaction_encoder 2022-06-15 09:16:55 -03:00
Pavel Baykov
647a5369fc process_proposal stub true 2022-06-14 13:13:52 -03:00
Pavel Baykov
6cdf6c68a7 add more traces 2022-06-13 09:12:53 -03:00
Pavel Baykov
4a63c202be websocket connection in a separate thread 2022-06-13 07:23:40 -03:00
Pavel Baykov
1a684df3f2 create_primary_wallet_address, create_public_key_data 2022-06-10 09:07:16 -03:00
Pavel Baykov
61fe72fb15 remove RLP 2022-06-09 05:21:00 -03:00
Pavel Baykov
9334080df5 sign transaction in peeerplays 2022-06-09 05:19:14 -03:00
Pavel Baykov
02f40093d0 ethereum transaction class and RLP 2022-06-07 09:02:56 -03:00
Pavel Baykov
4e9d5805f9 handle errors in geth webscoket replies, save signatures 2022-06-07 07:23:53 -03:00
Pavel Baykov
1bfa8dba14 use ethereum_function_call_encoder for signature calculation 2022-06-03 12:49:30 -03:00
Pavel Baykov
854e5ccb44 ethereum_function_call_encoder 2022-06-03 09:17:10 -03:00
Pavel Baykov
61923c797f eth_coinbase 2022-05-31 08:38:13 -03:00
Pavel Baykov
cff3bec507 parse output of rpc commands 2022-05-26 09:17:26 -03:00
Pavel Baykov
367a203007 add_owner, remove_owner, get_list_owners 2022-05-25 09:10:00 -03:00
Pavel Baykov
c09044bc70 implement helpers rpc functions, parse json reply, eth_rpc_client stop 2022-05-24 09:24:46 -03:00
Pavel Baykov
eac7f1ead6 websocket connection to geth 2022-05-20 12:11:59 -03:00
Pavel Baykov
bb976816af sidechain_net_handler_eth, eth_rpc_client 2022-05-18 19:38:49 -03:00
39 changed files with 3158 additions and 136 deletions

View file

@ -51,6 +51,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
list(GET arg_list 0 output) list(GET arg_list 0 output)
message("Ubuntu version is: ${output}") message("Ubuntu version is: ${output}")
add_definitions(-DPEERPLAYS_UBUNTU_VERSION=${output}) add_definitions(-DPEERPLAYS_UBUNTU_VERSION=${output})
endif() endif()
# function to help with cUrl # function to help with cUrl

View file

@ -19,7 +19,7 @@ RUN \
expect \ expect \
git \ git \
graphviz \ graphviz \
libboost1.67-all-dev \ libboost-all-dev \
libbz2-dev \ libbz2-dev \
libcurl4-openssl-dev \ libcurl4-openssl-dev \
libncurses-dev \ libncurses-dev \

View file

@ -58,9 +58,9 @@ EXPOSE 22
WORKDIR /home/peerplays/ WORKDIR /home/peerplays/
RUN \ RUN \
wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 && \ wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 && \
tar xjf boost_1_67_0.tar.bz2 && \ tar xjf boost_1_71_0.tar.bz2 && \
cd boost_1_67_0/ && \ cd boost_1_71_0/ && \
./bootstrap.sh && \ ./bootstrap.sh && \
./b2 install ./b2 install

View file

@ -5,6 +5,7 @@ add_subdirectory( egenesis )
add_subdirectory( fc ) add_subdirectory( fc )
add_subdirectory( net ) add_subdirectory( net )
add_subdirectory( plugins ) add_subdirectory( plugins )
add_subdirectory( sha3 )
add_subdirectory( time ) add_subdirectory( time )
add_subdirectory( utilities ) add_subdirectory( utilities )
add_subdirectory( wallet ) add_subdirectory( wallet )

View file

@ -1963,7 +1963,7 @@ void database::perform_son_tasks()
asset_issuer_permission_flags::override_authority; asset_issuer_permission_flags::override_authority;
a.options.core_exchange_rate.base.amount = 100000; 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.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.core_exchange_rate.quote.asset_id = a.id;
a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty 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.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set
@ -1977,6 +1977,40 @@ void database::perform_son_tasks()
gpo.pending_parameters->extensions.value.btc_asset = btc_asset.get_id(); 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>([](asset_dynamic_data_object& a) {
a.current_supply = 0;
});
const asset_object& eth_asset =
create<asset_object>( [&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, [&eth_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 // 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)
{ {
@ -1997,7 +2031,7 @@ void database::perform_son_tasks()
asset_issuer_permission_flags::override_authority; asset_issuer_permission_flags::override_authority;
a.options.core_exchange_rate.base.amount = 100000; 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.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.core_exchange_rate.quote.asset_id = a.id;
a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty 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.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set
@ -2031,7 +2065,7 @@ void database::perform_son_tasks()
asset_issuer_permission_flags::override_authority; asset_issuer_permission_flags::override_authority;
a.options.core_exchange_rate.base.amount = 100000; 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.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.core_exchange_rate.quote.asset_id = a.id;
a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty 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.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set
@ -2297,14 +2331,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; p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset;
if( !p.pending_parameters->extensions.value.hive_asset.valid() ) if( !p.pending_parameters->extensions.value.hive_asset.valid() )
p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; 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 // 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.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.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset(); p.pending_parameters.reset();

View file

@ -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

View file

@ -70,6 +70,7 @@ namespace graphene { namespace chain {
optional < uint16_t > maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS 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 > hbd_asset = asset_id_type();
optional < asset_id_type > hive_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 struct chain_parameters
@ -220,6 +221,9 @@ namespace graphene { namespace chain {
inline asset_id_type hive_asset() const { inline asset_id_type hive_asset() const {
return extensions.value.hive_asset.valid() ? *extensions.value.hive_asset : asset_id_type(); 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: private:
static void safe_copy(chain_parameters& to, const chain_parameters& from); static void safe_copy(chain_parameters& to, const chain_parameters& from);
}; };
@ -257,6 +261,7 @@ FC_REFLECT( graphene::chain::parameter_extension,
(maximum_son_count) (maximum_son_count)
(hbd_asset) (hbd_asset)
(hive_asset) (hive_asset)
(eth_asset)
) )
FC_REFLECT( graphene::chain::chain_parameters, FC_REFLECT( graphene::chain::chain_parameters,

View file

@ -6,6 +6,7 @@ add_library( peerplays_sidechain
sidechain_net_manager.cpp sidechain_net_manager.cpp
sidechain_net_handler.cpp sidechain_net_handler.cpp
sidechain_net_handler_bitcoin.cpp sidechain_net_handler_bitcoin.cpp
sidechain_net_handler_ethereum.cpp
sidechain_net_handler_hive.cpp sidechain_net_handler_hive.cpp
sidechain_net_handler_peerplays.cpp sidechain_net_handler_peerplays.cpp
bitcoin/bech32.cpp bitcoin/bech32.cpp
@ -17,6 +18,11 @@ add_library( peerplays_sidechain
bitcoin/sign_bitcoin_transaction.cpp bitcoin/sign_bitcoin_transaction.cpp
common/rpc_client.cpp common/rpc_client.cpp
common/utils.cpp common/utils.cpp
ethereum/encoders.cpp
ethereum/decoders.cpp
ethereum/transaction.cpp
ethereum/types.cpp
ethereum/utils.cpp
hive/asset.cpp hive/asset.cpp
hive/operations.cpp hive/operations.cpp
hive/transaction.cpp hive/transaction.cpp
@ -36,7 +42,7 @@ endif()
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) 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 target_include_directories( peerplays_sidechain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -76,14 +76,14 @@ bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const s
//! Get sig_temp //! Get sig_temp
FC_ASSERT(sig.size() > 70); FC_ASSERT(sig.size() > 70);
FC_ASSERT(sig[0] == 0x30); FC_ASSERT(sig[0] == 0x30);
FC_ASSERT(sig[1] == static_cast<char>(sig.size()-3)); FC_ASSERT(sig[1] == static_cast<char>(sig.size() - 3));
FC_ASSERT(sig[2] == 0x02); FC_ASSERT(sig[2] == 0x02);
const uint r_size = sig[3]; const uint r_size = sig[3];
std::vector<unsigned char> sig_temp(sig.begin()+4+(r_size-32), sig.begin()+4+r_size); std::vector<unsigned char> sig_temp(sig.begin() + 4 + (r_size - 32), sig.begin() + 4 + r_size);
FC_ASSERT(sig[4+r_size] == 0x02); FC_ASSERT(sig[4 + r_size] == 0x02);
const uint s_size = sig[5+r_size]; const uint s_size = sig[5 + r_size];
FC_ASSERT(sig.size() == r_size+s_size+7); FC_ASSERT(sig.size() == r_size + s_size + 7);
sig_temp.insert(sig_temp.end(), sig.begin()+6+r_size, sig.end()); sig_temp.insert(sig_temp.end(), sig.begin() + 6 + r_size, sig.end());
std::vector<unsigned char> pubkey_temp(pubkey.begin(), pubkey.end()); std::vector<unsigned char> pubkey_temp(pubkey.begin(), pubkey.end());
std::vector<unsigned char> msg_temp(msg.begin(), msg.end()); std::vector<unsigned char> msg_temp(msg.begin(), msg.end());

View file

@ -2,23 +2,20 @@
#include <regex> #include <regex>
#include <sstream> #include <sstream>
#include <string>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <boost/asio/buffers_iterator.hpp> #include <boost/asio/buffers_iterator.hpp>
#include <boost/asio/connect.hpp> #include <boost/asio/connect.hpp>
#include <boost/asio/ssl/error.hpp> #include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp> #include <boost/asio/ssl/stream.hpp>
#include <boost/beast/http.hpp> #include <boost/beast/http.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <curl/curl.h>
#include <fc/crypto/base64.hpp>
#include <fc/log/logger.hpp> #include <fc/log/logger.hpp>
#include <graphene/peerplays_sidechain/common/utils.hpp>
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : 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 = "/"; target = "/";
} }
authorization = "Basic " + fc::base64_encode(user + ":" + password); authorization = "Basic " + base64_encode(user + ":" + password);
results = resolver.resolve(host, port); results = resolver.resolve(host, port);
} else { } else {

View file

@ -1,8 +1,51 @@
#include <graphene/peerplays_sidechain/common/utils.hpp> #include <graphene/peerplays_sidechain/common/utils.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp>
//#include <boost/archive/iterators/ostream_iterator.hpp>
#include <boost/archive/iterators/transform_width.hpp>
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<transform_width<const char *, 6, 8>> base64_enc;
std::stringstream os;
std::copy(base64_enc(s.c_str()), base64_enc(s.c_str() + s.size()), std::ostream_iterator<char>(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<binary_from_base64<const char *>, 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<char>(os));
return os.str();
}
std::string object_id_to_string(graphene::chain::object_id_type id) { std::string object_id_to_string(graphene::chain::object_id_type id) {
std::string object_id = fc::to_string(id.space()) + "." + std::string object_id = fc::to_string(id.space()) + "." +
fc::to_string(id.type()) + "." + fc::to_string(id.type()) + "." +
fc::to_string(id.instance()); fc::to_string(id.instance());
return object_id; return object_id;
} }
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,241 @@
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <fc/exception/exception.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
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<std::string> rlp_decoder::decode(const std::string& str)
{
size_t consumed = 0;
const auto raw_vec = parse_hex(str);
const std::vector<std::string> rlp_array = decode_rlp(raw_vec.data(), raw_vec.size(), consumed);
std::vector<std::string> 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<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_t len, size_t& consumed)
{
std::vector<std::string> 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<unsigned char> 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<std::string>{};
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<std::string>{};
// parsing done; assign data buffer value.
const std::vector<unsigned char> 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<std::string>{};
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<std::string>{};
// 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<std::string>{};
// parsing done; assign data buffer value.
tok_start = raw + prefixlen + uintlen;
const unsigned char *tok_end = tok_start + slen;
const std::vector<unsigned char> 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<std::string>{};
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<std::string>{};
// 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<std::string>{};
// 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<std::string> rlp_decoder::decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen)
{
std::vector<std::string> 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<std::string>{};
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<unsigned char> rlp_decoder::parse_hex(const std::string& str)
{
return parse_hex(str.c_str());
}
std::vector<unsigned char> rlp_decoder::parse_hex(const char* psz)
{
// convert hex dump to vector
std::vector<unsigned char> 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

View file

@ -0,0 +1,120 @@
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <stdlib.h>
#include <boost/algorithm/hex.hpp>
#include <boost/format.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
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<std::pair<std::string, uint16_t>>& 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

View file

@ -0,0 +1,167 @@
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
#include <boost/algorithm/hex.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <secp256k1_recovery.h>
#include <sha3/sha3.h>
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/hex.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
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
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<std::string>("from");
if(tx_json.count("to"))
to = tx_json.get<std::string>("to");
if(tx_json.count("data"))
data = tx_json.get<std::string>("data");
}
//! raw_transaction
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;
//! Calculate keccak hash of transaction
bytes hash;
hash.resize(32);
const auto transaction_string = boost::algorithm::unhex( serialize() );
keccak_256((const unsigned char *) transaction_string.data(), transaction_string.size(), (unsigned char *) hash.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 v = bytes{char(recid + from_hex<int>(chain_id) * 2 + 35)};
bytes r;
for(int i = 1; i < 33; i++)
r.emplace_back((char) result.at(i));
bytes s;
for(int i = 33; i < 65; i++)
s.emplace_back((char) result.at(i));
tr.v = fc::to_hex((char *)&v[0], v.size());
tr.r = fc::to_hex((char *)&r[0], r.size());
tr.s = fc::to_hex((char *)&s[0], s.size());
return tr;
}
std::string raw_transaction::serialize() const
{
rlp_encoder encoder;
const std::string serialized = encoder.encode(remove_0x(nonce)) +
encoder.encode(remove_0x(gas_price)) +
encoder.encode(remove_0x(gas_limit)) +
encoder.encode(remove_0x(to)) +
encoder.encode(remove_0x(value)) +
encoder.encode(remove_0x(data)) +
encoder.encode(remove_0x(chain_id)) +
encoder.encode("") +
encoder.encode("");
return bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized );
}
void raw_transaction::deserialize(const std::string& raw_tx)
{
rlp_decoder decoder;
const auto rlp_array = decoder.decode(raw_tx);
FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format");
nonce = add_0x(rlp_array.at(0));
gas_price = add_0x(rlp_array.at(1));
gas_limit = add_0x(rlp_array.at(2));
to = add_0x(rlp_array.at(3));
value = add_0x(rlp_array.at(4));
data = add_0x(rlp_array.at(5));
chain_id = add_0x(rlp_array.at(6));
}
//! signed_transaction
std::string signed_transaction::serialize() const
{
rlp_encoder encoder;
const std::string serialized = encoder.encode(remove_0x(nonce)) +
encoder.encode(remove_0x(gas_price)) +
encoder.encode(remove_0x(gas_limit)) +
encoder.encode(remove_0x(to)) +
encoder.encode(remove_0x(value)) +
encoder.encode(remove_0x(data)) +
encoder.encode(remove_0x(v)) +
encoder.encode(remove_0x(r)) +
encoder.encode(remove_0x(s));
return bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized );
}
void signed_transaction::deserialize(const std::string& raw_tx)
{
rlp_decoder decoder;
const auto rlp_array = decoder.decode(raw_tx);
FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format");
nonce = add_0x(rlp_array.at(0));
gas_price = add_0x(rlp_array.at(1));
gas_limit = add_0x(rlp_array.at(2));
to = add_0x(rlp_array.at(3));
value = add_0x(rlp_array.at(4));
data = add_0x(rlp_array.at(5));
v = add_0x(rlp_array.at(6));
r = add_0x(rlp_array.at(7));
s = add_0x(rlp_array.at(8));
}
}}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -0,0 +1,5 @@
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
}}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -0,0 +1,57 @@
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
#include <fc/crypto/hex.hpp>
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

View file

@ -23,15 +23,16 @@ protected:
std::string send_post_request(std::string method, std::string params, bool show_log); std::string send_post_request(std::string method, std::string params, bool show_log);
std::string url; std::string url;
std::string user;
std::string password;
bool debug_rpc_calls;
std::string protocol; std::string protocol;
std::string host; std::string host;
std::string port; std::string port;
std::string target; std::string target;
std::string authorization; std::string authorization;
std::string user;
std::string password;
bool debug_rpc_calls;
uint32_t request_id; uint32_t request_id;
private: private:

View file

@ -2,4 +2,11 @@
#include <graphene/chain/protocol/asset.hpp> #include <graphene/chain/protocol/asset.hpp>
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); std::string object_id_to_string(graphene::chain::object_id_type id);
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,29 @@
#pragma once
#include <string>
#include <vector>
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<std::string> decode(const std::string& str);
private:
static std::vector<std::string> decode_rlp(const unsigned char *raw, size_t len, size_t& consumed);
static std::vector<std::string> 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<unsigned char> parse_hex(const std::string& str);
static std::vector<unsigned char> parse_hex(const char* psz);
static signed char hex_digit(char c);
};
}}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -0,0 +1,73 @@
#pragma once
#include <string>
#include <vector>
#include <boost/multiprecision/cpp_int.hpp>
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<std::pair<std::string, uint16_t>>& 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<std::string> &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

View file

@ -0,0 +1,149 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
class base_transaction
{
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;
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;
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;
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"
//}

View file

@ -0,0 +1,12 @@
#pragma once
#include <boost/multiprecision/cpp_int.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
typedef uint64_t chain_id_type;
typedef uint64_t network_id_type;
using bytes = std::vector<char>;
}}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -0,0 +1,39 @@
#pragma once
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
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<typename T>
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<typename T>
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

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <graphene/peerplays_sidechain/common/rpc_client.hpp> #include <graphene/peerplays_sidechain/common/rpc_client.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <string> #include <string>
#include <thread> #include <thread>
@ -23,7 +23,7 @@ public:
uint64_t amount_; uint64_t amount_;
}; };
class bitcoin_rpc_client: public rpc_client { class bitcoin_rpc_client : public rpc_client {
public: public:
enum class multi_type { enum class multi_type {
script, script,
@ -63,9 +63,8 @@ private:
uint32_t rpc_port; uint32_t rpc_port;
std::string user; std::string user;
std::string password; std::string password;
std::string wallet; std::string wallet_name;
std::string wallet_password; std::string wallet_password;
}; };
// ============================================================================= // =============================================================================
@ -112,17 +111,18 @@ private:
std::string ip; std::string ip;
uint32_t zmq_port; uint32_t zmq_port;
uint32_t rpc_port; uint32_t rpc_port;
uint32_t bitcoin_major_version;
std::string rpc_user; std::string rpc_user;
std::string rpc_password; std::string rpc_password;
std::string wallet; std::string wallet_name;
std::string wallet_password; std::string wallet_password;
std::unique_ptr<bitcoin_rpc_client> bitcoin_client; std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
std::unique_ptr<zmq_listener> listener; std::unique_ptr<zmq_listener> listener;
fc::future<void> on_changed_objects_task; fc::future<void> on_changed_objects_task;
bitcoin::bitcoin_address::network network_type; bitcoin::bitcoin_address::network network_type;
uint32_t bitcoin_major_version;
std::mutex event_handler_mutex; std::mutex event_handler_mutex;
typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock; typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock;

View file

@ -0,0 +1,74 @@
#pragma once
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
#include <string>
#include <boost/signals2.hpp>
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
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& params);
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& params);
std::string eth_get_transaction_receipt(const std::string& params);
};
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_info> &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<void> _listener_task;
boost::signals2::signal<void(const std::string &)> event_received;
void schedule_ethereum_listener();
void ethereum_listener_loop();
void handle_event(const std::string &event_data);
};
}} // namespace graphene::peerplays_sidechain

View file

@ -6,15 +6,14 @@
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
#include <fc/network/http/connection.hpp>
#include <graphene/peerplays_sidechain/common/rpc_client.hpp> #include <graphene/peerplays_sidechain/common/rpc_client.hpp>
#include <graphene/peerplays_sidechain/hive/types.hpp> #include <graphene/peerplays_sidechain/hive/types.hpp>
namespace graphene { namespace peerplays_sidechain { namespace graphene { namespace peerplays_sidechain {
class hive_node_rpc_client : public rpc_client { class hive_rpc_client : public rpc_client {
public: 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 account_history_api_get_transaction(std::string transaction_id);
std::string block_api_get_block(uint32_t block_number); 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); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
private: private:
std::string node_rpc_url; std::string rpc_url;
std::string node_rpc_user; std::string rpc_user;
std::string node_rpc_password; std::string rpc_password;
hive_node_rpc_client *node_rpc_client; std::string wallet_account_name;
hive_rpc_client *rpc_client;
hive::chain_id_type chain_id; hive::chain_id_type chain_id;
hive::network network_type; hive::network network_type;

View file

@ -152,15 +152,24 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node");
cli.add_options()("bitcoin-node-rpc-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user"); cli.add_options()("bitcoin-node-rpc-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user");
cli.add_options()("bitcoin-node-rpc-password", bpo::value<string>()->default_value("1"), "Bitcoin RPC password"); cli.add_options()("bitcoin-node-rpc-password", bpo::value<string>()->default_value("1"), "Bitcoin RPC password");
cli.add_options()("bitcoin-wallet", bpo::value<string>(), "Bitcoin wallet"); cli.add_options()("bitcoin-wallet-name", bpo::value<string>(), "Bitcoin wallet name");
cli.add_options()("bitcoin-wallet-password", bpo::value<string>(), "Bitcoin wallet password"); cli.add_options()("bitcoin-wallet-password", bpo::value<string>(), "Bitcoin wallet password");
cli.add_options()("bitcoin-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")), cli.add_options()("bitcoin-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")),
"Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)");
cli.add_options()("ethereum-sidechain-enabled", bpo::value<bool>()->default_value(false), "Ethereum sidechain handler enabled");
cli.add_options()("ethereum-node-rpc-url", bpo::value<string>()->default_value("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]");
cli.add_options()("ethereum-node-rpc-user", bpo::value<string>(), "Ethereum RPC user");
cli.add_options()("ethereum-node-rpc-password", bpo::value<string>(), "Ethereum RPC password");
cli.add_options()("ethereum-wallet-contract-address", bpo::value<string>(), "Ethereum wallet contract address");
cli.add_options()("ethereum-private-key", bpo::value<vector<string>>()->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<bool>()->default_value(false), "Hive sidechain handler enabled"); cli.add_options()("hive-sidechain-enabled", bpo::value<bool>()->default_value(false), "Hive sidechain handler enabled");
cli.add_options()("hive-node-rpc-url", bpo::value<string>()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-url", bpo::value<string>()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]");
cli.add_options()("hive-node-rpc-user", bpo::value<string>(), "Hive node RPC user"); cli.add_options()("hive-node-rpc-user", bpo::value<string>(), "Hive node RPC user");
cli.add_options()("hive-node-rpc-password", bpo::value<string>(), "Hive node RPC password"); cli.add_options()("hive-node-rpc-password", bpo::value<string>(), "Hive node RPC password");
cli.add_options()("hive-wallet-account-name", bpo::value<string>(), "Hive wallet account name"),
cli.add_options()("hive-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4", "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n")), cli.add_options()("hive-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4", "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n")),
"Tuple of [Hive public key, Hive private key] (may specify multiple times)"); "Tuple of [Hive public key, Hive private key] (may specify multiple times)");
@ -211,25 +220,27 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
config_ready_bitcoin = options.count("bitcoin-node-ip") && config_ready_bitcoin = options.count("bitcoin-node-ip") &&
options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") && 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-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"); options.count("bitcoin-private-key");
if (!config_ready_bitcoin) { if (sidechain_enabled_bitcoin && !config_ready_bitcoin) {
wlog("Haven't set up Bitcoin sidechain parameters"); wlog("Haven't set up Bitcoin sidechain parameters");
} }
//sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as<bool>(); sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as<bool>();
//config_ready_ethereum = options.count("ethereum-node-ip") && config_ready_ethereum = options.count("ethereum-node-rpc-url") &&
// options.count("ethereum-address") && /*options.count("ethereum-node-rpc-user") && options.count("ethereum-node-rpc-password") &&*/
// options.count("ethereum-public-key") && options.count("ethereum-private-key"); options.count("ethereum-wallet-contract-address") &&
//if (!config_ready_ethereum) { options.count("ethereum-private-key");
// wlog("Haven't set up Ethereum sidechain parameters"); if (sidechain_enabled_ethereum && !config_ready_ethereum) {
//} wlog("Haven't set up Ethereum sidechain parameters");
}
sidechain_enabled_hive = options.at("hive-sidechain-enabled").as<bool>(); sidechain_enabled_hive = options.at("hive-sidechain-enabled").as<bool>();
config_ready_hive = options.count("hive-node-rpc-url") && config_ready_hive = options.count("hive-node-rpc-url") &&
/*options.count("hive-node-rpc-user") && options.count("hive-node-rpc-password") &&*/ /*options.count("hive-node-rpc-user") && options.count("hive-node-rpc-password") &&*/
options.count("hive-wallet-account-name") &&
options.count("hive-private-key"); options.count("hive-private-key");
if (!config_ready_hive) { if (sidechain_enabled_hive && !config_ready_hive) {
wlog("Haven't set up Hive sidechain parameters"); wlog("Haven't set up Hive sidechain parameters");
} }
@ -244,7 +255,7 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
} }
if (!(config_ready_bitcoin && if (!(config_ready_bitcoin &&
/*config_ready_ethereum &&*/ config_ready_ethereum &&
config_ready_hive && config_ready_hive &&
config_ready_peerplays)) { config_ready_peerplays)) {
wlog("Haven't set up any sidechain parameters"); wlog("Haven't set up any sidechain parameters");
@ -269,10 +280,10 @@ void peerplays_sidechain_plugin_impl::plugin_startup() {
ilog("Bitcoin sidechain handler running"); ilog("Bitcoin sidechain handler running");
} }
//if (sidechain_enabled_ethereum && config_ready_ethereum) { if (sidechain_enabled_ethereum && config_ready_ethereum) {
// net_manager->create_handler(sidechain_type::ethereum, options); net_manager->create_handler(sidechain_type::ethereum, options);
// ilog("Ethereum sidechain handler running"); ilog("Ethereum sidechain handler running");
//} }
if (sidechain_enabled_hive && config_ready_hive) { if (sidechain_enabled_hive && config_ready_hive) {
net_manager->create_handler(sidechain_type::hive, options); net_manager->create_handler(sidechain_type::hive, options);

View file

@ -172,18 +172,21 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS #ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
//enable_peerplays_asset_deposits = (sed.sidechain == sidechain_type::peerplays) && //enable_peerplays_asset_deposits = (sed.sidechain == sidechain_type::peerplays) &&
// (sed.sidechain_currency.compare("BTC") != 0) && // (sed.sidechain_currency.compare("BTC") != 0) &&
// (sed.sidechain_currency.compare("ETH") != 0) &&
// (sed.sidechain_currency.compare("HBD") != 0) && // (sed.sidechain_currency.compare("HBD") != 0) &&
// (sed.sidechain_currency.compare("HIVE") != 0); // (sed.sidechain_currency.compare("HIVE") != 0);
#endif #endif
bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) && 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::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("HBD") == 0)) ||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) ||
enable_peerplays_asset_deposits); enable_peerplays_asset_deposits);
bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain_type::peerplays) && bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain_type::peerplays) &&
((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || ((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.hbd_asset())) ||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_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 = "BTC";
withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; withdraw_currency_price = database.get<asset_object>(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<asset_object>(database.get_global_properties().parameters.eth_asset()).options.core_exchange_rate;
}
if (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) { if (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) {
withdraw_currency = "HBD"; withdraw_currency = "HBD";
withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.hbd_asset()).options.core_exchange_rate; withdraw_currency_price = database.get<asset_object>(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 = bool is_tracked_asset =
((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_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.hbd_asset())) ||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset())); ((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset()));

View file

@ -334,9 +334,9 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
rpc_port = options.at("bitcoin-node-rpc-port").as<uint32_t>(); rpc_port = options.at("bitcoin-node-rpc-port").as<uint32_t>();
rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>(); rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>();
rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>(); rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>();
wallet = ""; wallet_name = "";
if (options.count("bitcoin-wallet")) { if (options.count("bitcoin-wallet-name")) {
wallet = options.at("bitcoin-wallet").as<std::string>(); wallet_name = options.at("bitcoin-wallet-name").as<std::string>();
} }
wallet_password = ""; wallet_password = "";
if (options.count("bitcoin-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); std::string url = ip + ":" + std::to_string(rpc_port);
if (wallet.length() > 0) { if (!wallet_name.empty()) {
url = url + "/wallet/" + wallet; url = url + "/wallet/" + wallet_name;
} }
bitcoin_client = std::unique_ptr<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); bitcoin_client = std::unique_ptr<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls));
if (!wallet.empty()) { if (!wallet_name.empty()) {
bitcoin_client->loadwallet(wallet); bitcoin_client->loadwallet(wallet_name);
} }
std::string blockchain_info = bitcoin_client->getblockchaininfo(); std::string blockchain_info = bitcoin_client->getblockchaininfo();

View file

@ -0,0 +1,736 @@
#include <graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp>
#include <algorithm>
#include <thread>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <fc/crypto/base64.hpp>
#include <fc/log/logger.hpp>
#include <fc/network/ip.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/protocol/son_wallet.hpp>
#include <graphene/chain/sidechain_transaction_object.hpp>
#include <graphene/chain/son_info.hpp>
#include <graphene/chain/son_wallet_object.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
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& params) {
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\"]");
return retrieve_value_from_reply(reply_str, "");
}
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<std::string>("result.gasLimit");
return gas_limit_s;
}
}
return std::string{};
}
std::string ethereum_rpc_client::eth_send_transaction(const std::string& params) {
return send_post_request("eth_sendTransaction", "[" + params + "]", debug_rpc_calls);
}
std::string ethereum_rpc_client::eth_get_transaction_receipt(const std::string& params) {
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<bool>();
}
rpc_url = options.at("ethereum-node-rpc-url").as<std::string>();
if (options.count("ethereum-node-rpc-user")) {
rpc_user = options.at("ethereum-node-rpc-user").as<std::string>();
} else {
rpc_user = "";
}
if (options.count("ethereum-node-rpc-password")) {
rpc_password = options.at("ethereum-node-rpc-password").as<std::string>();
} else {
rpc_password = "";
}
wallet_contract_address = options.at("ethereum-wallet-contract-address").as<std::string>();
if (options.count("ethereum-private-key")) {
const std::vector<std::string> pub_priv_keys = options["ethereum-private-key"].as<std::vector<std::string>>();
for (const std::string &itr_key_pair : pub_priv_keys) {
auto key_pair = graphene::app::dejsonify<std::pair<std::string, std::string>>(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()));
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<chain::son_wallet_update_operation>::value: {
bool address_ok = false;
bool transaction_ok = false;
son_wallet_id_type swo_id = op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id;
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(swo_id);
if (swo != idx.end()) {
auto active_sons = gpo.active_sons;
vector<son_info> wallet_sons = swo->sons;
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<son_wallet_update_operation>().address == wallet_contract_address);
}
if (po.proposed_transaction.operations.size() >= 2) {
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id);
if (st == st_idx.end()) {
std::string tx_str = "";
if (object_id.is<son_wallet_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
const auto swo = idx.find(object_id);
if (swo != idx.end()) {
tx_str = create_primary_wallet_transaction(gpo.active_sons, 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<chain::son_wallet_deposit_process_operation>::value: {
bool process_ok = false;
son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id;
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
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<std::string>("type");
//
// if (operation_type == "transfer_operation") {
// const auto &op_value = op.get_child("value");
//
// std::string sidechain_from = op_value.get<std::string>("from");
//
// const auto &amount_child = op_value.get_child("amount");
//
// uint64_t amount = amount_child.get<uint64_t>("amount");
// std::string nai = amount_child.get<std::string>("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<std::string>("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<chain::son_wallet_withdraw_process_operation>::value: {
bool process_ok = false;
bool transaction_ok = false;
son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id;
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
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<transfer_operation>();
price asset_price = database.get<asset_object>(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<sidechain_transaction_create_operation>().object_id;
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
const auto st = st_idx.find(object_id);
if (st == st_idx.end()) {
std::string tx_str = "";
if (object_id.is<son_wallet_withdraw_id_type>()) {
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
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<chain::sidechain_transaction_sign_operation>::value: {
should_approve = true;
son_id_type signer = op_obj_idx_0.get<sidechain_transaction_sign_operation>().signer;
std::string signature = op_obj_idx_0.get<sidechain_transaction_sign_operation>().signature;
sidechain_transaction_id_type sidechain_transaction_id = op_obj_idx_0.get<sidechain_transaction_sign_operation>().sidechain_transaction_id;
const auto &st_idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_id>();
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<son_index>().indices().get<by_id>();
const auto son = s_idx.find(signer);
if (son == s_idx.end()) {
should_approve = false;
break;
}
break;
}
case chain::operation::tag<chain::sidechain_transaction_settle_operation>::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<son_wallet_index>().indices().get<by_id>();
const auto &active_sw = swi.rbegin();
if (active_sw != swi.rend()) {
if ((active_sw->addresses.find(sidechain_type::ethereum) == active_sw->addresses.end()) ||
(active_sw->addresses.at(sidechain_type::ethereum).empty())) {
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) {
return;
}
if (!plugin.can_son_participate(chain::operation::tag<chain::son_wallet_update_operation>::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().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_type::ethereum;
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, 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;
proposal_op.proposed_ops.emplace_back(stc_op);
}
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
try {
trx.validate();
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
if (plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_update_operation>::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() {
int temp = 0;
}
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<asset_object>(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().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()), 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<chain::son_wallet_withdraw_process_operation>::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().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;
proposal_op.proposed_ops.emplace_back(stc_op);
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
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;
const std::string sidechain_transaction = rpc_client->eth_send_transaction(transaction);
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<std::string>("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));
}
}
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<std::string>("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<std::string>("result") == "null" ) {
wlog("Block is not minted yet for transaction ${id}", ("id", sto.id));
return false;
}
if( "0x1" == json_receipt.get<std::string>("result.status") )
{
count += 1;
//! Fixme - compare data somehow?
//if( sto.transaction == entry_receipt.second.get<std::string>("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_info> &son_pubkeys, const std::string& object_id) {
std::vector<std::pair<std::string, uint16_t>> owners_weights;
for (auto &son : son_pubkeys) {
FC_ASSERT(son.sidechain_public_keys.contains(sidechain_type::ethereum), "No public keys for son: ${son_id}", ("son_id", son.son_id));
const std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::ethereum);
owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight));
}
ethereum::transaction transaction;
ethereum::update_owners_encoder encoder;
transaction.data = encoder.encode(owners_weights, object_id);
return transaction.serialize();
}
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) {
ethereum::transaction transaction;
ethereum::withdrawal_encoder encoder;
transaction.data = encoder.encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value*10000000000, swwo.id.operator std::string());
return transaction.serialize();
}
std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) {
const auto& current_son = plugin.get_current_son_object();
FC_ASSERT(current_son.sidechain_public_keys.contains(sidechain_type::ethereum), "No public keys for current son: ${account_id}", ("account_id", current_son.son_account));
ethereum::transaction sign_transaction;
sign_transaction.deserialize(sto.transaction);
sign_transaction.to = wallet_contract_address;
sign_transaction.from = "0x" + current_son.sidechain_public_keys.at(sidechain);
return sign_transaction.sign(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))).serialize();
}
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<std::string>("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<std::string>("from");
std::string to = tx.get<std::string>("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<std::string>("value");
boost::multiprecision::uint256_t amount(value_s);
amount = amount / 100000;
amount = amount / 100000;
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
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<std::string>("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<std::string>("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<asset_object>(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

View file

@ -28,25 +28,23 @@
#include <graphene/peerplays_sidechain/hive/transaction.hpp> #include <graphene/peerplays_sidechain/hive/transaction.hpp>
#include <graphene/utilities/key_conversion.hpp> #include <graphene/utilities/key_conversion.hpp>
#include <boost/asio.hpp>
namespace graphene { namespace peerplays_sidechain { 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) { 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 + "\" }"; std::string params = "{ \"id\": \"" + transaction_id + "\" }";
return send_post_request("account_history_api.get_transaction", params, debug_rpc_calls); 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) + " }"; std::string params = "{ \"block_num\": " + std::to_string(block_number) + " }";
return send_post_request("block_api.get_block", params, debug_rpc_calls); return send_post_request("block_api.get_block", params, debug_rpc_calls);
} }
std::string hive_node_rpc_client::condenser_api_get_accounts(std::vector<std::string> accounts) { std::string hive_rpc_client::condenser_api_get_accounts(std::vector<std::string> accounts) {
std::string params = ""; std::string params = "";
for (auto account : accounts) { for (auto account : accounts) {
if (!params.empty()) { if (!params.empty()) {
@ -58,58 +56,58 @@ std::string hive_node_rpc_client::condenser_api_get_accounts(std::vector<std::st
return send_post_request("condenser_api.get_accounts", params, debug_rpc_calls); return send_post_request("condenser_api.get_accounts", params, debug_rpc_calls);
} }
std::string hive_node_rpc_client::condenser_api_get_config() { std::string hive_rpc_client::condenser_api_get_config() {
std::string params = "[]"; std::string params = "[]";
return send_post_request("condenser_api.get_config", params, debug_rpc_calls); return send_post_request("condenser_api.get_config", params, debug_rpc_calls);
} }
std::string hive_node_rpc_client::database_api_get_dynamic_global_properties() { std::string hive_rpc_client::database_api_get_dynamic_global_properties() {
return send_post_request("database_api.get_dynamic_global_properties", "", debug_rpc_calls); return send_post_request("database_api.get_dynamic_global_properties", "", debug_rpc_calls);
} }
std::string hive_node_rpc_client::database_api_get_version() { std::string hive_rpc_client::database_api_get_version() {
return send_post_request("database_api.get_version", "", debug_rpc_calls); return send_post_request("database_api.get_version", "", debug_rpc_calls);
} }
std::string hive_node_rpc_client::network_broadcast_api_broadcast_transaction(std::string htrx) { std::string hive_rpc_client::network_broadcast_api_broadcast_transaction(std::string htrx) {
std::string params = "{ \"trx\": " + htrx + ", \"max_block_age\": -1 }"; std::string params = "{ \"trx\": " + htrx + ", \"max_block_age\": -1 }";
return send_post_request("network_broadcast_api.broadcast_transaction", params, debug_rpc_calls); return send_post_request("network_broadcast_api.broadcast_transaction", params, debug_rpc_calls);
} }
std::string hive_node_rpc_client::get_account(std::string account) { std::string hive_rpc_client::get_account(std::string account) {
std::vector<std::string> accounts; std::vector<std::string> accounts;
accounts.push_back(account); accounts.push_back(account);
std::string reply_str = condenser_api_get_accounts(accounts); std::string reply_str = condenser_api_get_accounts(accounts);
return retrieve_array_value_from_reply(reply_str, "", 0); 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); std::string reply_str = get_account(account);
reply_str = "{\"result\":" + reply_str + "}"; reply_str = "{\"result\":" + reply_str + "}";
return retrieve_value_from_reply(reply_str, "memo_key"); 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(); std::string reply_str = database_api_get_version();
return retrieve_value_from_reply(reply_str, "chain_id"); 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(); std::string reply_str = database_api_get_dynamic_global_properties();
return retrieve_value_from_reply(reply_str, "head_block_id"); 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(); std::string reply_str = database_api_get_dynamic_global_properties();
return retrieve_value_from_reply(reply_str, "time"); 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(); std::string reply_str = condenser_api_get_config();
return retrieve_value_from_reply(reply_str, "IS_TEST_NET"); 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(); std::string reply_str = database_api_get_dynamic_global_properties();
return retrieve_value_from_reply(reply_str, "last_irreversible_block_num"); 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<bool>(); debug_rpc_calls = options.at("debug-rpc-calls").as<bool>();
} }
node_rpc_url = options.at("hive-node-rpc-url").as<std::string>(); rpc_url = options.at("hive-node-rpc-url").as<std::string>();
if (options.count("hive-node-rpc-user")) { if (options.count("hive-rpc-user")) {
node_rpc_user = options.at("hive-node-rpc-user").as<std::string>(); rpc_user = options.at("hive-rpc-user").as<std::string>();
} else { } else {
node_rpc_user = ""; rpc_user = "";
} }
if (options.count("hive-node-rpc-password")) { if (options.count("hive-rpc-password")) {
node_rpc_password = options.at("hive-node-rpc-password").as<std::string>(); rpc_password = options.at("hive-rpc-password").as<std::string>();
} else { } else {
node_rpc_password = ""; rpc_password = "";
} }
wallet_account_name = options.at("hive-wallet-account-name").as<std::string>();
if (options.count("hive-private-key")) { if (options.count("hive-private-key")) {
const std::vector<std::string> pub_priv_keys = options["hive-private-key"].as<std::vector<std::string>>(); const std::vector<std::string> pub_priv_keys = options["hive-private-key"].as<std::vector<std::string>>();
for (const std::string &itr_key_pair : pub_priv_keys) { 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()) { 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); FC_ASSERT(false);
} }
chain_id = chain_id_type(chain_id_str); 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; network_type = is_test_net.compare("true") == 0 ? hive::network::testnet : hive::network::mainnet;
if (network_type == 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)); 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) { if (son_sets_equal) {
address_ok = (op_obj_idx_0.get<son_wallet_update_operation>().address == "son-account"); address_ok = (op_obj_idx_0.get<son_wallet_update_operation>().address == wallet_account_name);
} }
if (po.proposed_transaction.operations.size() >= 2) { 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.sidechain_public_keys.at(sidechain)] = wallet_son.weight; account_auths[wallet_son.sidechain_public_keys.at(sidechain)] = 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; hive::authority active;
active.weight_threshold = total_weight * 2 / 3 + 1; active.weight_threshold = total_weight * 2 / 3 + 1;
active.account_auths = account_auths; active.account_auths = account_auths;
hive::account_update_operation auo; hive::account_update_operation auo;
auo.account = "son-account"; auo.account = wallet_account_name;
auo.active = active; auo.active = active;
auo.memo_key = op_trx.operations[0].get<hive::account_update_operation>().memo_key; auo.memo_key = op_trx.operations[0].get<hive::account_update_operation>().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_sidechain_amount = swdo->sidechain_amount.value;
uint64_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-"))); 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 != "") { if (tx_str != "") {
std::stringstream ss_tx(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; hive::transfer_operation t_op;
t_op.from = "son-account"; t_op.from = wallet_account_name;
t_op.to = swwo->withdraw_address; t_op.to = swwo->withdraw_address;
t_op.amount.amount = swwo->withdraw_amount; t_op.amount.amount = swwo->withdraw_amount;
t_op.amount.symbol = symbol; t_op.amount.symbol = symbol;
@ -495,7 +495,7 @@ void sidechain_net_handler_hive::process_primary_wallet() {
account_auths[active_son.sidechain_public_keys.at(sidechain)] = active_son.weight; account_auths[active_son.sidechain_public_keys.at(sidechain)] = 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()) { if (memo_key.empty()) {
return; return;
@ -506,14 +506,14 @@ void sidechain_net_handler_hive::process_primary_wallet() {
active.account_auths = account_auths; active.account_auths = account_auths;
hive::account_update_operation auo; hive::account_update_operation auo;
auo.account = "son-account"; auo.account = wallet_account_name;
auo.active = active; auo.active = active;
auo.memo_key = hive::public_key_type(memo_key); 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); 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); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str);
hive::signed_transaction htrx; 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.payer = gpo.parameters.son_account();
swu_op.son_wallet_id = active_sw->id; swu_op.son_wallet_id = active_sw->id;
swu_op.sidechain = sidechain; swu_op.sidechain = sidechain;
swu_op.address = "son-account"; swu_op.address = wallet_account_name;
proposal_op.proposed_ops.emplace_back(swu_op); 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; hive::transfer_operation t_op;
t_op.from = "son-account"; t_op.from = wallet_account_name;
t_op.to = swwo.withdraw_address; t_op.to = swwo.withdraw_address;
t_op.amount.amount = swwo.withdraw_amount; t_op.amount.amount = swwo.withdraw_amount;
t_op.amount.symbol = symbol; t_op.amount.symbol = symbol;
t_op.memo = ""; 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); 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); time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str);
hive::signed_transaction htrx; hive::signed_transaction htrx;
@ -727,7 +727,7 @@ std::string sidechain_net_handler_hive::process_sidechain_transaction(const side
hive::signed_transaction htrx; hive::signed_transaction htrx;
fc::raw::unpack(ss_trx, htrx, 1000); 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); const hive::chain_id_type chain_id(chain_id_str);
fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))); fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().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); 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(); return htrx.id().str();
} }
@ -770,7 +770,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr
return false; 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 != "") { if (tx_str != "") {
std::stringstream ss_tx(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<std::string>("result.transaction_id"); std::string tx_txid = tx_json.get<std::string>("result.transaction_id");
uint32_t tx_block_num = tx_json.get<uint32_t>("result.block_num"); uint32_t tx_block_num = tx_json.get<uint32_t>("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(); //std::string tx_address = addr.get_address();
//int64_t tx_amount = -1; //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() { void sidechain_net_handler_hive::hive_listener_loop() {
schedule_hive_listener(); 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()) { if (!reply.empty()) {
std::stringstream ss(reply); std::stringstream ss(reply);
boost::property_tree::ptree json; 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()) { //if (!reply.empty()) {
// uint64_t last_irreversible_block = std::stoul(reply); // uint64_t last_irreversible_block = std::stoul(reply);
// if (last_irreversible_block != last_block_received) { // 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) { 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 != "") { if (block != "") {
add_to_son_listener_log("BLOCK : " + event_data); add_to_son_listener_log("BLOCK : " + event_data);
std::stringstream ss(block); 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<std::string>("from"); std::string from = op_value.get<std::string>("from");
std::string to = op_value.get<std::string>("to"); std::string to = op_value.get<std::string>("to");
if (to == "son-account") { if (to == wallet_account_name) {
const auto &amount_child = op_value.get_child("amount"); const auto &amount_child = op_value.get_child("amount");

View file

@ -3,6 +3,7 @@
#include <fc/log/logger.hpp> #include <fc/log/logger.hpp>
#include <graphene/chain/son_wallet_object.hpp> #include <graphene/chain/son_wallet_object.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp> #include <graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp> #include <graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp>
#include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp> #include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp>
@ -31,6 +32,12 @@ bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost
ret_val = true; ret_val = true;
break; break;
} }
case sidechain_type::ethereum: {
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_ethereum(plugin, options));
net_handlers.push_back(std::move(h));
ret_val = true;
break;
}
case sidechain_type::hive: { case sidechain_type::hive: {
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_hive(plugin, options)); std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_hive(plugin, options));
net_handlers.push_back(std::move(h)); net_handlers.push_back(std::move(h));

View file

@ -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
)

View file

@ -0,0 +1,16 @@
#ifndef __MEMZERO_H__
#define __MEMZERO_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
void memzero(void* const pnt, const size_t len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View file

@ -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 <rhash.admin@gmail.com>
*
* 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 <stdint.h>
#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__ */

75
libraries/sha3/memzero.c Normal file
View file

@ -0,0 +1,75 @@
#ifndef __STDC_WANT_LIB_EXT1__
#define __STDC_WANT_LIB_EXT1__ 1 // C11's bounds-checking interface.
#endif
#include <string.h>
#ifdef _WIN32
#include <Windows.h>
#endif
#ifdef __unix__
#include <strings.h>
#include <sys/param.h>
#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
}

397
libraries/sha3/sha3.c Normal file
View file

@ -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 <rhash.admin@gmail.com>
*
* 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 <assert.h>
#include <string.h>
#include <sha3/sha3.h>
#include <sha3/memzero.h>
#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);
}

View file

@ -134,6 +134,8 @@ public:
std::string operator()(const T& op)const; std::string operator()(const T& op)const;
std::string operator()(const transfer_operation& op)const; std::string operator()(const transfer_operation& op)const;
std::string operator()(const transfer_from_blind_operation& op)const;
std::string operator()(const transfer_to_blind_operation& op)const;
std::string operator()(const account_create_operation& op)const; std::string operator()(const account_create_operation& op)const;
std::string operator()(const account_update_operation& op)const; std::string operator()(const account_update_operation& op)const;
std::string operator()(const asset_create_operation& op)const; std::string operator()(const asset_create_operation& op)const;
@ -2857,9 +2859,11 @@ public:
{ {
account_id_type son_owner_account_id = get_account_id(son); account_id_type son_owner_account_id = get_account_id(son);
fc::optional<son_object> son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); fc::optional<son_object> son_obj = _remote_db->get_son_by_account_id(son_owner_account_id);
if (!son_obj) if (!son_obj)
FC_THROW("Account ${son} is not registered as a SON", ("son", son)); FC_THROW("Account ${son} is not registered as a SON", ("son", son));
auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id);
dlog("Approving son account ${son}", ("son", son));
if (!insert_result.second) if (!insert_result.second)
FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son));
} }
@ -2870,13 +2874,16 @@ public:
if (!son_obj) if (!son_obj)
FC_THROW("Account ${son} is not registered as a SON", ("son", son)); FC_THROW("Account ${son} is not registered as a SON", ("son", son));
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id);
dlog("Rejecting son account ${son}", ("son", son));
dlog("votes_removed ${votes_removed}", ("votes_removed", votes_removed));
if (!votes_removed) if (!votes_removed)
FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son));
} }
voting_account_object.options.extensions.value.num_son = desired_number_of_sons; voting_account_object.options.num_son = desired_number_of_sons;
dlog("desired_number_of_sons = ${desired_number_of_sons}", ("desired_number_of_sons", desired_number_of_sons));
account_update_operation account_update_op; account_update_operation account_update_op;
account_update_op.account = voting_account_object.id; account_update_op.account = voting_account_object.id;
dlog("account_update_op.account = ${account_update_op.account}", ("account_update_op.account", account_update_op.account));
account_update_op.new_options = voting_account_object.options; account_update_op.new_options = voting_account_object.options;
signed_transaction tx; signed_transaction tx;
@ -3470,6 +3477,70 @@ public:
return ss.str(); return ss.str();
}; };
m["get_blind_balances"] = [this](variant result, const fc::variants& a)
{
auto r = result.as<vector<asset>>( GRAPHENE_MAX_NESTED_OBJECTS );
vector<asset_object> asset_recs;
std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) {
return get_asset(a.asset_id);
});
std::stringstream ss;
for( unsigned i = 0; i < asset_recs.size(); ++i )
ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n";
return ss.str();
};
m["transfer_to_blind"] = [this](variant result, const fc::variants& a)
{
auto r = result.as<blind_confirmation>( GRAPHENE_MAX_NESTED_OBJECTS );
std::stringstream ss;
r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) );
ss << "\n";
for( const auto& out : r.outputs )
{
asset_object a = get_asset( out.decrypted_memo.amount.asset_id );
ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n";
}
return ss.str();
};
m["blind_transfer"] = [this](variant result, const fc::variants& a)
{
auto r = result.as<blind_confirmation>( GRAPHENE_MAX_NESTED_OBJECTS );
std::stringstream ss;
r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) );
ss << "\n";
for( const auto& out : r.outputs )
{
asset_object a = get_asset( out.decrypted_memo.amount.asset_id );
ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n";
}
return ss.str();
};
m["receive_blind_transfer"] = [this](variant result, const fc::variants& a)
{
auto r = result.as<blind_receipt>( GRAPHENE_MAX_NESTED_OBJECTS );
std::stringstream ss;
asset_object as = get_asset( r.amount.asset_id );
ss << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n";
return ss.str();
};
m["blind_history"] = [this](variant result, const fc::variants& a)
{
auto records = result.as<vector<blind_receipt>>( GRAPHENE_MAX_NESTED_OBJECTS );
std::stringstream ss;
ss << "WHEN "
<< " " << "AMOUNT" << " " << "FROM" << " => " << "TO" << " " << "MEMO" <<"\n";
ss << "====================================================================================\n";
for( auto& r : records )
{
asset_object as = get_asset( r.amount.asset_id );
ss << fc::get_approximate_relative_time_string( r.date )
<< " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n";
}
return ss.str();
};
m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a) m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a)
{ {
const vector<tournament_object> tournaments = result.as<vector<tournament_object> >( GRAPHENE_MAX_NESTED_OBJECTS ); const vector<tournament_object> tournaments = result.as<vector<tournament_object> >( GRAPHENE_MAX_NESTED_OBJECTS );
@ -4330,6 +4401,26 @@ std::string operation_printer::operator()(const T& op)const
out << " result: " << str_result; out << " result: " << str_result;
return ""; return "";
} }
std::string operation_printer::operator()(const transfer_from_blind_operation& op)const
{
auto a = wallet.get_asset( op.fee.asset_id );
auto receiver = wallet.get_account( op.to );
out << receiver.name
<< " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance";
return "";
}
std::string operation_printer::operator()(const transfer_to_blind_operation& op)const
{
auto fa = wallet.get_asset( op.fee.asset_id );
auto a = wallet.get_asset( op.amount.asset_id );
auto sender = wallet.get_account( op.from );
out << sender.name
<< " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"")
<< " fee: " << fa.amount_to_pretty_string( op.fee );
return "";
}
string operation_printer::operator()(const transfer_operation& op) const string operation_printer::operator()(const transfer_operation& op) const
{ {
out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount) out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount)
@ -6100,6 +6191,495 @@ bool wallet_api::set_key_label( public_key_type key, string label
} }
return false; return false;
} }
map<string,public_key_type> wallet_api::get_blind_accounts()const
{
map<string,public_key_type> result;
for( const auto& item : my->_wallet.labeled_keys )
result[item.label] = item.key;
return result;
}
map<string,public_key_type> wallet_api::get_my_blind_accounts()const
{
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
map<string,public_key_type> result;
for( const auto& item : my->_wallet.labeled_keys )
{
if( my->_keys.find(item.key) != my->_keys.end() )
result[item.label] = item.key;
}
return result;
}
public_key_type wallet_api::create_blind_account( string label, string brain_key )
{
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
auto label_itr = my->_wallet.labeled_keys.get<by_label>().find(label);
if( label_itr != my->_wallet.labeled_keys.get<by_label>().end() )
FC_ASSERT( !"Key with label already exists" );
brain_key = fc::trim_and_normalize_spaces( brain_key );
auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() );
auto priv_key = fc::ecc::private_key::regenerate( secret );
public_key_type pub_key = priv_key.get_public_key();
FC_ASSERT( set_key_label( pub_key, label ) );
my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key );
save_wallet_file();
return pub_key;
}
vector<asset> wallet_api::get_blind_balances( string key_or_label )
{
vector<asset> result;
map<asset_id_type, share_type> balances;
vector<commitment_type> used;
auto pub_key = get_public_key( key_or_label );
auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();
auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false) );
auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true) );
while( start != end )
{
if( !start->used )
{
auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} );
if( answer.size() )
balances[start->amount.asset_id] += start->amount.amount;
else
used.push_back( start->commitment() );
}
++start;
}
for( const auto& u : used )
{
auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );
my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );
}
for( auto item : balances )
result.push_back( asset( item.second, item.first ) );
return result;
}
blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label,
string to_account_id_or_name,
string amount_in,
string symbol,
bool broadcast )
{ try {
transfer_from_blind_operation from_blind;
auto fees = my->_remote_db->get_global_properties().parameters.current_fees;
fc::optional<asset_object> asset_obj = get_asset(symbol);
FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol));
auto amount = asset_obj->amount_from_string(amount_in);
from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate );
auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount );
auto conf = blind_transfer_help( from_blind_account_key_or_label,
from_blind_account_key_or_label,
blind_in, symbol, false, true/*to_temp*/ );
FC_ASSERT( conf.outputs.size() > 0 );
auto to_account = my->get_account( to_account_id_or_name );
from_blind.to = to_account.id;
from_blind.amount = amount;
from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor;
from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } );
from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate );
idump( (from_blind) );
conf.trx.operations.push_back(from_blind);
ilog( "about to validate" );
conf.trx.validate();
if( broadcast && conf.outputs.size() == 2 ) {
// Save the change
blind_confirmation::output conf_output;
blind_confirmation::output change_output = conf.outputs[0];
// The wallet must have a private key for confirmation.to, this is used to decrypt the memo
public_key_type from_key = get_public_key(from_blind_account_key_or_label);
conf_output.confirmation.to = from_key;
conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key;
conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo;
conf_output.confirmation_receipt = conf_output.confirmation;
//try {
receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name );
//} catch ( ... ){}
}
ilog( "about to broadcast" );
conf.trx = sign_transaction( conf.trx, broadcast );
return conf;
} FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) }
blind_confirmation wallet_api::blind_transfer( string from_key_or_label,
string to_key_or_label,
string amount_in,
string symbol,
bool broadcast )
{
return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false );
}
blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
string to_key_or_label,
string amount_in,
string symbol,
bool broadcast,
bool to_temp )
{
blind_confirmation confirm;
try {
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
public_key_type from_key = get_public_key(from_key_or_label);
public_key_type to_key = get_public_key(to_key_or_label);
fc::optional<asset_object> asset_obj = get_asset(symbol);
FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol));
blind_transfer_operation blind_tr;
blind_tr.outputs.resize(2);
auto fees = my->_remote_db->get_global_properties().parameters.current_fees;
auto amount = asset_obj->amount_from_string(amount_in);
asset total_amount = asset_obj->amount(0);
vector<fc::sha256> blinding_factors;
//auto from_priv_key = my->get_private_key( from_key );
blind_tr.fee = fees->calculate_fee( blind_tr, asset_obj->options.core_exchange_rate );
vector<commitment_type> used;
auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();
auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false) );
auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true) );
while( start != end )
{
auto result = my->_remote_db->get_blinded_balances( {start->commitment() } );
if( result.size() == 0 )
{
used.push_back( start->commitment() );
}
else
{
blind_tr.inputs.push_back({start->commitment(), start->control_authority});
blinding_factors.push_back( start->data.blinding_factor );
total_amount += start->amount;
if( total_amount >= amount + blind_tr.fee )
break;
}
++start;
}
for( const auto& u : used )
{
auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );
my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );
}
FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficent Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) );
auto one_time_key = fc::ecc::private_key::generate();
auto secret = one_time_key.get_shared_secret( to_key );
auto child = fc::sha256::hash( secret );
auto nonce = fc::sha256::hash( one_time_key.get_secret() );
auto blind_factor = fc::sha256::hash( child );
auto from_secret = one_time_key.get_shared_secret( from_key );
auto from_child = fc::sha256::hash( from_secret );
auto from_nonce = fc::sha256::hash( nonce );
auto change = total_amount - amount - blind_tr.fee;
fc::sha256 change_blind_factor;
fc::sha256 to_blind_factor;
if( change.amount > 0 )
{
idump(("to_blind_factor")(blind_factor) );
blinding_factors.push_back( blind_factor );
change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 );
wdump(("change_blind_factor")(change_blind_factor) );
}
else // change == 0
{
blind_tr.outputs.resize(1);
blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );
idump(("to_sum_blind_factor")(blind_factor) );
blinding_factors.push_back( blind_factor );
idump(("nochange to_blind_factor")(blind_factor) );
}
fc::ecc::public_key from_pub_key = from_key;
fc::ecc::public_key to_pub_key = to_key;
blind_output to_out;
to_out.owner = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );
to_out.commitment = fc::ecc::blind( blind_factor, amount.amount.value );
idump(("to_out.blind")(blind_factor)(to_out.commitment) );
if( blind_tr.outputs.size() > 1 )
{
to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, 0, amount.amount.value );
blind_output change_out;
change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 );
change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value );
change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, 0, change.amount.value );
blind_tr.outputs[1] = change_out;
blind_confirmation::output conf_output;
conf_output.label = from_key_or_label;
conf_output.pub_key = from_key;
conf_output.decrypted_memo.from = from_key;
conf_output.decrypted_memo.amount = change;
conf_output.decrypted_memo.blinding_factor = change_blind_factor;
conf_output.decrypted_memo.commitment = change_out.commitment;
conf_output.decrypted_memo.check = from_secret._hash[0];
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
conf_output.confirmation.to = from_key;
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) );
conf_output.auth = change_out.owner;
conf_output.confirmation_receipt = conf_output.confirmation;
confirm.outputs.push_back( conf_output );
}
blind_tr.outputs[0] = to_out;
blind_confirmation::output conf_output;
conf_output.label = to_key_or_label;
conf_output.pub_key = to_key;
conf_output.decrypted_memo.from = from_key;
conf_output.decrypted_memo.amount = amount;
conf_output.decrypted_memo.blinding_factor = blind_factor;
conf_output.decrypted_memo.commitment = to_out.commitment;
conf_output.decrypted_memo.check = secret._hash[0];
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
conf_output.confirmation.to = to_key;
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
conf_output.auth = to_out.owner;
conf_output.confirmation_receipt = conf_output.confirmation;
confirm.outputs.push_back( conf_output );
/** commitments must be in sorted order */
std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(),
[&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );
std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(),
[&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } );
confirm.trx.operations.emplace_back( std::move(blind_tr) );
ilog( "validate before" );
confirm.trx.validate();
confirm.trx = sign_transaction(confirm.trx, broadcast);
if( broadcast )
{
for( const auto& out : confirm.outputs )
{
try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){}
}
}
return confirm;
} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) }
/**
* Transfers a public balance from @from to one or more blinded balances using a
* stealth transfer.
*/
blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name,
string asset_symbol,
/** map from key or label to amount */
vector<pair<string, string>> to_amounts,
bool broadcast )
{ try {
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
idump((to_amounts));
blind_confirmation confirm;
account_object from_account = my->get_account(from_account_id_or_name);
fc::optional<asset_object> asset_obj = get_asset(asset_symbol);
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol));
transfer_to_blind_operation bop;
bop.from = from_account.id;
vector<fc::sha256> blinding_factors;
asset total_amount = asset_obj->amount(0);
for( auto item : to_amounts )
{
auto one_time_key = fc::ecc::private_key::generate();
auto to_key = get_public_key( item.first );
auto secret = one_time_key.get_shared_secret( to_key );
auto child = fc::sha256::hash( secret );
auto nonce = fc::sha256::hash( one_time_key.get_secret() );
auto blind_factor = fc::sha256::hash( child );
blinding_factors.push_back( blind_factor );
auto amount = asset_obj->amount_from_string(item.second);
total_amount += amount;
fc::ecc::public_key to_pub_key = to_key;
blind_output out;
out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );
out.commitment = fc::ecc::blind( blind_factor, amount.amount.value );
if( to_amounts.size() > 1 )
out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, 0, amount.amount.value );
blind_confirmation::output conf_output;
conf_output.label = item.first;
conf_output.pub_key = to_key;
conf_output.decrypted_memo.amount = amount;
conf_output.decrypted_memo.blinding_factor = blind_factor;
conf_output.decrypted_memo.commitment = out.commitment;
conf_output.decrypted_memo.check = secret._hash[0];
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
conf_output.confirmation.to = to_key;
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
conf_output.confirmation_receipt = conf_output.confirmation;
confirm.outputs.push_back( conf_output );
bop.outputs.push_back(out);
}
bop.amount = total_amount;
bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );
/** commitments must be in sorted order */
std::sort( bop.outputs.begin(), bop.outputs.end(),
[&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );
confirm.trx.operations.push_back( bop );
my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.current_fees);
confirm.trx.validate();
confirm.trx = sign_transaction(confirm.trx, broadcast);
if( broadcast )
{
for( const auto& out : confirm.outputs )
{
try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){}
}
}
return confirm;
} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) }
blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo )
{
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
stealth_confirmation conf(confirmation_receipt);
FC_ASSERT( conf.to );
blind_receipt result;
result.conf = conf;
auto to_priv_key_itr = my->_keys.find( *conf.to );
FC_ASSERT( to_priv_key_itr != my->_keys.end(), "No private key for receiver", ("conf",conf) );
auto to_priv_key = wif_to_key( to_priv_key_itr->second );
FC_ASSERT( to_priv_key );
auto secret = to_priv_key->get_shared_secret( conf.one_time_key );
auto child = fc::sha256::hash( secret );
auto child_priv_key = to_priv_key->child( child );
//auto blind_factor = fc::sha256::hash( child );
auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo );
auto memo = fc::raw::unpack<stealth_confirmation::memo_data>( plain_memo );
result.to_key = *conf.to;
result.to_label = get_key_label( result.to_key );
if( memo.from )
{
result.from_key = *memo.from;
result.from_label = get_key_label( result.from_key );
if( result.from_label == string() )
{
result.from_label = opt_from;
set_key_label( result.from_key, result.from_label );
}
}
else
{
result.from_label = opt_from;
}
result.amount = memo.amount;
result.memo = opt_memo;
// confirm the amount matches the commitment (verify the blinding factor)
auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value );
FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) );
blind_balance bal;
bal.amount = memo.amount;
bal.to = *conf.to;
if( memo.from ) bal.from = *memo.from;
bal.one_time_key = conf.one_time_key;
bal.blinding_factor = memo.blinding_factor;
bal.commitment = memo.commitment;
bal.used = false;
auto child_pubkey = child_priv_key.get_public_key();
auto owner = authority(1, public_key_type(child_pubkey), 1);
result.control_authority = owner;
result.data = memo;
auto child_key_itr = owner.key_auths.find( child_pubkey );
if( child_key_itr != owner.key_auths.end() )
my->_keys[child_key_itr->first] = key_to_wif( child_priv_key );
// my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal );
result.date = fc::time_point::now();
my->_wallet.blind_receipts.insert( result );
my->_keys[child_pubkey] = key_to_wif( child_priv_key );
save_wallet_file();
return result;
}
vector<blind_receipt> wallet_api::blind_history( string key_or_account )
{
vector<blind_receipt> result;
auto pub_key = get_public_key( key_or_account );
if( pub_key == public_key_type() )
return vector<blind_receipt>();
for( auto& r : my->_wallet.blind_receipts )
{
if( r.from_key == pub_key || r.to_key == pub_key )
result.push_back( r );
}
std::sort( result.begin(), result.end(), [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } );
return result;
}
/////////////// ///////////////
// peerplays // // peerplays //

View file

@ -39,7 +39,7 @@ public:
fixture_(fixture) fixture_(fixture)
{ {
fixture_.init_nathan(); fixture_.init_nathan();
fixture_.generate_blocks(HARDFORK_SON3_TIME); fixture_.generate_blocks(HARDFORK_SON_FOR_HIVE_TIME);
fixture_.generate_block(); fixture_.generate_block();
} }
@ -232,6 +232,7 @@ BOOST_AUTO_TEST_CASE( son_voting )
con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true);
// Vote for a son1account // Vote for a son1account
BOOST_TEST_MESSAGE("Voting for son1account"); BOOST_TEST_MESSAGE("Voting for son1account");
BOOST_CHECK(generate_maintenance_block());
vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true);
BOOST_CHECK(generate_maintenance_block()); BOOST_CHECK(generate_maintenance_block());
@ -337,8 +338,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture )
global_property_object gpo; global_property_object gpo;
gpo = con.wallet_api_ptr->get_global_properties(); gpo = con.wallet_api_ptr->get_global_properties();
//! Set son number as 5 (as the begining son count) unsigned int son_number = gpo.parameters.maximum_son_count();
unsigned int son_number = 5;
flat_map<sidechain_type, string> sidechain_public_keys; flat_map<sidechain_type, string> sidechain_public_keys;
@ -401,7 +401,7 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture )
BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size());
BOOST_CHECK(generate_maintenance_block()); BOOST_CHECK(generate_maintenance_block());
BOOST_CHECK(gpo.active_sons.size() == son_number); BOOST_CHECK(gpo.active_sons.size() == gpo.parameters.maximum_son_count());
} catch( fc::exception& e ) { } catch( fc::exception& e ) {
BOOST_TEST_MESSAGE("SON cli wallet tests exception"); BOOST_TEST_MESSAGE("SON cli wallet tests exception");
@ -454,13 +454,20 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
sidechain_public_keys.clear(); sidechain_public_keys.clear();
sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1"; sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 1";
BOOST_TEST_MESSAGE("sidechain_public_keys add for bitcoin account 1");
sidechain_public_keys[sidechain_type::hive] = "hive account 1"; sidechain_public_keys[sidechain_type::hive] = "hive account 1";
BOOST_TEST_MESSAGE("sidechain_public_keys add for hive account 1");
sth.create_son("son1account", "http://son1", sidechain_public_keys); sth.create_son("son1account", "http://son1", sidechain_public_keys);
BOOST_TEST_MESSAGE("created son account 1");
sidechain_public_keys.clear(); sidechain_public_keys.clear();
sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2"; sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin_address 2";
BOOST_TEST_MESSAGE("sidechain_public_keys add for bitcoin account 2");
sidechain_public_keys[sidechain_type::hive] = "hive account 2"; sidechain_public_keys[sidechain_type::hive] = "hive account 2";
BOOST_TEST_MESSAGE("sidechain_public_keys add for hive account 2");
sth.create_son("son2account", "http://son2", sidechain_public_keys); sth.create_son("son2account", "http://son2", sidechain_public_keys);
BOOST_TEST_MESSAGE("created son account 2");
BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes"); BOOST_TEST_MESSAGE("Vote for 2 accounts with update_son_votes");
@ -472,13 +479,17 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
// Get votes at start // Get votes at start
son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_obj = con.wallet_api_ptr->get_son("son1account");
son1_start_votes = son1_obj.total_votes; son1_start_votes = son1_obj.total_votes;
BOOST_TEST_MESSAGE("son1_start_votes: " << son1_start_votes);
son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_obj = con.wallet_api_ptr->get_son("son2account");
son2_start_votes = son2_obj.total_votes; son2_start_votes = son2_obj.total_votes;
BOOST_TEST_MESSAGE("son2_start_votes: " << son2_start_votes);
std::vector<std::string> accepted; std::vector<std::string> accepted;
std::vector<std::string> rejected; std::vector<std::string> rejected;
signed_transaction update_votes_tx; signed_transaction update_votes_tx;
generate_block();
BOOST_CHECK(generate_maintenance_block());
// Vote for both SONs // Vote for both SONs
accepted.clear(); accepted.clear();
rejected.clear(); rejected.clear();
@ -493,10 +504,12 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
// Verify the votes // Verify the votes
son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_obj = con.wallet_api_ptr->get_son("son1account");
son1_end_votes = son1_obj.total_votes; son1_end_votes = son1_obj.total_votes;
BOOST_TEST_MESSAGE("son1_end_votes: " << son1_end_votes);
BOOST_CHECK(son1_end_votes > son1_start_votes); BOOST_CHECK(son1_end_votes > son1_start_votes);
son1_start_votes = son1_end_votes; son1_start_votes = son1_end_votes;
son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_obj = con.wallet_api_ptr->get_son("son2account");
son2_end_votes = son2_obj.total_votes; son2_end_votes = son2_obj.total_votes;
BOOST_TEST_MESSAGE("son2_end_votes: " << son2_end_votes);
BOOST_CHECK(son2_end_votes > son2_start_votes); BOOST_CHECK(son2_end_votes > son2_start_votes);
son2_start_votes = son2_end_votes; son2_start_votes = son2_end_votes;
@ -513,18 +526,21 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
// Verify the votes // Verify the votes
son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_obj = con.wallet_api_ptr->get_son("son1account");
son1_end_votes = son1_obj.total_votes; son1_end_votes = son1_obj.total_votes;
BOOST_TEST_MESSAGE("son1_end_votes: " << son1_end_votes);
BOOST_CHECK(son1_end_votes < son1_start_votes); BOOST_CHECK(son1_end_votes < son1_start_votes);
son1_start_votes = son1_end_votes; son1_start_votes = son1_end_votes;
son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_obj = con.wallet_api_ptr->get_son("son2account");
// voice distribution changed, SON2 now has all voices // voice distribution changed, SON2 now has all voices
son2_end_votes = son2_obj.total_votes; son2_end_votes = son2_obj.total_votes;
BOOST_TEST_MESSAGE("son2_end_votes: " << son2_end_votes);
BOOST_CHECK((son2_end_votes > son2_start_votes)); // nathan spent funds for vb, it has different voting power BOOST_CHECK((son2_end_votes > son2_start_votes)); // nathan spent funds for vb, it has different voting power
son2_start_votes = son2_end_votes; son2_start_votes = son2_end_votes;
// Try to reject incorrect SON // Try to reject incorrect SON
accepted.clear(); accepted.clear();
rejected.clear(); rejected.clear();
rejected.push_back("son1accnt"); rejected.push_back("son1account");
BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
rejected, 1, true), fc::exception); rejected, 1, true), fc::exception);
generate_block(); generate_block();
@ -560,8 +576,8 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test )
// Try to accept and reject the same SON // Try to accept and reject the same SON
accepted.clear(); accepted.clear();
rejected.clear(); rejected.clear();
rejected.push_back("son1accnt"); rejected.push_back("son1account");
accepted.push_back("son1accnt"); accepted.push_back("son1account");
BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted,
rejected, 1, true), fc::exception); rejected, 1, true), fc::exception);
BOOST_CHECK(generate_maintenance_block()); BOOST_CHECK(generate_maintenance_block());
@ -645,8 +661,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture )
global_property_object gpo; global_property_object gpo;
gpo = con.wallet_api_ptr->get_global_properties(); gpo = con.wallet_api_ptr->get_global_properties();
//! Set son number as 5 (as the begining son count) unsigned int son_number = gpo.parameters.maximum_son_count();
unsigned int son_number = 5;
flat_map<sidechain_type, string> sidechain_public_keys; flat_map<sidechain_type, string> sidechain_public_keys;
@ -688,7 +703,7 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture )
map<string, son_id_type> active_sons = con.wallet_api_ptr->list_active_sons(); map<string, son_id_type> active_sons = con.wallet_api_ptr->list_active_sons();
BOOST_CHECK(active_sons.size() == son_number); BOOST_CHECK(active_sons.size() == son_number);
for(unsigned int i = 1; i < son_number + 1; i++) for(unsigned int i = 1; i < son_number; i++)
{ {
std::string name = "sonaccount" + fc::to_pretty_string(i); std::string name = "sonaccount" + fc::to_pretty_string(i);
BOOST_CHECK(active_sons.find(name) != active_sons.end()); BOOST_CHECK(active_sons.find(name) != active_sons.end());