Merge branch 'develop' into feature/libbitcoin-son-final
This commit is contained in:
commit
8d5f84f46b
80 changed files with 4405 additions and 1196 deletions
|
|
@ -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 )
|
||||||
|
|
|
||||||
|
|
@ -917,7 +917,8 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti
|
||||||
wanted.insert("accounts_list");
|
wanted.insert("accounts_list");
|
||||||
wanted.insert("affiliate_stats");
|
wanted.insert("affiliate_stats");
|
||||||
}
|
}
|
||||||
wanted.insert("witness");
|
if (!wanted.count("delayed_node") && !wanted.count("debug_witness") && !wanted.count("witness")) // explicitly requested delayed_node or debug_witness functionality suppresses witness functions
|
||||||
|
wanted.insert("witness");
|
||||||
wanted.insert("bookie");
|
wanted.insert("bookie");
|
||||||
|
|
||||||
int es_ah_conflict_counter = 0;
|
int es_ah_conflict_counter = 0;
|
||||||
|
|
@ -949,7 +950,7 @@ void application::startup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<abstract_plugin> application::get_plugin(const string &name) const {
|
std::shared_ptr<abstract_plugin> application::get_plugin(const string &name) const {
|
||||||
return my->_active_plugins[name];
|
return is_plugin_enabled(name) ? my->_active_plugins[name] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool application::is_plugin_enabled(const string &name) const {
|
bool application::is_plugin_enabled(const string &name) const {
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,10 @@ public:
|
||||||
fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) const;
|
fc::optional<son_object> get_son_by_account(const std::string account_id_or_name) const;
|
||||||
map<string, son_id_type> lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const;
|
map<string, son_id_type> lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const;
|
||||||
uint64_t get_son_count() const;
|
uint64_t get_son_count() const;
|
||||||
|
flat_map<sidechain_type, vector<son_info>> get_active_sons();
|
||||||
|
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||||
|
map<sidechain_type, map<son_id_type, string>> get_son_network_status();
|
||||||
|
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain);
|
||||||
|
|
||||||
// SON wallets
|
// SON wallets
|
||||||
optional<son_wallet_object> get_active_son_wallet();
|
optional<son_wallet_object> get_active_son_wallet();
|
||||||
|
|
@ -1848,6 +1852,80 @@ uint64_t database_api_impl::get_son_count() const {
|
||||||
return _db.get_index_type<son_index>().indices().size();
|
return _db.get_index_type<son_index>().indices().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flat_map<sidechain_type, vector<son_info>> database_api::get_active_sons() {
|
||||||
|
return my->get_active_sons();
|
||||||
|
}
|
||||||
|
|
||||||
|
flat_map<sidechain_type, vector<son_info>> database_api_impl::get_active_sons() {
|
||||||
|
return get_global_properties().active_sons;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<son_info> database_api::get_active_sons_by_sidechain(sidechain_type sidechain) {
|
||||||
|
return my->get_active_sons_by_sidechain(sidechain);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<son_info> database_api_impl::get_active_sons_by_sidechain(sidechain_type sidechain) {
|
||||||
|
const global_property_object &gpo = get_global_properties();
|
||||||
|
|
||||||
|
vector<son_info> result;
|
||||||
|
|
||||||
|
if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) {
|
||||||
|
result = gpo.active_sons.at(sidechain);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<sidechain_type, map<son_id_type, string>> database_api::get_son_network_status() {
|
||||||
|
return my->get_son_network_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
map<sidechain_type, map<son_id_type, string>> database_api_impl::get_son_network_status() {
|
||||||
|
map<sidechain_type, map<son_id_type, string>> result;
|
||||||
|
|
||||||
|
for (auto active_sidechain_type : active_sidechain_types) {
|
||||||
|
result[active_sidechain_type] = get_son_network_status_by_sidechain(active_sidechain_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<son_id_type, string> database_api::get_son_network_status_by_sidechain(sidechain_type sidechain) {
|
||||||
|
return my->get_son_network_status_by_sidechain(sidechain);
|
||||||
|
}
|
||||||
|
|
||||||
|
map<son_id_type, string> database_api_impl::get_son_network_status_by_sidechain(sidechain_type sidechain) {
|
||||||
|
const global_property_object &gpo = get_global_properties();
|
||||||
|
|
||||||
|
map<son_id_type, string> result;
|
||||||
|
|
||||||
|
if (gpo.active_sons.find(sidechain) != gpo.active_sons.end()) {
|
||||||
|
for (const auto si : gpo.active_sons.at(sidechain)) {
|
||||||
|
const auto son_obj = si.son_id(_db);
|
||||||
|
const auto sso = son_obj.statistics(_db);
|
||||||
|
string status;
|
||||||
|
|
||||||
|
if (sso.last_active_timestamp.find(sidechain) != sso.last_active_timestamp.end()) {
|
||||||
|
if (time_point_sec(sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_heartbeat_frequency())) > _db.head_block_time()) {
|
||||||
|
status = "OK, regular SON heartbeat";
|
||||||
|
} else {
|
||||||
|
if (time_point_sec(sso.last_active_timestamp.at(sidechain) + fc::seconds(gpo.parameters.son_down_time())) > _db.head_block_time()) {
|
||||||
|
status = "OK, irregular SON heartbeat, but not triggering SON down proposal";
|
||||||
|
} else {
|
||||||
|
status = "NOT OK, irregular SON heartbeat, triggering SON down proposal]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = "No heartbeats sent";
|
||||||
|
}
|
||||||
|
|
||||||
|
result[si.son_id] = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// //
|
// //
|
||||||
// SON Wallets //
|
// SON Wallets //
|
||||||
|
|
@ -2085,6 +2163,7 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
|
||||||
const auto &against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
|
const auto &against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
|
||||||
const auto &son_bictoin_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_bitcoin>();
|
const auto &son_bictoin_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_bitcoin>();
|
||||||
const auto &son_hive_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_hive>();
|
const auto &son_hive_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_hive>();
|
||||||
|
const auto &son_ethereum_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_ethereum>();
|
||||||
|
|
||||||
vector<variant> result;
|
vector<variant> result;
|
||||||
result.reserve(votes.size());
|
result.reserve(votes.size());
|
||||||
|
|
@ -2136,6 +2215,14 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
|
||||||
result.emplace_back(variant());
|
result.emplace_back(variant());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case vote_id_type::son_ethereum: {
|
||||||
|
auto itr = son_ethereum_idx.find(id);
|
||||||
|
if (itr != son_ethereum_idx.end())
|
||||||
|
result.emplace_back(variant(*itr, 5));
|
||||||
|
else
|
||||||
|
result.emplace_back(variant());
|
||||||
|
break;
|
||||||
|
}
|
||||||
case vote_id_type::VOTE_TYPE_COUNT:
|
case vote_id_type::VOTE_TYPE_COUNT:
|
||||||
break; // supress unused enum value warnings
|
break; // supress unused enum value warnings
|
||||||
default:
|
default:
|
||||||
|
|
@ -2173,6 +2260,9 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const
|
||||||
const auto son_hive_ids = get_votes_objects<son_index, by_vote_id_hive>(votes_ids, 5);
|
const auto son_hive_ids = get_votes_objects<son_index, by_vote_id_hive>(votes_ids, 5);
|
||||||
if (!son_hive_ids.empty())
|
if (!son_hive_ids.empty())
|
||||||
son_ids[sidechain_type::hive] = std::move(son_hive_ids);
|
son_ids[sidechain_type::hive] = std::move(son_hive_ids);
|
||||||
|
const auto son_ethereum_ids = get_votes_objects<son_index, by_vote_id_ethereum>(votes_ids, 5);
|
||||||
|
if (!son_ethereum_ids.empty())
|
||||||
|
son_ids[sidechain_type::ethereum] = std::move(son_ethereum_ids);
|
||||||
return son_ids;
|
return son_ids;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -675,6 +675,32 @@ public:
|
||||||
*/
|
*/
|
||||||
uint64_t get_son_count() const;
|
uint64_t get_son_count() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get list of active sons
|
||||||
|
* @return List of active SONs
|
||||||
|
*/
|
||||||
|
flat_map<sidechain_type, vector<son_info>> get_active_sons();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get list of active sons
|
||||||
|
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
|
||||||
|
* @return List of active SONs
|
||||||
|
*/
|
||||||
|
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get SON network status
|
||||||
|
* @return SON network status description for a given sidechain type
|
||||||
|
*/
|
||||||
|
map<sidechain_type, map<son_id_type, string>> get_son_network_status();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get SON network status
|
||||||
|
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
|
||||||
|
* @return SON network status description for a given sidechain type
|
||||||
|
*/
|
||||||
|
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain);
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// SON Wallets //
|
// SON Wallets //
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
@ -1149,6 +1175,10 @@ FC_API(graphene::app::database_api,
|
||||||
(get_son_by_account)
|
(get_son_by_account)
|
||||||
(lookup_son_accounts)
|
(lookup_son_accounts)
|
||||||
(get_son_count)
|
(get_son_count)
|
||||||
|
(get_active_sons)
|
||||||
|
(get_active_sons_by_sidechain)
|
||||||
|
(get_son_network_status)
|
||||||
|
(get_son_network_status_by_sidechain)
|
||||||
|
|
||||||
// SON wallets
|
// SON wallets
|
||||||
(get_active_son_wallet)
|
(get_active_son_wallet)
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,10 @@
|
||||||
#include <graphene/chain/exceptions.hpp>
|
#include <graphene/chain/exceptions.hpp>
|
||||||
#include <graphene/chain/evaluator.hpp>
|
#include <graphene/chain/evaluator.hpp>
|
||||||
#include <graphene/chain/witness_schedule_object.hpp>
|
#include <graphene/chain/witness_schedule_object.hpp>
|
||||||
|
#include <graphene/db/object_database.hpp>
|
||||||
#include <fc/crypto/digest.hpp>
|
#include <fc/crypto/digest.hpp>
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -160,10 +162,13 @@ void database::check_transaction_for_duplicated_operations(const signed_transact
|
||||||
existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() );
|
existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() );
|
||||||
});
|
});
|
||||||
|
|
||||||
for (auto& pending_transaction: _pending_tx)
|
|
||||||
{
|
{
|
||||||
auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction);
|
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
|
||||||
existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end());
|
for (auto &pending_transaction : _pending_tx)
|
||||||
|
{
|
||||||
|
auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction);
|
||||||
|
existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto proposed_operations_digests = gather_proposed_operations_digests(trx);
|
auto proposed_operations_digests = gather_proposed_operations_digests(trx);
|
||||||
|
|
@ -185,7 +190,12 @@ bool database::push_block(const signed_block& new_block, uint32_t skip)
|
||||||
bool result;
|
bool result;
|
||||||
detail::with_skip_flags( *this, skip, [&]()
|
detail::with_skip_flags( *this, skip, [&]()
|
||||||
{
|
{
|
||||||
detail::without_pending_transactions( *this, std::move(_pending_tx),
|
std::vector<processed_transaction> pending_tx = [this] {
|
||||||
|
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
|
||||||
|
return std::move(_pending_tx);
|
||||||
|
}();
|
||||||
|
|
||||||
|
detail::without_pending_transactions( *this, std::move(pending_tx),
|
||||||
[&]()
|
[&]()
|
||||||
{
|
{
|
||||||
result = _push_block(new_block);
|
result = _push_block(new_block);
|
||||||
|
|
@ -196,6 +206,9 @@ bool database::push_block(const signed_block& new_block, uint32_t skip)
|
||||||
|
|
||||||
bool database::_push_block(const signed_block& new_block)
|
bool database::_push_block(const signed_block& new_block)
|
||||||
{ try {
|
{ try {
|
||||||
|
boost::filesystem::space_info si = boost::filesystem::space(get_data_dir());
|
||||||
|
FC_ASSERT((si.available) > 104857600, "Rejecting block due to low disk space"); // 104857600 bytes = 100 MB
|
||||||
|
|
||||||
uint32_t skip = get_node_properties().skip_flags;
|
uint32_t skip = get_node_properties().skip_flags;
|
||||||
const auto now = fc::time_point::now().sec_since_epoch();
|
const auto now = fc::time_point::now().sec_since_epoch();
|
||||||
|
|
||||||
|
|
@ -382,17 +395,26 @@ processed_transaction database::_push_transaction( const signed_transaction& trx
|
||||||
{
|
{
|
||||||
// If this is the first transaction pushed after applying a block, start a new undo session.
|
// If this is the first transaction pushed after applying a block, start a new undo session.
|
||||||
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
|
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
|
||||||
if( !_pending_tx_session.valid() )
|
{
|
||||||
_pending_tx_session = _undo_db.start_undo_session();
|
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
|
||||||
|
if (!_pending_tx_session.valid()) {
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||||
|
_pending_tx_session = _undo_db.start_undo_session();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a temporary undo session as a child of _pending_tx_session.
|
// Create a temporary undo session as a child of _pending_tx_session.
|
||||||
// The temporary session will be discarded by the destructor if
|
// The temporary session will be discarded by the destructor if
|
||||||
// _apply_transaction fails. If we make it to merge(), we
|
// _apply_transaction fails. If we make it to merge(), we
|
||||||
// apply the changes.
|
// apply the changes.
|
||||||
|
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||||
auto temp_session = _undo_db.start_undo_session();
|
auto temp_session = _undo_db.start_undo_session();
|
||||||
auto processed_trx = _apply_transaction( trx );
|
auto processed_trx = _apply_transaction(trx);
|
||||||
_pending_tx.push_back(processed_trx);
|
{
|
||||||
|
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
|
||||||
|
_pending_tx.push_back(processed_trx);
|
||||||
|
}
|
||||||
|
|
||||||
// notify_changed_objects();
|
// notify_changed_objects();
|
||||||
// The transaction applied successfully. Merge its changes into the pending block session.
|
// The transaction applied successfully. Merge its changes into the pending block session.
|
||||||
|
|
@ -405,6 +427,7 @@ processed_transaction database::_push_transaction( const signed_transaction& trx
|
||||||
|
|
||||||
processed_transaction database::validate_transaction( const signed_transaction& trx )
|
processed_transaction database::validate_transaction( const signed_transaction& trx )
|
||||||
{
|
{
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||||
auto session = _undo_db.start_undo_session();
|
auto session = _undo_db.start_undo_session();
|
||||||
return _apply_transaction( trx );
|
return _apply_transaction( trx );
|
||||||
}
|
}
|
||||||
|
|
@ -504,47 +527,52 @@ signed_block database::_generate_block(
|
||||||
// the value of the "when" variable is known, which means we need to
|
// the value of the "when" variable is known, which means we need to
|
||||||
// re-apply pending transactions in this method.
|
// re-apply pending transactions in this method.
|
||||||
//
|
//
|
||||||
_pending_tx_session.reset();
|
{
|
||||||
_pending_tx_session = _undo_db.start_undo_session();
|
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
|
||||||
|
_pending_tx_session.reset();
|
||||||
|
_pending_tx_session = _undo_db.start_undo_session();
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t postponed_tx_count = 0;
|
uint64_t postponed_tx_count = 0;
|
||||||
// pop pending state (reset to head block state)
|
// pop pending state (reset to head block state)
|
||||||
for( const processed_transaction& tx : _pending_tx )
|
|
||||||
{
|
{
|
||||||
size_t new_total_size = total_block_size + fc::raw::pack_size( tx );
|
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
|
||||||
|
for (const processed_transaction &tx : _pending_tx) {
|
||||||
|
size_t new_total_size = total_block_size + fc::raw::pack_size(tx);
|
||||||
|
|
||||||
// postpone transaction if it would make block too big
|
// postpone transaction if it would make block too big
|
||||||
if( new_total_size >= maximum_block_size )
|
if (new_total_size >= maximum_block_size) {
|
||||||
{
|
postponed_tx_count++;
|
||||||
postponed_tx_count++;
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
auto temp_session = _undo_db.start_undo_session();
|
||||||
auto temp_session = _undo_db.start_undo_session();
|
processed_transaction ptx = _apply_transaction(tx);
|
||||||
processed_transaction ptx = _apply_transaction( tx );
|
temp_session.merge();
|
||||||
temp_session.merge();
|
|
||||||
|
|
||||||
// We have to recompute pack_size(ptx) because it may be different
|
// We have to recompute pack_size(ptx) because it may be different
|
||||||
// than pack_size(tx) (i.e. if one or more results increased
|
// than pack_size(tx) (i.e. if one or more results increased
|
||||||
// their size)
|
// their size)
|
||||||
total_block_size += fc::raw::pack_size( ptx );
|
total_block_size += fc::raw::pack_size(ptx);
|
||||||
pending_block.transactions.push_back( ptx );
|
pending_block.transactions.push_back(ptx);
|
||||||
}
|
} catch (const fc::exception &e) {
|
||||||
catch ( const fc::exception& e )
|
// Do nothing, transaction will not be re-applied
|
||||||
{
|
wlog("Transaction was not processed while generating block due to ${e}", ("e", e));
|
||||||
// Do nothing, transaction will not be re-applied
|
wlog("The transaction was ${t}", ("t", tx));
|
||||||
wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
|
}
|
||||||
wlog( "The transaction was ${t}", ("t", tx) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( postponed_tx_count > 0 )
|
if( postponed_tx_count > 0 )
|
||||||
{
|
{
|
||||||
wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
|
wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
|
||||||
}
|
}
|
||||||
|
|
||||||
_pending_tx_session.reset();
|
{
|
||||||
|
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
|
||||||
|
_pending_tx_session.reset();
|
||||||
|
}
|
||||||
|
|
||||||
// We have temporarily broken the invariant that
|
// We have temporarily broken the invariant that
|
||||||
// _pending_tx_session is the result of applying _pending_tx, as
|
// _pending_tx_session is the result of applying _pending_tx, as
|
||||||
|
|
@ -592,7 +620,11 @@ signed_block database::_generate_block(
|
||||||
*/
|
*/
|
||||||
void database::pop_block()
|
void database::pop_block()
|
||||||
{ try {
|
{ try {
|
||||||
_pending_tx_session.reset();
|
{
|
||||||
|
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
|
||||||
|
_pending_tx_session.reset();
|
||||||
|
}
|
||||||
|
|
||||||
auto head_id = head_block_id();
|
auto head_id = head_block_id();
|
||||||
optional<signed_block> head_block = fetch_block_by_id( head_id );
|
optional<signed_block> head_block = fetch_block_by_id( head_id );
|
||||||
GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );
|
GRAPHENE_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" );
|
||||||
|
|
@ -606,6 +638,8 @@ void database::pop_block()
|
||||||
|
|
||||||
void database::clear_pending()
|
void database::clear_pending()
|
||||||
{ try {
|
{ try {
|
||||||
|
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
|
||||||
|
const std::lock_guard<std::mutex> pending_tx_session_lock{_pending_tx_session_mutex};
|
||||||
assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() );
|
assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() );
|
||||||
_pending_tx.clear();
|
_pending_tx.clear();
|
||||||
_pending_tx_session.reset();
|
_pending_tx_session.reset();
|
||||||
|
|
@ -726,7 +760,7 @@ void database::_apply_block( const signed_block& next_block )
|
||||||
|
|
||||||
check_ending_lotteries();
|
check_ending_lotteries();
|
||||||
check_ending_nft_lotteries();
|
check_ending_nft_lotteries();
|
||||||
|
|
||||||
create_block_summary(next_block);
|
create_block_summary(next_block);
|
||||||
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
|
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
|
||||||
clear_expired_transactions();
|
clear_expired_transactions();
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,9 @@ void database::initialize_hardforks()
|
||||||
void database::initialize_indexes()
|
void database::initialize_indexes()
|
||||||
{
|
{
|
||||||
reset_indexes();
|
reset_indexes();
|
||||||
_undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY );
|
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||||
|
_undo_db.set_max_size(GRAPHENE_MIN_UNDO_HISTORY);
|
||||||
|
|
||||||
//Protocol object indexes
|
//Protocol object indexes
|
||||||
add_index< primary_index<asset_index, 13> >(); // 8192 assets per chunk
|
add_index< primary_index<asset_index, 13> >(); // 8192 assets per chunk
|
||||||
|
|
@ -474,7 +476,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
||||||
FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(),
|
FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(),
|
||||||
"initial_active_witnesses is larger than the number of candidate witnesses.");
|
"initial_active_witnesses is larger than the number of candidate witnesses.");
|
||||||
|
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||||
_undo_db.disable();
|
_undo_db.disable();
|
||||||
|
|
||||||
struct auth_inhibitor {
|
struct auth_inhibitor {
|
||||||
auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags)
|
auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags)
|
||||||
{ db.node_properties().skip_flags |= skip_authority_check; }
|
{ db.node_properties().skip_flags |= skip_authority_check; }
|
||||||
|
|
@ -1101,28 +1105,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
||||||
|
|
||||||
// Initialize witness schedule
|
// Initialize witness schedule
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
const son_schedule_object& ssohive =
|
|
||||||
#endif
|
|
||||||
create<son_schedule_object>([&](son_schedule_object& _sso)
|
|
||||||
{
|
|
||||||
// for scheduled
|
|
||||||
memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size());
|
|
||||||
|
|
||||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
|
||||||
|
|
||||||
auto init_witnesses = get_global_properties().active_witnesses;
|
|
||||||
|
|
||||||
_sso.scheduler = son_scheduler();
|
|
||||||
_sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
|
|
||||||
|
|
||||||
|
|
||||||
_sso.last_scheduling_block = 0;
|
|
||||||
|
|
||||||
_sso.recent_slots_filled = fc::uint128::max_value();
|
|
||||||
});
|
|
||||||
assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) );
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
const son_schedule_object& ssobitcoin =
|
const son_schedule_object& ssobitcoin =
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1133,11 +1115,10 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
||||||
|
|
||||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||||
|
|
||||||
auto init_witnesses = get_global_properties().active_witnesses;
|
auto init_bitcoin_sons = get_global_properties().active_sons.at(sidechain_type::bitcoin);
|
||||||
|
|
||||||
_sso.scheduler = son_scheduler();
|
_sso.scheduler = son_scheduler();
|
||||||
_sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
|
_sso.scheduler._min_token_count = std::max(int(init_bitcoin_sons.size()) / 2, 1);
|
||||||
|
|
||||||
|
|
||||||
_sso.last_scheduling_block = 0;
|
_sso.last_scheduling_block = 0;
|
||||||
|
|
||||||
|
|
@ -1145,6 +1126,48 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
||||||
});
|
});
|
||||||
assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) );
|
assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) );
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
const son_schedule_object& ssoethereum =
|
||||||
|
#endif
|
||||||
|
create<son_schedule_object>([&](son_schedule_object& _sso)
|
||||||
|
{
|
||||||
|
// for scheduled
|
||||||
|
memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size());
|
||||||
|
|
||||||
|
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||||
|
|
||||||
|
auto init_ethereum_sons = get_global_properties().active_sons.at(sidechain_type::ethereum);
|
||||||
|
|
||||||
|
_sso.scheduler = son_scheduler();
|
||||||
|
_sso.scheduler._min_token_count = std::max(int(init_ethereum_sons.size()) / 2, 1);
|
||||||
|
|
||||||
|
_sso.last_scheduling_block = 0;
|
||||||
|
|
||||||
|
_sso.recent_slots_filled = fc::uint128::max_value();
|
||||||
|
});
|
||||||
|
assert( ssoethereum.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::ethereum)) );
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
const son_schedule_object& ssohive =
|
||||||
|
#endif
|
||||||
|
create<son_schedule_object>([&](son_schedule_object& _sso)
|
||||||
|
{
|
||||||
|
// for scheduled
|
||||||
|
memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size());
|
||||||
|
|
||||||
|
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||||
|
|
||||||
|
auto init_hive_sons = get_global_properties().active_sons.at(sidechain_type::hive);
|
||||||
|
|
||||||
|
_sso.scheduler = son_scheduler();
|
||||||
|
_sso.scheduler._min_token_count = std::max(int(init_hive_sons.size()) / 2, 1);
|
||||||
|
|
||||||
|
_sso.last_scheduling_block = 0;
|
||||||
|
|
||||||
|
_sso.recent_slots_filled = fc::uint128::max_value();
|
||||||
|
});
|
||||||
|
assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) );
|
||||||
|
|
||||||
// Create FBA counters
|
// Create FBA counters
|
||||||
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
|
create<fba_accumulator_object>([&]( fba_accumulator_object& acc )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -91,17 +91,20 @@ vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<
|
||||||
}
|
}
|
||||||
count = std::min(count, refs.size());
|
count = std::min(count, refs.size());
|
||||||
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
|
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
|
||||||
[this, sidechain](const son_object& a, const son_object& b)->bool {
|
[this, sidechain](const son_object& a, const son_object& b)->bool {
|
||||||
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type");
|
FC_ASSERT(sidechain == sidechain_type::bitcoin ||
|
||||||
|
sidechain == sidechain_type::ethereum ||
|
||||||
|
sidechain == sidechain_type::hive,
|
||||||
|
"Unexpected sidechain type");
|
||||||
|
|
||||||
const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)];
|
const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)];
|
||||||
const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)];
|
const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)];
|
||||||
|
|
||||||
if( oa_vote != ob_vote )
|
if( oa_vote != ob_vote )
|
||||||
return oa_vote > ob_vote;
|
return oa_vote > ob_vote;
|
||||||
|
|
||||||
return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain);
|
return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain);
|
||||||
});
|
});
|
||||||
|
|
||||||
refs.resize(count, refs.front());
|
refs.resize(count, refs.front());
|
||||||
return refs;
|
return refs;
|
||||||
|
|
@ -718,7 +721,9 @@ void database::update_active_sons()
|
||||||
|
|
||||||
assert( _son_count_histogram_buffer.size() > 0 );
|
assert( _son_count_histogram_buffer.size() > 0 );
|
||||||
for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){
|
for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){
|
||||||
|
#ifndef NDEBUG
|
||||||
assert( son_count_histogram_buffer.second.size() > 0 );
|
assert( son_count_histogram_buffer.second.size() > 0 );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const flat_map<sidechain_type, share_type> stake_target = [this]{
|
const flat_map<sidechain_type, share_type> stake_target = [this]{
|
||||||
|
|
@ -1458,17 +1463,19 @@ void rolling_period_start(database& db)
|
||||||
{
|
{
|
||||||
if(db.head_block_time() >= HARDFORK_GPOS_TIME)
|
if(db.head_block_time() >= HARDFORK_GPOS_TIME)
|
||||||
{
|
{
|
||||||
auto gpo = db.get_global_properties();
|
const auto gpo = db.get_global_properties();
|
||||||
auto period_start = db.get_global_properties().parameters.gpos_period_start();
|
auto period_start = db.get_global_properties().parameters.gpos_period_start();
|
||||||
auto vesting_period = db.get_global_properties().parameters.gpos_period();
|
const auto vesting_period = db.get_global_properties().parameters.gpos_period();
|
||||||
|
|
||||||
auto now = db.head_block_time();
|
const auto now = db.head_block_time();
|
||||||
if(now.sec_since_epoch() >= (period_start + vesting_period))
|
while(now.sec_since_epoch() >= (period_start + vesting_period))
|
||||||
{
|
{
|
||||||
// roll
|
// roll
|
||||||
db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) {
|
db.modify(db.get_global_properties(), [period_start, vesting_period](global_property_object& p) {
|
||||||
p.parameters.extensions.value.gpos_period_start = period_start + vesting_period;
|
p.parameters.extensions.value.gpos_period_start = period_start + vesting_period;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
period_start = db.get_global_properties().parameters.gpos_period_start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2044,7 +2051,7 @@ void database::perform_son_tasks()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// create BTC asset here because son_account is the issuer of the BTC
|
// create BTC asset here because son_account is the issuer of the BTC
|
||||||
if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME)
|
if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME)
|
||||||
{
|
{
|
||||||
const asset_dynamic_data_object& dyn_asset =
|
const asset_dynamic_data_object& dyn_asset =
|
||||||
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
||||||
|
|
@ -2063,7 +2070,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
|
||||||
|
|
@ -2077,8 +2084,42 @@ 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, [ð_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)
|
||||||
{
|
{
|
||||||
const asset_dynamic_data_object& dyn_asset =
|
const asset_dynamic_data_object& dyn_asset =
|
||||||
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
||||||
|
|
@ -2097,7 +2138,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
|
||||||
|
|
@ -2131,7 +2172,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
|
||||||
|
|
@ -2413,14 +2454,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();
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ void database::reindex( fc::path data_dir )
|
||||||
uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50;
|
uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50;
|
||||||
|
|
||||||
ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) );
|
ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) );
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||||
auto_undo_enabler undo(_slow_replays, _undo_db);
|
auto_undo_enabler undo(_slow_replays, _undo_db);
|
||||||
if( head_block_num() >= undo_point )
|
if( head_block_num() >= undo_point )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,9 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
|
||||||
unsigned_int database::get_son_schedule_id( sidechain_type type )const
|
unsigned_int database::get_son_schedule_id( sidechain_type type )const
|
||||||
{
|
{
|
||||||
static const map<sidechain_type, unsigned_int> schedule_map = {
|
static const map<sidechain_type, unsigned_int> schedule_map = {
|
||||||
{ sidechain_type::hive, 0 },
|
{ sidechain_type::bitcoin, 0 },
|
||||||
{ sidechain_type::bitcoin, 1 }
|
{ sidechain_type::ethereum, 1 },
|
||||||
|
{ sidechain_type::hive, 2 }
|
||||||
};
|
};
|
||||||
|
|
||||||
return schedule_map.at(type);
|
return schedule_map.at(type);
|
||||||
|
|
@ -322,8 +323,10 @@ void database::update_witness_schedule(const signed_block& next_block)
|
||||||
void database::update_son_schedule(const signed_block& next_block)
|
void database::update_son_schedule(const signed_block& next_block)
|
||||||
{
|
{
|
||||||
auto start = fc::time_point::now();
|
auto start = fc::time_point::now();
|
||||||
const global_property_object& gpo = get_global_properties();
|
#ifndef NDEBUG
|
||||||
const son_schedule_object& sso = get(son_schedule_id_type());
|
const son_schedule_object& sso = get(son_schedule_id_type());
|
||||||
|
#endif
|
||||||
|
const global_property_object& gpo = get_global_properties();
|
||||||
const flat_map<sidechain_type, uint32_t> schedule_needs_filled = [&gpo]()
|
const flat_map<sidechain_type, uint32_t> schedule_needs_filled = [&gpo]()
|
||||||
{
|
{
|
||||||
flat_map<sidechain_type, uint32_t> schedule_needs_filled;
|
flat_map<sidechain_type, uint32_t> schedule_needs_filled;
|
||||||
|
|
|
||||||
7
libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf
Normal file
7
libraries/chain/hardfork.d/SON_FOR_ETHEREUM.hf
Normal 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
|
||||||
|
|
@ -520,6 +520,7 @@ namespace graphene { namespace chain {
|
||||||
void notify_changed_objects();
|
void notify_changed_objects();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::mutex _pending_tx_session_mutex;
|
||||||
optional<undo_database::session> _pending_tx_session;
|
optional<undo_database::session> _pending_tx_session;
|
||||||
vector< unique_ptr<op_evaluator> > _operation_evaluators;
|
vector< unique_ptr<op_evaluator> > _operation_evaluators;
|
||||||
|
|
||||||
|
|
@ -602,6 +603,7 @@ namespace graphene { namespace chain {
|
||||||
///@}
|
///@}
|
||||||
///@}
|
///@}
|
||||||
|
|
||||||
|
std::mutex _pending_tx_mutex;
|
||||||
vector< processed_transaction > _pending_tx;
|
vector< processed_transaction > _pending_tx;
|
||||||
fork_database _fork_db;
|
fork_database _fork_db;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,13 @@ namespace graphene { namespace chain {
|
||||||
bool is_valid_muse( const std::string& base58str );
|
bool is_valid_muse( const std::string& base58str );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class pubkey_comparator {
|
||||||
|
public:
|
||||||
|
inline bool operator()(const public_key_type& a, const public_key_type& b) const {
|
||||||
|
return a.key_data < b.key_data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct extended_public_key_type
|
struct extended_public_key_type
|
||||||
{
|
{
|
||||||
struct binary_key
|
struct binary_key
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ struct vote_id_type
|
||||||
worker,
|
worker,
|
||||||
son_bitcoin,
|
son_bitcoin,
|
||||||
son_hive,
|
son_hive,
|
||||||
|
son_ethereum,
|
||||||
VOTE_TYPE_COUNT
|
VOTE_TYPE_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -145,7 +146,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui
|
||||||
|
|
||||||
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
|
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
|
||||||
|
|
||||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(VOTE_TYPE_COUNT) )
|
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(son_ethereum)(VOTE_TYPE_COUNT) )
|
||||||
FC_REFLECT( graphene::chain::vote_id_type, (content) )
|
FC_REFLECT( graphene::chain::vote_id_type, (content) )
|
||||||
|
|
||||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type )
|
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type )
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ enum class sidechain_type {
|
||||||
hive
|
hive
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::set<sidechain_type> active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::hive};
|
static const std::set<sidechain_type> active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive};
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
|
@ -24,4 +24,4 @@ FC_REFLECT_ENUM(graphene::chain::sidechain_type,
|
||||||
(ethereum)
|
(ethereum)
|
||||||
(eos)
|
(eos)
|
||||||
(hive)
|
(hive)
|
||||||
(peerplays) )
|
(peerplays) )
|
||||||
|
|
|
||||||
|
|
@ -89,11 +89,13 @@ namespace graphene { namespace chain {
|
||||||
inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); }
|
inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); }
|
||||||
inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); }
|
inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); }
|
||||||
inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); }
|
inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); }
|
||||||
|
inline vote_id_type get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct by_account;
|
struct by_account;
|
||||||
struct by_vote_id_bitcoin;
|
struct by_vote_id_bitcoin;
|
||||||
struct by_vote_id_hive;
|
struct by_vote_id_hive;
|
||||||
|
struct by_vote_id_ethereum;
|
||||||
using son_multi_index_type = multi_index_container<
|
using son_multi_index_type = multi_index_container<
|
||||||
son_object,
|
son_object,
|
||||||
indexed_by<
|
indexed_by<
|
||||||
|
|
@ -108,6 +110,9 @@ namespace graphene { namespace chain {
|
||||||
>,
|
>,
|
||||||
ordered_unique< tag<by_vote_id_hive>,
|
ordered_unique< tag<by_vote_id_hive>,
|
||||||
const_mem_fun<son_object, vote_id_type, &son_object::get_hive_vote_id>
|
const_mem_fun<son_object, vote_id_type, &son_object::get_hive_vote_id>
|
||||||
|
>,
|
||||||
|
ordered_unique< tag<by_vote_id_ethereum>,
|
||||||
|
const_mem_fun<son_object, vote_id_type, &son_object::get_ethereum_vote_id>
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,8 @@ void account_options::validate() const
|
||||||
--needed_sons[sidechain_type::bitcoin];
|
--needed_sons[sidechain_type::bitcoin];
|
||||||
else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] )
|
else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] )
|
||||||
--needed_sons[sidechain_type::hive];
|
--needed_sons[sidechain_type::hive];
|
||||||
|
else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] )
|
||||||
|
--needed_sons[sidechain_type::ethereum];
|
||||||
|
|
||||||
FC_ASSERT( needed_witnesses == 0,
|
FC_ASSERT( needed_witnesses == 0,
|
||||||
"May not specify fewer witnesses than the number voted for.");
|
"May not specify fewer witnesses than the number voted for.");
|
||||||
|
|
@ -195,6 +197,8 @@ void account_options::validate() const
|
||||||
"May not specify fewer Bitcoin SONs than the number voted for.");
|
"May not specify fewer Bitcoin SONs than the number voted for.");
|
||||||
FC_ASSERT( needed_sons[sidechain_type::hive] == 0,
|
FC_ASSERT( needed_sons[sidechain_type::hive] == 0,
|
||||||
"May not specify fewer Hive SONs than the number voted for.");
|
"May not specify fewer Hive SONs than the number voted for.");
|
||||||
|
FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0,
|
||||||
|
"May not specify fewer Ethereum SONs than the number voted for.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void affiliate_reward_distribution::validate() const
|
void affiliate_reward_distribution::validate() const
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,18 @@ object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
|
||||||
{ try {
|
{ try {
|
||||||
vote_id_type vote_id_bitcoin;
|
vote_id_type vote_id_bitcoin;
|
||||||
vote_id_type vote_id_hive;
|
vote_id_type vote_id_hive;
|
||||||
db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive](global_property_object& p) {
|
vote_id_type vote_id_ethereum;
|
||||||
|
db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive, &vote_id_ethereum](global_property_object& p) {
|
||||||
vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin);
|
vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin);
|
||||||
vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
|
vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
|
||||||
|
vote_id_ethereum = get_next_vote_id(p, vote_id_type::son_ethereum);
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ){
|
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ){
|
||||||
obj.son_account = op.owner_account;
|
obj.son_account = op.owner_account;
|
||||||
obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin;
|
obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin;
|
||||||
obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive;
|
obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive;
|
||||||
|
obj.sidechain_vote_ids[sidechain_type::ethereum] = vote_id_ethereum;
|
||||||
obj.url = op.url;
|
obj.url = op.url;
|
||||||
obj.deposit = op.deposit;
|
obj.deposit = op.deposit;
|
||||||
obj.signing_key = op.signing_key;
|
obj.signing_key = op.signing_key;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ namespace graphene { namespace chain {
|
||||||
retval = retval &&
|
retval = retval &&
|
||||||
(sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) &&
|
(sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) &&
|
||||||
(sidechain_public_keys.at(sidechain_type::hive).length() > 0);
|
(sidechain_public_keys.at(sidechain_type::hive).length() > 0);
|
||||||
|
|
||||||
|
retval = retval &&
|
||||||
|
(sidechain_public_keys.find( sidechain_type::ethereum ) != sidechain_public_keys.end()) &&
|
||||||
|
(sidechain_public_keys.at(sidechain_type::ethereum).length() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include <fc/log/logger.hpp>
|
#include <fc/log/logger.hpp>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace graphene { namespace db {
|
namespace graphene { namespace db {
|
||||||
|
|
||||||
|
|
@ -144,6 +145,7 @@ namespace graphene { namespace db {
|
||||||
fc::path get_data_dir()const { return _data_dir; }
|
fc::path get_data_dir()const { return _data_dir; }
|
||||||
|
|
||||||
/** public for testing purposes only... should be private in practice. */
|
/** public for testing purposes only... should be private in practice. */
|
||||||
|
mutable std::mutex _undo_db_mutex;
|
||||||
undo_database _undo_db;
|
undo_database _undo_db;
|
||||||
protected:
|
protected:
|
||||||
template<typename IndexType>
|
template<typename IndexType>
|
||||||
|
|
|
||||||
|
|
@ -47,4 +47,3 @@ namespace graphene { namespace net {
|
||||||
const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type;
|
const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type;
|
||||||
|
|
||||||
} } // graphene::net
|
} } // graphene::net
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#define GRAPHENE_NET_PROTOCOL_VERSION 106
|
#define GRAPHENE_NET_PROTOCOL_VERSION 106
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,3 +112,6 @@
|
||||||
#define GRAPHENE_NET_MAX_NESTED_OBJECTS (250)
|
#define GRAPHENE_NET_MAX_NESTED_OBJECTS (250)
|
||||||
|
|
||||||
#define MAXIMUM_PEERDB_SIZE 1000
|
#define MAXIMUM_PEERDB_SIZE 1000
|
||||||
|
|
||||||
|
constexpr size_t MAX_BLOCKS_TO_HANDLE_AT_ONCE = 200;
|
||||||
|
constexpr size_t MAX_SYNC_BLOCKS_TO_PREFETCH = 10 * MAX_BLOCKS_TO_HANDLE_AT_ONCE;
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ namespace graphene { namespace net {
|
||||||
class node_delegate
|
class node_delegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~node_delegate(){}
|
virtual ~node_delegate() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If delegate has the item, the network has no need to fetch it.
|
* If delegate has the item, the network has no need to fetch it.
|
||||||
|
|
@ -71,7 +71,9 @@ namespace graphene { namespace net {
|
||||||
/**
|
/**
|
||||||
* @brief Called when a new block comes in from the network
|
* @brief Called when a new block comes in from the network
|
||||||
*
|
*
|
||||||
|
* @param blk_msg the message which contains the block
|
||||||
* @param sync_mode true if the message was fetched through the sync process, false during normal operation
|
* @param sync_mode true if the message was fetched through the sync process, false during normal operation
|
||||||
|
* @param contained_transaction_msg_ids container for the transactions to write back into
|
||||||
* @returns true if this message caused the blockchain to switch forks, false if it did not
|
* @returns true if this message caused the blockchain to switch forks, false if it did not
|
||||||
*
|
*
|
||||||
* @throws exception if error validating the item, otherwise the item is
|
* @throws exception if error validating the item, otherwise the item is
|
||||||
|
|
@ -195,7 +197,7 @@ namespace graphene { namespace net {
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
node(const std::string& user_agent);
|
node(const std::string& user_agent);
|
||||||
~node();
|
virtual ~node();
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
|
|
@ -213,11 +215,34 @@ namespace graphene { namespace net {
|
||||||
*/
|
*/
|
||||||
void add_node( const fc::ip::endpoint& ep );
|
void add_node( const fc::ip::endpoint& ep );
|
||||||
|
|
||||||
|
/*****
|
||||||
|
* @brief add a list of nodes to seed the p2p network
|
||||||
|
* @param seeds a vector of url strings
|
||||||
|
*/
|
||||||
|
void add_seed_nodes( std::vector<std::string> seeds );
|
||||||
|
|
||||||
|
/****
|
||||||
|
* @brief add a node to seed the p2p network
|
||||||
|
* @param in the url as a string
|
||||||
|
*/
|
||||||
|
void add_seed_node( const std::string& in);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to connect to the specified endpoint immediately.
|
* Attempt to connect to the specified endpoint immediately.
|
||||||
*/
|
*/
|
||||||
virtual void connect_to_endpoint( const fc::ip::endpoint& ep );
|
virtual void connect_to_endpoint( const fc::ip::endpoint& ep );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper to convert a string to a collection of endpoints
|
||||||
|
*
|
||||||
|
* This converts a string (i.e. "bitshares.eu:665535" to a collection of endpoints.
|
||||||
|
* NOTE: Throws an exception if not in correct format or was unable to resolve URL.
|
||||||
|
*
|
||||||
|
* @param in the incoming string
|
||||||
|
* @returns a vector of endpoints
|
||||||
|
*/
|
||||||
|
static std::vector<fc::ip::endpoint> resolve_string_to_ip_endpoints( const std::string& in );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the network interface and port upon which incoming
|
* Specifies the network interface and port upon which incoming
|
||||||
* connections should be accepted.
|
* connections should be accepted.
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ namespace graphene { namespace net
|
||||||
class peer_connection_delegate
|
class peer_connection_delegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
virtual ~peer_connection_delegate() = default;
|
||||||
virtual void on_message(peer_connection* originating_peer,
|
virtual void on_message(peer_connection* originating_peer,
|
||||||
const message& received_message) = 0;
|
const message& received_message) = 0;
|
||||||
virtual void on_connection_closed(peer_connection* originating_peer) = 0;
|
virtual void on_connection_closed(peer_connection* originating_peer) = 0;
|
||||||
|
|
@ -125,7 +126,7 @@ namespace graphene { namespace net
|
||||||
* it is sitting on the queue
|
* it is sitting on the queue
|
||||||
*/
|
*/
|
||||||
virtual size_t get_size_in_queue() = 0;
|
virtual size_t get_size_in_queue() = 0;
|
||||||
virtual ~queued_message() {}
|
virtual ~queued_message() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* when you queue up a 'real_queued_message', a full copy of the message is
|
/* when you queue up a 'real_queued_message', a full copy of the message is
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ namespace graphene { namespace net {
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
peer_database();
|
peer_database();
|
||||||
~peer_database();
|
virtual ~peer_database();
|
||||||
|
|
||||||
void open(const fc::path& databaseFilename);
|
void open(const fc::path& databaseFilename);
|
||||||
void close();
|
void close();
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@
|
||||||
#include <fc/io/raw_fwd.hpp>
|
#include <fc/io/raw_fwd.hpp>
|
||||||
#include <fc/network/rate_limiting.hpp>
|
#include <fc/network/rate_limiting.hpp>
|
||||||
#include <fc/network/ip.hpp>
|
#include <fc/network/ip.hpp>
|
||||||
|
#include <fc/network/resolve.hpp>
|
||||||
|
|
||||||
#include <graphene/net/node.hpp>
|
#include <graphene/net/node.hpp>
|
||||||
#include <graphene/net/peer_database.hpp>
|
#include <graphene/net/peer_database.hpp>
|
||||||
|
|
@ -555,6 +556,10 @@ namespace graphene { namespace net { namespace detail {
|
||||||
fc::future<void> _bandwidth_monitor_loop_done;
|
fc::future<void> _bandwidth_monitor_loop_done;
|
||||||
|
|
||||||
fc::future<void> _dump_node_status_task_done;
|
fc::future<void> _dump_node_status_task_done;
|
||||||
|
/// Used by the task that checks whether addresses of seed nodes have been updated
|
||||||
|
/// @{
|
||||||
|
boost::container::flat_set<std::string> _seed_nodes;
|
||||||
|
fc::future<void> _update_seed_nodes_loop_done;
|
||||||
|
|
||||||
/* We have two alternate paths through the schedule_peer_for_deletion code -- one that
|
/* We have two alternate paths through the schedule_peer_for_deletion code -- one that
|
||||||
* uses a mutex to prevent one fiber from adding items to the queue while another is deleting
|
* uses a mutex to prevent one fiber from adding items to the queue while another is deleting
|
||||||
|
|
@ -728,6 +733,11 @@ namespace graphene { namespace net { namespace detail {
|
||||||
void listen_to_p2p_network();
|
void listen_to_p2p_network();
|
||||||
void connect_to_p2p_network();
|
void connect_to_p2p_network();
|
||||||
void add_node( const fc::ip::endpoint& ep );
|
void add_node( const fc::ip::endpoint& ep );
|
||||||
|
void add_seed_node( const std::string& in);
|
||||||
|
void add_seed_nodes( std::vector<std::string> seeds );
|
||||||
|
void resolve_seed_node_and_add( const std::string& seed_string );
|
||||||
|
void update_seed_nodes_task();
|
||||||
|
void schedule_next_update_seed_nodes_task();
|
||||||
void initiate_connect_to(const peer_connection_ptr& peer);
|
void initiate_connect_to(const peer_connection_ptr& peer);
|
||||||
void connect_to_endpoint(const fc::ip::endpoint& ep);
|
void connect_to_endpoint(const fc::ip::endpoint& ep);
|
||||||
void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available);
|
void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available);
|
||||||
|
|
@ -4757,7 +4767,69 @@ namespace graphene { namespace net { namespace detail {
|
||||||
_potential_peer_db.update_entry(updated_peer_record);
|
_potential_peer_db.update_entry(updated_peer_record);
|
||||||
trigger_p2p_network_connect_loop();
|
trigger_p2p_network_connect_loop();
|
||||||
}
|
}
|
||||||
|
void node_impl::add_seed_node(const std::string& endpoint_string)
|
||||||
|
{
|
||||||
|
VERIFY_CORRECT_THREAD();
|
||||||
|
_seed_nodes.insert( endpoint_string );
|
||||||
|
resolve_seed_node_and_add( endpoint_string );
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_impl::resolve_seed_node_and_add(const std::string& endpoint_string)
|
||||||
|
{
|
||||||
|
VERIFY_CORRECT_THREAD();
|
||||||
|
std::vector<fc::ip::endpoint> endpoints;
|
||||||
|
ilog("Resolving seed node ${endpoint}", ("endpoint", endpoint_string));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
endpoints = graphene::net::node::resolve_string_to_ip_endpoints(endpoint_string);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
wlog( "Unable to resolve endpoint during attempt to add seed node ${ep}", ("ep", endpoint_string) );
|
||||||
|
}
|
||||||
|
for (const fc::ip::endpoint& endpoint : endpoints)
|
||||||
|
{
|
||||||
|
ilog("Adding seed node ${endpoint}", ("endpoint", endpoint));
|
||||||
|
add_node(endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void node_impl::update_seed_nodes_task()
|
||||||
|
{
|
||||||
|
VERIFY_CORRECT_THREAD();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dlog("Starting an iteration of update_seed_nodes loop.");
|
||||||
|
for( const std::string& endpoint_string : _seed_nodes )
|
||||||
|
{
|
||||||
|
resolve_seed_node_and_add( endpoint_string );
|
||||||
|
}
|
||||||
|
dlog("Done an iteration of update_seed_nodes loop.");
|
||||||
|
}
|
||||||
|
catch (const fc::canceled_exception&)
|
||||||
|
{
|
||||||
|
ilog( "update_seed_nodes_task canceled" );
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_LOG( (_seed_nodes) )
|
||||||
|
|
||||||
|
schedule_next_update_seed_nodes_task();
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_impl::schedule_next_update_seed_nodes_task()
|
||||||
|
{
|
||||||
|
VERIFY_CORRECT_THREAD();
|
||||||
|
|
||||||
|
if( _node_is_shutting_down )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( _update_seed_nodes_loop_done.valid() && _update_seed_nodes_loop_done.canceled() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
_update_seed_nodes_loop_done = fc::schedule( [this]() { update_seed_nodes_task(); },
|
||||||
|
fc::time_point::now() + fc::hours(3),
|
||||||
|
"update_seed_nodes_loop" );
|
||||||
|
}
|
||||||
|
|
||||||
void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer)
|
void node_impl::initiate_connect_to(const peer_connection_ptr& new_peer)
|
||||||
{
|
{
|
||||||
new_peer->get_socket().open();
|
new_peer->get_socket().open();
|
||||||
|
|
@ -5296,6 +5368,11 @@ namespace graphene { namespace net { namespace detail {
|
||||||
INVOKE_IN_IMPL(add_node, ep);
|
INVOKE_IN_IMPL(add_node, ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void node::add_seed_node(const std::string& in)
|
||||||
|
{
|
||||||
|
INVOKE_IN_IMPL(add_seed_node, in);
|
||||||
|
}
|
||||||
|
|
||||||
void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint )
|
void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint )
|
||||||
{
|
{
|
||||||
INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint);
|
INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint);
|
||||||
|
|
@ -5677,5 +5754,45 @@ namespace graphene { namespace net { namespace detail {
|
||||||
#undef INVOKE_AND_COLLECT_STATISTICS
|
#undef INVOKE_AND_COLLECT_STATISTICS
|
||||||
|
|
||||||
} // end namespace detail
|
} // end namespace detail
|
||||||
|
std::vector<fc::ip::endpoint> node::resolve_string_to_ip_endpoints(const std::string& in)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::string::size_type colon_pos = in.find(':');
|
||||||
|
if (colon_pos == std::string::npos)
|
||||||
|
FC_THROW("Missing required port number in endpoint string \"${endpoint_string}\"",
|
||||||
|
("endpoint_string", in));
|
||||||
|
std::string port_string = in.substr(colon_pos + 1);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
uint16_t port = boost::lexical_cast<uint16_t>(port_string);
|
||||||
|
|
||||||
|
std::string hostname = in.substr(0, colon_pos);
|
||||||
|
std::vector<fc::ip::endpoint> endpoints = fc::resolve(hostname, port);
|
||||||
|
if (endpoints.empty())
|
||||||
|
FC_THROW_EXCEPTION( fc::unknown_host_exception,
|
||||||
|
"The host name can not be resolved: ${hostname}",
|
||||||
|
("hostname", hostname) );
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
catch (const boost::bad_lexical_cast&)
|
||||||
|
{
|
||||||
|
FC_THROW("Bad port: ${port}", ("port", port_string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FC_CAPTURE_AND_RETHROW((in))
|
||||||
|
}
|
||||||
|
void node::add_seed_nodes(std::vector<std::string> seeds)
|
||||||
|
{
|
||||||
|
for(const std::string& endpoint_string : seeds )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
add_seed_node(endpoint_string);
|
||||||
|
} catch( const fc::exception& e ) {
|
||||||
|
wlog( "caught exception ${e} while adding seed node ${endpoint}",
|
||||||
|
("e", e.to_detail_string())("endpoint", endpoint_string) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} } // end namespace graphene::net
|
} } // end namespace graphene::net
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,8 @@ namespace graphene { namespace net {
|
||||||
indexed_by<ordered_non_unique<tag<last_seen_time_index>,
|
indexed_by<ordered_non_unique<tag<last_seen_time_index>,
|
||||||
member<potential_peer_record,
|
member<potential_peer_record,
|
||||||
fc::time_point_sec,
|
fc::time_point_sec,
|
||||||
&potential_peer_record::last_seen_time> >,
|
&potential_peer_record::last_seen_time>,
|
||||||
|
std::greater<fc::time_point_sec> >,
|
||||||
hashed_unique<tag<endpoint_index>,
|
hashed_unique<tag<endpoint_index>,
|
||||||
member<potential_peer_record,
|
member<potential_peer_record,
|
||||||
fc::ip::endpoint,
|
fc::ip::endpoint,
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
|
||||||
vector<optional< operation_history_object > >& hist = db.get_applied_operations();
|
vector<optional< operation_history_object > >& hist = db.get_applied_operations();
|
||||||
bool is_first = true;
|
bool is_first = true;
|
||||||
auto skip_oho_id = [&is_first,&db,this]() {
|
auto skip_oho_id = [&is_first,&db,this]() {
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{db._undo_db_mutex};
|
||||||
if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo
|
if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo
|
||||||
{
|
{
|
||||||
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );
|
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,4 @@ install( TARGETS
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION lib
|
||||||
ARCHIVE DESTINATION lib
|
ARCHIVE DESTINATION lib
|
||||||
)
|
)
|
||||||
|
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/debug_witness" )
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ class debug_api_impl
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_api_impl::debug_api_impl( graphene::app::application& _app ) : app( _app )
|
debug_api_impl::debug_api_impl( graphene::app::application& _app ) : app( _app )
|
||||||
{}
|
{
|
||||||
|
// Nothing else to do
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void debug_api_impl::debug_push_blocks( const std::string& src_filename, uint32_t count )
|
void debug_api_impl::debug_push_blocks( const std::string& src_filename, uint32_t count )
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,10 @@ using std::vector;
|
||||||
|
|
||||||
namespace bpo = boost::program_options;
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
debug_witness_plugin::~debug_witness_plugin() {}
|
debug_witness_plugin::~debug_witness_plugin()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
void debug_witness_plugin::plugin_set_program_options(
|
void debug_witness_plugin::plugin_set_program_options(
|
||||||
boost::program_options::options_description& command_line_options,
|
boost::program_options::options_description& command_line_options,
|
||||||
|
|
@ -62,7 +65,7 @@ void debug_witness_plugin::plugin_initialize(const boost::program_options::varia
|
||||||
ilog("debug_witness plugin: plugin_initialize() begin");
|
ilog("debug_witness plugin: plugin_initialize() begin");
|
||||||
_options = &options;
|
_options = &options;
|
||||||
|
|
||||||
if( options.count("debug-private-key") )
|
if( options.count("debug-private-key") > 0 )
|
||||||
{
|
{
|
||||||
const std::vector<std::string> key_id_to_wif_pair_strings = options["debug-private-key"].as<std::vector<std::string>>();
|
const std::vector<std::string> key_id_to_wif_pair_strings = options["debug-private-key"].as<std::vector<std::string>>();
|
||||||
for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
|
for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
|
||||||
|
|
@ -100,7 +103,6 @@ void debug_witness_plugin::plugin_startup()
|
||||||
_changed_objects_conn = db.changed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_changed_objects(ids, impacted_accounts); });
|
_changed_objects_conn = db.changed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_changed_objects(ids, impacted_accounts); });
|
||||||
_removed_objects_conn = db.removed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*>& objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_removed_objects(ids, objs, impacted_accounts); });
|
_removed_objects_conn = db.removed_objects.connect([this](const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*>& objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ on_removed_objects(ids, objs, impacted_accounts); });
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_witness_plugin::on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts )
|
void debug_witness_plugin::on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts )
|
||||||
|
|
@ -155,11 +157,15 @@ void debug_witness_plugin::flush_json_object_stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_witness_plugin::plugin_shutdown()
|
void debug_witness_plugin::plugin_shutdown()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_witness_plugin::cleanup()
|
||||||
{
|
{
|
||||||
if( _json_object_stream )
|
if( _json_object_stream )
|
||||||
{
|
{
|
||||||
_json_object_stream->close();
|
_json_object_stream->close();
|
||||||
_json_object_stream.reset();
|
_json_object_stream.reset();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,23 +34,25 @@ namespace graphene { namespace debug_witness_plugin {
|
||||||
|
|
||||||
class debug_witness_plugin : public graphene::app::plugin {
|
class debug_witness_plugin : public graphene::app::plugin {
|
||||||
public:
|
public:
|
||||||
~debug_witness_plugin();
|
using graphene::app::plugin::plugin;
|
||||||
|
~debug_witness_plugin() override;
|
||||||
|
|
||||||
std::string plugin_name()const override;
|
std::string plugin_name()const override;
|
||||||
|
|
||||||
virtual void plugin_set_program_options(
|
void plugin_set_program_options(
|
||||||
boost::program_options::options_description &command_line_options,
|
boost::program_options::options_description &command_line_options,
|
||||||
boost::program_options::options_description &config_file_options
|
boost::program_options::options_description &config_file_options
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
|
void plugin_initialize( const boost::program_options::variables_map& options ) override;
|
||||||
virtual void plugin_startup() override;
|
void plugin_startup() override;
|
||||||
virtual void plugin_shutdown() override;
|
void plugin_shutdown() override;
|
||||||
|
|
||||||
void set_json_object_stream( const std::string& filename );
|
void set_json_object_stream( const std::string& filename );
|
||||||
void flush_json_object_stream();
|
void flush_json_object_stream();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
void on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );
|
void on_changed_objects( const std::vector<graphene::db::object_id_type>& ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );
|
||||||
void on_removed_objects( const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*> objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );
|
void on_removed_objects( const std::vector<graphene::db::object_id_type>& ids, const std::vector<const graphene::db::object*> objs, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts );
|
||||||
|
|
@ -58,7 +60,7 @@ private:
|
||||||
|
|
||||||
boost::program_options::variables_map _options;
|
boost::program_options::variables_map _options;
|
||||||
|
|
||||||
std::map<chain::public_key_type, fc::ecc::private_key> _private_keys;
|
std::map<chain::public_key_type, fc::ecc::private_key, chain::pubkey_comparator> _private_keys;
|
||||||
|
|
||||||
std::shared_ptr< std::ofstream > _json_object_stream;
|
std::shared_ptr< std::ofstream > _json_object_stream;
|
||||||
boost::signals2::scoped_connection _applied_block_conn;
|
boost::signals2::scoped_connection _applied_block_conn;
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,24 @@ void delayed_node_plugin::plugin_set_program_options(bpo::options_description& c
|
||||||
|
|
||||||
void delayed_node_plugin::connect()
|
void delayed_node_plugin::connect()
|
||||||
{
|
{
|
||||||
my->client_connection = std::make_shared<fc::rpc::websocket_api_connection>(my->client.connect(my->remote_endpoint), GRAPHENE_MAX_NESTED_OBJECTS);
|
fc::http::websocket_connection_ptr con;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
con = my->client.connect(my->remote_endpoint);
|
||||||
|
}
|
||||||
|
catch( const fc::exception& e )
|
||||||
|
{
|
||||||
|
wlog("Error while connecting: ${e}", ("e", e.to_detail_string()));
|
||||||
|
connection_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
my->client_connection = std::make_shared<fc::rpc::websocket_api_connection>(
|
||||||
|
con, GRAPHENE_NET_MAX_NESTED_OBJECTS );
|
||||||
my->database_api = my->client_connection->get_remote_api<graphene::app::database_api>(0);
|
my->database_api = my->client_connection->get_remote_api<graphene::app::database_api>(0);
|
||||||
|
my->database_api->set_block_applied_callback([this]( const fc::variant& block_id )
|
||||||
|
{
|
||||||
|
fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS );
|
||||||
|
} );
|
||||||
my->client_connection_closed = my->client_connection->closed.connect([this] {
|
my->client_connection_closed = my->client_connection->closed.connect([this] {
|
||||||
connection_failed();
|
connection_failed();
|
||||||
});
|
});
|
||||||
|
|
@ -73,7 +89,9 @@ void delayed_node_plugin::connect()
|
||||||
void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||||
{
|
{
|
||||||
FC_ASSERT(options.count("trusted-node") > 0);
|
FC_ASSERT(options.count("trusted-node") > 0);
|
||||||
|
ilog("delayed_node_plugin: plugin_initialize() begin");
|
||||||
my->remote_endpoint = "ws://" + options.at("trusted-node").as<std::string>();
|
my->remote_endpoint = "ws://" + options.at("trusted-node").as<std::string>();
|
||||||
|
ilog("delayed_node_plugin: plugin_initialize() end");
|
||||||
}
|
}
|
||||||
|
|
||||||
void delayed_node_plugin::sync_with_trusted_node()
|
void delayed_node_plugin::sync_with_trusted_node()
|
||||||
|
|
@ -100,8 +118,11 @@ void delayed_node_plugin::sync_with_trusted_node()
|
||||||
while( remote_dpo.last_irreversible_block_num > db.head_block_num() )
|
while( remote_dpo.last_irreversible_block_num > db.head_block_num() )
|
||||||
{
|
{
|
||||||
fc::optional<graphene::chain::signed_block> block = my->database_api->get_block( db.head_block_num()+1 );
|
fc::optional<graphene::chain::signed_block> block = my->database_api->get_block( db.head_block_num()+1 );
|
||||||
|
// TODO: during sync, decouple requesting blocks from preprocessing + applying them
|
||||||
FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have.");
|
FC_ASSERT(block, "Trusted node claims it has blocks it doesn't actually have.");
|
||||||
ilog("Pushing block #${n}", ("n", block->block_num()));
|
ilog("Pushing block #${n}", ("n", block->block_num()));
|
||||||
|
// timur: failed to merge from bitshares, API n/a in peerplays
|
||||||
|
// db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait();
|
||||||
db.push_block(*block);
|
db.push_block(*block);
|
||||||
synced_blocks++;
|
synced_blocks++;
|
||||||
}
|
}
|
||||||
|
|
@ -136,24 +157,12 @@ void delayed_node_plugin::plugin_startup()
|
||||||
mainloop();
|
mainloop();
|
||||||
});
|
});
|
||||||
|
|
||||||
try
|
connect();
|
||||||
{
|
|
||||||
connect();
|
|
||||||
my->database_api->set_block_applied_callback([this]( const fc::variant& block_id )
|
|
||||||
{
|
|
||||||
fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS );
|
|
||||||
} );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (const fc::exception& e)
|
|
||||||
{
|
|
||||||
elog("Error during connection: ${e}", ("e", e.to_detail_string()));
|
|
||||||
}
|
|
||||||
fc::async([this]{connection_failed();});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void delayed_node_plugin::connection_failed()
|
void delayed_node_plugin::connection_failed()
|
||||||
{
|
{
|
||||||
|
my->last_received_remote_head = my->last_processed_remote_head;
|
||||||
elog("Connection to trusted node failed; retrying in 5 seconds...");
|
elog("Connection to trusted node failed; retrying in 5 seconds...");
|
||||||
fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5));
|
fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b
|
||||||
const vector<optional< operation_history_object > >& hist = db.get_applied_operations();
|
const vector<optional< operation_history_object > >& hist = db.get_applied_operations();
|
||||||
bool is_first = true;
|
bool is_first = true;
|
||||||
auto skip_oho_id = [&is_first,&db,this]() {
|
auto skip_oho_id = [&is_first,&db,this]() {
|
||||||
|
const std::lock_guard<std::mutex> undo_db_lock{db._undo_db_mutex};
|
||||||
if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo
|
if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo
|
||||||
{
|
{
|
||||||
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );
|
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ add_library( peerplays_sidechain
|
||||||
sidechain_net_handler_factory.cpp
|
sidechain_net_handler_factory.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
|
||||||
|
|
@ -18,6 +19,11 @@ add_library( peerplays_sidechain
|
||||||
bitcoin/libbitcoin_client.cpp
|
bitcoin/libbitcoin_client.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
|
||||||
|
|
@ -37,7 +43,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 bitcoin-system bitcoin-protocol bitcoin-client bitcoin-explorer )
|
target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin sha3 zmq bitcoin-system bitcoin-protocol bitcoin-client bitcoin-explorer )
|
||||||
target_include_directories( peerplays_sidechain
|
target_include_directories( peerplays_sidechain
|
||||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,50 @@
|
||||||
#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/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
|
||||||
|
|
|
||||||
224
libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp
Normal file
224
libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
#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
|
||||||
102
libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp
Normal file
102
libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
|
||||||
|
|
||||||
|
#include <boost/algorithm/hex.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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
|
||||||
229
libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp
Normal file
229
libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
|
||||||
|
|
||||||
|
#include <boost/algorithm/hex.hpp>
|
||||||
|
#include <boost/algorithm/string.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/decoders.hpp>
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/encoders.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
|
||||||
|
|
||||||
|
base_transaction::base_transaction(const std::string &raw_tx) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//! transaction
|
||||||
|
|
||||||
|
transaction::transaction(const std::string &raw_tx) :
|
||||||
|
base_transaction{raw_tx} {
|
||||||
|
deserialize(raw_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const transaction &transaction::sign(const std::string &private_key) const {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string transaction::serialize() const {
|
||||||
|
boost::property_tree::ptree pt;
|
||||||
|
pt.put("from", from);
|
||||||
|
pt.put("to", to);
|
||||||
|
pt.put("data", data);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
boost::property_tree::json_parser::write_json(ss, pt);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void transaction::deserialize(const std::string &raw_tx) {
|
||||||
|
std::stringstream ss_tx(raw_tx);
|
||||||
|
boost::property_tree::ptree tx_json;
|
||||||
|
boost::property_tree::read_json(ss_tx, tx_json);
|
||||||
|
|
||||||
|
if (tx_json.count("from"))
|
||||||
|
from = tx_json.get<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
|
||||||
|
|
||||||
|
raw_transaction::raw_transaction(const std::string &raw_tx) :
|
||||||
|
base_transaction{raw_tx} {
|
||||||
|
deserialize(raw_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes raw_transaction::hash() const {
|
||||||
|
bytes hash;
|
||||||
|
hash.resize(32);
|
||||||
|
const auto transaction_string = boost::algorithm::unhex(remove_0x(serialize()));
|
||||||
|
keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data());
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
signed_transaction raw_transaction::sign(const std::string &private_key) const {
|
||||||
|
//! Prepare signed transaction
|
||||||
|
signed_transaction tr;
|
||||||
|
tr.nonce = nonce;
|
||||||
|
tr.gas_price = gas_price;
|
||||||
|
tr.gas_limit = gas_limit;
|
||||||
|
tr.to = to;
|
||||||
|
tr.value = value;
|
||||||
|
tr.data = data;
|
||||||
|
|
||||||
|
const bytes priv_key = parse_hex(private_key);
|
||||||
|
|
||||||
|
int recid = 0;
|
||||||
|
secp256k1_ecdsa_recoverable_signature sig;
|
||||||
|
FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash().data(), (const unsigned char *)priv_key.data(), NULL, NULL));
|
||||||
|
fc::ecc::compact_signature result;
|
||||||
|
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig));
|
||||||
|
|
||||||
|
bytes r;
|
||||||
|
for (int i = 1; i < 33; i++)
|
||||||
|
r.emplace_back((char)result.at(i));
|
||||||
|
|
||||||
|
bytes v = bytes{char(recid + from_hex<int>(chain_id) * 2 + 35)};
|
||||||
|
|
||||||
|
bytes s;
|
||||||
|
for (int i = 33; i < 65; i++)
|
||||||
|
s.emplace_back((char)result.at(i));
|
||||||
|
|
||||||
|
tr.r = fc::to_hex((char *)&r[0], r.size());
|
||||||
|
tr.v = fc::to_hex((char *)&v[0], v.size());
|
||||||
|
tr.s = fc::to_hex((char *)&s[0], s.size());
|
||||||
|
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string raw_transaction::serialize() const {
|
||||||
|
const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) +
|
||||||
|
rlp_encoder::encode(remove_0x(gas_price)) +
|
||||||
|
rlp_encoder::encode(remove_0x(gas_limit)) +
|
||||||
|
rlp_encoder::encode(remove_0x(to)) +
|
||||||
|
rlp_encoder::encode(remove_0x(value)) +
|
||||||
|
rlp_encoder::encode(remove_0x(data)) +
|
||||||
|
rlp_encoder::encode(remove_0x(chain_id)) +
|
||||||
|
rlp_encoder::encode("") +
|
||||||
|
rlp_encoder::encode("");
|
||||||
|
|
||||||
|
return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized));
|
||||||
|
}
|
||||||
|
|
||||||
|
void raw_transaction::deserialize(const std::string &raw_tx) {
|
||||||
|
const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx));
|
||||||
|
FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format");
|
||||||
|
|
||||||
|
nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0");
|
||||||
|
boost::algorithm::to_lower(nonce);
|
||||||
|
gas_price = add_0x(rlp_array.at(1));
|
||||||
|
boost::algorithm::to_lower(gas_price);
|
||||||
|
gas_limit = add_0x(rlp_array.at(2));
|
||||||
|
boost::algorithm::to_lower(gas_limit);
|
||||||
|
to = add_0x(rlp_array.at(3));
|
||||||
|
boost::algorithm::to_lower(to);
|
||||||
|
value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0");
|
||||||
|
boost::algorithm::to_lower(value);
|
||||||
|
data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : "";
|
||||||
|
boost::algorithm::to_lower(data);
|
||||||
|
chain_id = add_0x(rlp_array.at(6));
|
||||||
|
boost::algorithm::to_lower(chain_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! signed_transaction
|
||||||
|
|
||||||
|
signed_transaction::signed_transaction(const std::string &raw_tx) :
|
||||||
|
base_transaction{raw_tx} {
|
||||||
|
deserialize(raw_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string signed_transaction::recover(const std::string &chain_id) const {
|
||||||
|
fc::ecc::compact_signature input64;
|
||||||
|
fc::from_hex(r, (char *)&input64.at(1), 32);
|
||||||
|
fc::from_hex(v, (char *)&input64.at(0), 1);
|
||||||
|
int recid = input64.at(0) - from_hex<int>(chain_id) * 2 - 35;
|
||||||
|
fc::from_hex(s, (char *)&input64.at(33), 32);
|
||||||
|
|
||||||
|
secp256k1_ecdsa_recoverable_signature sig;
|
||||||
|
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_parse_compact(eth_context(), &sig, (const unsigned char *)&input64.data[1], recid));
|
||||||
|
|
||||||
|
raw_transaction tr;
|
||||||
|
tr.nonce = nonce;
|
||||||
|
tr.gas_price = gas_price;
|
||||||
|
tr.gas_limit = gas_limit;
|
||||||
|
tr.to = to;
|
||||||
|
tr.value = value;
|
||||||
|
tr.data = data;
|
||||||
|
tr.chain_id = chain_id;
|
||||||
|
|
||||||
|
secp256k1_pubkey rawPubkey;
|
||||||
|
FC_ASSERT(secp256k1_ecdsa_recover(eth_context(), &rawPubkey, &sig, (const unsigned char *)tr.hash().data()));
|
||||||
|
|
||||||
|
std::array<uint8_t, 65> pubkey;
|
||||||
|
size_t biglen = 65;
|
||||||
|
FC_ASSERT(secp256k1_ec_pubkey_serialize(eth_context(), pubkey.data(), &biglen, &rawPubkey, SECP256K1_EC_UNCOMPRESSED));
|
||||||
|
|
||||||
|
const std::string out = std::string(pubkey.begin(), pubkey.end()).substr(1);
|
||||||
|
bytes hash;
|
||||||
|
hash.resize(32);
|
||||||
|
keccak_256((const unsigned char *)out.data(), out.size(), (unsigned char *)hash.data());
|
||||||
|
|
||||||
|
return add_0x(fc::to_hex((char *)&hash[0], hash.size()).substr(24));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string signed_transaction::serialize() const {
|
||||||
|
const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) +
|
||||||
|
rlp_encoder::encode(remove_0x(gas_price)) +
|
||||||
|
rlp_encoder::encode(remove_0x(gas_limit)) +
|
||||||
|
rlp_encoder::encode(remove_0x(to)) +
|
||||||
|
rlp_encoder::encode(remove_0x(value)) +
|
||||||
|
rlp_encoder::encode(remove_0x(data)) +
|
||||||
|
rlp_encoder::encode(remove_0x(v)) +
|
||||||
|
rlp_encoder::encode(remove_0x(r)) +
|
||||||
|
rlp_encoder::encode(remove_0x(s));
|
||||||
|
|
||||||
|
return add_0x(bytes2hex(rlp_encoder::encode_length(serialized.size(), 192) + serialized));
|
||||||
|
}
|
||||||
|
|
||||||
|
void signed_transaction::deserialize(const std::string &raw_tx) {
|
||||||
|
const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx));
|
||||||
|
FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format");
|
||||||
|
|
||||||
|
nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0");
|
||||||
|
boost::algorithm::to_lower(nonce);
|
||||||
|
gas_price = add_0x(rlp_array.at(1));
|
||||||
|
boost::algorithm::to_lower(gas_price);
|
||||||
|
gas_limit = add_0x(rlp_array.at(2));
|
||||||
|
boost::algorithm::to_lower(gas_limit);
|
||||||
|
to = add_0x(rlp_array.at(3));
|
||||||
|
boost::algorithm::to_lower(to);
|
||||||
|
value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0");
|
||||||
|
boost::algorithm::to_lower(value);
|
||||||
|
data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : "";
|
||||||
|
boost::algorithm::to_lower(data);
|
||||||
|
v = add_0x(rlp_array.at(6));
|
||||||
|
boost::algorithm::to_lower(v);
|
||||||
|
r = add_0x(rlp_array.at(7));
|
||||||
|
boost::algorithm::to_lower(r);
|
||||||
|
s = add_0x(rlp_array.at(8));
|
||||||
|
boost::algorithm::to_lower(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||||
5
libraries/plugins/peerplays_sidechain/ethereum/types.cpp
Normal file
5
libraries/plugins/peerplays_sidechain/ethereum/types.cpp
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
|
||||||
|
|
||||||
|
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||||
|
|
||||||
|
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||||
52
libraries/plugins/peerplays_sidechain/ethereum/utils.cpp
Normal file
52
libraries/plugins/peerplays_sidechain/ethereum/utils.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#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
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#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
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/multiprecision/cpp_int.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
|
||||||
|
|
||||||
|
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||||
|
|
||||||
|
class base_transaction {
|
||||||
|
public:
|
||||||
|
base_transaction() = default;
|
||||||
|
base_transaction(const std::string &raw_tx);
|
||||||
|
|
||||||
|
virtual std::string serialize() const = 0;
|
||||||
|
virtual void deserialize(const std::string &raw_tx) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class transaction : base_transaction {
|
||||||
|
public:
|
||||||
|
std::string from;
|
||||||
|
std::string to;
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
transaction() = default;
|
||||||
|
transaction(const std::string &raw_tx);
|
||||||
|
|
||||||
|
const transaction &sign(const std::string &private_key) const;
|
||||||
|
|
||||||
|
virtual std::string serialize() const override;
|
||||||
|
virtual void deserialize(const std::string &raw_tx) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class signed_transaction;
|
||||||
|
class raw_transaction : base_transaction {
|
||||||
|
public:
|
||||||
|
std::string nonce;
|
||||||
|
std::string gas_price;
|
||||||
|
std::string gas_limit;
|
||||||
|
std::string to;
|
||||||
|
std::string value;
|
||||||
|
std::string data;
|
||||||
|
std::string chain_id;
|
||||||
|
|
||||||
|
raw_transaction() = default;
|
||||||
|
raw_transaction(const std::string &raw_tx);
|
||||||
|
|
||||||
|
bytes hash() const;
|
||||||
|
signed_transaction sign(const std::string &private_key) const;
|
||||||
|
|
||||||
|
virtual std::string serialize() const override;
|
||||||
|
virtual void deserialize(const std::string &raw_tx) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class signed_transaction : base_transaction {
|
||||||
|
public:
|
||||||
|
std::string nonce;
|
||||||
|
std::string gas_price;
|
||||||
|
std::string gas_limit;
|
||||||
|
std::string to;
|
||||||
|
std::string value;
|
||||||
|
std::string data;
|
||||||
|
std::string v;
|
||||||
|
std::string r;
|
||||||
|
std::string s;
|
||||||
|
|
||||||
|
signed_transaction() = default;
|
||||||
|
signed_transaction(const std::string &raw_tx);
|
||||||
|
|
||||||
|
std::string recover(const std::string &chain_id) const;
|
||||||
|
|
||||||
|
virtual std::string serialize() const override;
|
||||||
|
virtual void deserialize(const std::string &raw_tx) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}} // namespace graphene::peerplays_sidechain::ethereum
|
||||||
|
|
||||||
|
// Example 1
|
||||||
|
//{
|
||||||
|
// "blockHash": "0x64a6706ecaf5a97b7f3e047abb20ff223ce82c6994d80e68fdb1fdfb38d0209c",
|
||||||
|
// "blockNumber": "0xe5827c",
|
||||||
|
// "from": "0x8614c67e085f2334010f2a28e806c6f1cc176d12",
|
||||||
|
// "gas": "0x38822",
|
||||||
|
// "gasPrice": "0xce42cba69",
|
||||||
|
// "maxFeePerGas": "0xddb4d8d16",
|
||||||
|
// "maxPriorityFeePerGas": "0x3b9aca00",
|
||||||
|
// "hash": "0xeac92ea09fa8eb3ca2fb0d156cceb38ae69d4345869d41e8e49d5ecbcbb622dc",
|
||||||
|
// "input": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000",
|
||||||
|
// "nonce": "0x32",
|
||||||
|
// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45",
|
||||||
|
// "transactionIndex": "0xb6",
|
||||||
|
// "value": "0x2514d9d7d7d8000",
|
||||||
|
// "type": "0x2",
|
||||||
|
// "accessList": [],
|
||||||
|
// "chainId": "0x1",
|
||||||
|
// "v": "0x1",
|
||||||
|
// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c",
|
||||||
|
// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//"0xf9021332850ce42cba69830388229468b3465833fb72a70ecdf485e0e4c7bd8665fc458802514d9d7d7d8000b901a45ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde0312150000000000000000000000000000000000000000000000000000000001a02f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28ca0782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
|
||||||
|
//
|
||||||
|
//{
|
||||||
|
// "nonce": 50,
|
||||||
|
// "gasPrice": {
|
||||||
|
// "_hex": "0x0ce42cba69"
|
||||||
|
// },
|
||||||
|
// "gasLimit": {
|
||||||
|
// "_hex": "0x038822"
|
||||||
|
// },
|
||||||
|
// "to": "0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45",
|
||||||
|
// "value": {
|
||||||
|
// "_hex": "0x02514d9d7d7d8000"
|
||||||
|
// },
|
||||||
|
// "data": "0x5ae401dc0000000000000000000000000000000000000000000000000000000062bb57cf00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e4472b43f300000000000000000000000000000000000000000000000002514d9d7d7d8000000000000000000000000000000000000000000007dced93dd41fd3e1f9e80c200000000000000000000000000000000000000000000000000000000000000800000000000000000000000008614c67e085f2334010f2a28e806c6f1cc176d120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000059c12ed5aaf25adbc6e15f9cc9bab2dde03121500000000000000000000000000000000000000000000000000000000",
|
||||||
|
// "v": 1,
|
||||||
|
// "r": "0x2f8d6a9c737ed98792bafc903b8f1aa54adc731bd3cf9a8b25246a1c9095a28c",
|
||||||
|
// "s": "0x782c40e64b47a221a07612c822c08763f626e53c4b00b73f4c5ba86304c43f14"
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Example 2
|
||||||
|
//{
|
||||||
|
// "blockHash": "0xe2ae3afd86dc7343c7fb753441447a0a51bb19499325ad6e278256f0cd1b5894",
|
||||||
|
// "blockNumber": "0xe58271",
|
||||||
|
// "from": "0xb895ade6d337fbb8cb97f2ea7da43106c7f5cc26",
|
||||||
|
// "gas": "0x5208",
|
||||||
|
// "gasPrice": "0xe6f3b322e",
|
||||||
|
// "maxFeePerGas": "0x1322455fd3",
|
||||||
|
// "maxPriorityFeePerGas": "0x53724e00",
|
||||||
|
// "hash": "0xed29b56e52ad2d452e25b8ec70c37f59d935cd6d0f8fe8e83b256f3ffdfd3fce",
|
||||||
|
// "input": "0x",
|
||||||
|
// "nonce": "0x37",
|
||||||
|
// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373",
|
||||||
|
// "transactionIndex": "0x8a",
|
||||||
|
// "value": "0x4563918244f40000",
|
||||||
|
// "type": "0x2",
|
||||||
|
// "accessList": [],
|
||||||
|
// "chainId": "0x1",
|
||||||
|
// "v": "0x0",
|
||||||
|
// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c",
|
||||||
|
// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//"0xf86c37850e6f3b322e82520894176386b6ffc469ac049f9ec1f6cc0efd1d09b373884563918244f400008000a0dcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4ca028c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
|
||||||
|
//
|
||||||
|
//{
|
||||||
|
// "nonce": 55,
|
||||||
|
// "gasPrice": {
|
||||||
|
// "_hex": "0x0e6f3b322e"
|
||||||
|
// },
|
||||||
|
// "gasLimit": {
|
||||||
|
// "_hex": "0x5208"
|
||||||
|
// },
|
||||||
|
// "to": "0x176386b6ffc469ac049f9ec1f6cc0efd1d09b373",
|
||||||
|
// "value": {
|
||||||
|
// "_hex": "0x4563918244f40000"
|
||||||
|
// },
|
||||||
|
// "data": "0x",
|
||||||
|
// "v": 0,
|
||||||
|
// "r": "0xdcc588257770e08660cb809e71b293f556cd5f5323e832d96ee896ff8830ca4c",
|
||||||
|
// "s": "0x28c7ce6a539d9318688687097a2db29e15c32ba8c085275fdd3dddf047d4bd1a"
|
||||||
|
//}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#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
|
||||||
|
|
@ -102,7 +102,7 @@ private:
|
||||||
std::string ip;
|
std::string ip;
|
||||||
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;
|
||||||
uint32_t bitcoin_major_version;
|
uint32_t bitcoin_major_version;
|
||||||
};
|
};
|
||||||
|
|
@ -192,14 +192,16 @@ private:
|
||||||
uint32_t rpc_port;
|
uint32_t rpc_port;
|
||||||
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_client_base> bitcoin_client;
|
std::unique_ptr<bitcoin_client_base> bitcoin_client;
|
||||||
std::unique_ptr<zmq_listener_base> listener;
|
std::unique_ptr<zmq_listener_base> 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;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
#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 ¶ms);
|
||||||
|
std::string eth_gas_price();
|
||||||
|
|
||||||
|
std::string get_chain_id();
|
||||||
|
std::string get_network_id();
|
||||||
|
std::string get_nonce(const std::string &address);
|
||||||
|
std::string get_gas_price();
|
||||||
|
std::string get_gas_limit();
|
||||||
|
|
||||||
|
std::string eth_send_transaction(const std::string ¶ms);
|
||||||
|
std::string eth_send_raw_transaction(const std::string ¶ms);
|
||||||
|
std::string eth_get_transaction_receipt(const std::string ¶ms);
|
||||||
|
};
|
||||||
|
|
||||||
|
class sidechain_net_handler_ethereum : public sidechain_net_handler {
|
||||||
|
public:
|
||||||
|
sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||||
|
virtual ~sidechain_net_handler_ethereum();
|
||||||
|
|
||||||
|
bool process_proposal(const proposal_object &po);
|
||||||
|
void process_primary_wallet();
|
||||||
|
void process_sidechain_addresses();
|
||||||
|
bool process_deposit(const son_wallet_deposit_object &swdo);
|
||||||
|
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
|
||||||
|
std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||||
|
std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||||
|
bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string rpc_url;
|
||||||
|
std::string rpc_user;
|
||||||
|
std::string rpc_password;
|
||||||
|
std::string wallet_contract_address;
|
||||||
|
|
||||||
|
ethereum_rpc_client *rpc_client;
|
||||||
|
|
||||||
|
ethereum::chain_id_type chain_id;
|
||||||
|
ethereum::network_id_type network_id;
|
||||||
|
|
||||||
|
std::string create_primary_wallet_transaction(const std::vector<son_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
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,9 @@ private:
|
||||||
std::mutex access_db_mutex;
|
std::mutex access_db_mutex;
|
||||||
std::mutex access_approve_prop_mutex;
|
std::mutex access_approve_prop_mutex;
|
||||||
std::mutex access_son_down_prop_mutex;
|
std::mutex access_son_down_prop_mutex;
|
||||||
|
std::mutex access_son_deregister_prop_mutex;
|
||||||
|
|
||||||
|
std::map<sidechain_type, bool> sidechain_enabled;
|
||||||
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
|
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||||
std::set<chain::son_id_type> sons;
|
std::set<chain::son_id_type> sons;
|
||||||
std::map<chain::public_key_type, fc::ecc::private_key> private_keys;
|
std::map<chain::public_key_type, fc::ecc::private_key> private_keys;
|
||||||
|
|
@ -118,6 +120,13 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec
|
||||||
}
|
}
|
||||||
return current_son_id;
|
return current_son_id;
|
||||||
}()),
|
}()),
|
||||||
|
sidechain_enabled([] {
|
||||||
|
std::map<sidechain_type, bool> sidechain_enabled;
|
||||||
|
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||||
|
sidechain_enabled.emplace(active_sidechain_type, false);
|
||||||
|
}
|
||||||
|
return sidechain_enabled;
|
||||||
|
}()),
|
||||||
net_handlers([] {
|
net_handlers([] {
|
||||||
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
|
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||||
|
|
@ -173,15 +182,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)");
|
||||||
|
|
||||||
|
|
@ -232,45 +250,39 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
|
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
|
||||||
sidechain_enabled_peerplays = true; //options.at("peerplays-sidechain-enabled").as<bool>();
|
sidechain_enabled_peerplays = true;
|
||||||
#else
|
#else
|
||||||
sidechain_enabled_peerplays = false;
|
sidechain_enabled_peerplays = false;
|
||||||
#endif
|
#endif
|
||||||
config_ready_peerplays = true;
|
config_ready_peerplays = true;
|
||||||
if (!config_ready_peerplays) {
|
if (sidechain_enabled_peerplays && !config_ready_peerplays) {
|
||||||
wlog("Haven't set up Peerplays sidechain parameters");
|
wlog("Haven't set up Peerplays sidechain parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(config_ready_bitcoin &&
|
|
||||||
/*config_ready_ethereum &&*/
|
|
||||||
config_ready_hive &&
|
|
||||||
config_ready_peerplays)) {
|
|
||||||
wlog("Haven't set up any sidechain parameters");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::plugin_startup() {
|
void peerplays_sidechain_plugin_impl::plugin_startup() {
|
||||||
|
|
@ -286,21 +298,25 @@ void peerplays_sidechain_plugin_impl::plugin_startup() {
|
||||||
sidechain_net_handler_factory net_handler_factory(plugin);
|
sidechain_net_handler_factory net_handler_factory(plugin);
|
||||||
|
|
||||||
if (sidechain_enabled_bitcoin && config_ready_bitcoin) {
|
if (sidechain_enabled_bitcoin && config_ready_bitcoin) {
|
||||||
|
sidechain_enabled.at(sidechain_type::bitcoin) = true;
|
||||||
net_handlers.at(sidechain_type::bitcoin) = net_handler_factory.create_handler(sidechain_type::bitcoin, options);
|
net_handlers.at(sidechain_type::bitcoin) = net_handler_factory.create_handler(sidechain_type::bitcoin, options);
|
||||||
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);
|
sidechain_enabled.at(sidechain_type::ethereum) = true;
|
||||||
// ilog("Ethereum sidechain handler running");
|
net_handlers.at(sidechain_type::ethereum) = net_handler_factory.create_handler(sidechain_type::ethereum, options);
|
||||||
//}
|
ilog("Ethereum sidechain handler running");
|
||||||
|
}
|
||||||
|
|
||||||
if (sidechain_enabled_hive && config_ready_hive) {
|
if (sidechain_enabled_hive && config_ready_hive) {
|
||||||
|
sidechain_enabled.at(sidechain_type::hive) = true;
|
||||||
net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options);
|
net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options);
|
||||||
ilog("Hive sidechain handler running");
|
ilog("Hive sidechain handler running");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sidechain_enabled_peerplays && config_ready_peerplays) {
|
if (sidechain_enabled_peerplays && config_ready_peerplays) {
|
||||||
|
sidechain_enabled.at(sidechain_type::peerplays) = true;
|
||||||
net_handlers.at(sidechain_type::peerplays) = net_handler_factory.create_handler(sidechain_type::peerplays, options);
|
net_handlers.at(sidechain_type::peerplays) = net_handler_factory.create_handler(sidechain_type::peerplays, options);
|
||||||
ilog("Peerplays sidechain handler running");
|
ilog("Peerplays sidechain handler running");
|
||||||
}
|
}
|
||||||
|
|
@ -449,8 +465,10 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
|
||||||
//! Check that son is active (at least for one sidechain_type)
|
//! Check that son is active (at least for one sidechain_type)
|
||||||
bool is_son_active = false;
|
bool is_son_active = false;
|
||||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||||
if (is_active_son(active_sidechain_type, son_id))
|
if (sidechain_enabled.at(active_sidechain_type)) {
|
||||||
is_son_active = true;
|
if (is_active_son(active_sidechain_type, son_id))
|
||||||
|
is_son_active = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_son_active || status_in_maintenance) {
|
if (is_son_active || status_in_maintenance) {
|
||||||
|
|
@ -485,9 +503,16 @@ void peerplays_sidechain_plugin_impl::schedule_son_processing() {
|
||||||
const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing);
|
const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing);
|
||||||
|
|
||||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||||
|
if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
|
||||||
|
wlog("Son doesn't process in time for sidechain: ${active_sidechain_type}", ("active_sidechain_type", active_sidechain_type));
|
||||||
|
_son_processing_task.at(active_sidechain_type).wait();
|
||||||
|
}
|
||||||
|
|
||||||
_son_processing_task[active_sidechain_type] = std::async(std::launch::async, [this, next_wakeup, active_sidechain_type] {
|
_son_processing_task[active_sidechain_type] = std::async(std::launch::async, [this, next_wakeup, active_sidechain_type] {
|
||||||
std::this_thread::sleep_until(next_wakeup);
|
if (sidechain_enabled.at(active_sidechain_type)) {
|
||||||
son_processing(active_sidechain_type);
|
std::this_thread::sleep_until(next_wakeup);
|
||||||
|
son_processing(active_sidechain_type);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -595,7 +620,9 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidecha
|
||||||
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() {
|
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() {
|
||||||
std::map<sidechain_type, std::vector<std::string>> result;
|
std::map<sidechain_type, std::vector<std::string>> result;
|
||||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||||
result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log());
|
if (net_handlers.at(active_sidechain_type)) {
|
||||||
|
result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -606,7 +633,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain
|
||||||
// into problem of approving the same propsal since it might happens that previous
|
// into problem of approving the same propsal since it might happens that previous
|
||||||
// approved proposal didn't have time or chance to populate the list of available
|
// approved proposal didn't have time or chance to populate the list of available
|
||||||
// active proposals which is consulted here in the code.
|
// active proposals which is consulted here in the code.
|
||||||
std::lock_guard<std::mutex> lck(access_approve_prop_mutex);
|
const std::lock_guard<std::mutex> lck{access_approve_prop_mutex};
|
||||||
auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) {
|
auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) {
|
||||||
if (!is_valid_son_proposal(proposal)) {
|
if (!is_valid_son_proposal(proposal)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -656,7 +683,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type sidechain) {
|
||||||
std::lock_guard<std::mutex> lck(access_son_down_prop_mutex);
|
const std::lock_guard<std::mutex> lck{access_son_down_prop_mutex};
|
||||||
auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) {
|
auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) {
|
||||||
chain::database &d = plugin.database();
|
chain::database &d = plugin.database();
|
||||||
const chain::global_property_object &gpo = d.get_global_properties();
|
const chain::global_property_object &gpo = d.get_global_properties();
|
||||||
|
|
@ -720,6 +747,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type s
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) {
|
||||||
|
const std::lock_guard<std::mutex> lck{access_son_down_prop_mutex};
|
||||||
chain::database &d = plugin.database();
|
chain::database &d = plugin.database();
|
||||||
std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered();
|
std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered();
|
||||||
chain::son_id_type my_son_id = get_current_son_id(sidechain);
|
chain::son_id_type my_son_id = get_current_son_id(sidechain);
|
||||||
|
|
@ -758,35 +786,51 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->process_proposals();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->process_proposals();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->process_active_sons_change();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->process_active_sons_change();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->create_deposit_addresses();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->create_deposit_addresses();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->process_deposits();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->process_deposits();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->process_withdrawals();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->process_withdrawals();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->process_sidechain_transactions();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->process_sidechain_transactions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->send_sidechain_transactions();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->send_sidechain_transactions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) {
|
void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) {
|
||||||
net_handlers.at(sidechain)->settle_sidechain_transactions();
|
if (net_handlers.at(sidechain)) {
|
||||||
|
net_handlers.at(sidechain)->settle_sidechain_transactions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
|
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
|
||||||
|
|
|
||||||
|
|
@ -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) && //! Fixme -> sidechain_type::peerplays
|
bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays
|
||||||
((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) ||
|
((sed.sidechain_currency == object_id_to_string(gpo.parameters.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()));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -610,9 +610,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>();
|
||||||
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")) {
|
||||||
|
|
@ -631,16 +631,15 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string url = ip;
|
std::string url = ip + ":" + std::to_string(rpc_port);
|
||||||
|
|
||||||
if (use_bitcoind_client) {
|
if (use_bitcoind_client) {
|
||||||
url = ip + ":" + std::to_string(rpc_port);
|
if (!wallet_name.empty()) {
|
||||||
if (wallet.length() > 0) {
|
url = url + "/wallet/" + wallet_name;
|
||||||
url = url + "/wallet/" + wallet;
|
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
listener = std::unique_ptr<zmq_listener>(new zmq_listener(ip, zmq_port));
|
listener = std::unique_ptr<zmq_listener>(new zmq_listener(ip, zmq_port));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,761 @@
|
||||||
|
#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>
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
|
||||||
|
|
||||||
|
#define SEND_RAW_TRANSACTION 1
|
||||||
|
|
||||||
|
namespace graphene { namespace peerplays_sidechain {
|
||||||
|
|
||||||
|
ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) :
|
||||||
|
rpc_client(url, user_name, password, debug_rpc_calls) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::admin_node_info() {
|
||||||
|
return send_post_request("admin_nodeInfo", "", debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::eth_get_block_by_number(std::string block_number, bool full_block) {
|
||||||
|
std::string params = "[ \"" + block_number + "\", " + (full_block ? "true" : "false") + "]";
|
||||||
|
return send_post_request("eth_getBlockByNumber", params, debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::eth_get_logs(std::string wallet_contract_address) {
|
||||||
|
std::string params = "[{\"address\": \"" + wallet_contract_address + "\"}]";
|
||||||
|
std::string reply_str = send_post_request("eth_getLogs", params, debug_rpc_calls);
|
||||||
|
return retrieve_value_from_reply(reply_str, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::net_version() {
|
||||||
|
return send_post_request("net_version", "", debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::eth_get_transaction_count(const std::string ¶ms) {
|
||||||
|
return send_post_request("eth_getTransactionCount", params, debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::eth_gas_price() {
|
||||||
|
return send_post_request("eth_gasPrice", "", debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::get_chain_id() {
|
||||||
|
std::string reply_str = net_version();
|
||||||
|
return retrieve_value_from_reply(reply_str, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::get_network_id() {
|
||||||
|
std::string reply_str = admin_node_info();
|
||||||
|
return retrieve_value_from_reply(reply_str, "protocols.eth.network");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::get_nonce(const std::string &address) {
|
||||||
|
std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]");
|
||||||
|
const auto nonce_val = ethereum::from_hex<boost::multiprecision::uint256_t>(retrieve_value_from_reply(reply_str, ""));
|
||||||
|
return nonce_val == 0 ? ethereum::add_0x("0") : ethereum::add_0x(ethereum::to_hex(nonce_val));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::get_gas_price() {
|
||||||
|
std::string reply_str = eth_gas_price();
|
||||||
|
return retrieve_value_from_reply(reply_str, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::get_gas_limit() {
|
||||||
|
std::string reply_str = eth_get_block_by_number("latest", false);
|
||||||
|
if (!reply_str.empty()) {
|
||||||
|
std::stringstream ss(reply_str);
|
||||||
|
boost::property_tree::ptree json;
|
||||||
|
boost::property_tree::read_json(ss, json);
|
||||||
|
if (json.count("result")) {
|
||||||
|
std::string gas_limit_s = json.get<std::string>("result.gasLimit");
|
||||||
|
return gas_limit_s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::eth_send_transaction(const std::string ¶ms) {
|
||||||
|
return send_post_request("eth_sendTransaction", "[" + params + "]", debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::eth_send_raw_transaction(const std::string ¶ms) {
|
||||||
|
return send_post_request("eth_sendRawTransaction", "[ \"" + params + "\" ]", debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ethereum_rpc_client::eth_get_transaction_receipt(const std::string ¶ms) {
|
||||||
|
return send_post_request("eth_getTransactionReceipt", "[\"" + params + "\"]", debug_rpc_calls);
|
||||||
|
}
|
||||||
|
|
||||||
|
sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||||
|
sidechain_net_handler(_plugin, options) {
|
||||||
|
sidechain = sidechain_type::ethereum;
|
||||||
|
|
||||||
|
if (options.count("debug-rpc-calls")) {
|
||||||
|
debug_rpc_calls = options.at("debug-rpc-calls").as<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(sidechain)));
|
||||||
|
|
||||||
|
bool should_approve = false;
|
||||||
|
|
||||||
|
const chain::global_property_object &gpo = database.get_global_properties();
|
||||||
|
|
||||||
|
int32_t op_idx_0 = -1;
|
||||||
|
chain::operation op_obj_idx_0;
|
||||||
|
|
||||||
|
if (po.proposed_transaction.operations.size() >= 1) {
|
||||||
|
op_idx_0 = po.proposed_transaction.operations[0].which();
|
||||||
|
op_obj_idx_0 = po.proposed_transaction.operations[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t op_idx_1 = -1;
|
||||||
|
chain::operation op_obj_idx_1;
|
||||||
|
(void)op_idx_1;
|
||||||
|
|
||||||
|
if (po.proposed_transaction.operations.size() >= 2) {
|
||||||
|
op_idx_1 = po.proposed_transaction.operations[1].which();
|
||||||
|
op_obj_idx_1 = po.proposed_transaction.operations[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (op_idx_0) {
|
||||||
|
|
||||||
|
case chain::operation::tag<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.at(sidechain);
|
||||||
|
vector<son_info> wallet_sons = swo->sons.at(sidechain);
|
||||||
|
|
||||||
|
bool son_sets_equal = (active_sons.size() == wallet_sons.size());
|
||||||
|
|
||||||
|
if (son_sets_equal) {
|
||||||
|
for (size_t i = 0; i < active_sons.size(); i++) {
|
||||||
|
son_sets_equal = son_sets_equal && active_sons.at(i) == wallet_sons.at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (son_sets_equal) {
|
||||||
|
address_ok = (op_obj_idx_0.get<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.at(sidechain), object_id.operator std::string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction_ok = (op_tx_str == tx_str);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transaction_ok = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
should_approve = address_ok &&
|
||||||
|
transaction_ok;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case chain::operation::tag<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) == active_sw->addresses.end()) ||
|
||||||
|
(active_sw->addresses.at(sidechain).empty())) {
|
||||||
|
|
||||||
|
if (proposal_exists(chain::operation::tag<chain::son_wallet_update_operation>::value, active_sw->id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!plugin.can_son_participate(sidechain, 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(sidechain).son_account;
|
||||||
|
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||||
|
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||||
|
|
||||||
|
son_wallet_update_operation swu_op;
|
||||||
|
swu_op.payer = gpo.parameters.son_account();
|
||||||
|
swu_op.son_wallet_id = active_sw->id;
|
||||||
|
swu_op.sidechain = sidechain;
|
||||||
|
swu_op.address = wallet_contract_address;
|
||||||
|
proposal_op.proposed_ops.emplace_back(swu_op);
|
||||||
|
|
||||||
|
std::string tx_str = create_primary_wallet_transaction(gpo.active_sons.at(sidechain), active_sw->id.operator std::string());
|
||||||
|
if (!tx_str.empty()) {
|
||||||
|
sidechain_transaction_create_operation stc_op;
|
||||||
|
stc_op.payer = gpo.parameters.son_account();
|
||||||
|
stc_op.object_id = active_sw->id;
|
||||||
|
stc_op.sidechain = sidechain;
|
||||||
|
stc_op.transaction = tx_str;
|
||||||
|
stc_op.signers = gpo.active_sons.at(sidechain);
|
||||||
|
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||||
|
}
|
||||||
|
|
||||||
|
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
|
||||||
|
try {
|
||||||
|
trx.validate();
|
||||||
|
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||||
|
if (plugin.app().p2p_node())
|
||||||
|
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||||
|
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
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(sidechain).son_account;
|
||||||
|
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||||
|
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||||
|
|
||||||
|
son_wallet_deposit_process_operation swdp_op;
|
||||||
|
swdp_op.payer = gpo.parameters.son_account();
|
||||||
|
swdp_op.son_wallet_deposit_id = swdo.id;
|
||||||
|
proposal_op.proposed_ops.emplace_back(swdp_op);
|
||||||
|
|
||||||
|
asset_issue_operation ai_op;
|
||||||
|
ai_op.fee = database.current_fee_schedule().calculate_fee(ai_op);
|
||||||
|
ai_op.issuer = gpo.parameters.son_account();
|
||||||
|
ai_op.asset_to_issue = asset_to_issue;
|
||||||
|
ai_op.issue_to_account = swdo.peerplays_from;
|
||||||
|
proposal_op.proposed_ops.emplace_back(ai_op);
|
||||||
|
|
||||||
|
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
|
||||||
|
try {
|
||||||
|
trx.validate();
|
||||||
|
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||||
|
if (plugin.app().p2p_node())
|
||||||
|
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||||
|
return true;
|
||||||
|
} catch (fc::exception &e) {
|
||||||
|
elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sidechain_net_handler_ethereum::process_withdrawal(const son_wallet_withdraw_object &swwo) {
|
||||||
|
|
||||||
|
if (proposal_exists(chain::operation::tag<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(sidechain).son_account;
|
||||||
|
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||||
|
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||||
|
|
||||||
|
son_wallet_withdraw_process_operation swwp_op;
|
||||||
|
swwp_op.payer = gpo.parameters.son_account();
|
||||||
|
swwp_op.son_wallet_withdraw_id = swwo.id;
|
||||||
|
proposal_op.proposed_ops.emplace_back(swwp_op);
|
||||||
|
|
||||||
|
sidechain_transaction_create_operation stc_op;
|
||||||
|
stc_op.payer = gpo.parameters.son_account();
|
||||||
|
stc_op.object_id = swwo.id;
|
||||||
|
stc_op.sidechain = sidechain;
|
||||||
|
stc_op.transaction = tx_str;
|
||||||
|
stc_op.signers = gpo.active_sons.at(sidechain);
|
||||||
|
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||||
|
|
||||||
|
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op);
|
||||||
|
try {
|
||||||
|
trx.validate();
|
||||||
|
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||||
|
if (plugin.app().p2p_node())
|
||||||
|
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||||
|
return true;
|
||||||
|
} catch (fc::exception &e) {
|
||||||
|
elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sidechain_net_handler_ethereum::process_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||||
|
return sign_transaction(sto);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||||
|
boost::property_tree::ptree pt;
|
||||||
|
boost::property_tree::ptree pt_array;
|
||||||
|
for (const auto &signature : sto.signatures) {
|
||||||
|
const auto &transaction = signature.second;
|
||||||
|
|
||||||
|
//! Check if we have this signed transaction, if not, don't send it
|
||||||
|
if (transaction.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#ifdef SEND_RAW_TRANSACTION
|
||||||
|
const std::string sidechain_transaction = rpc_client->eth_send_raw_transaction(transaction);
|
||||||
|
#else
|
||||||
|
const std::string sidechain_transaction = rpc_client->eth_send_transaction(transaction);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::stringstream ss_tx(sidechain_transaction);
|
||||||
|
boost::property_tree::ptree tx_json;
|
||||||
|
boost::property_tree::read_json(ss_tx, tx_json);
|
||||||
|
if (tx_json.count("result") && !tx_json.count("error")) {
|
||||||
|
boost::property_tree::ptree node;
|
||||||
|
node.put("transaction", transaction);
|
||||||
|
node.put("transaction_receipt", tx_json.get<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));
|
||||||
|
return std::string{}; //! Return empty string, as we have error in sending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pt.add_child("result_array", pt_array);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
boost::property_tree::json_parser::write_json(ss, pt);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount) {
|
||||||
|
std::stringstream ss(sto.sidechain_transaction);
|
||||||
|
boost::property_tree::ptree json;
|
||||||
|
boost::property_tree::read_json(ss, json);
|
||||||
|
|
||||||
|
if (!json.count("result_array")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
for (const auto &entry : json.get_child("result_array")) {
|
||||||
|
const std::string receipt = rpc_client->eth_get_transaction_receipt(entry.second.get<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) {
|
||||||
|
const std::string pub_key_str = son.public_key;
|
||||||
|
owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ethereum::update_owners_encoder encoder;
|
||||||
|
return encoder.encode(owners_weights, object_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) {
|
||||||
|
return "Deposit-Transaction";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) {
|
||||||
|
const ethereum::withdrawal_encoder encoder;
|
||||||
|
return encoder.encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) {
|
||||||
|
const auto ¤t_son = plugin.get_current_son_object(sidechain);
|
||||||
|
FC_ASSERT(current_son.sidechain_public_keys.contains(sidechain), "No public keys for current son: ${account_id}", ("account_id", current_son.son_account));
|
||||||
|
|
||||||
|
const auto &public_key = current_son.sidechain_public_keys.at(sidechain);
|
||||||
|
|
||||||
|
#ifdef SEND_RAW_TRANSACTION
|
||||||
|
ethereum::raw_transaction raw_tr;
|
||||||
|
raw_tr.nonce = rpc_client->get_nonce(ethereum::add_0x(public_key));
|
||||||
|
raw_tr.gas_price = rpc_client->get_gas_price();
|
||||||
|
raw_tr.gas_limit = rpc_client->get_gas_limit();
|
||||||
|
raw_tr.to = wallet_contract_address;
|
||||||
|
raw_tr.value = "";
|
||||||
|
raw_tr.data = sto.transaction;
|
||||||
|
raw_tr.chain_id = ethereum::add_0x(ethereum::to_hex(chain_id));
|
||||||
|
|
||||||
|
const auto sign_tr = raw_tr.sign(get_private_key(public_key));
|
||||||
|
return sign_tr.serialize();
|
||||||
|
#else
|
||||||
|
ethereum::transaction sign_transaction;
|
||||||
|
sign_transaction.data = sto.transaction;
|
||||||
|
sign_transaction.to = wallet_contract_address;
|
||||||
|
sign_transaction.from = "0x" + public_key;
|
||||||
|
return sign_transaction.sign(get_private_key(public_key)).serialize();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void sidechain_net_handler_ethereum::schedule_ethereum_listener() {
|
||||||
|
fc::time_point now = fc::time_point::now();
|
||||||
|
int64_t time_to_next = 5000;
|
||||||
|
|
||||||
|
fc::time_point next_wakeup(now + fc::milliseconds(time_to_next));
|
||||||
|
|
||||||
|
_listener_task = fc::schedule([this] {
|
||||||
|
ethereum_listener_loop();
|
||||||
|
},
|
||||||
|
next_wakeup, "SON Ethereum listener task");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sidechain_net_handler_ethereum::ethereum_listener_loop() {
|
||||||
|
schedule_ethereum_listener();
|
||||||
|
|
||||||
|
std::string reply = rpc_client->eth_get_block_by_number("latest", false);
|
||||||
|
//std::string reply = rpc_client->eth_get_logs(wallet_contract_address);
|
||||||
|
if (!reply.empty()) {
|
||||||
|
std::stringstream ss(reply);
|
||||||
|
boost::property_tree::ptree json;
|
||||||
|
boost::property_tree::read_json(ss, json);
|
||||||
|
if (json.count("result")) {
|
||||||
|
std::string head_block_number_s = json.get<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
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp>
|
#include <graphene/peerplays_sidechain/sidechain_net_handler_factory.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>
|
||||||
|
|
||||||
|
|
@ -18,6 +19,9 @@ std::unique_ptr<sidechain_net_handler> sidechain_net_handler_factory::create_han
|
||||||
case sidechain_type::hive: {
|
case sidechain_type::hive: {
|
||||||
return std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_hive(plugin, options));
|
return std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_hive(plugin, options));
|
||||||
}
|
}
|
||||||
|
case sidechain_type::ethereum: {
|
||||||
|
return std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_ethereum(plugin, options));
|
||||||
|
}
|
||||||
case sidechain_type::peerplays: {
|
case sidechain_type::peerplays: {
|
||||||
return std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_peerplays(plugin, options));
|
return std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_peerplays(plugin, options));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.public_key] = wallet_son.weight;
|
account_auths[wallet_son.public_key] = wallet_son.weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string memo_key = node_rpc_client->get_account_memo_key("son-account");
|
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.public_key] = active_son.weight;
|
account_auths[active_son.public_key] = active_son.weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string memo_key = node_rpc_client->get_account_memo_key("son-account");
|
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).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).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");
|
||||||
|
|
||||||
|
|
|
||||||
16
libraries/sha3/CMakeLists.txt
Normal file
16
libraries/sha3/CMakeLists.txt
Normal 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
|
||||||
|
)
|
||||||
16
libraries/sha3/include/sha3/memzero.h
Normal file
16
libraries/sha3/include/sha3/memzero.h
Normal 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
|
||||||
88
libraries/sha3/include/sha3/sha3.h
Normal file
88
libraries/sha3/include/sha3/sha3.h
Normal 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
75
libraries/sha3/memzero.c
Normal 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
397
libraries/sha3/sha3.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -44,13 +44,37 @@ for my $class (@{$doxydocs->{classes}})
|
||||||
if ($member->{kind} eq 'function')
|
if ($member->{kind} eq 'function')
|
||||||
{
|
{
|
||||||
my @params = map { join(' ', cleanupDoxygenType($_->{type}), $_->{declaration_name}) } @{$member->{parameters}};
|
my @params = map { join(' ', cleanupDoxygenType($_->{type}), $_->{declaration_name}) } @{$member->{parameters}};
|
||||||
my $briefDescription = sprintf("%40s %s(%s)\n", cleanupDoxygenType($member->{type}), $member->{name}, join(', ', @params));
|
my $callDescription = sprintf("%40s %s(%s)\n", cleanupDoxygenType($member->{type}), $member->{name}, join(', ', @params));
|
||||||
my $escapedBriefDescription = "\"" . escapeStringForC($briefDescription) . "\"";
|
my $escapedBriefDescription = "\"" . escapeStringForC($callDescription) . "\"";
|
||||||
my %paramInfo = map { $_->{declaration_name} => { type => $_->{type}} } @{$member->{parameters}};
|
my %paramInfo = map { $_->{declaration_name} => { type => explainCType(cleanupDoxygenType($_->{type})) } } @{$member->{parameters}};
|
||||||
my $escapedDetailedDescription = "\"\"\n";
|
my $escapedDetailedDescription = "\"\"\n";
|
||||||
if ($member->{detailed}->{doc})
|
my $doc = $member->{detailed}->{doc};
|
||||||
|
if ($doc)
|
||||||
{
|
{
|
||||||
my $docString = formatDocComment($member->{detailed}->{doc}, \%paramInfo);
|
my $briefDescr = formatDocComment($member->{brief}->{doc}); # get from the proper place
|
||||||
|
unless ($briefDescr =~ /\w/) # if not provided (API author forgot to add '@brief' comment),
|
||||||
|
{
|
||||||
|
for (my $i = 0; $i < @{$doc}; ++$i) # then look inside 'detailed' section
|
||||||
|
{
|
||||||
|
my $docElement = $doc->[$i];
|
||||||
|
if ($docElement->{type} eq 'text' and $docElement->{content} =~ /\w+/) # use first meaningful line as brief description
|
||||||
|
{
|
||||||
|
$briefDescr = $docElement->{content};
|
||||||
|
$briefDescr =~ s/^\s+|\s+$//g;
|
||||||
|
splice @{$doc}, $i, 1; # this section shouldn't be used twice
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $cmdSyntax = $member->{name};
|
||||||
|
my $cmdArgs = join(' ', map { $_->{declaration_name} } @{$member->{parameters}});
|
||||||
|
$cmdSyntax .= " $cmdArgs" if $cmdArgs;
|
||||||
|
|
||||||
|
my $docString;
|
||||||
|
$docString .= $briefDescr;
|
||||||
|
$docString .= "\n\n" . formatDocComment($doc, \%paramInfo, $cmdSyntax);
|
||||||
|
|
||||||
for my $line (split(/\n/, $docString))
|
for my $line (split(/\n/, $docString))
|
||||||
{
|
{
|
||||||
$escapedDetailedDescription .= " \"" . escapeStringForC($line . "\n") . "\"\n";
|
$escapedDetailedDescription .= " \"" . escapeStringForC($line . "\n") . "\"\n";
|
||||||
|
|
@ -96,62 +120,86 @@ sub cleanupDoxygenType
|
||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub explainCType
|
||||||
|
{
|
||||||
|
my($type) = @_;
|
||||||
|
$type =~ s/\b\w+:://g; # remove namespaces
|
||||||
|
$type =~ s/^(?:optional|api)<(.+)>$/$1/; # disregard optional<> and some other templates
|
||||||
|
$type =~ s/^const\s+(.+)/$1/; # strip const modifier
|
||||||
|
$type =~ s/^(.+)&/$1/; # strip references
|
||||||
|
$type =~ s/\s+$/$1/;
|
||||||
|
$type =~ s/\b(u?int(8|16|32|64)_t|int|unsigned)\b/integer/; # spare the user from width and signedness
|
||||||
|
$type =~ s/\bbool\b/boolean/; # they're not C++ people
|
||||||
|
$type =~ s/^(?:vector|set|flat_set)<(.+)>$/[$1, ...]/; # represent as JSon-like array notation
|
||||||
|
$type =~ s/^map<(.+)\s*,\s*(.+)>$/{$1: $2, ...}/; # same for map
|
||||||
|
$type =~ s/^flat_map<(.+)\s*,\s*(.+)>$/[[$1, $2], ...]/; # same for flat_map
|
||||||
|
$type =~ s/^time_point_sec$/time, e.g. 2021-12-25T14:30:05/;
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
sub formatDocComment
|
sub formatDocComment
|
||||||
{
|
{
|
||||||
my($doc, $paramInfo) = @_;
|
my($doc, $paramInfo, $cmdSyntax) = @_;
|
||||||
|
|
||||||
my $bodyDocs = '';
|
my $bodyDocs = '';
|
||||||
|
my $notes = '';
|
||||||
|
my $see = '';
|
||||||
my $paramDocs = '';
|
my $paramDocs = '';
|
||||||
my $returnDocs = '';
|
my $returnDocs = '';
|
||||||
|
|
||||||
for (my $i = 0; $i < @{$doc}; ++$i)
|
for (my $i = 0; $i < @{$doc}; ++$i)
|
||||||
{
|
{
|
||||||
if ($doc->[$i] eq 'params')
|
my $docElement = $doc->[$i];
|
||||||
|
|
||||||
|
if ($docElement->{params})
|
||||||
{
|
{
|
||||||
$paramDocs .= "Parameters:\n";
|
$paramDocs .= "Parameters:\n";
|
||||||
@parametersList = @{$doc->[$i + 1]};
|
for my $parameter (@{$docElement->{params}})
|
||||||
for my $parameter (@parametersList)
|
|
||||||
{
|
{
|
||||||
my $declname = $parameter->{parameters}->[0]->{name};
|
my $declname = $parameter->{parameters}->[0]->{name};
|
||||||
my $decltype = cleanupDoxygenType($paramInfo->{$declname}->{type});
|
my $decltype = cleanupDoxygenType($paramInfo->{$declname}->{type});
|
||||||
$paramDocs .= Text::Wrap::fill(' ', ' ', "$declname: " . formatDocComment($parameter->{doc}) . " (type: $decltype)") . "\n";
|
$paramDocs .= Text::Wrap::fill(' ', ' ', "$declname ($decltype): " . formatDocComment($parameter->{doc})) . "\n";
|
||||||
}
|
}
|
||||||
++$i;
|
|
||||||
}
|
}
|
||||||
elsif ($doc->[$i]->{return})
|
elsif ($docElement->{return})
|
||||||
{
|
{
|
||||||
$returnDocs .= "Returns\n";
|
$returnDocs .= "Returns:\n";
|
||||||
$returnDocs .= Text::Wrap::fill(' ',' ', formatDocComment($doc->[$i]->{return})) . "\n";
|
$returnDocs .= Text::Wrap::fill(' ',' ', formatDocComment($docElement->{return})) . "\n";
|
||||||
}
|
}
|
||||||
else
|
elsif ($docElement->{note})
|
||||||
{
|
{
|
||||||
my $docElement = $doc->[$i];
|
$notes .= Text::Wrap::fill(' ',' ', "Note: ".formatDocComment($docElement->{note})) . "\n";
|
||||||
if ($docElement->{type} eq 'text' or $docElement->{type} eq 'url')
|
}
|
||||||
{
|
elsif ($docElement->{see})
|
||||||
$bodyDocs .= $docElement->{content};
|
{
|
||||||
}
|
$see .= Text::Wrap::fill(' ',' ', "See: ".formatDocComment($docElement->{see})) . "\n";
|
||||||
elsif ($docElement->{type} eq 'parbreak')
|
}
|
||||||
{
|
elsif ($docElement->{type} eq 'text' or $docElement->{type} eq 'url')
|
||||||
$bodyDocs .= "\n\n";
|
{
|
||||||
}
|
$bodyDocs .= $docElement->{content};
|
||||||
elsif ($docElement->{type} eq 'style' and $docElement->{style} eq 'code')
|
}
|
||||||
{
|
elsif ($docElement->{type} eq 'parbreak')
|
||||||
$bodyDocs .= "'";
|
{
|
||||||
}
|
$bodyDocs .= "\n\n";
|
||||||
|
}
|
||||||
|
elsif ($docElement->{type} eq 'style' and $docElement->{style} eq 'code')
|
||||||
|
{
|
||||||
|
$bodyDocs .= "'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$bodyDocs =~ s/^\s+|\s+$//g;
|
$bodyDocs =~ s/^\s+|\s+$//g;
|
||||||
$bodyDocs = Text::Wrap::fill('', '', $bodyDocs);
|
$bodyDocs = Text::Wrap::fill('', '', $bodyDocs);
|
||||||
|
|
||||||
$paramDocs =~ s/^\s+|\s+$//g;
|
$notes =~ s/^\s+|\s+$//g;
|
||||||
|
$see =~ s/^\s+|\s+$//g;
|
||||||
|
$paramDocs =~ s/^\s+|\s+$//g;
|
||||||
$returnDocs =~ s/^\s+|\s+$//g;
|
$returnDocs =~ s/^\s+|\s+$//g;
|
||||||
|
|
||||||
my $result = Text::Wrap::fill('', '', $bodyDocs);
|
my $cmdDocs;
|
||||||
$result .= "\n\n" . $paramDocs if $paramDocs;
|
$cmdDocs = "Command:\n" . Text::Wrap::fill(' ',' ', $cmdSyntax) if $cmdSyntax;
|
||||||
$result .= "\n\n" . $returnDocs if $returnDocs;
|
|
||||||
|
return join "\n\n", grep {$_} ($bodyDocs, $notes, $see, $cmdDocs, $paramDocs, $returnDocs);
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub escapeCharForCString
|
sub escapeCharForCString
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,32 @@ typedef multi_index_container<
|
||||||
>
|
>
|
||||||
> key_label_index_type;
|
> key_label_index_type;
|
||||||
|
|
||||||
|
/* How to write doxygen docs
|
||||||
|
*
|
||||||
|
* Good
|
||||||
|
* / ** Returns the block chain's rapidly-changing properties.
|
||||||
|
* *
|
||||||
|
* * The returned object contains information that changes every block interval
|
||||||
|
* * such as the head block number, the next witness, etc.
|
||||||
|
* * /
|
||||||
|
*
|
||||||
|
* Bad, no empty line
|
||||||
|
* / ** Returns the block chain's rapidly-changing properties.
|
||||||
|
* * The returned object contains information that changes every block interval
|
||||||
|
* * such as the head block number, the next witness, etc.
|
||||||
|
* * /
|
||||||
|
*
|
||||||
|
* Better, using @brief tag
|
||||||
|
* / **
|
||||||
|
* * @brief Returns the block chain's rapidly-changing properties.
|
||||||
|
* * Long description text 1
|
||||||
|
* * Long description text 2
|
||||||
|
* * @param param1 param1 description
|
||||||
|
* * @param param2 param2 description
|
||||||
|
* * @returns return value description
|
||||||
|
* * /
|
||||||
|
* string get_rapidly_changing_properties(int32_t interval, string chain_id)
|
||||||
|
*/
|
||||||
|
|
||||||
struct wallet_data
|
struct wallet_data
|
||||||
{
|
{
|
||||||
|
|
@ -249,6 +275,7 @@ class wallet_api
|
||||||
*/
|
*/
|
||||||
uint64_t get_account_count()const;
|
uint64_t get_account_count()const;
|
||||||
/** Lists all accounts controlled by this wallet.
|
/** Lists all accounts controlled by this wallet.
|
||||||
|
*
|
||||||
* This returns a list of the full account objects for all accounts whose private keys
|
* This returns a list of the full account objects for all accounts whose private keys
|
||||||
* we possess.
|
* we possess.
|
||||||
* @returns a list of account objects
|
* @returns a list of account objects
|
||||||
|
|
@ -330,6 +357,7 @@ class wallet_api
|
||||||
vector<force_settlement_object> get_settle_orders(string a, uint32_t limit)const;
|
vector<force_settlement_object> get_settle_orders(string a, uint32_t limit)const;
|
||||||
|
|
||||||
/** Returns the block chain's slowly-changing settings.
|
/** Returns the block chain's slowly-changing settings.
|
||||||
|
*
|
||||||
* This object contains all of the properties of the blockchain that are fixed
|
* This object contains all of the properties of the blockchain that are fixed
|
||||||
* or that change only once per maintenance interval (daily) such as the
|
* or that change only once per maintenance interval (daily) such as the
|
||||||
* current list of witnesses, committee_members, block interval, etc.
|
* current list of witnesses, committee_members, block interval, etc.
|
||||||
|
|
@ -339,6 +367,7 @@ class wallet_api
|
||||||
global_property_object get_global_properties() const;
|
global_property_object get_global_properties() const;
|
||||||
|
|
||||||
/** Returns the block chain's rapidly-changing properties.
|
/** Returns the block chain's rapidly-changing properties.
|
||||||
|
*
|
||||||
* The returned object contains information that changes every block interval
|
* The returned object contains information that changes every block interval
|
||||||
* such as the head block number, the next witness, etc.
|
* such as the head block number, the next witness, etc.
|
||||||
* @see \c get_global_properties() for less-frequently changing properties
|
* @see \c get_global_properties() for less-frequently changing properties
|
||||||
|
|
@ -354,6 +383,7 @@ class wallet_api
|
||||||
account_object get_account(string account_name_or_id) const;
|
account_object get_account(string account_name_or_id) const;
|
||||||
|
|
||||||
/** Returns information about the given asset.
|
/** Returns information about the given asset.
|
||||||
|
*
|
||||||
* @param asset_name_or_id the symbol or id of the asset in question
|
* @param asset_name_or_id the symbol or id of the asset in question
|
||||||
* @returns the information about the asset stored in the block chain
|
* @returns the information about the asset stored in the block chain
|
||||||
*/
|
*/
|
||||||
|
|
@ -368,6 +398,7 @@ class wallet_api
|
||||||
asset_bitasset_data_object get_bitasset_data(string asset_name_or_id)const;
|
asset_bitasset_data_object get_bitasset_data(string asset_name_or_id)const;
|
||||||
|
|
||||||
/** Lookup the id of a named account.
|
/** Lookup the id of a named account.
|
||||||
|
*
|
||||||
* @param account_name_or_id the name of the account to look up
|
* @param account_name_or_id the name of the account to look up
|
||||||
* @returns the id of the named account
|
* @returns the id of the named account
|
||||||
*/
|
*/
|
||||||
|
|
@ -375,6 +406,7 @@ class wallet_api
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup the id of a named asset.
|
* Lookup the id of a named asset.
|
||||||
|
*
|
||||||
* @param asset_name_or_id the symbol of an asset to look up
|
* @param asset_name_or_id the symbol of an asset to look up
|
||||||
* @returns the id of the given asset
|
* @returns the id of the given asset
|
||||||
*/
|
*/
|
||||||
|
|
@ -405,42 +437,90 @@ class wallet_api
|
||||||
/**
|
/**
|
||||||
* Get the WIF private key corresponding to a public key. The
|
* Get the WIF private key corresponding to a public key. The
|
||||||
* private key must already be in the wallet.
|
* private key must already be in the wallet.
|
||||||
|
* @param pubkey a public key in Base58 format
|
||||||
|
* @return the WIF private key
|
||||||
*/
|
*/
|
||||||
string get_private_key( public_key_type pubkey )const;
|
string get_private_key( public_key_type pubkey )const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Create a new transaction builder.
|
||||||
|
* @return handle of the new transaction builder
|
||||||
*/
|
*/
|
||||||
transaction_handle_type begin_builder_transaction();
|
transaction_handle_type begin_builder_transaction();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Append a new operation to a transaction builder.
|
||||||
|
* @param transaction_handle handle of the transaction builder
|
||||||
|
* @param op the operation in JSON format
|
||||||
*/
|
*/
|
||||||
void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op);
|
void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Replace an operation in a transaction builder with a new operation.
|
||||||
|
* @param handle handle of the transaction builder
|
||||||
|
* @param operation_index the index of the old operation in the builder to be replaced
|
||||||
|
* @param new_op the new operation in JSON format
|
||||||
*/
|
*/
|
||||||
void replace_operation_in_builder_transaction(transaction_handle_type handle,
|
void replace_operation_in_builder_transaction(transaction_handle_type handle,
|
||||||
unsigned operation_index,
|
unsigned operation_index,
|
||||||
const operation& new_op);
|
const operation& new_op);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Calculate and update fees for the operations in a transaction builder.
|
||||||
|
* @param handle handle of the transaction builder
|
||||||
|
* @param fee_asset symbol or ID of an asset that to be used to pay fees
|
||||||
|
* @return total fees
|
||||||
*/
|
*/
|
||||||
asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL);
|
asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Show content of a transaction builder.
|
||||||
|
* @param handle handle of the transaction builder
|
||||||
|
* @return a transaction
|
||||||
*/
|
*/
|
||||||
transaction preview_builder_transaction(transaction_handle_type handle);
|
transaction preview_builder_transaction(transaction_handle_type handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Sign the transaction in a transaction builder and optionally broadcast to the network.
|
||||||
|
* @param transaction_handle handle of the transaction builder
|
||||||
|
* @param broadcast whether to broadcast the signed transaction to the network
|
||||||
|
* @return a signed transaction
|
||||||
*/
|
*/
|
||||||
signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true);
|
signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true);
|
||||||
|
|
||||||
/** Broadcast signed transaction
|
/** Broadcast signed transaction
|
||||||
* @param tx signed transaction
|
* @param tx signed transaction
|
||||||
* @returns the transaction ID along with the signed transaction.
|
* @returns the transaction ID along with the signed transaction.
|
||||||
*/
|
*/
|
||||||
pair<transaction_id_type,signed_transaction> broadcast_transaction(signed_transaction tx);
|
pair<transaction_id_type,signed_transaction> broadcast_transaction(signed_transaction tx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Create a proposal containing the operations in a transaction builder (create a new proposal_create
|
||||||
|
* operation, then replace the transaction builder with the new operation), then sign the transaction
|
||||||
|
* and optionally broadcast to the network.
|
||||||
|
*
|
||||||
|
* Note: this command is buggy because unable to specify proposer. It will be deprecated in a future release.
|
||||||
|
* Please use \c propose_builder_transaction2() instead.
|
||||||
|
*
|
||||||
|
* @param handle handle of the transaction builder
|
||||||
|
* @param expiration when the proposal will expire
|
||||||
|
* @param review_period_seconds review period of the proposal in seconds
|
||||||
|
* @param broadcast whether to broadcast the signed transaction to the network
|
||||||
|
* @return a signed transaction
|
||||||
*/
|
*/
|
||||||
signed_transaction propose_builder_transaction(
|
signed_transaction propose_builder_transaction(
|
||||||
transaction_handle_type handle,
|
transaction_handle_type handle,
|
||||||
|
|
@ -449,6 +529,20 @@ class wallet_api
|
||||||
bool broadcast = true
|
bool broadcast = true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Create a proposal containing the operations in a transaction builder (create a new proposal_create
|
||||||
|
* operation, then replace the transaction builder with the new operation), then sign the transaction
|
||||||
|
* and optionally broadcast to the network.
|
||||||
|
*
|
||||||
|
* @param handle handle of the transaction builder
|
||||||
|
* @param account_name_or_id name or ID of the account who would pay fees for creating the proposal
|
||||||
|
* @param expiration when the proposal will expire
|
||||||
|
* @param review_period_seconds review period of the proposal in seconds
|
||||||
|
* @param broadcast whether to broadcast the signed transaction to the network
|
||||||
|
* @return a signed transaction
|
||||||
|
*/
|
||||||
signed_transaction propose_builder_transaction2(
|
signed_transaction propose_builder_transaction2(
|
||||||
transaction_handle_type handle,
|
transaction_handle_type handle,
|
||||||
string account_name_or_id,
|
string account_name_or_id,
|
||||||
|
|
@ -459,6 +553,9 @@ class wallet_api
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ingroup Transaction Builder API
|
* @ingroup Transaction Builder API
|
||||||
|
*
|
||||||
|
* Destroy a transaction builder.
|
||||||
|
* @param handle handle of the transaction builder
|
||||||
*/
|
*/
|
||||||
void remove_builder_transaction(transaction_handle_type handle);
|
void remove_builder_transaction(transaction_handle_type handle);
|
||||||
|
|
||||||
|
|
@ -496,6 +593,11 @@ class wallet_api
|
||||||
*
|
*
|
||||||
* The wallet must be either 'new' or 'unlocked' to
|
* The wallet must be either 'new' or 'unlocked' to
|
||||||
* execute this command.
|
* execute this command.
|
||||||
|
*
|
||||||
|
* When used in command line, if typed "set_password" without a password followed, the user will be prompted
|
||||||
|
* to input a password without echo.
|
||||||
|
*
|
||||||
|
* @param password a new password
|
||||||
* @ingroup Wallet Management
|
* @ingroup Wallet Management
|
||||||
*/
|
*/
|
||||||
void set_password(string password);
|
void set_password(string password);
|
||||||
|
|
@ -577,7 +679,7 @@ class wallet_api
|
||||||
/**
|
/**
|
||||||
* Derive any number of *possible* owner keys from a given brain key.
|
* Derive any number of *possible* owner keys from a given brain key.
|
||||||
*
|
*
|
||||||
* NOTE: These keys may or may not match with the owner keys of any account.
|
* @note These keys may or may not match with the owner keys of any account.
|
||||||
* This function is merely intended to assist with account or key recovery.
|
* This function is merely intended to assist with account or key recovery.
|
||||||
*
|
*
|
||||||
* @see suggest_brain_key()
|
* @see suggest_brain_key()
|
||||||
|
|
@ -591,7 +693,8 @@ class wallet_api
|
||||||
/**
|
/**
|
||||||
* Determine whether a textual representation of a public key
|
* Determine whether a textual representation of a public key
|
||||||
* (in Base-58 format) is *currently* linked
|
* (in Base-58 format) is *currently* linked
|
||||||
* to any *registered* (i.e. non-stealth) account on the blockchain
|
* to any *registered* (i.e. non-stealth) account on the blockchain.
|
||||||
|
*
|
||||||
* @param public_key Public key
|
* @param public_key Public key
|
||||||
* @return Whether a public key is known
|
* @return Whether a public key is known
|
||||||
*/
|
*/
|
||||||
|
|
@ -636,6 +739,10 @@ class wallet_api
|
||||||
/**
|
/**
|
||||||
* This call will construct transaction(s) that will claim all balances controled
|
* This call will construct transaction(s) that will claim all balances controled
|
||||||
* by wif_keys and deposit them into the given account.
|
* by wif_keys and deposit them into the given account.
|
||||||
|
*
|
||||||
|
* @param account_name_or_id name or ID of an account that to claim balances to
|
||||||
|
* @param wif_keys private WIF keys of balance objects to claim balances from
|
||||||
|
* @param broadcast true to broadcast the transaction on the network
|
||||||
*/
|
*/
|
||||||
vector< signed_transaction > import_balance( string account_name_or_id, const vector<string>& wif_keys, bool broadcast );
|
vector< signed_transaction > import_balance( string account_name_or_id, const vector<string>& wif_keys, bool broadcast );
|
||||||
|
|
||||||
|
|
@ -682,7 +789,7 @@ class wallet_api
|
||||||
uint32_t referrer_percent,
|
uint32_t referrer_percent,
|
||||||
bool broadcast = false);
|
bool broadcast = false);
|
||||||
|
|
||||||
/** Updates account public keys
|
/** Updates account public keys.
|
||||||
*
|
*
|
||||||
* @param name the name of the existing account
|
* @param name the name of the existing account
|
||||||
* @param old_owner the owner key for the named account to be replaced
|
* @param old_owner the owner key for the named account to be replaced
|
||||||
|
|
@ -700,7 +807,8 @@ class wallet_api
|
||||||
bool broadcast = false);
|
bool broadcast = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method updates the key of an authority for an exisiting account.
|
* Updates the key of an authority for an exisiting account.
|
||||||
|
|
||||||
* Warning: You can create impossible authorities using this method. The method
|
* Warning: You can create impossible authorities using this method. The method
|
||||||
* will fail if you create an impossible owner authority, but will allow impossible
|
* will fail if you create an impossible owner authority, but will allow impossible
|
||||||
* active and posting authorities.
|
* active and posting authorities.
|
||||||
|
|
@ -719,6 +827,7 @@ class wallet_api
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrades an account to prime status.
|
* Upgrades an account to prime status.
|
||||||
|
*
|
||||||
* This makes the account holder a 'lifetime member'.
|
* This makes the account holder a 'lifetime member'.
|
||||||
*
|
*
|
||||||
* @todo there is no option for annual membership
|
* @todo there is no option for annual membership
|
||||||
|
|
@ -773,7 +882,16 @@ class wallet_api
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method works just like transfer, except it always broadcasts and
|
* This method works just like transfer, except it always broadcasts and
|
||||||
* returns the transaction ID along with the signed transaction.
|
* returns the transaction ID (hash) along with the signed transaction.
|
||||||
|
* @param from the name or id of the account sending the funds
|
||||||
|
* @param to the name or id of the account receiving the funds
|
||||||
|
* @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5)
|
||||||
|
* @param asset_symbol the symbol or id of the asset to send
|
||||||
|
* @param memo a memo to attach to the transaction. The memo will be encrypted in the
|
||||||
|
* transaction and readable for the receiver. There is no length limit
|
||||||
|
* other than the limit imposed by maximum transaction size, but transaction
|
||||||
|
* increase with transaction size
|
||||||
|
* @returns the transaction ID (hash) along with the signed transaction transferring funds
|
||||||
*/
|
*/
|
||||||
pair<transaction_id_type,signed_transaction> transfer2(string from,
|
pair<transaction_id_type,signed_transaction> transfer2(string from,
|
||||||
string to,
|
string to,
|
||||||
|
|
@ -786,7 +904,9 @@ class wallet_api
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to convert a JSON transaction to its transactin ID.
|
* This method is used to convert a JSON transaction to its transactin ID.
|
||||||
|
* @param trx a JSON transaction
|
||||||
|
* @return the ID (hash) of the transaction
|
||||||
*/
|
*/
|
||||||
transaction_id_type get_transaction_id( const signed_transaction& trx )const { return trx.id(); }
|
transaction_id_type get_transaction_id( const signed_transaction& trx )const { return trx.id(); }
|
||||||
|
|
||||||
|
|
@ -794,7 +914,7 @@ class wallet_api
|
||||||
/** These methods are used for stealth transfers */
|
/** These methods are used for stealth transfers */
|
||||||
///@{
|
///@{
|
||||||
/**
|
/**
|
||||||
* This method can be used to set the label for a public key
|
* Set the label for a public key
|
||||||
*
|
*
|
||||||
* @note No two keys can have the same label.
|
* @note No two keys can have the same label.
|
||||||
*
|
*
|
||||||
|
|
@ -918,7 +1038,7 @@ class wallet_api
|
||||||
signed_transaction borrow_asset(string borrower_name, string amount_to_borrow, string asset_symbol,
|
signed_transaction borrow_asset(string borrower_name, string amount_to_borrow, string asset_symbol,
|
||||||
string amount_of_collateral, bool broadcast = false);
|
string amount_of_collateral, bool broadcast = false);
|
||||||
|
|
||||||
/** Cancel an existing order
|
/** Cancel an existing order.
|
||||||
*
|
*
|
||||||
* @param order_id the id of order to be cancelled
|
* @param order_id the id of order to be cancelled
|
||||||
* @param broadcast true to broadcast the transaction on the network
|
* @param broadcast true to broadcast the transaction on the network
|
||||||
|
|
@ -978,6 +1098,7 @@ class wallet_api
|
||||||
bool broadcast = false);
|
bool broadcast = false);
|
||||||
|
|
||||||
/** Update the core options on an asset.
|
/** Update the core options on an asset.
|
||||||
|
*
|
||||||
* There are a number of options which all assets in the network use. These options are
|
* There are a number of options which all assets in the network use. These options are
|
||||||
* enumerated in the asset_object::asset_options struct. This command is used to update
|
* enumerated in the asset_object::asset_options struct. This command is used to update
|
||||||
* these options for an existing asset.
|
* these options for an existing asset.
|
||||||
|
|
@ -1184,6 +1305,7 @@ class wallet_api
|
||||||
bool broadcast = false);
|
bool broadcast = false);
|
||||||
|
|
||||||
/** Lists all witnesses registered in the blockchain.
|
/** Lists all witnesses registered in the blockchain.
|
||||||
|
*
|
||||||
* This returns a list of all account names that own witnesses, and the associated witness id,
|
* This returns a list of all account names that own witnesses, and the associated witness id,
|
||||||
* sorted by name. This lists witnesses whether they are currently voted in or not.
|
* sorted by name. This lists witnesses whether they are currently voted in or not.
|
||||||
*
|
*
|
||||||
|
|
@ -1214,6 +1336,7 @@ class wallet_api
|
||||||
map<string, committee_member_id_type> list_committee_members(const string& lowerbound, uint32_t limit);
|
map<string, committee_member_id_type> list_committee_members(const string& lowerbound, uint32_t limit);
|
||||||
|
|
||||||
/** Lists all workers in the blockchain.
|
/** Lists all workers in the blockchain.
|
||||||
|
*
|
||||||
* This returns a list of all account names that own worker, and the associated worker id,
|
* This returns a list of all account names that own worker, and the associated worker id,
|
||||||
* sorted by name. This lists workers whether they are currently voted in or not.
|
* sorted by name. This lists workers whether they are currently voted in or not.
|
||||||
*
|
*
|
||||||
|
|
@ -1355,6 +1478,7 @@ class wallet_api
|
||||||
bool broadcast = false);
|
bool broadcast = false);
|
||||||
|
|
||||||
/** Lists all SONs in the blockchain.
|
/** Lists all SONs in the blockchain.
|
||||||
|
*
|
||||||
* This returns a list of all account names that own SON, and the associated SON id,
|
* This returns a list of all account names that own SON, and the associated SON id,
|
||||||
* sorted by name. This lists SONs whether they are currently voted in or not.
|
* sorted by name. This lists SONs whether they are currently voted in or not.
|
||||||
*
|
*
|
||||||
|
|
@ -1369,18 +1493,31 @@ class wallet_api
|
||||||
*/
|
*/
|
||||||
map<string, son_id_type> list_sons(const string& lowerbound, uint32_t limit);
|
map<string, son_id_type> list_sons(const string& lowerbound, uint32_t limit);
|
||||||
|
|
||||||
/** Lists active at the moment SONs.
|
/**
|
||||||
* This returns a list of all account names that own active SON, and the associated SON id,
|
* @brief Get list of active sons
|
||||||
* sorted by name.
|
* @return List of active SONs
|
||||||
* @returns a list of active SONs mapping SON names to SON ids
|
|
||||||
*/
|
*/
|
||||||
map<string, son_id_type> list_active_sons();
|
flat_map<sidechain_type, vector<son_info>> get_active_sons();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get list of active sons
|
||||||
|
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
|
||||||
|
* @return List of active SONs
|
||||||
|
*/
|
||||||
|
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get SON network status
|
* @brief Get SON network status
|
||||||
* @return SON network status description
|
* @return SON network status description for a given sidechain type
|
||||||
*/
|
*/
|
||||||
map<son_id_type, string> get_son_network_status();
|
map<sidechain_type, map<son_id_type, string>> get_son_network_status();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get SON network status
|
||||||
|
* @param sidechain Sidechain type [bitcoin|ethereum|hive]
|
||||||
|
* @return SON network status description for a given sidechain type
|
||||||
|
*/
|
||||||
|
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get active SON wallet
|
* @brief Get active SON wallet
|
||||||
|
|
@ -1562,8 +1699,7 @@ class wallet_api
|
||||||
string asset_symbol,
|
string asset_symbol,
|
||||||
bool broadcast = false);
|
bool broadcast = false);
|
||||||
|
|
||||||
/**
|
/** Withdraw a GPOS vesting balance.
|
||||||
* Withdraw a GPOS vesting balance.
|
|
||||||
*
|
*
|
||||||
* @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type.
|
* @param account_name The account name of the witness/user, also accepts account ID or vesting balance ID type.
|
||||||
* @param amount The amount to withdraw.
|
* @param amount The amount to withdraw.
|
||||||
|
|
@ -2598,8 +2734,10 @@ FC_API( graphene::wallet::wallet_api,
|
||||||
(update_son_vesting_balances)
|
(update_son_vesting_balances)
|
||||||
(activate_deregistered_son)
|
(activate_deregistered_son)
|
||||||
(list_sons)
|
(list_sons)
|
||||||
(list_active_sons)
|
(get_active_sons)
|
||||||
|
(get_active_sons_by_sidechain)
|
||||||
(get_son_network_status)
|
(get_son_network_status)
|
||||||
|
(get_son_network_status_by_sidechain)
|
||||||
(request_son_maintenance)
|
(request_son_maintenance)
|
||||||
(cancel_request_son_maintenance)
|
(cancel_request_son_maintenance)
|
||||||
(get_active_son_wallet)
|
(get_active_son_wallet)
|
||||||
|
|
|
||||||
|
|
@ -2230,99 +2230,25 @@ public:
|
||||||
return sign_transaction( tx, broadcast );
|
return sign_transaction( tx, broadcast );
|
||||||
} FC_CAPTURE_AND_RETHROW( (owner_account) ) }
|
} FC_CAPTURE_AND_RETHROW( (owner_account) ) }
|
||||||
|
|
||||||
//! Fixme - do we need to specify sidechain_type as params here?
|
flat_map<sidechain_type, vector<son_info>> get_active_sons()
|
||||||
map<string, son_id_type> list_active_sons()
|
{ try {
|
||||||
{
|
return _remote_db->get_active_sons();
|
||||||
try
|
} FC_CAPTURE_AND_RETHROW() }
|
||||||
{
|
|
||||||
const global_property_object& gpo = get_global_properties();
|
|
||||||
set<son_id_type> son_ids_set;
|
|
||||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
|
||||||
{
|
|
||||||
std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(),
|
|
||||||
std::inserter(son_ids_set, son_ids_set.end()),
|
|
||||||
[](const son_info &swi) {
|
|
||||||
return swi.son_id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
vector<son_id_type> son_ids;
|
|
||||||
son_ids.reserve(son_ids_set.size());
|
|
||||||
for(const auto& son_id : son_ids_set)
|
|
||||||
{
|
|
||||||
son_ids.emplace_back(son_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<fc::optional<son_object>> son_objects = _remote_db->get_sons(son_ids);
|
vector<son_info> get_active_sons_by_sidechain(sidechain_type sidechain)
|
||||||
vector<std::string> owners;
|
{ try {
|
||||||
for(auto obj: son_objects)
|
return _remote_db->get_active_sons_by_sidechain(sidechain);
|
||||||
{
|
} FC_CAPTURE_AND_RETHROW() }
|
||||||
std::string acc_id = account_id_to_string(obj->son_account);
|
|
||||||
owners.push_back(acc_id);
|
|
||||||
}
|
|
||||||
vector< optional< account_object> > accs = _remote_db->get_accounts(owners);
|
|
||||||
std::remove_if(son_objects.begin(), son_objects.end(),
|
|
||||||
[](const fc::optional<son_object>& obj) -> bool { return obj.valid(); });
|
|
||||||
map<string, son_id_type> result;
|
|
||||||
std::transform(accs.begin(), accs.end(), son_objects.begin(),
|
|
||||||
std::inserter(result, result.end()),
|
|
||||||
[](fc::optional<account_object>& acct, fc::optional<son_object> son) {
|
|
||||||
FC_ASSERT(acct, "Invalid active SONs list in global properties.");
|
|
||||||
return std::make_pair<string, son_id_type>(string(acct->name), std::move(son->id));
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
FC_CAPTURE_AND_RETHROW()
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Fixme - do we need to specify sidechain_type as params here?
|
map<sidechain_type, map<son_id_type, string>> get_son_network_status()
|
||||||
map<son_id_type, string> get_son_network_status()
|
{ try {
|
||||||
{
|
return _remote_db->get_son_network_status();
|
||||||
try
|
} FC_CAPTURE_AND_RETHROW() }
|
||||||
{
|
|
||||||
const global_property_object& gpo = get_global_properties();
|
|
||||||
|
|
||||||
set<son_id_type> son_ids_set;
|
map<son_id_type, string> get_son_network_status_by_sidechain(sidechain_type sidechain)
|
||||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
{ try {
|
||||||
std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(),
|
return _remote_db->get_son_network_status_by_sidechain(sidechain);
|
||||||
std::inserter(son_ids_set, son_ids_set.end()),
|
} FC_CAPTURE_AND_RETHROW() }
|
||||||
[](const son_info &swi) {
|
|
||||||
return swi.son_id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
vector<son_id_type> son_ids;
|
|
||||||
son_ids.reserve(son_ids_set.size());
|
|
||||||
std::transform(son_ids_set.cbegin(), son_ids_set.cend(),
|
|
||||||
std::inserter(son_ids, son_ids.end()),
|
|
||||||
[](const son_id_type& sit) {
|
|
||||||
return sit;
|
|
||||||
});
|
|
||||||
|
|
||||||
map<son_id_type, string> result;
|
|
||||||
std::vector<fc::optional<son_object>> son_objects = _remote_db->get_sons(son_ids);
|
|
||||||
for(auto son_obj: son_objects) {
|
|
||||||
string status;
|
|
||||||
if (son_obj) {
|
|
||||||
son_statistics_object sso = get_object(son_obj->statistics);
|
|
||||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
|
||||||
if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) {
|
|
||||||
status = "[OK, regular SON heartbeat for sidechain " + std::to_string(static_cast<unsigned int>(active_sidechain_type)) + "] ";
|
|
||||||
} else {
|
|
||||||
if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) {
|
|
||||||
status = "[OK, irregular SON heartbeat, but not triggering SON down proposal for sidechain " + std::to_string(static_cast<unsigned int>(active_sidechain_type)) + "] ";
|
|
||||||
} else {
|
|
||||||
status = "[NOT OK, irregular SON heartbeat, triggering SON down proposal for sidechain " + std::to_string(static_cast<unsigned int>(active_sidechain_type)) + "] ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
status = "NOT OK, invalid SON id";
|
|
||||||
}
|
|
||||||
result[son_obj->id] = status;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
FC_CAPTURE_AND_RETHROW()
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<son_wallet_object> get_active_son_wallet()
|
optional<son_wallet_object> get_active_son_wallet()
|
||||||
{ try {
|
{ try {
|
||||||
|
|
@ -2845,7 +2771,7 @@ public:
|
||||||
account_id_type son_account_id = get_account_id(son);
|
account_id_type son_account_id = get_account_id(son);
|
||||||
fc::optional<son_object> son_obj = _remote_db->get_son_by_account_id(son_account_id);
|
fc::optional<son_object> son_obj = _remote_db->get_son_by_account_id(son_account_id);
|
||||||
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
||||||
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type");
|
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
|
||||||
|
|
||||||
if (approve)
|
if (approve)
|
||||||
{
|
{
|
||||||
|
|
@ -2891,7 +2817,7 @@ 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);
|
||||||
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
||||||
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type");
|
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
|
||||||
|
|
||||||
auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain));
|
auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain));
|
||||||
if (!insert_result.second)
|
if (!insert_result.second)
|
||||||
|
|
@ -2902,7 +2828,7 @@ 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);
|
||||||
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son));
|
||||||
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type");
|
FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive || sidechain == sidechain_type::ethereum, "Unexpected sidechain type");
|
||||||
|
|
||||||
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain));
|
unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain));
|
||||||
if (!votes_removed)
|
if (!votes_removed)
|
||||||
|
|
@ -2943,6 +2869,7 @@ public:
|
||||||
case sidechain_type::peerplays : return "peerplays";
|
case sidechain_type::peerplays : return "peerplays";
|
||||||
case sidechain_type::bitcoin : return "bitcoin";
|
case sidechain_type::bitcoin : return "bitcoin";
|
||||||
case sidechain_type::hive : return "hive";
|
case sidechain_type::hive : return "hive";
|
||||||
|
case sidechain_type::ethereum : return "ethereum";
|
||||||
default:
|
default:
|
||||||
FC_THROW("Wrong sidechain type: ${sidechain}", ("sidechain", sidechain));
|
FC_THROW("Wrong sidechain type: ${sidechain}", ("sidechain", sidechain));
|
||||||
}
|
}
|
||||||
|
|
@ -4143,7 +4070,7 @@ public:
|
||||||
"to access the debug API on the node you are connecting to.\n"
|
"to access the debug API on the node you are connecting to.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"To fix this problem:\n"
|
"To fix this problem:\n"
|
||||||
"- Please ensure you are running debug_node, not witness_node.\n"
|
"- Please ensure you enable debug_witness plugin in witness_node.\n"
|
||||||
"- Please follow the instructions in README.md to set up an apiaccess file.\n"
|
"- Please follow the instructions in README.md to set up an apiaccess file.\n"
|
||||||
"\n";
|
"\n";
|
||||||
}
|
}
|
||||||
|
|
@ -5305,16 +5232,26 @@ map<string, son_id_type> wallet_api::list_sons(const string& lowerbound, uint32_
|
||||||
return my->_remote_db->lookup_son_accounts(lowerbound, limit);
|
return my->_remote_db->lookup_son_accounts(lowerbound, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
map<string, son_id_type> wallet_api::list_active_sons()
|
flat_map<sidechain_type, vector<son_info>> wallet_api::get_active_sons()
|
||||||
{
|
{
|
||||||
return my->list_active_sons();
|
return my->get_active_sons();
|
||||||
}
|
}
|
||||||
|
|
||||||
map<son_id_type, string> wallet_api::get_son_network_status()
|
vector<son_info> wallet_api::get_active_sons_by_sidechain(sidechain_type sidechain)
|
||||||
|
{
|
||||||
|
return my->get_active_sons_by_sidechain(sidechain);
|
||||||
|
}
|
||||||
|
|
||||||
|
map<sidechain_type, map<son_id_type, string>> wallet_api::get_son_network_status()
|
||||||
{
|
{
|
||||||
return my->get_son_network_status();
|
return my->get_son_network_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
map<son_id_type, string> wallet_api::get_son_network_status_by_sidechain(sidechain_type sidechain)
|
||||||
|
{
|
||||||
|
return my->get_son_network_status_by_sidechain(sidechain);
|
||||||
|
}
|
||||||
|
|
||||||
optional<son_wallet_object> wallet_api::get_active_son_wallet()
|
optional<son_wallet_object> wallet_api::get_active_son_wallet()
|
||||||
{
|
{
|
||||||
return my->get_active_son_wallet();
|
return my->get_active_son_wallet();
|
||||||
|
|
@ -5772,25 +5709,24 @@ string wallet_api::gethelp(const string& method)const
|
||||||
if( method == "import_key" )
|
if( method == "import_key" )
|
||||||
{
|
{
|
||||||
ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n";
|
ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n";
|
||||||
ss << "example: import_key \"1.3.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n";
|
ss << "example: import_key \"1.2.11\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n";
|
||||||
ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n";
|
ss << "example: import_key \"usera\" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3\n";
|
||||||
}
|
}
|
||||||
else if( method == "transfer" )
|
else if( method == "transfer" )
|
||||||
{
|
{
|
||||||
ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n";
|
ss << "usage: transfer FROM TO AMOUNT SYMBOL \"memo\" BROADCAST\n\n";
|
||||||
ss << "example: transfer \"1.3.11\" \"1.3.4\" 1000.03 CORE \"memo\" true\n";
|
ss << "example: transfer \"1.2.11\" \"1.2.4\" 1000.03 CORE \"memo\" true\n";
|
||||||
ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n";
|
ss << "example: transfer \"usera\" \"userb\" 1000.123 CORE \"memo\" true\n";
|
||||||
}
|
}
|
||||||
else if( method == "create_account_with_brain_key" )
|
else if( method == "create_account_with_brain_key" )
|
||||||
{
|
{
|
||||||
ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n";
|
ss << "usage: create_account_with_brain_key BRAIN_KEY ACCOUNT_NAME REGISTRAR REFERRER BROADCAST\n\n";
|
||||||
ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.3.11\" \"1.3.11\" true\n";
|
ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"1.2.11\" \"1.2.11\" true\n";
|
||||||
ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n";
|
ss << "example: create_account_with_brain_key \"my really long brain key\" \"newaccount\" \"someaccount\" \"otheraccount\" true\n";
|
||||||
ss << "\n";
|
ss << "\n";
|
||||||
ss << "This method should be used if you would like the wallet to generate new keys derived from the brain key.\n";
|
ss << "This method should be used if you would like the wallet to generate new keys derived from the brain key.\n";
|
||||||
ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY. Use\n";
|
ss << "The BRAIN_KEY will be used as the owner key, and the active key will be derived from the BRAIN_KEY. Use\n";
|
||||||
ss << "register_account if you already know the keys you know the public keys that you would like to register.\n";
|
ss << "register_account if you already know the keys you know the public keys that you would like to register.\n";
|
||||||
|
|
||||||
}
|
}
|
||||||
else if( method == "register_account" )
|
else if( method == "register_account" )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ if( BUILD_PEERPLAYS_PROGRAMS )
|
||||||
add_subdirectory( cli_wallet )
|
add_subdirectory( cli_wallet )
|
||||||
add_subdirectory( genesis_util )
|
add_subdirectory( genesis_util )
|
||||||
add_subdirectory( witness_node )
|
add_subdirectory( witness_node )
|
||||||
add_subdirectory( debug_node )
|
|
||||||
add_subdirectory( delayed_node )
|
|
||||||
add_subdirectory( js_operation_serializer )
|
add_subdirectory( js_operation_serializer )
|
||||||
add_subdirectory( size_checker )
|
add_subdirectory( size_checker )
|
||||||
endif( BUILD_PEERPLAYS_PROGRAMS )
|
endif( BUILD_PEERPLAYS_PROGRAMS )
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
add_executable( debug_node main.cpp )
|
|
||||||
if( UNIX AND NOT APPLE )
|
|
||||||
set(rt_library rt )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package( Gperftools QUIET )
|
|
||||||
if( GPERFTOOLS_FOUND )
|
|
||||||
message( STATUS "Found gperftools; compiling debug_node with TCMalloc")
|
|
||||||
list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries( debug_node
|
|
||||||
PRIVATE graphene_app graphene_egenesis_full ${PLATFORM_SPECIFIC_LIBS} )
|
|
||||||
|
|
||||||
install( TARGETS
|
|
||||||
debug_node
|
|
||||||
|
|
||||||
RUNTIME DESTINATION bin
|
|
||||||
LIBRARY DESTINATION lib
|
|
||||||
ARCHIVE DESTINATION lib
|
|
||||||
)
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
|
|
||||||
The `debug_node` is a tool to allow developers to run many interesting sorts of "what-if" tests using state from a production blockchain.
|
|
||||||
Like "what happens if I produce enough blocks for the next hardfork time to arrive?" or "what would happen if this account (which I don't have a private key for) did this transaction?"
|
|
||||||
|
|
||||||
Setup
|
|
||||||
-----
|
|
||||||
|
|
||||||
Be sure you've built the right build targets:
|
|
||||||
|
|
||||||
$ make get_dev_key debug_node cli_wallet witness_node
|
|
||||||
|
|
||||||
Use the `get_dev_key` utility to generate a keypair:
|
|
||||||
|
|
||||||
$ programs/genesis_util/get_dev_key "" nathan
|
|
||||||
[{"private_key":"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3","public_key":"BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","address":"BTSFAbAx7yuxt725qSZvfwWqkdCwp9ZnUama"}]
|
|
||||||
|
|
||||||
Obtain a copy of the blockchain in `block_db` directory:
|
|
||||||
$ programs/witness_node/witness_node --data-dir data/mydatadir
|
|
||||||
# ... wait for chain to sync
|
|
||||||
^C
|
|
||||||
$ cp -Rp data/mydatadir/blockchain/database/block_num_to_block ./block_db
|
|
||||||
|
|
||||||
Set up a new datadir with the following `config.ini` settings:
|
|
||||||
|
|
||||||
# setup API endpoint
|
|
||||||
rpc-endpoint = 127.0.0.1:8090
|
|
||||||
# setting this to empty effectively disables the p2p network
|
|
||||||
seed-nodes = []
|
|
||||||
# set apiaccess.json so we can set up
|
|
||||||
api-access = "data/debug_datadir/api-access.json"
|
|
||||||
|
|
||||||
Then set up `data/debug_datadir/api-access.json` to allow access to the debug API like this:
|
|
||||||
|
|
||||||
{
|
|
||||||
"permission_map" :
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"bytemaster",
|
|
||||||
{
|
|
||||||
"password_hash_b64" : "9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=",
|
|
||||||
"password_salt_b64" : "INDdM6iCi/8=",
|
|
||||||
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api", "network_node_api", "debug_api"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"*",
|
|
||||||
{
|
|
||||||
"password_hash_b64" : "*",
|
|
||||||
"password_salt_b64" : "*",
|
|
||||||
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
See [here](https://github.com/cryptonomex/graphene#accessing-restricted-apis) for more detail on the `api-access.json` format.
|
|
||||||
|
|
||||||
Once that is set up, run `debug_node` against your newly prepared datadir:
|
|
||||||
|
|
||||||
programs/debug_node/debug_node --data-dir data/debug_datadir
|
|
||||||
|
|
||||||
Run `cli_wallet` to connect to the `debug_node` port, using the username and password to access the new `debug_api` (and also a different wallet file):
|
|
||||||
|
|
||||||
programs/cli_wallet/cli_wallet -s 127.0.0.1:8090 -w debug.wallet -u bytemaster -p supersecret
|
|
||||||
|
|
||||||
Example usage
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Load some blocks from the datadir:
|
|
||||||
|
|
||||||
dbg_push_blocks block_db 20000
|
|
||||||
|
|
||||||
Note, when pushing a very large number of blocks sometimes `cli_wallet` hangs and you must Ctrl+C and restart it (leaving the `debug_node` running).
|
|
||||||
|
|
||||||
Generate (fake) blocks with our own private key:
|
|
||||||
|
|
||||||
dbg_generate_blocks 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 1000
|
|
||||||
|
|
||||||
Update `angel` account to be controlled by our own private key and generate a (fake) transfer:
|
|
||||||
|
|
||||||
dbg_update_object {"_action":"update", "id":"1.2.1090", "active":{"weight_threshold":1,"key_auths":[["BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",1]]}}
|
|
||||||
import_key angel 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
|
|
||||||
transfer angel init0 999999 BTS "" true
|
|
||||||
|
|
||||||
How it works
|
|
||||||
------------
|
|
||||||
|
|
||||||
The commands work by creating diff(s) from the main chain that are applied to the local chain at specified block height(s). It lets you easily check out "what-if"
|
|
||||||
scenarios in a fantasy debug toy world forked from the real chain, e.g. "if we take all of the blocks until today, then generate a bunch more until a hardfork time
|
|
||||||
in the future arrives, does the chain stay up? Can I do transactions X, Y, and Z in the wallet after the hardfork?" Anyone connecting to this node sees the same
|
|
||||||
fantasy world, so you can e.g. make changes with the `cli_wallet` and see them exist in other `cli_wallet` instances (or GUI wallets or API scripts).
|
|
||||||
|
|
||||||
Limitations
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The main limitations are:
|
|
||||||
|
|
||||||
- No export format for the diffs, so you can't really [1] connect multiple `debug_node` to each other.
|
|
||||||
- Once faked block(s) or tx(s) have been produced on your chain, you can't really [1] stream blocks or tx's from the main network to your chain.
|
|
||||||
|
|
||||||
[1] It should theoretically be possible, but it's non-trivial and totally untested.
|
|
||||||
|
|
@ -1,307 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include <graphene/app/application.hpp>
|
|
||||||
|
|
||||||
#include <graphene/debug_witness/debug_witness.hpp>
|
|
||||||
#include <graphene/account_history/account_history_plugin.hpp>
|
|
||||||
#include <graphene/market_history/market_history_plugin.hpp>
|
|
||||||
|
|
||||||
#include <fc/exception/exception.hpp>
|
|
||||||
#include <fc/thread/thread.hpp>
|
|
||||||
#include <fc/interprocess/signals.hpp>
|
|
||||||
#include <fc/log/console_appender.hpp>
|
|
||||||
#include <fc/log/file_appender.hpp>
|
|
||||||
#include <fc/log/logger.hpp>
|
|
||||||
#include <fc/log/logger_config.hpp>
|
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
# include <signal.h>
|
|
||||||
#else
|
|
||||||
# include <csignal>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace graphene;
|
|
||||||
namespace bpo = boost::program_options;
|
|
||||||
|
|
||||||
void write_default_logging_config_to_stream(std::ostream& out);
|
|
||||||
fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename);
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
app::application* node = new app::application();
|
|
||||||
fc::oexception unhandled_exception;
|
|
||||||
try {
|
|
||||||
bpo::options_description app_options("Graphene Witness Node");
|
|
||||||
bpo::options_description cfg_options("Graphene Witness Node");
|
|
||||||
app_options.add_options()
|
|
||||||
("help,h", "Print this help message and exit.")
|
|
||||||
("data-dir,d", bpo::value<boost::filesystem::path>()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.")
|
|
||||||
;
|
|
||||||
|
|
||||||
bpo::variables_map options;
|
|
||||||
|
|
||||||
auto witness_plug = node->register_plugin<debug_witness_plugin::debug_witness_plugin>();
|
|
||||||
auto history_plug = node->register_plugin<account_history::account_history_plugin>();
|
|
||||||
auto market_history_plug = node->register_plugin<market_history::market_history_plugin>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bpo::options_description cli, cfg;
|
|
||||||
node->set_program_options(cli, cfg);
|
|
||||||
app_options.add(cli);
|
|
||||||
cfg_options.add(cfg);
|
|
||||||
bpo::store(bpo::parse_command_line(argc, argv, app_options), options);
|
|
||||||
}
|
|
||||||
catch (const boost::program_options::error& e)
|
|
||||||
{
|
|
||||||
std::cerr << "Error parsing command line: " << e.what() << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( options.count("help") )
|
|
||||||
{
|
|
||||||
std::cout << app_options << "\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fc::path data_dir;
|
|
||||||
if( options.count("data-dir") )
|
|
||||||
{
|
|
||||||
data_dir = options["data-dir"].as<boost::filesystem::path>();
|
|
||||||
if( data_dir.is_relative() )
|
|
||||||
data_dir = fc::current_path() / data_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
fc::path config_ini_path = data_dir / "config.ini";
|
|
||||||
if( fc::exists(config_ini_path) )
|
|
||||||
{
|
|
||||||
// get the basic options
|
|
||||||
bpo::store(bpo::parse_config_file<char>(config_ini_path.preferred_string().c_str(), cfg_options, true), options);
|
|
||||||
|
|
||||||
// try to get logging options from the config file.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
|
|
||||||
if (logging_config)
|
|
||||||
fc::configure_logging(*logging_config);
|
|
||||||
}
|
|
||||||
catch (const fc::exception&)
|
|
||||||
{
|
|
||||||
wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ilog("Writing new config file at ${path}", ("path", config_ini_path));
|
|
||||||
if( !fc::exists(data_dir) )
|
|
||||||
fc::create_directories(data_dir);
|
|
||||||
|
|
||||||
std::ofstream out_cfg(config_ini_path.preferred_string());
|
|
||||||
for( const boost::shared_ptr<bpo::option_description> od : cfg_options.options() )
|
|
||||||
{
|
|
||||||
if( !od->description().empty() )
|
|
||||||
out_cfg << "# " << od->description() << "\n";
|
|
||||||
boost::any store;
|
|
||||||
if( !od->semantic()->apply_default(store) )
|
|
||||||
out_cfg << "# " << od->long_name() << " = \n";
|
|
||||||
else {
|
|
||||||
auto example = od->format_parameter();
|
|
||||||
if( example.empty() )
|
|
||||||
// This is a boolean switch
|
|
||||||
out_cfg << od->long_name() << " = " << "false\n";
|
|
||||||
else {
|
|
||||||
// The string is formatted "arg (=<interesting part>)"
|
|
||||||
example.erase(0, 6);
|
|
||||||
example.erase(example.length()-1);
|
|
||||||
out_cfg << od->long_name() << " = " << example << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out_cfg << "\n";
|
|
||||||
}
|
|
||||||
write_default_logging_config_to_stream(out_cfg);
|
|
||||||
out_cfg.close();
|
|
||||||
// read the default logging config we just wrote out to the file and start using it
|
|
||||||
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
|
|
||||||
if (logging_config)
|
|
||||||
fc::configure_logging(*logging_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
bpo::notify(options);
|
|
||||||
node->initialize(data_dir, options);
|
|
||||||
node->initialize_plugins( options );
|
|
||||||
|
|
||||||
node->startup();
|
|
||||||
node->startup_plugins();
|
|
||||||
|
|
||||||
fc::promise<int>::ptr exit_promise = new fc::promise<int>("UNIX Signal Handler");
|
|
||||||
|
|
||||||
fc::set_signal_handler([&exit_promise](int signal) {
|
|
||||||
elog( "Caught SIGINT attempting to exit cleanly" );
|
|
||||||
exit_promise->set_value(signal);
|
|
||||||
}, SIGINT);
|
|
||||||
|
|
||||||
fc::set_signal_handler([&exit_promise](int signal) {
|
|
||||||
elog( "Caught SIGTERM attempting to exit cleanly" );
|
|
||||||
exit_promise->set_value(signal);
|
|
||||||
}, SIGTERM);
|
|
||||||
|
|
||||||
ilog("Started witness node on a chain with ${h} blocks.", ("h", node->chain_database()->head_block_num()));
|
|
||||||
ilog("Chain ID is ${id}", ("id", node->chain_database()->get_chain_id()) );
|
|
||||||
|
|
||||||
int signal = exit_promise->wait();
|
|
||||||
ilog("Exiting from signal ${n}", ("n", signal));
|
|
||||||
node->shutdown_plugins();
|
|
||||||
node->shutdown();
|
|
||||||
delete node;
|
|
||||||
return 0;
|
|
||||||
} catch( const fc::exception& e ) {
|
|
||||||
// deleting the node can yield, so do this outside the exception handler
|
|
||||||
unhandled_exception = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unhandled_exception)
|
|
||||||
{
|
|
||||||
elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string()));
|
|
||||||
node->shutdown();
|
|
||||||
delete node;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// logging config is too complicated to be parsed by boost::program_options,
|
|
||||||
// so we do it by hand
|
|
||||||
//
|
|
||||||
// Currently, you can only specify the filenames and logging levels, which
|
|
||||||
// are all most users would want to change. At a later time, options can
|
|
||||||
// be added to control rotation intervals, compression, and other seldom-
|
|
||||||
// used features
|
|
||||||
void write_default_logging_config_to_stream(std::ostream& out)
|
|
||||||
{
|
|
||||||
out << "# declare an appender named \"stderr\" that writes messages to the console\n"
|
|
||||||
"[log.console_appender.stderr]\n"
|
|
||||||
"stream=std_error\n\n"
|
|
||||||
"# declare an appender named \"p2p\" that writes messages to p2p.log\n"
|
|
||||||
"[log.file_appender.p2p]\n"
|
|
||||||
"filename=logs/p2p/p2p.log\n"
|
|
||||||
"# filename can be absolute or relative to this config file\n\n"
|
|
||||||
"# route any messages logged to the default logger to the \"stderr\" logger we\n"
|
|
||||||
"# declared above, if they are info level are higher\n"
|
|
||||||
"[logger.default]\n"
|
|
||||||
"level=info\n"
|
|
||||||
"appenders=stderr\n\n"
|
|
||||||
"# route messages sent to the \"p2p\" logger to the p2p appender declared above\n"
|
|
||||||
"[logger.p2p]\n"
|
|
||||||
"level=info\n"
|
|
||||||
"appenders=p2p\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fc::logging_config logging_config;
|
|
||||||
bool found_logging_config = false;
|
|
||||||
|
|
||||||
boost::property_tree::ptree config_ini_tree;
|
|
||||||
boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree);
|
|
||||||
for (const auto& section : config_ini_tree)
|
|
||||||
{
|
|
||||||
const std::string& section_name = section.first;
|
|
||||||
const boost::property_tree::ptree& section_tree = section.second;
|
|
||||||
|
|
||||||
const std::string console_appender_section_prefix = "log.console_appender.";
|
|
||||||
const std::string file_appender_section_prefix = "log.file_appender.";
|
|
||||||
const std::string logger_section_prefix = "logger.";
|
|
||||||
|
|
||||||
if (boost::starts_with(section_name, console_appender_section_prefix))
|
|
||||||
{
|
|
||||||
std::string console_appender_name = section_name.substr(console_appender_section_prefix.length());
|
|
||||||
std::string stream_name = section_tree.get<std::string>("stream");
|
|
||||||
|
|
||||||
// construct a default console appender config here
|
|
||||||
// stdout/stderr will be taken from ini file, everything else hard-coded here
|
|
||||||
fc::console_appender::config console_appender_config;
|
|
||||||
console_appender_config.level_colors.emplace_back(
|
|
||||||
fc::console_appender::level_color(fc::log_level::debug,
|
|
||||||
fc::console_appender::color::green));
|
|
||||||
console_appender_config.level_colors.emplace_back(
|
|
||||||
fc::console_appender::level_color(fc::log_level::warn,
|
|
||||||
fc::console_appender::color::brown));
|
|
||||||
console_appender_config.level_colors.emplace_back(
|
|
||||||
fc::console_appender::level_color(fc::log_level::error,
|
|
||||||
fc::console_appender::color::cyan));
|
|
||||||
console_appender_config.stream = fc::variant(stream_name, 1).as<fc::console_appender::stream::type>(1);
|
|
||||||
logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config, 20)));
|
|
||||||
found_logging_config = true;
|
|
||||||
}
|
|
||||||
else if (boost::starts_with(section_name, file_appender_section_prefix))
|
|
||||||
{
|
|
||||||
std::string file_appender_name = section_name.substr(file_appender_section_prefix.length());
|
|
||||||
fc::path file_name = section_tree.get<std::string>("filename");
|
|
||||||
if (file_name.is_relative())
|
|
||||||
file_name = fc::absolute(config_ini_filename).parent_path() / file_name;
|
|
||||||
|
|
||||||
|
|
||||||
// construct a default file appender config here
|
|
||||||
// filename will be taken from ini file, everything else hard-coded here
|
|
||||||
fc::file_appender::config file_appender_config;
|
|
||||||
file_appender_config.filename = file_name;
|
|
||||||
file_appender_config.flush = true;
|
|
||||||
file_appender_config.rotate = true;
|
|
||||||
file_appender_config.rotation_interval = fc::hours(1);
|
|
||||||
file_appender_config.rotation_limit = fc::days(1);
|
|
||||||
logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config, 20)));
|
|
||||||
found_logging_config = true;
|
|
||||||
}
|
|
||||||
else if (boost::starts_with(section_name, logger_section_prefix))
|
|
||||||
{
|
|
||||||
std::string logger_name = section_name.substr(logger_section_prefix.length());
|
|
||||||
std::string level_string = section_tree.get<std::string>("level");
|
|
||||||
std::string appenders_string = section_tree.get<std::string>("appenders");
|
|
||||||
fc::logger_config logger_config(logger_name);
|
|
||||||
logger_config.level = fc::variant(level_string, 1).as<fc::log_level>(1);
|
|
||||||
boost::split(logger_config.appenders, appenders_string,
|
|
||||||
boost::is_any_of(" ,"),
|
|
||||||
boost::token_compress_on);
|
|
||||||
logging_config.loggers.push_back(logger_config);
|
|
||||||
found_logging_config = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found_logging_config)
|
|
||||||
return logging_config;
|
|
||||||
else
|
|
||||||
return fc::optional<fc::logging_config>();
|
|
||||||
}
|
|
||||||
FC_RETHROW_EXCEPTIONS(warn, "")
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
add_executable( delayed_node main.cpp )
|
|
||||||
if( UNIX AND NOT APPLE )
|
|
||||||
set(rt_library rt )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package( Gperftools QUIET )
|
|
||||||
if( GPERFTOOLS_FOUND )
|
|
||||||
message( STATUS "Found gperftools; compiling delayed_node with TCMalloc")
|
|
||||||
list( APPEND PLATFORM_SPECIFIC_LIBS tcmalloc )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries( delayed_node
|
|
||||||
PRIVATE graphene_app graphene_egenesis_full graphene_delayed_node ${PLATFORM_SPECIFIC_LIBS} )
|
|
||||||
|
|
||||||
install( TARGETS
|
|
||||||
delayed_node
|
|
||||||
|
|
||||||
RUNTIME DESTINATION bin
|
|
||||||
LIBRARY DESTINATION lib
|
|
||||||
ARCHIVE DESTINATION lib
|
|
||||||
)
|
|
||||||
|
|
@ -1,305 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
|
||||||
*
|
|
||||||
* The MIT License
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
#include <graphene/app/application.hpp>
|
|
||||||
|
|
||||||
#include <graphene/delayed_node/delayed_node_plugin.hpp>
|
|
||||||
#include <graphene/account_history/account_history_plugin.hpp>
|
|
||||||
#include <graphene/market_history/market_history_plugin.hpp>
|
|
||||||
|
|
||||||
#include <fc/exception/exception.hpp>
|
|
||||||
#include <fc/thread/thread.hpp>
|
|
||||||
#include <fc/interprocess/signals.hpp>
|
|
||||||
#include <fc/log/console_appender.hpp>
|
|
||||||
#include <fc/log/file_appender.hpp>
|
|
||||||
#include <fc/log/logger.hpp>
|
|
||||||
#include <fc/log/logger_config.hpp>
|
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
# include <signal.h>
|
|
||||||
#else
|
|
||||||
# include <csignal>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace graphene;
|
|
||||||
namespace bpo = boost::program_options;
|
|
||||||
|
|
||||||
void write_default_logging_config_to_stream(std::ostream& out);
|
|
||||||
fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename);
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
try {
|
|
||||||
app::application node;
|
|
||||||
bpo::options_description app_options("Graphene Delayed Node");
|
|
||||||
bpo::options_description cfg_options("Graphene Delayed Node");
|
|
||||||
app_options.add_options()
|
|
||||||
("help,h", "Print this help message and exit.")
|
|
||||||
("data-dir,d", bpo::value<boost::filesystem::path>()->default_value("delayed_node_data_dir"), "Directory containing databases, configuration file, etc.")
|
|
||||||
;
|
|
||||||
|
|
||||||
bpo::variables_map options;
|
|
||||||
|
|
||||||
bpo::options_description cli, cfg;
|
|
||||||
node.set_program_options(cli, cfg);
|
|
||||||
cfg_options.add(cfg);
|
|
||||||
|
|
||||||
cfg_options.add_options()
|
|
||||||
("plugins", bpo::value<std::string>()->default_value("delayed_node account_history market_history"),
|
|
||||||
"Space-separated list of plugins to activate");
|
|
||||||
|
|
||||||
auto delayed_plug = node.register_plugin<delayed_node::delayed_node_plugin>();
|
|
||||||
auto history_plug = node.register_plugin<account_history::account_history_plugin>();
|
|
||||||
auto market_history_plug = node.register_plugin<market_history::market_history_plugin>();
|
|
||||||
|
|
||||||
// add plugin options to config
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bpo::options_description cli, cfg;
|
|
||||||
node.set_program_options(cli, cfg);
|
|
||||||
app_options.add(cli);
|
|
||||||
cfg_options.add(cfg);
|
|
||||||
bpo::store(bpo::parse_command_line(argc, argv, app_options), options);
|
|
||||||
}
|
|
||||||
catch (const boost::program_options::error& e)
|
|
||||||
{
|
|
||||||
std::cerr << "Error parsing command line: " << e.what() << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( options.count("help") )
|
|
||||||
{
|
|
||||||
std::cout << app_options << "\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fc::path data_dir;
|
|
||||||
if( options.count("data-dir") )
|
|
||||||
{
|
|
||||||
data_dir = options["data-dir"].as<boost::filesystem::path>();
|
|
||||||
if( data_dir.is_relative() )
|
|
||||||
data_dir = fc::current_path() / data_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
fc::path config_ini_path = data_dir / "config.ini";
|
|
||||||
// Create config file if not already present
|
|
||||||
if( !fc::exists(config_ini_path) )
|
|
||||||
{
|
|
||||||
ilog("Writing new config file at ${path}", ("path", config_ini_path));
|
|
||||||
if( !fc::exists(data_dir) )
|
|
||||||
fc::create_directories(data_dir);
|
|
||||||
|
|
||||||
std::ofstream out_cfg(config_ini_path.preferred_string());
|
|
||||||
for( const boost::shared_ptr<bpo::option_description> od : cfg_options.options() )
|
|
||||||
{
|
|
||||||
if( !od->description().empty() )
|
|
||||||
out_cfg << "# " << od->description() << "\n";
|
|
||||||
boost::any store;
|
|
||||||
if( !od->semantic()->apply_default(store) )
|
|
||||||
out_cfg << "# " << od->long_name() << " = \n";
|
|
||||||
else {
|
|
||||||
auto example = od->format_parameter();
|
|
||||||
if( example.empty() )
|
|
||||||
// This is a boolean switch
|
|
||||||
out_cfg << od->long_name() << " = " << "false\n";
|
|
||||||
else {
|
|
||||||
// The string is formatted "arg (=<interesting part>)"
|
|
||||||
example.erase(0, 6);
|
|
||||||
example.erase(example.length()-1);
|
|
||||||
out_cfg << od->long_name() << " = " << example << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out_cfg << "\n";
|
|
||||||
}
|
|
||||||
write_default_logging_config_to_stream(out_cfg);
|
|
||||||
out_cfg.close();
|
|
||||||
// read the default logging config we just wrote out to the file and start using it
|
|
||||||
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
|
|
||||||
if (logging_config)
|
|
||||||
fc::configure_logging(*logging_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse configuration file
|
|
||||||
try {
|
|
||||||
bpo::store(bpo::parse_config_file<char>(config_ini_path.preferred_string().c_str(), cfg_options, true), options);
|
|
||||||
// try to get logging options from the config file.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fc::optional<fc::logging_config> logging_config = load_logging_config_from_ini_file(config_ini_path);
|
|
||||||
if (logging_config)
|
|
||||||
fc::configure_logging(*logging_config);
|
|
||||||
}
|
|
||||||
catch (const fc::exception&)
|
|
||||||
{
|
|
||||||
wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bpo::notify(options);
|
|
||||||
} catch( const boost::program_options::error& e ) {
|
|
||||||
elog("Error parsing configuration file: ${e}", ("e", e.what()));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !options.count("plugins") )
|
|
||||||
options.insert( std::make_pair( "plugins", bpo::variable_value(std::string("delayed_node account_history market_history"), true) ) );
|
|
||||||
|
|
||||||
node.initialize(data_dir, options);
|
|
||||||
node.initialize_plugins( options );
|
|
||||||
|
|
||||||
node.startup();
|
|
||||||
node.startup_plugins();
|
|
||||||
|
|
||||||
fc::promise<int>::ptr exit_promise = new fc::promise<int>("UNIX Signal Handler");
|
|
||||||
fc::set_signal_handler([&exit_promise](int signal) {
|
|
||||||
exit_promise->set_value(signal);
|
|
||||||
}, SIGINT);
|
|
||||||
|
|
||||||
ilog("Started delayed node on a chain with ${h} blocks.", ("h", node.chain_database()->head_block_num()));
|
|
||||||
ilog("Chain ID is ${id}", ("id", node.chain_database()->get_chain_id()) );
|
|
||||||
|
|
||||||
int signal = exit_promise->wait();
|
|
||||||
ilog("Exiting from signal ${n}", ("n", signal));
|
|
||||||
node.shutdown_plugins();
|
|
||||||
return 0;
|
|
||||||
} catch( const fc::exception& e ) {
|
|
||||||
elog("Exiting with error:\n${e}", ("e", e.to_detail_string()));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// logging config is too complicated to be parsed by boost::program_options,
|
|
||||||
// so we do it by hand
|
|
||||||
//
|
|
||||||
// Currently, you can only specify the filenames and logging levels, which
|
|
||||||
// are all most users would want to change. At a later time, options can
|
|
||||||
// be added to control rotation intervals, compression, and other seldom-
|
|
||||||
// used features
|
|
||||||
void write_default_logging_config_to_stream(std::ostream& out)
|
|
||||||
{
|
|
||||||
out << "# declare an appender named \"stderr\" that writes messages to the console\n"
|
|
||||||
"[log.console_appender.stderr]\n"
|
|
||||||
"stream=std_error\n\n"
|
|
||||||
"# declare an appender named \"p2p\" that writes messages to p2p.log\n"
|
|
||||||
"[log.file_appender.p2p]\n"
|
|
||||||
"filename=logs/p2p/p2p.log\n"
|
|
||||||
"# filename can be absolute or relative to this config file\n\n"
|
|
||||||
"# route any messages logged to the default logger to the \"stderr\" logger we\n"
|
|
||||||
"# declared above, if they are info level are higher\n"
|
|
||||||
"[logger.default]\n"
|
|
||||||
"level=info\n"
|
|
||||||
"appenders=stderr\n\n"
|
|
||||||
"# route messages sent to the \"p2p\" logger to the p2p appender declared above\n"
|
|
||||||
"[logger.p2p]\n"
|
|
||||||
"level=info\n"
|
|
||||||
"appenders=p2p\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
fc::optional<fc::logging_config> load_logging_config_from_ini_file(const fc::path& config_ini_filename)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fc::logging_config logging_config;
|
|
||||||
bool found_logging_config = false;
|
|
||||||
|
|
||||||
boost::property_tree::ptree config_ini_tree;
|
|
||||||
boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree);
|
|
||||||
for (const auto& section : config_ini_tree)
|
|
||||||
{
|
|
||||||
const std::string& section_name = section.first;
|
|
||||||
const boost::property_tree::ptree& section_tree = section.second;
|
|
||||||
|
|
||||||
const std::string console_appender_section_prefix = "log.console_appender.";
|
|
||||||
const std::string file_appender_section_prefix = "log.file_appender.";
|
|
||||||
const std::string logger_section_prefix = "logger.";
|
|
||||||
|
|
||||||
if (boost::starts_with(section_name, console_appender_section_prefix))
|
|
||||||
{
|
|
||||||
std::string console_appender_name = section_name.substr(console_appender_section_prefix.length());
|
|
||||||
std::string stream_name = section_tree.get<std::string>("stream");
|
|
||||||
|
|
||||||
// construct a default console appender config here
|
|
||||||
// stdout/stderr will be taken from ini file, everything else hard-coded here
|
|
||||||
fc::console_appender::config console_appender_config;
|
|
||||||
console_appender_config.level_colors.emplace_back(
|
|
||||||
fc::console_appender::level_color(fc::log_level::debug,
|
|
||||||
fc::console_appender::color::green));
|
|
||||||
console_appender_config.level_colors.emplace_back(
|
|
||||||
fc::console_appender::level_color(fc::log_level::warn,
|
|
||||||
fc::console_appender::color::brown));
|
|
||||||
console_appender_config.level_colors.emplace_back(
|
|
||||||
fc::console_appender::level_color(fc::log_level::error,
|
|
||||||
fc::console_appender::color::cyan));
|
|
||||||
console_appender_config.stream = fc::variant(stream_name, 1).as<fc::console_appender::stream::type>(1);
|
|
||||||
logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config, GRAPHENE_MAX_NESTED_OBJECTS)));
|
|
||||||
found_logging_config = true;
|
|
||||||
}
|
|
||||||
else if (boost::starts_with(section_name, file_appender_section_prefix))
|
|
||||||
{
|
|
||||||
std::string file_appender_name = section_name.substr(file_appender_section_prefix.length());
|
|
||||||
fc::path file_name = section_tree.get<std::string>("filename");
|
|
||||||
if (file_name.is_relative())
|
|
||||||
file_name = fc::absolute(config_ini_filename).parent_path() / file_name;
|
|
||||||
|
|
||||||
|
|
||||||
// construct a default file appender config here
|
|
||||||
// filename will be taken from ini file, everything else hard-coded here
|
|
||||||
fc::file_appender::config file_appender_config;
|
|
||||||
file_appender_config.filename = file_name;
|
|
||||||
file_appender_config.flush = true;
|
|
||||||
file_appender_config.rotate = true;
|
|
||||||
file_appender_config.rotation_interval = fc::hours(1);
|
|
||||||
file_appender_config.rotation_limit = fc::days(1);
|
|
||||||
logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config, GRAPHENE_MAX_NESTED_OBJECTS)));
|
|
||||||
found_logging_config = true;
|
|
||||||
}
|
|
||||||
else if (boost::starts_with(section_name, logger_section_prefix))
|
|
||||||
{
|
|
||||||
std::string logger_name = section_name.substr(logger_section_prefix.length());
|
|
||||||
std::string level_string = section_tree.get<std::string>("level");
|
|
||||||
std::string appenders_string = section_tree.get<std::string>("appenders");
|
|
||||||
fc::logger_config logger_config(logger_name);
|
|
||||||
logger_config.level = fc::variant(level_string, 1).as<fc::log_level>(1);
|
|
||||||
boost::split(logger_config.appenders, appenders_string,
|
|
||||||
boost::is_any_of(" ,"),
|
|
||||||
boost::token_compress_on);
|
|
||||||
logging_config.loggers.push_back(logger_config);
|
|
||||||
found_logging_config = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found_logging_config)
|
|
||||||
return logging_config;
|
|
||||||
else
|
|
||||||
return fc::optional<fc::logging_config>();
|
|
||||||
}
|
|
||||||
FC_RETHROW_EXCEPTIONS(warn, "")
|
|
||||||
}
|
|
||||||
|
|
@ -11,7 +11,7 @@ endif()
|
||||||
|
|
||||||
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
|
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
|
||||||
target_link_libraries( witness_node
|
target_link_libraries( witness_node
|
||||||
PRIVATE graphene_app graphene_egenesis_full graphene_snapshot graphene_witness peerplays_sidechain ${PLATFORM_SPECIFIC_LIBS} )
|
PRIVATE graphene_app graphene_egenesis_full graphene_snapshot graphene_delayed_node graphene_witness peerplays_sidechain ${PLATFORM_SPECIFIC_LIBS} )
|
||||||
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
|
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
|
||||||
|
|
||||||
install( TARGETS
|
install( TARGETS
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
#include <graphene/affiliate_stats/affiliate_stats_plugin.hpp>
|
#include <graphene/affiliate_stats/affiliate_stats_plugin.hpp>
|
||||||
#include <graphene/bookie/bookie_plugin.hpp>
|
#include <graphene/bookie/bookie_plugin.hpp>
|
||||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||||
|
#include <graphene/delayed_node/delayed_node_plugin.hpp>
|
||||||
#include <graphene/utilities/git_revision.hpp>
|
#include <graphene/utilities/git_revision.hpp>
|
||||||
#include <graphene/snapshot/snapshot.hpp>
|
#include <graphene/snapshot/snapshot.hpp>
|
||||||
|
|
||||||
|
|
@ -90,6 +91,7 @@ int main(int argc, char** argv) {
|
||||||
auto bookie_plug = node->register_plugin<bookie::bookie_plugin>();
|
auto bookie_plug = node->register_plugin<bookie::bookie_plugin>();
|
||||||
auto peerplays_sidechain = node->register_plugin<peerplays_sidechain::peerplays_sidechain_plugin>();
|
auto peerplays_sidechain = node->register_plugin<peerplays_sidechain::peerplays_sidechain_plugin>();
|
||||||
auto snapshot_plug = node->register_plugin<snapshot_plugin::snapshot_plugin>();
|
auto snapshot_plug = node->register_plugin<snapshot_plugin::snapshot_plugin>();
|
||||||
|
auto delayed_plug = node->register_plugin<delayed_node::delayed_node_plugin>();
|
||||||
|
|
||||||
// add plugin options to config
|
// add plugin options to config
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,12 @@ signed_block cli_fixture::generate_block(uint32_t skip, const fc::ecc::private_k
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cli_fixture::generate_blocks( uint32_t block_count )
|
||||||
|
{
|
||||||
|
for( uint32_t i = 0; i < block_count; ++i )
|
||||||
|
generate_block();
|
||||||
|
}
|
||||||
|
|
||||||
bool cli_fixture::generate_maintenance_block() {
|
bool cli_fixture::generate_maintenance_block() {
|
||||||
try {
|
try {
|
||||||
fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
|
fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ struct cli_fixture
|
||||||
int miss_blocks = 0);
|
int miss_blocks = 0);
|
||||||
|
|
||||||
void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0);
|
void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0);
|
||||||
|
void generate_blocks( uint32_t block_count );
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
/// @brief Skip intermediate blocks, and generate a maintenance block
|
/// @brief Skip intermediate blocks, and generate a maintenance block
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
147
tests/peerplays_sidechain/ethereum_transaction_tests.cpp
Normal file
147
tests/peerplays_sidechain/ethereum_transaction_tests.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <fc/crypto/hex.hpp>
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
|
||||||
|
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
|
||||||
|
|
||||||
|
using namespace graphene::peerplays_sidechain::ethereum;
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) {
|
||||||
|
const withdrawal_encoder encoder;
|
||||||
|
const auto tx = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0");
|
||||||
|
BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000");
|
||||||
|
|
||||||
|
const auto tx1 = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1");
|
||||||
|
BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(update_owners_encoder_test) {
|
||||||
|
std::vector<std::pair<std::string, uint16_t>> owners_weights;
|
||||||
|
owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1);
|
||||||
|
owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1);
|
||||||
|
|
||||||
|
const update_owners_encoder encoder;
|
||||||
|
const auto tx = encoder.encode(owners_weights, "1.35.0");
|
||||||
|
BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000");
|
||||||
|
|
||||||
|
owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1);
|
||||||
|
const auto tx1 = encoder.encode(owners_weights, "1.36.1");
|
||||||
|
BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) {
|
||||||
|
raw_transaction raw_tr;
|
||||||
|
raw_tr.nonce = "0x0";
|
||||||
|
raw_tr.gas_price = "0x3b9aca07";
|
||||||
|
raw_tr.gas_limit = "0x7a1200";
|
||||||
|
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
|
||||||
|
raw_tr.value = "";
|
||||||
|
raw_tr.data = "";
|
||||||
|
raw_tr.chain_id = "0x21";
|
||||||
|
|
||||||
|
const auto tx = raw_tr.serialize();
|
||||||
|
BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080");
|
||||||
|
|
||||||
|
//! Change value
|
||||||
|
raw_tr.value = "0x1BC16D674EC80000";
|
||||||
|
const auto tx1 = raw_tr.serialize();
|
||||||
|
BOOST_CHECK_EQUAL(tx1, "0xEC80843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000080218080");
|
||||||
|
|
||||||
|
//! Change data
|
||||||
|
raw_tr.data = "0x893d20e8";
|
||||||
|
const auto tx2 = raw_tr.serialize();
|
||||||
|
BOOST_CHECK_EQUAL(tx2, "0xF080843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000084893D20E8218080");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(raw_transaction_deserialization_test) {
|
||||||
|
const raw_transaction raw_tr{"E480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(raw_tr.nonce, "0x0");
|
||||||
|
BOOST_CHECK_EQUAL(raw_tr.gas_price, "0x3b9aca07");
|
||||||
|
BOOST_CHECK_EQUAL(raw_tr.gas_limit, "0x7a1200");
|
||||||
|
BOOST_CHECK_EQUAL(raw_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348");
|
||||||
|
BOOST_CHECK_EQUAL(raw_tr.value, "0x0");
|
||||||
|
BOOST_CHECK_EQUAL(raw_tr.data, "");
|
||||||
|
BOOST_CHECK_EQUAL(raw_tr.chain_id, "0x21");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(raw_transaction_hash_test) {
|
||||||
|
raw_transaction raw_tr;
|
||||||
|
raw_tr.nonce = "0x0";
|
||||||
|
raw_tr.gas_price = "0x3b9aca07";
|
||||||
|
raw_tr.gas_limit = "0x7a1200";
|
||||||
|
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
|
||||||
|
raw_tr.value = "";
|
||||||
|
raw_tr.data = "";
|
||||||
|
raw_tr.chain_id = "0x21";
|
||||||
|
|
||||||
|
const auto tx = raw_tr.serialize();
|
||||||
|
BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080");
|
||||||
|
|
||||||
|
const auto hash = raw_tr.hash();
|
||||||
|
const auto hash_str = fc::to_hex((char *)&hash[0], hash.size());
|
||||||
|
BOOST_CHECK_EQUAL(hash_str, "34934410cd305f4fa4e75a2c9294d625d6fbba729b5642ed2ca757ead50bb1fb");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(sign_transaction_test) {
|
||||||
|
raw_transaction raw_tr;
|
||||||
|
raw_tr.nonce = "0x0";
|
||||||
|
raw_tr.gas_price = "0x3b9aca07";
|
||||||
|
raw_tr.gas_limit = "0x7a1200";
|
||||||
|
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
|
||||||
|
raw_tr.value = "";
|
||||||
|
raw_tr.data = "";
|
||||||
|
raw_tr.chain_id = "0x21";
|
||||||
|
|
||||||
|
const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.r, "5f09de6ac850b2a9e94acd709c12d4e9adbabc6b72281ec0bbe13bca7e57c7ce");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.v, "65");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.s, "7ca5f26c5b3e25f14a32b18ac9a2a41b7c68efd3b04b118e1b1f4bf1c4e299b0");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(sign_transaction_serialization_test) {
|
||||||
|
raw_transaction raw_tr;
|
||||||
|
raw_tr.nonce = "0x0";
|
||||||
|
raw_tr.gas_price = "0x3b9aca07";
|
||||||
|
raw_tr.gas_limit = "0x7a1200";
|
||||||
|
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
|
||||||
|
raw_tr.value = "";
|
||||||
|
raw_tr.data = "";
|
||||||
|
raw_tr.chain_id = "0x21";
|
||||||
|
|
||||||
|
const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060");
|
||||||
|
const auto tx = sign_tr.serialize();
|
||||||
|
BOOST_CHECK_EQUAL(tx, "0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(sign_transaction_deserialization_test) {
|
||||||
|
const signed_transaction sign_tr{"0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0"};
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.nonce, "0x0");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.gas_price, "0x3b9aca07");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.gas_limit, "0x7a1200");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.value, "0x0");
|
||||||
|
BOOST_CHECK_EQUAL(sign_tr.data, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(sign_transaction_recover_test) {
|
||||||
|
const std::string chain_id = "0x21";
|
||||||
|
|
||||||
|
raw_transaction raw_tr;
|
||||||
|
raw_tr.nonce = "0x0";
|
||||||
|
raw_tr.gas_price = "0x3b9aca07";
|
||||||
|
raw_tr.gas_limit = "0x7a1200";
|
||||||
|
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
|
||||||
|
raw_tr.value = "";
|
||||||
|
raw_tr.data = "";
|
||||||
|
raw_tr.chain_id = chain_id;
|
||||||
|
|
||||||
|
const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060");
|
||||||
|
const auto from = sign_tr.recover(chain_id);
|
||||||
|
BOOST_CHECK_EQUAL(from, "0x5fbbb31be52608d2f52247e8400b7fcaa9e0bc12");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
@ -135,6 +135,7 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) {
|
||||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||||
sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address";
|
sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address";
|
||||||
sidechain_public_keys[sidechain_type::hive] = "hive address";
|
sidechain_public_keys[sidechain_type::hive] = "hive address";
|
||||||
|
sidechain_public_keys[sidechain_type::ethereum] = "ethereum address";
|
||||||
|
|
||||||
son_create_operation op;
|
son_create_operation op;
|
||||||
op.owner_account = bob_id;
|
op.owner_account = bob_id;
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) {
|
||||||
{
|
{
|
||||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||||
sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address";
|
sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address";
|
||||||
|
sidechain_public_keys[sidechain_type::hive] = "hive account";
|
||||||
|
sidechain_public_keys[sidechain_type::ethereum] = "ethereum address";
|
||||||
|
|
||||||
son_create_operation op;
|
son_create_operation op;
|
||||||
op.owner_account = alice_id;
|
op.owner_account = alice_id;
|
||||||
|
|
@ -111,6 +113,8 @@ BOOST_AUTO_TEST_CASE( create_son_test ) {
|
||||||
BOOST_CHECK( obj->url == test_url );
|
BOOST_CHECK( obj->url == test_url );
|
||||||
BOOST_CHECK( obj->signing_key == alice_public_key );
|
BOOST_CHECK( obj->signing_key == alice_public_key );
|
||||||
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" );
|
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" );
|
||||||
|
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::hive) == "hive account" );
|
||||||
|
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::ethereum) == "ethereum address" );
|
||||||
BOOST_CHECK( obj->deposit.instance == deposit.instance.value );
|
BOOST_CHECK( obj->deposit.instance == deposit.instance.value );
|
||||||
BOOST_CHECK( obj->pay_vb.instance == payment.instance.value );
|
BOOST_CHECK( obj->pay_vb.instance == payment.instance.value );
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +128,9 @@ BOOST_AUTO_TEST_CASE( update_son_test ) {
|
||||||
|
|
||||||
{
|
{
|
||||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||||
sidechain_public_keys[sidechain_type::bitcoin] = "new bitcoin address";
|
sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address";
|
||||||
|
sidechain_public_keys[sidechain_type::hive] = "hive account";
|
||||||
|
sidechain_public_keys[sidechain_type::ethereum] = "ethereum address";
|
||||||
|
|
||||||
son_update_operation op;
|
son_update_operation op;
|
||||||
op.son_id = son_id_type(0);
|
op.son_id = son_id_type(0);
|
||||||
|
|
@ -143,7 +149,9 @@ BOOST_AUTO_TEST_CASE( update_son_test ) {
|
||||||
auto obj = idx.find( alice_id );
|
auto obj = idx.find( alice_id );
|
||||||
BOOST_REQUIRE( obj != idx.end() );
|
BOOST_REQUIRE( obj != idx.end() );
|
||||||
BOOST_CHECK( obj->url == new_url );
|
BOOST_CHECK( obj->url == new_url );
|
||||||
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "new bitcoin address" );
|
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address" );
|
||||||
|
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::hive) == "hive account" );
|
||||||
|
BOOST_CHECK( obj->sidechain_public_keys.at(sidechain_type::ethereum) == "ethereum address" );
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( deregister_son_test ) {
|
BOOST_AUTO_TEST_CASE( deregister_son_test ) {
|
||||||
|
|
@ -195,12 +203,14 @@ try {
|
||||||
{
|
{
|
||||||
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
||||||
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
||||||
|
_s.statuses[sidechain_type::ethereum] = son_status::in_maintenance;
|
||||||
});
|
});
|
||||||
|
|
||||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||||
{
|
{
|
||||||
_s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
_s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
||||||
_s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
_s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
||||||
|
_s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
||||||
});
|
});
|
||||||
|
|
||||||
auto deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
auto deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||||
|
|
@ -222,6 +232,7 @@ try {
|
||||||
BOOST_REQUIRE( idx.size() == 1 );
|
BOOST_REQUIRE( idx.size() == 1 );
|
||||||
BOOST_REQUIRE( obj->statuses.at(sidechain_type::bitcoin) == son_status::deregistered );
|
BOOST_REQUIRE( obj->statuses.at(sidechain_type::bitcoin) == son_status::deregistered );
|
||||||
BOOST_REQUIRE( obj->statuses.at(sidechain_type::hive) == son_status::deregistered );
|
BOOST_REQUIRE( obj->statuses.at(sidechain_type::hive) == son_status::deregistered );
|
||||||
|
BOOST_REQUIRE( obj->statuses.at(sidechain_type::ethereum) == son_status::deregistered );
|
||||||
BOOST_REQUIRE( son_stats_obj->deregistered_timestamp == now );
|
BOOST_REQUIRE( son_stats_obj->deregistered_timestamp == now );
|
||||||
|
|
||||||
deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||||
|
|
@ -496,30 +507,38 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
||||||
{
|
{
|
||||||
_s.txs_signed[sidechain_type::bitcoin] = 2;
|
_s.txs_signed[sidechain_type::bitcoin] = 2;
|
||||||
_s.txs_signed[sidechain_type::hive] = 4;
|
_s.txs_signed[sidechain_type::hive] = 4;
|
||||||
|
_s.txs_signed[sidechain_type::ethereum] = 6;
|
||||||
|
|
||||||
_s.total_txs_signed[sidechain_type::bitcoin] = 2;
|
_s.total_txs_signed[sidechain_type::bitcoin] = 2;
|
||||||
_s.total_txs_signed[sidechain_type::hive] = 4;
|
_s.total_txs_signed[sidechain_type::hive] = 4;
|
||||||
|
_s.total_txs_signed[sidechain_type::ethereum] = 6;
|
||||||
|
|
||||||
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
||||||
_s.sidechain_txs_reported[sidechain_type::hive] = 8;
|
_s.sidechain_txs_reported[sidechain_type::hive] = 8;
|
||||||
|
_s.sidechain_txs_reported[sidechain_type::ethereum] = 12;
|
||||||
|
|
||||||
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
||||||
_s.total_sidechain_txs_reported[sidechain_type::hive] = 8;
|
_s.total_sidechain_txs_reported[sidechain_type::hive] = 8;
|
||||||
|
_s.total_sidechain_txs_reported[sidechain_type::ethereum] = 12;
|
||||||
});
|
});
|
||||||
// Modify the transaction signed statistics of Bob's SON
|
// Modify the transaction signed statistics of Bob's SON
|
||||||
db.modify( *son_stats_obj2, [&]( son_statistics_object& _s)
|
db.modify( *son_stats_obj2, [&]( son_statistics_object& _s)
|
||||||
{
|
{
|
||||||
_s.txs_signed[sidechain_type::bitcoin] = 3;
|
_s.txs_signed[sidechain_type::bitcoin] = 3;
|
||||||
_s.txs_signed[sidechain_type::hive] = 6;
|
_s.txs_signed[sidechain_type::hive] = 6;
|
||||||
|
_s.txs_signed[sidechain_type::ethereum] = 9;
|
||||||
|
|
||||||
_s.total_txs_signed[sidechain_type::bitcoin] = 3;
|
_s.total_txs_signed[sidechain_type::bitcoin] = 3;
|
||||||
_s.total_txs_signed[sidechain_type::hive] = 6;
|
_s.total_txs_signed[sidechain_type::hive] = 6;
|
||||||
|
_s.total_txs_signed[sidechain_type::ethereum] = 9;
|
||||||
|
|
||||||
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
||||||
_s.sidechain_txs_reported[sidechain_type::hive] = 12;
|
_s.sidechain_txs_reported[sidechain_type::hive] = 12;
|
||||||
|
_s.sidechain_txs_reported[sidechain_type::ethereum] = 18;
|
||||||
|
|
||||||
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
||||||
_s.total_sidechain_txs_reported[sidechain_type::hive] = 12;
|
_s.total_sidechain_txs_reported[sidechain_type::hive] = 12;
|
||||||
|
_s.total_sidechain_txs_reported[sidechain_type::ethereum] = 18;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Note the balances before the maintenance
|
// Note the balances before the maintenance
|
||||||
|
|
@ -531,21 +550,29 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
||||||
// Check if the signed transaction statistics are reset for both SONs
|
// Check if the signed transaction statistics are reset for both SONs
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::bitcoin), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::bitcoin), 0);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::hive), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::hive), 0);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->txs_signed.at(sidechain_type::ethereum), 0);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::bitcoin), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::bitcoin), 0);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::hive), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::hive), 0);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->txs_signed.at(sidechain_type::ethereum), 0);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::bitcoin), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::bitcoin), 0);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::hive), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::hive), 0);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->sidechain_txs_reported.at(sidechain_type::ethereum), 0);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::bitcoin), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::bitcoin), 0);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::hive), 0);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::hive), 0);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->sidechain_txs_reported.at(sidechain_type::ethereum), 0);
|
||||||
|
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::bitcoin), 2);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::bitcoin), 2);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::hive), 4);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::hive), 4);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_txs_signed.at(sidechain_type::ethereum), 6);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::bitcoin), 3);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::bitcoin), 3);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::hive), 6);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::hive), 6);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_txs_signed.at(sidechain_type::ethereum), 9);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 4);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 4);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::hive), 8);
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::hive), 8);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj1->total_sidechain_txs_reported.at(sidechain_type::ethereum), 12);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 6);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::bitcoin), 6);
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::hive), 12);
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::hive), 12);
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj2->total_sidechain_txs_reported.at(sidechain_type::ethereum), 18);
|
||||||
// Check that Alice and Bob are paid for signing the transactions in the previous day/cycle
|
// Check that Alice and Bob are paid for signing the transactions in the previous day/cycle
|
||||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance);
|
BOOST_REQUIRE_EQUAL(db.get_balance(obj1->son_account, asset_id_type()).amount.value, 80+obj1_balance);
|
||||||
BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance);
|
BOOST_REQUIRE_EQUAL(db.get_balance(obj2->son_account, asset_id_type()).amount.value, 120+obj2_balance);
|
||||||
|
|
@ -609,12 +636,14 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
{
|
{
|
||||||
_s.statuses[sidechain_type::bitcoin] = son_status::active;
|
_s.statuses[sidechain_type::bitcoin] = son_status::active;
|
||||||
_s.statuses[sidechain_type::hive] = son_status::active;
|
_s.statuses[sidechain_type::hive] = son_status::active;
|
||||||
|
_s.statuses[sidechain_type::ethereum] = son_status::active;
|
||||||
});
|
});
|
||||||
|
|
||||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||||
{
|
{
|
||||||
_s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time());
|
_s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time());
|
||||||
_s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time());
|
_s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time());
|
||||||
|
_s.last_down_timestamp[sidechain_type::ethereum] = fc::time_point_sec(db.head_block_time());
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -633,6 +662,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
trx.clear();
|
trx.clear();
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance);
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance);
|
||||||
|
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::request_maintenance);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -651,6 +681,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
trx.clear();
|
trx.clear();
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||||
|
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify SON's status to in_maintenance
|
// Modify SON's status to in_maintenance
|
||||||
|
|
@ -658,11 +689,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
{
|
{
|
||||||
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
||||||
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
||||||
|
_s.statuses[sidechain_type::ethereum] = son_status::in_maintenance;
|
||||||
});
|
});
|
||||||
|
|
||||||
flat_map<sidechain_type, uint64_t> downtime;
|
flat_map<sidechain_type, uint64_t> downtime;
|
||||||
downtime[sidechain_type::bitcoin] = 0;
|
downtime[sidechain_type::bitcoin] = 0;
|
||||||
downtime[sidechain_type::hive] = 0;
|
downtime[sidechain_type::hive] = 0;
|
||||||
|
downtime[sidechain_type::ethereum] = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
generate_block();
|
generate_block();
|
||||||
|
|
@ -680,12 +713,16 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
trx.clear();
|
trx.clear();
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch());
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch());
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch());
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch());
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch());
|
||||||
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch();
|
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch();
|
||||||
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch();
|
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch();
|
||||||
|
downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch();
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive);
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive);
|
||||||
|
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::inactive);
|
||||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
||||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
||||||
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify SON's status to in_maintenance
|
// Modify SON's status to in_maintenance
|
||||||
|
|
@ -693,6 +730,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
{
|
{
|
||||||
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
_s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance;
|
||||||
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
_s.statuses[sidechain_type::hive] = son_status::in_maintenance;
|
||||||
|
_s.statuses[sidechain_type::ethereum] = son_status::in_maintenance;
|
||||||
});
|
});
|
||||||
|
|
||||||
// SON is selected as one of the active SONs
|
// SON is selected as one of the active SONs
|
||||||
|
|
@ -702,6 +740,7 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
son_inf.son_id = son_id_type(0);
|
son_inf.son_id = son_id_type(0);
|
||||||
_gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf);
|
_gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf);
|
||||||
_gpo.active_sons[sidechain_type::hive].push_back(son_inf);
|
_gpo.active_sons[sidechain_type::hive].push_back(son_inf);
|
||||||
|
_gpo.active_sons[sidechain_type::ethereum].push_back(son_inf);
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -721,12 +760,16 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
|
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch());
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch());
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch());
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch());
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch());
|
||||||
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch();
|
downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch();
|
||||||
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch();
|
downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch();
|
||||||
|
downtime[sidechain_type::ethereum] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum).sec_since_epoch();
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||||
|
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
||||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
||||||
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -745,10 +788,13 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
||||||
trx.clear();
|
trx.clear();
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin));
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin));
|
||||||
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive));
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive));
|
||||||
|
BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::ethereum), downtime.at(sidechain_type::ethereum));
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||||
|
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts);
|
||||||
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts);
|
||||||
|
BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::ethereum) == op.ts);
|
||||||
}
|
}
|
||||||
} FC_LOG_AND_RETHROW()
|
} FC_LOG_AND_RETHROW()
|
||||||
}
|
}
|
||||||
|
|
@ -775,6 +821,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) {
|
||||||
|
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active);
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active);
|
||||||
|
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::active);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Check that transaction fails if down_ts < last_active_timestamp
|
// Check that transaction fails if down_ts < last_active_timestamp
|
||||||
|
|
@ -828,8 +875,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) {
|
||||||
|
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance);
|
||||||
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance);
|
BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance);
|
||||||
|
BOOST_CHECK( obj->statuses.at(sidechain_type::ethereum) == son_status::in_maintenance);
|
||||||
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts);
|
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts);
|
||||||
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts);
|
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts);
|
||||||
|
BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::ethereum) == op.down_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue