Merge branch 'develop' into 'beatrice'
Merge develop to beatrice 2022-11 See merge request PBSA/peerplays!180
This commit is contained in:
commit
d76b752c8c
127 changed files with 7346 additions and 5963 deletions
|
|
@ -44,8 +44,6 @@ test-mainnet:
|
|||
|
||||
dockerize-mainnet:
|
||||
stage: dockerize
|
||||
dependencies:
|
||||
- test-mainnet
|
||||
variables:
|
||||
IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
|
||||
before_script:
|
||||
|
|
@ -105,8 +103,6 @@ test-testnet:
|
|||
|
||||
dockerize-testnet:
|
||||
stage: dockerize
|
||||
dependencies:
|
||||
- test-testnet
|
||||
variables:
|
||||
IMAGE: $CI_REGISTRY_IMAGE/testnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
|
||||
before_script:
|
||||
|
|
|
|||
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -5,5 +5,5 @@
|
|||
[submodule "libraries/fc"]
|
||||
path = libraries/fc
|
||||
url = https://gitlab.com/PBSA/tools-libs/peerplays-fc.git
|
||||
branch = latest-fc
|
||||
branch = develop
|
||||
ignore = dirty
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ RUN \
|
|||
expect \
|
||||
git \
|
||||
graphviz \
|
||||
libboost1.67-all-dev \
|
||||
libboost-all-dev \
|
||||
libbz2-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libncurses-dev \
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ EXPOSE 22
|
|||
WORKDIR /home/peerplays/
|
||||
|
||||
RUN \
|
||||
wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 && \
|
||||
tar xjf boost_1_67_0.tar.bz2 && \
|
||||
cd boost_1_67_0/ && \
|
||||
wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 && \
|
||||
tar xjf boost_1_71_0.tar.bz2 && \
|
||||
cd boost_1_71_0/ && \
|
||||
./bootstrap.sh && \
|
||||
./b2 install
|
||||
|
||||
|
|
|
|||
|
|
@ -84,9 +84,9 @@ sudo apt-get install \
|
|||
|
||||
Install Boost libraries from source
|
||||
```
|
||||
wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2
|
||||
tar xjf boost_1_67_0.tar.bz2
|
||||
cd boost_1_67_0/
|
||||
wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2
|
||||
tar xjf boost_1_71_0.tar.bz2
|
||||
cd boost_1_71_0/
|
||||
./bootstrap.sh
|
||||
sudo ./b2 install
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ add_subdirectory( egenesis )
|
|||
add_subdirectory( fc )
|
||||
add_subdirectory( net )
|
||||
add_subdirectory( plugins )
|
||||
add_subdirectory( sha3 )
|
||||
add_subdirectory( time )
|
||||
add_subdirectory( utilities )
|
||||
add_subdirectory( wallet )
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ template class fc::api<graphene::app::block_api>;
|
|||
template class fc::api<graphene::app::network_broadcast_api>;
|
||||
template class fc::api<graphene::app::network_node_api>;
|
||||
template class fc::api<graphene::app::history_api>;
|
||||
template class fc::api<graphene::app::crypto_api>;
|
||||
template class fc::api<graphene::app::asset_api>;
|
||||
template class fc::api<graphene::debug_witness::debug_api>;
|
||||
template class fc::api<graphene::app::login_api>;
|
||||
|
|
@ -90,8 +89,6 @@ void login_api::enable_api(const std::string &api_name) {
|
|||
_history_api = std::make_shared<history_api>(_app);
|
||||
} else if (api_name == "network_node_api") {
|
||||
_network_node_api = std::make_shared<network_node_api>(std::ref(_app));
|
||||
} else if (api_name == "crypto_api") {
|
||||
_crypto_api = std::make_shared<crypto_api>();
|
||||
} else if (api_name == "asset_api") {
|
||||
_asset_api = std::make_shared<asset_api>(_app);
|
||||
} else if (api_name == "debug_api") {
|
||||
|
|
@ -289,11 +286,6 @@ fc::api<history_api> login_api::history() const {
|
|||
return *_history_api;
|
||||
}
|
||||
|
||||
fc::api<crypto_api> login_api::crypto() const {
|
||||
FC_ASSERT(_crypto_api);
|
||||
return *_crypto_api;
|
||||
}
|
||||
|
||||
fc::api<asset_api> login_api::asset() const {
|
||||
FC_ASSERT(_asset_api);
|
||||
return *_asset_api;
|
||||
|
|
@ -522,55 +514,6 @@ vector<bucket_object> history_api::get_market_history(std::string asset_a, std::
|
|||
FC_CAPTURE_AND_RETHROW((asset_a)(asset_b)(bucket_seconds)(start)(end))
|
||||
}
|
||||
|
||||
crypto_api::crypto_api(){};
|
||||
|
||||
commitment_type crypto_api::blind(const blind_factor_type &blind, uint64_t value) {
|
||||
return fc::ecc::blind(blind, value);
|
||||
}
|
||||
|
||||
blind_factor_type crypto_api::blind_sum(const std::vector<blind_factor_type> &blinds_in, uint32_t non_neg) {
|
||||
return fc::ecc::blind_sum(blinds_in, non_neg);
|
||||
}
|
||||
|
||||
bool crypto_api::verify_sum(const std::vector<commitment_type> &commits_in, const std::vector<commitment_type> &neg_commits_in, int64_t excess) {
|
||||
return fc::ecc::verify_sum(commits_in, neg_commits_in, excess);
|
||||
}
|
||||
|
||||
verify_range_result crypto_api::verify_range(const commitment_type &commit, const std::vector<char> &proof) {
|
||||
verify_range_result result;
|
||||
result.success = fc::ecc::verify_range(result.min_val, result.max_val, commit, proof);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<char> crypto_api::range_proof_sign(uint64_t min_value,
|
||||
const commitment_type &commit,
|
||||
const blind_factor_type &commit_blind,
|
||||
const blind_factor_type &nonce,
|
||||
int8_t base10_exp,
|
||||
uint8_t min_bits,
|
||||
uint64_t actual_value) {
|
||||
return fc::ecc::range_proof_sign(min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value);
|
||||
}
|
||||
|
||||
verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind(const blind_factor_type &nonce,
|
||||
const commitment_type &commit,
|
||||
const std::vector<char> &proof) {
|
||||
verify_range_proof_rewind_result result;
|
||||
result.success = fc::ecc::verify_range_proof_rewind(result.blind_out,
|
||||
result.value_out,
|
||||
result.message_out,
|
||||
nonce,
|
||||
result.min_val,
|
||||
result.max_val,
|
||||
const_cast<commitment_type &>(commit),
|
||||
proof);
|
||||
return result;
|
||||
}
|
||||
|
||||
range_proof_info crypto_api::range_get_info(const std::vector<char> &proof) {
|
||||
return fc::ecc::range_get_info(proof);
|
||||
}
|
||||
|
||||
// asset_api
|
||||
asset_api::asset_api(graphene::app::application &app) :
|
||||
_app(app),
|
||||
|
|
|
|||
|
|
@ -362,7 +362,6 @@ public:
|
|||
wild_access.allowed_apis.push_back("database_api");
|
||||
wild_access.allowed_apis.push_back("network_broadcast_api");
|
||||
wild_access.allowed_apis.push_back("history_api");
|
||||
wild_access.allowed_apis.push_back("crypto_api");
|
||||
wild_access.allowed_apis.push_back("bookie_api");
|
||||
wild_access.allowed_apis.push_back("affiliate_stats_api");
|
||||
wild_access.allowed_apis.push_back("sidechain_api");
|
||||
|
|
@ -765,6 +764,10 @@ public:
|
|||
FC_CAPTURE_AND_RETHROW((block_id))
|
||||
}
|
||||
|
||||
virtual fc::time_point_sec get_last_known_hardfork_time() override {
|
||||
return _chain_db->_hardfork_times[_chain_db->_hardfork_times.size() - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time a block was produced (if block_id = 0, returns genesis time).
|
||||
* If we don't know about the block, returns time_point_sec::min()
|
||||
|
|
@ -914,7 +917,8 @@ void application::initialize(const fc::path &data_dir, const boost::program_opti
|
|||
wanted.insert("accounts_list");
|
||||
wanted.insert("affiliate_stats");
|
||||
}
|
||||
wanted.insert("witness");
|
||||
if (!wanted.count("delayed_node") && !wanted.count("debug_witness") && !wanted.count("witness")) // explicitly requested delayed_node or debug_witness functionality suppresses witness functions
|
||||
wanted.insert("witness");
|
||||
wanted.insert("bookie");
|
||||
|
||||
int es_ah_conflict_counter = 0;
|
||||
|
|
@ -946,7 +950,7 @@ void application::startup() {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -184,6 +184,10 @@ public:
|
|||
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;
|
||||
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
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
|
|
@ -237,9 +241,6 @@ public:
|
|||
// Proposed transactions
|
||||
vector<proposal_object> get_proposed_transactions(const std::string account_id_or_name) const;
|
||||
|
||||
// Blinded balances
|
||||
vector<blinded_balance_object> get_blinded_balances(const flat_set<commitment_type> &commitments) const;
|
||||
|
||||
// Tournaments
|
||||
vector<tournament_object> get_tournaments_in_state(tournament_state state, uint32_t limit) const;
|
||||
vector<tournament_object> get_tournaments(tournament_id_type stop, unsigned limit, tournament_id_type start);
|
||||
|
|
@ -1851,6 +1852,80 @@ uint64_t database_api_impl::get_son_count() const {
|
|||
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 //
|
||||
|
|
@ -2086,7 +2161,9 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
|
|||
const auto &committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>();
|
||||
const auto &for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>();
|
||||
const auto &against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
|
||||
const auto &son_idx = _db.get_index_type<son_index>().indices().get<by_vote_id>();
|
||||
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_ethereum_idx = _db.get_index_type<son_index>().indices().get<by_vote_id_ethereum>();
|
||||
|
||||
vector<variant> result;
|
||||
result.reserve(votes.size());
|
||||
|
|
@ -2122,15 +2199,30 @@ vector<variant> database_api_impl::lookup_vote_ids(const vector<vote_id_type> &v
|
|||
}
|
||||
break;
|
||||
}
|
||||
case vote_id_type::son: {
|
||||
auto itr = son_idx.find(id);
|
||||
if (itr != son_idx.end())
|
||||
case vote_id_type::son_bitcoin: {
|
||||
auto itr = son_bictoin_idx.find(id);
|
||||
if (itr != son_bictoin_idx.end())
|
||||
result.emplace_back(variant(*itr, 5));
|
||||
else
|
||||
result.emplace_back(variant());
|
||||
break;
|
||||
}
|
||||
case vote_id_type::son_hive: {
|
||||
auto itr = son_hive_idx.find(id);
|
||||
if (itr != son_hive_idx.end())
|
||||
result.emplace_back(variant(*itr, 5));
|
||||
else
|
||||
result.emplace_back(variant());
|
||||
break;
|
||||
}
|
||||
case vote_id_type::son_ethereum: {
|
||||
auto itr = son_ethereum_idx.find(id);
|
||||
if (itr != son_ethereum_idx.end())
|
||||
result.emplace_back(variant(*itr, 5));
|
||||
else
|
||||
result.emplace_back(variant());
|
||||
break;
|
||||
}
|
||||
|
||||
case vote_id_type::VOTE_TYPE_COUNT:
|
||||
break; // supress unused enum value warnings
|
||||
default:
|
||||
|
|
@ -2155,12 +2247,24 @@ vector<vote_id_type> database_api_impl::get_votes_ids(const string &account_name
|
|||
votes_info database_api_impl::get_votes(const string &account_name_or_id) const {
|
||||
votes_info result;
|
||||
|
||||
const auto &votes_ids = get_votes_ids(account_name_or_id);
|
||||
const auto &committee_ids = get_votes_objects<committee_member_index, by_vote_id>(votes_ids);
|
||||
const auto &witness_ids = get_votes_objects<witness_index, by_vote_id>(votes_ids);
|
||||
const auto &for_worker_ids = get_votes_objects<worker_index, by_vote_for>(votes_ids);
|
||||
const auto &against_worker_ids = get_votes_objects<worker_index, by_vote_against>(votes_ids);
|
||||
const auto &son_ids = get_votes_objects<son_index, by_vote_id>(votes_ids, 5);
|
||||
const auto votes_ids = get_votes_ids(account_name_or_id);
|
||||
const auto committee_ids = get_votes_objects<committee_member_index, by_vote_id>(votes_ids);
|
||||
const auto witness_ids = get_votes_objects<witness_index, by_vote_id>(votes_ids);
|
||||
const auto for_worker_ids = get_votes_objects<worker_index, by_vote_for>(votes_ids);
|
||||
const auto against_worker_ids = get_votes_objects<worker_index, by_vote_against>(votes_ids);
|
||||
const auto son_ids = [this, &votes_ids]() {
|
||||
flat_map<sidechain_type, vector<variant>> son_ids;
|
||||
const auto son_bitcoin_ids = get_votes_objects<son_index, by_vote_id_bitcoin>(votes_ids, 5);
|
||||
if (!son_bitcoin_ids.empty())
|
||||
son_ids[sidechain_type::bitcoin] = std::move(son_bitcoin_ids);
|
||||
const auto son_hive_ids = get_votes_objects<son_index, by_vote_id_hive>(votes_ids, 5);
|
||||
if (!son_hive_ids.empty())
|
||||
son_ids[sidechain_type::hive] = std::move(son_hive_ids);
|
||||
const auto son_ethereum_ids = get_votes_objects<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;
|
||||
}();
|
||||
|
||||
//! Fill votes info
|
||||
if (!committee_ids.empty()) {
|
||||
|
|
@ -2204,11 +2308,15 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const
|
|||
}
|
||||
|
||||
if (!son_ids.empty()) {
|
||||
vector<votes_info_object> votes_for_sons;
|
||||
votes_for_sons.reserve(son_ids.size());
|
||||
for (const auto &son : son_ids) {
|
||||
const auto &son_obj = son.as<son_object>(6);
|
||||
votes_for_sons.emplace_back(votes_info_object{son_obj.vote_id, son_obj.id});
|
||||
flat_map<sidechain_type, vector<votes_info_object>> votes_for_sons;
|
||||
for (const auto &son_sidechain_ids : son_ids) {
|
||||
const auto &sidechain = son_sidechain_ids.first;
|
||||
const auto &sidechain_ids = son_sidechain_ids.second;
|
||||
votes_for_sons[sidechain].reserve(sidechain_ids.size());
|
||||
for (const auto &son : sidechain_ids) {
|
||||
const auto &son_obj = son.as<son_object>(6);
|
||||
votes_for_sons[sidechain].emplace_back(votes_info_object{son_obj.get_sidechain_vote_id(sidechain), son_obj.id});
|
||||
}
|
||||
}
|
||||
result.votes_for_sons = std::move(votes_for_sons);
|
||||
}
|
||||
|
|
@ -2382,12 +2490,16 @@ voters_info database_api_impl::get_voters(const string &account_name_or_id) cons
|
|||
|
||||
//! Info for son voters
|
||||
if (son_object) {
|
||||
const auto &son_voters = get_voters_by_id(son_object->vote_id);
|
||||
voters_info_object voters_for_son;
|
||||
voters_for_son.vote_id = son_object->vote_id;
|
||||
voters_for_son.voters.reserve(son_voters.size());
|
||||
for (const auto &voter : son_voters) {
|
||||
voters_for_son.voters.emplace_back(voter.get_id());
|
||||
flat_map<sidechain_type, voters_info_object> voters_for_son;
|
||||
for (const auto &vote_id : son_object->sidechain_vote_ids) {
|
||||
const auto &son_voters = get_voters_by_id(vote_id.second);
|
||||
voters_info_object voters_for_sidechain_son;
|
||||
voters_for_sidechain_son.vote_id = vote_id.second;
|
||||
voters_for_sidechain_son.voters.reserve(son_voters.size());
|
||||
for (const auto &voter : son_voters) {
|
||||
voters_for_sidechain_son.voters.emplace_back(voter.get_id());
|
||||
}
|
||||
voters_for_son[vote_id.first] = std::move(voters_for_sidechain_son);
|
||||
}
|
||||
result.voters_for_son = std::move(voters_for_son);
|
||||
}
|
||||
|
|
@ -2650,29 +2762,6 @@ vector<proposal_object> database_api_impl::get_proposed_transactions(const std::
|
|||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Blinded balances //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<blinded_balance_object> database_api::get_blinded_balances(const flat_set<commitment_type> &commitments) const {
|
||||
return my->get_blinded_balances(commitments);
|
||||
}
|
||||
|
||||
vector<blinded_balance_object> database_api_impl::get_blinded_balances(const flat_set<commitment_type> &commitments) const {
|
||||
vector<blinded_balance_object> result;
|
||||
result.reserve(commitments.size());
|
||||
const auto &bal_idx = _db.get_index_type<blinded_balance_index>();
|
||||
const auto &by_commitment_idx = bal_idx.indices().get<by_commitment>();
|
||||
for (const auto &c : commitments) {
|
||||
auto itr = by_commitment_idx.find(c);
|
||||
if (itr != by_commitment_idx.end())
|
||||
result.push_back(*itr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Tournament methods //
|
||||
|
|
@ -2975,8 +3064,8 @@ uint64_t database_api::nft_get_total_supply(const nft_metadata_id_type nft_metad
|
|||
}
|
||||
|
||||
uint64_t database_api_impl::nft_get_total_supply(const nft_metadata_id_type nft_metadata_id) const {
|
||||
const auto &idx_nft_md = _db.get_index_type<nft_metadata_index>().indices().get<by_id>();
|
||||
return idx_nft_md.size();
|
||||
const auto &idx_nft = _db.get_index_type<nft_index>().indices().get<by_metadata>();
|
||||
return idx_nft.count(nft_metadata_id);
|
||||
}
|
||||
|
||||
nft_object database_api::nft_token_by_index(const nft_metadata_id_type nft_metadata_id, const uint64_t token_idx) const {
|
||||
|
|
|
|||
|
|
@ -289,33 +289,6 @@ private:
|
|||
std::function<void(const variant &)> _on_pending_transaction;
|
||||
};
|
||||
|
||||
class crypto_api {
|
||||
public:
|
||||
crypto_api();
|
||||
|
||||
fc::ecc::commitment_type blind(const fc::ecc::blind_factor_type &blind, uint64_t value);
|
||||
|
||||
fc::ecc::blind_factor_type blind_sum(const std::vector<blind_factor_type> &blinds_in, uint32_t non_neg);
|
||||
|
||||
bool verify_sum(const std::vector<commitment_type> &commits_in, const std::vector<commitment_type> &neg_commits_in, int64_t excess);
|
||||
|
||||
verify_range_result verify_range(const fc::ecc::commitment_type &commit, const std::vector<char> &proof);
|
||||
|
||||
std::vector<char> range_proof_sign(uint64_t min_value,
|
||||
const commitment_type &commit,
|
||||
const blind_factor_type &commit_blind,
|
||||
const blind_factor_type &nonce,
|
||||
int8_t base10_exp,
|
||||
uint8_t min_bits,
|
||||
uint64_t actual_value);
|
||||
|
||||
verify_range_proof_rewind_result verify_range_proof_rewind(const blind_factor_type &nonce,
|
||||
const fc::ecc::commitment_type &commit,
|
||||
const std::vector<char> &proof);
|
||||
|
||||
range_proof_info range_get_info(const std::vector<char> &proof);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
|
|
@ -359,7 +332,6 @@ extern template class fc::api<graphene::app::block_api>;
|
|||
extern template class fc::api<graphene::app::network_broadcast_api>;
|
||||
extern template class fc::api<graphene::app::network_node_api>;
|
||||
extern template class fc::api<graphene::app::history_api>;
|
||||
extern template class fc::api<graphene::app::crypto_api>;
|
||||
extern template class fc::api<graphene::app::asset_api>;
|
||||
extern template class fc::api<graphene::debug_witness::debug_api>;
|
||||
|
||||
|
|
@ -394,8 +366,6 @@ public:
|
|||
fc::api<history_api> history() const;
|
||||
/// @brief Retrieve the network node API
|
||||
fc::api<network_node_api> network_node() const;
|
||||
/// @brief Retrieve the cryptography API
|
||||
fc::api<crypto_api> crypto() const;
|
||||
/// @brief Retrieve the asset API
|
||||
fc::api<asset_api> asset() const;
|
||||
/// @brief Retrieve the debug API (if available)
|
||||
|
|
@ -417,7 +387,6 @@ private:
|
|||
optional<fc::api<network_broadcast_api>> _network_broadcast_api;
|
||||
optional<fc::api<network_node_api>> _network_node_api;
|
||||
optional<fc::api<history_api>> _history_api;
|
||||
optional<fc::api<crypto_api>> _crypto_api;
|
||||
optional<fc::api<asset_api>> _asset_api;
|
||||
optional<fc::api<graphene::debug_witness::debug_api>> _debug_api;
|
||||
optional<fc::api<graphene::bookie::bookie_api>> _bookie_api;
|
||||
|
|
@ -475,15 +444,6 @@ FC_API(graphene::app::network_node_api,
|
|||
(subscribe_to_pending_transactions)
|
||||
(unsubscribe_from_pending_transactions))
|
||||
|
||||
FC_API(graphene::app::crypto_api,
|
||||
(blind)
|
||||
(blind_sum)
|
||||
(verify_sum)
|
||||
(verify_range)
|
||||
(range_proof_sign)
|
||||
(verify_range_proof_rewind)
|
||||
(range_get_info))
|
||||
|
||||
FC_API(graphene::app::asset_api,
|
||||
(get_asset_holders)
|
||||
(get_asset_holders_count)
|
||||
|
|
@ -496,7 +456,6 @@ FC_API(graphene::app::login_api,
|
|||
(database)
|
||||
(history)
|
||||
(network_node)
|
||||
(crypto)
|
||||
(asset)
|
||||
(debug)
|
||||
(bookie)
|
||||
|
|
|
|||
|
|
@ -675,6 +675,32 @@ public:
|
|||
*/
|
||||
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 //
|
||||
/////////////////////////
|
||||
|
|
@ -877,15 +903,6 @@ public:
|
|||
*/
|
||||
vector<proposal_object> get_proposed_transactions(const std::string account_id_or_name) const;
|
||||
|
||||
//////////////////////
|
||||
// Blinded balances //
|
||||
//////////////////////
|
||||
|
||||
/**
|
||||
* @return the set of blinded balance objects by commitment ID
|
||||
*/
|
||||
vector<blinded_balance_object> get_blinded_balances(const flat_set<commitment_type> &commitments) const;
|
||||
|
||||
/////////////////
|
||||
// Tournaments //
|
||||
/////////////////
|
||||
|
|
@ -1158,6 +1175,10 @@ FC_API(graphene::app::database_api,
|
|||
(get_son_by_account)
|
||||
(lookup_son_accounts)
|
||||
(get_son_count)
|
||||
(get_active_sons)
|
||||
(get_active_sons_by_sidechain)
|
||||
(get_son_network_status)
|
||||
(get_son_network_status_by_sidechain)
|
||||
|
||||
// SON wallets
|
||||
(get_active_son_wallet)
|
||||
|
|
@ -1198,9 +1219,6 @@ FC_API(graphene::app::database_api,
|
|||
// Proposed transactions
|
||||
(get_proposed_transactions)
|
||||
|
||||
// Blinded balances
|
||||
(get_blinded_balances)
|
||||
|
||||
// Tournaments
|
||||
(get_tournaments_in_state)
|
||||
(get_tournaments_by_state)
|
||||
|
|
|
|||
|
|
@ -62,10 +62,19 @@ void verify_account_votes( const database& db, const account_options& options )
|
|||
const auto& gpo = db.get_global_properties();
|
||||
const auto& chain_params = gpo.parameters;
|
||||
|
||||
FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
|
||||
|
||||
FC_ASSERT( options.num_witness <= chain_params.maximum_witness_count,
|
||||
"Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) );
|
||||
FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count,
|
||||
"Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) );
|
||||
FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" );
|
||||
FC_ASSERT( options.extensions.value.num_son.valid() , "Invalid son number" );
|
||||
for(const auto& num_sons : *options.extensions.value.num_son)
|
||||
{
|
||||
FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count,
|
||||
"Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) );
|
||||
}
|
||||
FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." );
|
||||
|
||||
uint32_t max_vote_id = gpo.next_available_vote_id;
|
||||
|
|
|
|||
|
|
@ -33,149 +33,45 @@ namespace graphene { namespace chain {
|
|||
|
||||
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
|
||||
{ try {
|
||||
const auto& d = db();
|
||||
|
||||
const auto& atype = o.amount.asset_id(db());
|
||||
FC_ASSERT( atype.allow_confidential() );
|
||||
FC_ASSERT( !atype.is_transfer_restricted() );
|
||||
FC_ASSERT( !(atype.options.flags & white_list) );
|
||||
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
for( const auto& a : out.owner.account_auths )
|
||||
a.first(d); // verify all accounts exist and are valid
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
||||
void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o )
|
||||
{ try {
|
||||
db().adjust_balance( o.from, -o.amount );
|
||||
|
||||
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
|
||||
db().modify( add, [&]( asset_dynamic_data_object& obj ){
|
||||
obj.confidential_supply += o.amount.amount;
|
||||
FC_ASSERT( obj.confidential_supply >= 0 );
|
||||
});
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
|
||||
obj.asset_id = o.amount.asset_id;
|
||||
obj.owner = out.owner;
|
||||
obj.commitment = out.commitment;
|
||||
});
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void transfer_to_blind_evaluator::pay_fee()
|
||||
{
|
||||
if( db().head_block_time() >= HARDFORK_563_TIME )
|
||||
pay_fba_fee( fba_accumulator_id_transfer_to_blind );
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
|
||||
void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )
|
||||
{ try {
|
||||
const auto& d = db();
|
||||
o.fee.asset_id(d); // verify fee is a legit asset
|
||||
const auto& bbi = d.get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
FC_ASSERT( itr != cidx.end() );
|
||||
FC_ASSERT( itr->asset_id == o.fee.asset_id );
|
||||
FC_ASSERT( itr->owner == in.owner );
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o )
|
||||
{ try {
|
||||
db().adjust_balance( o.fee_payer(), o.fee );
|
||||
db().adjust_balance( o.to, o.amount );
|
||||
const auto& bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
FC_ASSERT( itr != cidx.end() );
|
||||
db().remove( *itr );
|
||||
}
|
||||
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
|
||||
db().modify( add, [&]( asset_dynamic_data_object& obj ){
|
||||
obj.confidential_supply -= o.amount.amount + o.fee.amount;
|
||||
FC_ASSERT( obj.confidential_supply >= 0 );
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void transfer_from_blind_evaluator::pay_fee()
|
||||
{
|
||||
if( db().head_block_time() >= HARDFORK_563_TIME )
|
||||
pay_fba_fee( fba_accumulator_id_transfer_from_blind );
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
|
||||
void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )
|
||||
{ try {
|
||||
const auto& d = db();
|
||||
o.fee.asset_id(db()); // verify fee is a legit asset
|
||||
const auto& bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
for( const auto& a : out.owner.account_auths )
|
||||
a.first(d); // verify all accounts exist and are valid
|
||||
}
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
|
||||
FC_ASSERT( itr->asset_id == o.fee.asset_id );
|
||||
FC_ASSERT( itr->owner == in.owner );
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o )
|
||||
{ try {
|
||||
db().adjust_balance( o.fee_payer(), o.fee ); // deposit the fee to the temp account
|
||||
const auto& bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
|
||||
db().remove( *itr );
|
||||
}
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
|
||||
obj.asset_id = o.fee.asset_id;
|
||||
obj.owner = out.owner;
|
||||
obj.commitment = out.commitment;
|
||||
});
|
||||
}
|
||||
const auto& add = o.fee.asset_id(db()).dynamic_asset_data_id(db());
|
||||
db().modify( add, [&]( asset_dynamic_data_object& obj ){
|
||||
obj.confidential_supply -= o.fee.amount;
|
||||
FC_ASSERT( obj.confidential_supply >= 0 );
|
||||
});
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void blind_transfer_evaluator::pay_fee()
|
||||
{
|
||||
if( db().head_block_time() >= HARDFORK_563_TIME )
|
||||
pay_fba_fee( fba_accumulator_id_blind_transfer );
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@
|
|||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/db/object_database.hpp>
|
||||
#include <fc/crypto/digest.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
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() );
|
||||
});
|
||||
|
||||
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());
|
||||
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
|
||||
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);
|
||||
|
|
@ -185,7 +190,12 @@ bool database::push_block(const signed_block& new_block, uint32_t skip)
|
|||
bool result;
|
||||
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);
|
||||
|
|
@ -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)
|
||||
{ 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;
|
||||
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.
|
||||
// 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.
|
||||
// The temporary session will be discarded by the destructor if
|
||||
// _apply_transaction fails. If we make it to merge(), we
|
||||
// apply the changes.
|
||||
|
||||
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||
auto temp_session = _undo_db.start_undo_session();
|
||||
auto processed_trx = _apply_transaction( trx );
|
||||
_pending_tx.push_back(processed_trx);
|
||||
auto processed_trx = _apply_transaction(trx);
|
||||
{
|
||||
const std::lock_guard<std::mutex> pending_tx_lock{_pending_tx_mutex};
|
||||
_pending_tx.push_back(processed_trx);
|
||||
}
|
||||
|
||||
// notify_changed_objects();
|
||||
// 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 )
|
||||
{
|
||||
const std::lock_guard<std::mutex> undo_db_lock{_undo_db_mutex};
|
||||
auto session = _undo_db.start_undo_session();
|
||||
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
|
||||
// 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;
|
||||
// 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
|
||||
if( new_total_size >= maximum_block_size )
|
||||
{
|
||||
postponed_tx_count++;
|
||||
continue;
|
||||
}
|
||||
// postpone transaction if it would make block too big
|
||||
if (new_total_size >= maximum_block_size) {
|
||||
postponed_tx_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto temp_session = _undo_db.start_undo_session();
|
||||
processed_transaction ptx = _apply_transaction( tx );
|
||||
temp_session.merge();
|
||||
try {
|
||||
auto temp_session = _undo_db.start_undo_session();
|
||||
processed_transaction ptx = _apply_transaction(tx);
|
||||
temp_session.merge();
|
||||
|
||||
// We have to recompute pack_size(ptx) because it may be different
|
||||
// than pack_size(tx) (i.e. if one or more results increased
|
||||
// their size)
|
||||
total_block_size += fc::raw::pack_size( ptx );
|
||||
pending_block.transactions.push_back( ptx );
|
||||
}
|
||||
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) );
|
||||
wlog( "The transaction was ${t}", ("t", tx) );
|
||||
// We have to recompute pack_size(ptx) because it may be different
|
||||
// than pack_size(tx) (i.e. if one or more results increased
|
||||
// their size)
|
||||
total_block_size += fc::raw::pack_size(ptx);
|
||||
pending_block.transactions.push_back(ptx);
|
||||
} 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));
|
||||
wlog("The transaction was ${t}", ("t", tx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( postponed_tx_count > 0 )
|
||||
{
|
||||
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
|
||||
// _pending_tx_session is the result of applying _pending_tx, as
|
||||
|
|
@ -592,7 +620,11 @@ signed_block database::_generate_block(
|
|||
*/
|
||||
void database::pop_block()
|
||||
{ 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();
|
||||
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" );
|
||||
|
|
@ -606,6 +638,8 @@ void database::pop_block()
|
|||
|
||||
void database::clear_pending()
|
||||
{ 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() );
|
||||
_pending_tx.clear();
|
||||
_pending_tx_session.reset();
|
||||
|
|
@ -705,7 +739,12 @@ void database::_apply_block( const signed_block& next_block )
|
|||
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) {
|
||||
update_witness_schedule(next_block);
|
||||
if(global_props.active_sons.size() > 0) {
|
||||
bool need_to_update_son_schedule = false;
|
||||
for(const auto& active_sons : global_props.active_sons){
|
||||
if(!active_sons.second.empty())
|
||||
need_to_update_son_schedule = true;
|
||||
}
|
||||
if(need_to_update_son_schedule) {
|
||||
update_son_schedule(next_block);
|
||||
}
|
||||
}
|
||||
|
|
@ -721,7 +760,7 @@ void database::_apply_block( const signed_block& next_block )
|
|||
|
||||
check_ending_lotteries();
|
||||
check_ending_nft_lotteries();
|
||||
|
||||
|
||||
create_block_summary(next_block);
|
||||
place_delayed_bets(); // must happen after update_global_dynamic_data() updates the time
|
||||
clear_expired_transactions();
|
||||
|
|
@ -739,10 +778,18 @@ void database::_apply_block( const signed_block& next_block )
|
|||
// TODO: figure out if we could collapse this function into
|
||||
// update_global_dynamic_data() as perhaps these methods only need
|
||||
// to be called for header validation?
|
||||
|
||||
update_maintenance_flag( maint_needed );
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) {
|
||||
update_witness_schedule();
|
||||
if(global_props.active_sons.size() > 0) {
|
||||
|
||||
bool need_update_son_schedule = false;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
||||
if(global_props.active_sons.at(active_sidechain_type).size() > 0) {
|
||||
need_update_son_schedule = true;
|
||||
}
|
||||
}
|
||||
if(need_update_son_schedule) {
|
||||
update_son_schedule();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,17 +222,30 @@ std::set<son_id_type> database::get_sons_to_be_deregistered()
|
|||
|
||||
for( auto& son : son_idx )
|
||||
{
|
||||
if(son.status == son_status::in_maintenance)
|
||||
bool need_to_be_deregistered = true;
|
||||
for(const auto& status : son.statuses)
|
||||
{
|
||||
auto stats = son.statistics(*this);
|
||||
// TODO : We need to add a function that returns if we can deregister SON
|
||||
// i.e. with introduction of PW code, we have to make a decision if the SON
|
||||
// is needed for release of funds from the PW
|
||||
if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))
|
||||
const auto& sidechain = status.first;
|
||||
if(status.second != son_status::in_maintenance)
|
||||
need_to_be_deregistered = false;
|
||||
|
||||
if(need_to_be_deregistered)
|
||||
{
|
||||
ret.insert(son.id);
|
||||
auto stats = son.statistics(*this);
|
||||
|
||||
// TODO : We need to add a function that returns if we can deregister SON
|
||||
// i.e. with introduction of PW code, we have to make a decision if the SON
|
||||
// is needed for release of funds from the PW
|
||||
if (head_block_time() - stats.last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) {
|
||||
need_to_be_deregistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(need_to_be_deregistered)
|
||||
{
|
||||
ret.insert(son.id);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -289,28 +302,51 @@ bool database::is_son_dereg_valid( son_id_type son_id )
|
|||
return false;
|
||||
}
|
||||
|
||||
return (son->status == son_status::in_maintenance &&
|
||||
(head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())));
|
||||
bool status_son_dereg_valid = true;
|
||||
for(const auto& status : son->statuses)
|
||||
{
|
||||
const auto& sidechain = status.first;
|
||||
if(status.second != son_status::in_maintenance)
|
||||
status_son_dereg_valid = false;
|
||||
|
||||
if(status_son_dereg_valid)
|
||||
{
|
||||
if(head_block_time() - son->statistics(*this).last_active_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time()))
|
||||
{
|
||||
status_son_dereg_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status_son_dereg_valid;
|
||||
}
|
||||
|
||||
bool database::is_son_active( son_id_type son_id )
|
||||
bool database::is_son_active( sidechain_type type, son_id_type son_id )
|
||||
{
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
auto son = son_idx.find( son_id );
|
||||
if(son == son_idx.end())
|
||||
{
|
||||
if(son == son_idx.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
if(!gpo.active_sons.contains(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& gpo_as = gpo.active_sons.at(type);
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
active_son_ids.reserve(gpo_as.size());
|
||||
std::transform(gpo_as.cbegin(), gpo_as.cend(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
if(active_son_ids.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), son_id);
|
||||
return (it_son != active_son_ids.end());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,10 +329,54 @@ void database::initialize_evaluators()
|
|||
register_evaluator<random_number_store_evaluator>();
|
||||
}
|
||||
|
||||
|
||||
void database::initialize_hardforks()
|
||||
{
|
||||
_hardfork_times.emplace_back(HARDFORK_357_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_359_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_385_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_409_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_413_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_415_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_416_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_419_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_436_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_445_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_453_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_480_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_483_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_516_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_533_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_538_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_555_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_563_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_572_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_599_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_607_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_613_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_615_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_999_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_1000_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_1001_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_5050_1_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_CORE_429_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_GPOS_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_NFT_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON_FOR_HIVE_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON2_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SON3_TIME);
|
||||
_hardfork_times.emplace_back(HARDFORK_SWEEPS_TIME);
|
||||
|
||||
std::sort(_hardfork_times.begin(), _hardfork_times.end());
|
||||
}
|
||||
|
||||
void database::initialize_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
|
||||
add_index< primary_index<asset_index, 13> >(); // 8192 assets per chunk
|
||||
|
|
@ -432,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(),
|
||||
"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();
|
||||
|
||||
struct auth_inhibitor {
|
||||
auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags)
|
||||
{ db.node_properties().skip_flags |= skip_authority_check; }
|
||||
|
|
@ -1058,8 +1104,9 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() );
|
||||
|
||||
// Initialize witness schedule
|
||||
|
||||
#ifndef NDEBUG
|
||||
const son_schedule_object& sso =
|
||||
const son_schedule_object& ssobitcoin =
|
||||
#endif
|
||||
create<son_schedule_object>([&](son_schedule_object& _sso)
|
||||
{
|
||||
|
|
@ -1068,23 +1115,58 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
|
||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
|
||||
auto init_witnesses = get_global_properties().active_witnesses;
|
||||
auto init_bitcoin_sons = get_global_properties().active_sons.at(sidechain_type::bitcoin);
|
||||
|
||||
_sso.scheduler = son_scheduler();
|
||||
_sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
|
||||
|
||||
_sso.scheduler._min_token_count = std::max(int(init_bitcoin_sons.size()) / 2, 1);
|
||||
|
||||
_sso.last_scheduling_block = 0;
|
||||
|
||||
_sso.recent_slots_filled = fc::uint128::max_value();
|
||||
});
|
||||
assert( sso.id == son_schedule_id_type() );
|
||||
assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) );
|
||||
|
||||
// Enable fees
|
||||
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
|
||||
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
|
||||
#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_accumulator_object>([&]( fba_accumulator_object& acc )
|
||||
|
|
|
|||
|
|
@ -78,25 +78,32 @@ vector<std::reference_wrapper<const typename Index::object_type>> database::sort
|
|||
}
|
||||
|
||||
template<>
|
||||
vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<son_index>(size_t count) const
|
||||
vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<son_index>(sidechain_type sidechain, size_t count) const
|
||||
{
|
||||
const auto& all_sons = get_index_type<son_index>().indices().get< by_id >();
|
||||
std::vector<std::reference_wrapper<const son_object>> refs;
|
||||
for( auto& son : all_sons )
|
||||
{
|
||||
if(son.has_valid_config(head_block_time()) && son.status != son_status::deregistered)
|
||||
if(son.has_valid_config(head_block_time()) && son.statuses.at(sidechain) != son_status::deregistered)
|
||||
{
|
||||
refs.push_back(std::cref(son));
|
||||
}
|
||||
}
|
||||
count = std::min(count, refs.size());
|
||||
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
|
||||
[this](const son_object& a, const son_object& b)->bool {
|
||||
share_type oa_vote = _vote_tally_buffer[a.vote_id];
|
||||
share_type ob_vote = _vote_tally_buffer[b.vote_id];
|
||||
if( oa_vote != ob_vote )
|
||||
return oa_vote > ob_vote;
|
||||
return a.vote_id < b.vote_id;
|
||||
[this, sidechain](const son_object& a, const son_object& b)->bool {
|
||||
FC_ASSERT(sidechain == sidechain_type::bitcoin ||
|
||||
sidechain == sidechain_type::ethereum ||
|
||||
sidechain == sidechain_type::hive,
|
||||
"Unexpected sidechain type");
|
||||
|
||||
const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)];
|
||||
const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)];
|
||||
|
||||
if( oa_vote != ob_vote )
|
||||
return oa_vote > ob_vote;
|
||||
|
||||
return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain);
|
||||
});
|
||||
|
||||
refs.resize(count, refs.front());
|
||||
|
|
@ -178,203 +185,233 @@ void database::update_worker_votes()
|
|||
|
||||
void database::pay_sons()
|
||||
{
|
||||
time_point_sec now = head_block_time();
|
||||
const time_point_sec now = head_block_time();
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
// Current requirement is that we have to pay every 24 hours, so the following check
|
||||
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) {
|
||||
auto sons = sort_votable_objects<son_index>(get_global_properties().parameters.maximum_son_count());
|
||||
// After SON2 HF
|
||||
uint64_t total_votes = 0;
|
||||
for( const son_object& son : sons )
|
||||
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time())))
|
||||
{
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
{
|
||||
total_votes += _vote_tally_buffer[son.vote_id];
|
||||
}
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
auto get_weight = [&bits_to_drop]( uint64_t son_votes ) {
|
||||
uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
// Before SON2 HF
|
||||
auto get_weight_before_son2_hf = []( uint64_t son_votes ) {
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0);
|
||||
uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
uint64_t weighted_total_txs_signed = 0;
|
||||
share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]);
|
||||
if( now < HARDFORK_SON2_TIME ) {
|
||||
son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]);
|
||||
}
|
||||
uint64_t txs_signed = 0;
|
||||
for (const auto &ts : s.txs_signed) {
|
||||
txs_signed = txs_signed + ts.second;
|
||||
}
|
||||
weighted_total_txs_signed += (txs_signed * son_weight);
|
||||
});
|
||||
|
||||
// Now pay off each SON proportional to the number of transactions signed.
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
uint64_t txs_signed = 0;
|
||||
for (const auto &ts : s.txs_signed) {
|
||||
txs_signed = txs_signed + ts.second;
|
||||
assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 );
|
||||
const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2;
|
||||
/// accounts that vote for 0 or 1 son do not get to express an opinion on
|
||||
/// the number of sons to have (they abstain and are non-voting accounts)
|
||||
share_type stake_tally = 0;
|
||||
size_t son_count = 0;
|
||||
if( stake_target > 0 )
|
||||
{
|
||||
while( (son_count < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1)
|
||||
&& (stake_tally <= stake_target) )
|
||||
{
|
||||
stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[++son_count];
|
||||
}
|
||||
}
|
||||
|
||||
if(txs_signed > 0){
|
||||
const auto sons = sort_votable_objects<son_index>(active_sidechain_type,
|
||||
(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count))
|
||||
);
|
||||
|
||||
// After SON2 HF
|
||||
uint64_t total_votes = 0;
|
||||
for( const son_object& son : sons )
|
||||
{
|
||||
total_votes += _vote_tally_buffer[son.sidechain_vote_ids.at(active_sidechain_type)];
|
||||
}
|
||||
const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
auto get_weight = [&bits_to_drop]( uint64_t son_votes ) {
|
||||
const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
// Before SON2 HF
|
||||
auto get_weight_before_son2_hf = []( uint64_t son_votes ) {
|
||||
const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0);
|
||||
const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
uint64_t weighted_total_txs_signed = 0;
|
||||
const share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]);
|
||||
if( now < HARDFORK_SON2_TIME ) {
|
||||
son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]);
|
||||
const auto son_obj = idx.find( s.owner );
|
||||
uint16_t son_weight = 0;
|
||||
if( now >= HARDFORK_SON2_TIME ) {
|
||||
son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed;
|
||||
modify( *son_obj, [&]( son_object& _son_obj)
|
||||
{
|
||||
_son_obj.pay_son_fee(pay, *this);
|
||||
});
|
||||
//Remove the amount paid out to SON from global SON Budget
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.son_budget -= pay;
|
||||
} );
|
||||
//Reset the tx counter in each son statistics object
|
||||
modify( s, [&]( son_statistics_object& _s)
|
||||
{
|
||||
for (const auto &ts : s.txs_signed) {
|
||||
_s.txs_signed.at(ts.first) = 0;
|
||||
else {
|
||||
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
|
||||
weighted_total_txs_signed += (txs_signed * son_weight);
|
||||
});
|
||||
|
||||
// Now pay off each SON proportional to the number of transactions signed.
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0;
|
||||
|
||||
if(txs_signed > 0){
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
uint16_t son_weight = 0;
|
||||
if( now >= HARDFORK_SON2_TIME ) {
|
||||
son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
//Note the last son pay out time
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_son_payout_time = now;
|
||||
});
|
||||
else {
|
||||
son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]);
|
||||
}
|
||||
const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed;
|
||||
modify( *son_obj, [&]( son_object& _son_obj)
|
||||
{
|
||||
_son_obj.pay_son_fee(pay, *this);
|
||||
});
|
||||
//Remove the amount paid out to SON from global SON Budget
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.son_budget -= pay;
|
||||
} );
|
||||
//Reset the tx counter in each son statistics object
|
||||
modify( s, [&]( son_statistics_object& _s)
|
||||
{
|
||||
if(_s.txs_signed.contains(active_sidechain_type))
|
||||
_s.txs_signed.at(active_sidechain_type) = 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
//Note the last son pay out time
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_son_payout_time = now;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_metrics(const vector<son_info>& curr_active_sons)
|
||||
void database::update_son_metrics(const flat_map<sidechain_type, vector<son_info> >& curr_active_sons)
|
||||
{
|
||||
vector<son_id_type> current_sons;
|
||||
for(const auto& curr_active_sidechain_sons : curr_active_sons) {
|
||||
const auto& sidechain = curr_active_sidechain_sons.first;
|
||||
const auto& _curr_active_sidechain_sons = curr_active_sidechain_sons.second;
|
||||
|
||||
current_sons.reserve(curr_active_sons.size());
|
||||
std::transform(curr_active_sons.begin(), curr_active_sons.end(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
vector<son_id_type> current_sons;
|
||||
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
for( auto& son : son_idx )
|
||||
{
|
||||
auto& stats = son.statistics(*this);
|
||||
bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end());
|
||||
modify( stats, [&]( son_statistics_object& _stats )
|
||||
{
|
||||
if(is_active_son) {
|
||||
_stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval;
|
||||
}
|
||||
_stats.total_downtime += _stats.current_interval_downtime;
|
||||
_stats.current_interval_downtime = 0;
|
||||
for (const auto &str : _stats.sidechain_txs_reported) {
|
||||
_stats.sidechain_txs_reported.at(str.first) = 0;
|
||||
}
|
||||
});
|
||||
current_sons.reserve(_curr_active_sidechain_sons.size());
|
||||
std::transform(_curr_active_sidechain_sons.cbegin(), _curr_active_sidechain_sons.cend(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
const auto &son_idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
for (auto &son : son_idx) {
|
||||
auto &stats = son.statistics(*this);
|
||||
bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end());
|
||||
modify(stats, [&](son_statistics_object &_stats) {
|
||||
if (is_active_son) {
|
||||
_stats.total_voted_time[sidechain] = _stats.total_voted_time[sidechain] + get_global_properties().parameters.maintenance_interval;
|
||||
}
|
||||
_stats.total_downtime[sidechain] += _stats.current_interval_downtime[sidechain];
|
||||
_stats.current_interval_downtime[sidechain] = 0;
|
||||
_stats.sidechain_txs_reported[sidechain] = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_statuses(const vector<son_info>& curr_active_sons, const vector<son_info>& new_active_sons)
|
||||
void database::update_son_statuses( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons,
|
||||
const flat_map<sidechain_type, vector<son_info> >& new_active_sons )
|
||||
{
|
||||
vector<son_id_type> current_sons, new_sons;
|
||||
vector<son_id_type> sons_to_remove, sons_to_add;
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
for(const auto& new_active_sidechain_sons : new_active_sons) {
|
||||
const auto& sidechain = new_active_sidechain_sons.first;
|
||||
|
||||
current_sons.reserve(curr_active_sons.size());
|
||||
std::transform(curr_active_sons.begin(), curr_active_sons.end(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
vector<son_id_type> current_sons, new_sons;
|
||||
vector<son_id_type> sons_to_remove, sons_to_add;
|
||||
const auto &idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
|
||||
new_sons.reserve(new_active_sons.size());
|
||||
std::transform(new_active_sons.begin(), new_active_sons.end(),
|
||||
std::inserter(new_sons, new_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
// find all cur_active_sons members that is not in new_active_sons
|
||||
for_each(current_sons.begin(), current_sons.end(),
|
||||
[&sons_to_remove, &new_sons](const son_id_type& si)
|
||||
{
|
||||
if(std::find(new_sons.begin(), new_sons.end(), si) ==
|
||||
new_sons.end())
|
||||
{
|
||||
sons_to_remove.push_back(si);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
for( const auto& sid : sons_to_remove )
|
||||
{
|
||||
auto son = idx.find( sid );
|
||||
if(son == idx.end()) // SON is deleted already
|
||||
continue;
|
||||
// keep maintenance status for nodes becoming inactive
|
||||
if(son->status == son_status::active)
|
||||
{
|
||||
modify( *son, [&]( son_object& obj ){
|
||||
obj.status = son_status::inactive;
|
||||
});
|
||||
if(curr_active_sons.contains(sidechain)) {
|
||||
current_sons.reserve(curr_active_sons.at(sidechain).size());
|
||||
std::transform(curr_active_sons.at(sidechain).cbegin(), curr_active_sons.at(sidechain).cend(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// find all new_active_sons members that is not in cur_active_sons
|
||||
for_each(new_sons.begin(), new_sons.end(),
|
||||
[&sons_to_add, ¤t_sons](const son_id_type& si)
|
||||
{
|
||||
if(std::find(current_sons.begin(), current_sons.end(), si) ==
|
||||
current_sons.end())
|
||||
{
|
||||
sons_to_add.push_back(si);
|
||||
}
|
||||
}
|
||||
);
|
||||
new_sons.reserve(new_active_sons.at(sidechain).size());
|
||||
std::transform(new_active_sons.at(sidechain).cbegin(), new_active_sons.at(sidechain).cend(),
|
||||
std::inserter(new_sons, new_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
for( const auto& sid : sons_to_add )
|
||||
{
|
||||
auto son = idx.find( sid );
|
||||
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid));
|
||||
// keep maintenance status for new nodes
|
||||
if(son->status == son_status::inactive)
|
||||
{
|
||||
modify( *son, [&]( son_object& obj ){
|
||||
obj.status = son_status::active;
|
||||
});
|
||||
}
|
||||
}
|
||||
// find all cur_active_sons members that is not in new_active_sons
|
||||
for_each(current_sons.begin(), current_sons.end(),
|
||||
[&sons_to_remove, &new_sons](const son_id_type &si) {
|
||||
if (std::find(new_sons.begin(), new_sons.end(), si) ==
|
||||
new_sons.end()) {
|
||||
sons_to_remove.push_back(si);
|
||||
}
|
||||
});
|
||||
|
||||
ilog("New SONS");
|
||||
for(size_t i = 0; i < new_sons.size(); i++) {
|
||||
auto son = idx.find( new_sons[i] );
|
||||
if(son == idx.end()) // SON is deleted already
|
||||
for (const auto &sid : sons_to_remove) {
|
||||
auto son = idx.find(sid);
|
||||
if (son == idx.end()) // SON is deleted already
|
||||
continue;
|
||||
ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) );
|
||||
// keep maintenance status for nodes becoming inactive
|
||||
if (son->statuses.at(sidechain) == son_status::active) {
|
||||
modify(*son, [&](son_object &obj) {
|
||||
obj.statuses.at(sidechain) = son_status::inactive;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// find all new_active_sons members that is not in cur_active_sons
|
||||
for_each(new_sons.begin(), new_sons.end(),
|
||||
[&sons_to_add, ¤t_sons](const son_id_type &si) {
|
||||
if (std::find(current_sons.begin(), current_sons.end(), si) ==
|
||||
current_sons.end()) {
|
||||
sons_to_add.push_back(si);
|
||||
}
|
||||
});
|
||||
|
||||
for (const auto &sid : sons_to_add) {
|
||||
auto son = idx.find(sid);
|
||||
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid));
|
||||
// keep maintenance status for new nodes
|
||||
if (son->statuses.at(sidechain) == son_status::inactive) {
|
||||
modify(*son, [&](son_object &obj) {
|
||||
obj.statuses.at(sidechain) = son_status::active;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ilog("New SONS for sidechain = ${sidechain}", ("sidechain", sidechain));
|
||||
for (size_t i = 0; i < new_sons.size(); i++) {
|
||||
auto son = idx.find(new_sons[i]);
|
||||
if (son == idx.end()) // SON is deleted already
|
||||
continue;
|
||||
ilog("${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->statuses.at(sidechain))("sv", son->total_votes));
|
||||
}
|
||||
}
|
||||
|
||||
if( sons_to_remove.size() > 0 )
|
||||
{
|
||||
//! Remove inactive sons (when all sidechain inactive)
|
||||
vector<son_id_type> sons_to_remove;
|
||||
const auto &idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
for(const auto& son : idx) {
|
||||
bool inactive_son = true;
|
||||
for(const auto& status : son.statuses) {
|
||||
if (status.second != son_status::inactive)
|
||||
inactive_son = false;
|
||||
}
|
||||
if (inactive_son)
|
||||
sons_to_remove.emplace_back(son.id);
|
||||
}
|
||||
if (sons_to_remove.size() > 0) {
|
||||
remove_inactive_son_proposals(sons_to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_wallet(const vector<son_info>& new_active_sons)
|
||||
void database::update_son_wallet(const flat_map<sidechain_type, vector<son_info> >& new_active_sons)
|
||||
{
|
||||
bool should_recreate_pw = true;
|
||||
|
||||
|
|
@ -387,8 +424,16 @@ void database::update_son_wallet(const vector<son_info>& new_active_sons)
|
|||
|
||||
bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size());
|
||||
if (wallet_son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i);
|
||||
for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) {
|
||||
const auto& sidechain = cur_wallet_sidechain_sons.first;
|
||||
const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second;
|
||||
|
||||
wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_active_sons.at(sidechain).size());
|
||||
if (wallet_son_sets_equal) {
|
||||
for (size_t i = 0; i < _cur_wallet_sidechain_sons.size(); i++) {
|
||||
wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -401,14 +446,24 @@ void database::update_son_wallet(const vector<son_info>& new_active_sons)
|
|||
}
|
||||
}
|
||||
|
||||
should_recreate_pw = should_recreate_pw && (new_active_sons.size() >= get_chain_properties().immutable_parameters.min_son_count);
|
||||
bool should_recreate_pw_sidechain = false;
|
||||
for(const auto& new_active_sidechain_sons : new_active_sons) {
|
||||
if(new_active_sidechain_sons.second.size() >= get_chain_properties().immutable_parameters.min_son_count)
|
||||
should_recreate_pw_sidechain = true;
|
||||
}
|
||||
should_recreate_pw = should_recreate_pw && should_recreate_pw_sidechain;
|
||||
|
||||
if (should_recreate_pw) {
|
||||
// Create new son_wallet_object, to initiate wallet recreation
|
||||
create<son_wallet_object>( [&]( son_wallet_object& obj ) {
|
||||
obj.valid_from = head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end());
|
||||
for(const auto& new_active_sidechain_sons : new_active_sons){
|
||||
const auto& sidechain = new_active_sidechain_sons.first;
|
||||
const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second;
|
||||
|
||||
obj.sons[sidechain].insert(obj.sons[sidechain].end(), _new_active_sidechain_sons.cbegin(), _new_active_sidechain_sons.cend());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -665,44 +720,89 @@ void database::update_active_sons()
|
|||
}
|
||||
|
||||
assert( _son_count_histogram_buffer.size() > 0 );
|
||||
share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2;
|
||||
#ifndef NDEBUG
|
||||
for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){
|
||||
assert( son_count_histogram_buffer.second.size() > 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
const flat_map<sidechain_type, share_type> stake_target = [this]{
|
||||
flat_map<sidechain_type, share_type> stake_target;
|
||||
for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){
|
||||
const auto sidechain = son_count_histogram_buffer.first;
|
||||
stake_target[sidechain] = (_total_voting_stake-son_count_histogram_buffer.second[0]) / 2;
|
||||
}
|
||||
return stake_target;
|
||||
}();
|
||||
|
||||
/// accounts that vote for 0 or 1 son do not get to express an opinion on
|
||||
/// the number of sons to have (they abstain and are non-voting accounts)
|
||||
|
||||
share_type stake_tally = 0;
|
||||
|
||||
size_t son_count = 0;
|
||||
if( stake_target > 0 )
|
||||
{
|
||||
while( (son_count < _son_count_histogram_buffer.size() - 1)
|
||||
&& (stake_tally <= stake_target) )
|
||||
flat_map<sidechain_type, share_type> stake_tally = []{
|
||||
flat_map<sidechain_type, share_type> stake_tally;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
stake_tally[active_sidechain_type] = 0;
|
||||
}
|
||||
return stake_tally;
|
||||
}();
|
||||
flat_map<sidechain_type, size_t> son_count = []{
|
||||
flat_map<sidechain_type, size_t> son_count;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
son_count[active_sidechain_type] = 0;
|
||||
}
|
||||
return son_count;
|
||||
}();
|
||||
for( const auto& stake_target_sidechain : stake_target ){
|
||||
const auto sidechain = stake_target_sidechain.first;
|
||||
if( stake_target_sidechain.second > 0 )
|
||||
{
|
||||
stake_tally += _son_count_histogram_buffer[++son_count];
|
||||
while( (son_count[sidechain] < _son_count_histogram_buffer.at(sidechain).size() - 1)
|
||||
&& (stake_tally[sidechain] <= stake_target_sidechain.second) )
|
||||
{
|
||||
stake_tally[sidechain] += _son_count_histogram_buffer.at(sidechain)[ ++son_count[sidechain] ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const chain_parameters& cp = gpo.parameters;
|
||||
auto sons = sort_votable_objects<son_index>(cp.maximum_son_count());
|
||||
|
||||
const chain_property_object& cpo = get_chain_properties();
|
||||
const auto& all_sons = get_index_type<son_index>().indices();
|
||||
flat_map<sidechain_type, vector<std::reference_wrapper<const son_object> > > sons;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
{
|
||||
if(head_block_time() >= HARDFORK_SON3_TIME) {
|
||||
sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type,
|
||||
(std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count)));
|
||||
}
|
||||
else {
|
||||
sons[active_sidechain_type] = sort_votable_objects<son_index>(active_sidechain_type, get_global_properties().parameters.maximum_son_count());
|
||||
}
|
||||
}
|
||||
|
||||
auto& local_vote_buffer_ref = _vote_tally_buffer;
|
||||
for( const son_object& son : all_sons )
|
||||
{
|
||||
if(son.status == son_status::request_maintenance)
|
||||
for(const auto& status: son.statuses)
|
||||
{
|
||||
auto& stats = son.statistics(*this);
|
||||
modify( stats, [&]( son_statistics_object& _s){
|
||||
_s.last_down_timestamp = head_block_time();
|
||||
const auto& sidechain = status.first;
|
||||
if(status.second == son_status::in_maintenance)
|
||||
{
|
||||
auto &stats = son.statistics(*this);
|
||||
modify(stats, [&](son_statistics_object &_s) {
|
||||
_s.last_down_timestamp[sidechain] = head_block_time();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
modify( son, [local_vote_buffer_ref]( son_object& obj ){
|
||||
obj.total_votes = local_vote_buffer_ref[obj.vote_id];
|
||||
if(obj.status == son_status::request_maintenance)
|
||||
obj.status = son_status::in_maintenance;
|
||||
});
|
||||
for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){
|
||||
obj.total_votes[sidechain_vote_id.first] = local_vote_buffer_ref[sidechain_vote_id.second];
|
||||
}
|
||||
for(auto& status: obj.statuses)
|
||||
{
|
||||
if (status.second == son_status::request_maintenance)
|
||||
status.second = son_status::in_maintenance;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update SON authority
|
||||
|
|
@ -710,21 +810,24 @@ void database::update_active_sons()
|
|||
{
|
||||
modify( get(gpo.parameters.son_account()), [&]( account_object& a )
|
||||
{
|
||||
set<account_id_type> account_ids;
|
||||
for(const auto& sidechain_sons : sons)
|
||||
{
|
||||
for( const son_object& son : sidechain_sons.second )
|
||||
{
|
||||
account_ids.emplace(son.son_account);
|
||||
}
|
||||
}
|
||||
|
||||
if( head_block_time() < HARDFORK_533_TIME )
|
||||
{
|
||||
map<account_id_type, uint64_t> weights;
|
||||
a.active.weight_threshold = 0;
|
||||
a.active.account_auths.clear();
|
||||
|
||||
for( const son_object& son : sons )
|
||||
{
|
||||
weights.emplace(son.son_account, uint64_t(1));
|
||||
}
|
||||
|
||||
for( const auto& weight : weights )
|
||||
for( const auto& account_id : account_ids )
|
||||
{
|
||||
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
|
||||
a.active.account_auths[weight.first] += 1;
|
||||
a.active.account_auths[account_id] += 1;
|
||||
a.active.weight_threshold += 1;
|
||||
}
|
||||
|
||||
|
|
@ -735,8 +838,10 @@ void database::update_active_sons()
|
|||
else
|
||||
{
|
||||
vote_counter vc;
|
||||
for( const son_object& son : sons )
|
||||
vc.add( son.son_account, UINT64_C(1) );
|
||||
for( const auto& account_id : account_ids )
|
||||
{
|
||||
vc.add(account_id, UINT64_C(1));
|
||||
}
|
||||
vc.finish_2_3( a.active );
|
||||
}
|
||||
} );
|
||||
|
|
@ -744,22 +849,36 @@ void database::update_active_sons()
|
|||
|
||||
|
||||
// Compare current and to-be lists of active sons
|
||||
auto cur_active_sons = gpo.active_sons;
|
||||
vector<son_info> new_active_sons;
|
||||
const auto cur_active_sons = gpo.active_sons;
|
||||
flat_map<sidechain_type, vector<son_info> > new_active_sons;
|
||||
const auto &acc = get(gpo.parameters.son_account());
|
||||
for( const son_object& son : sons ) {
|
||||
son_info swi;
|
||||
swi.son_id = son.id;
|
||||
swi.weight = acc.active.account_auths.at(son.son_account);
|
||||
swi.signing_key = son.signing_key;
|
||||
swi.sidechain_public_keys = son.sidechain_public_keys;
|
||||
new_active_sons.push_back(swi);
|
||||
for( const auto& sidechain_sons : sons ){
|
||||
const auto& sidechain = sidechain_sons.first;
|
||||
const auto& sons_array = sidechain_sons.second;
|
||||
|
||||
new_active_sons[sidechain].reserve(sons_array.size());
|
||||
for( const son_object& son : sons_array ) {
|
||||
son_info swi;
|
||||
swi.son_id = son.id;
|
||||
swi.weight = acc.active.account_auths.at(son.son_account);
|
||||
swi.signing_key = son.signing_key;
|
||||
swi.public_key = son.sidechain_public_keys.at(sidechain);
|
||||
new_active_sons[sidechain].push_back(swi);
|
||||
}
|
||||
}
|
||||
|
||||
bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size());
|
||||
if (son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_active_sons.size(); i++ ) {
|
||||
son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i);
|
||||
for( const auto& cur_active_sidechain_sons : cur_active_sons ){
|
||||
const auto& sidechain = cur_active_sidechain_sons.first;
|
||||
const auto& _cur_active_sidechain_sons = cur_active_sidechain_sons.second;
|
||||
|
||||
son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.size() == new_active_sons.at(sidechain).size());
|
||||
if (son_sets_equal) {
|
||||
for (size_t i = 0; i < _cur_active_sidechain_sons.size(); i++) {
|
||||
son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -774,28 +893,37 @@ void database::update_active_sons()
|
|||
modify(gpo, [&]( global_property_object& gp ){
|
||||
gp.active_sons.clear();
|
||||
gp.active_sons.reserve(new_active_sons.size());
|
||||
gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end());
|
||||
});
|
||||
for( const auto& new_active_sidechain_sons : new_active_sons ) {
|
||||
const auto& sidechain = new_active_sidechain_sons.first;
|
||||
const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second;
|
||||
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
modify(sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
flat_set<son_id_type> active_sons;
|
||||
active_sons.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_sons, active_sons.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
_sso.scheduler.update(active_sons);
|
||||
// similar to witness, produce schedule for sons
|
||||
if(cur_active_sons.size() == 0 && new_active_sons.size() > 0)
|
||||
{
|
||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
for( size_t i=0; i<new_active_sons.size(); ++i )
|
||||
_sso.scheduler.produce_schedule(rng);
|
||||
gp.active_sons[sidechain].reserve(_new_active_sidechain_sons.size());
|
||||
gp.active_sons[sidechain].insert(gp.active_sons[sidechain].end(), _new_active_sidechain_sons.cbegin(), _new_active_sidechain_sons.cend());
|
||||
}
|
||||
});
|
||||
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
{
|
||||
const son_schedule_object& sidechain_sso = son_schedule_id_type(get_son_schedule_id(active_sidechain_type))(*this);
|
||||
modify(sidechain_sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
flat_set<son_id_type> active_sons;
|
||||
active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size());
|
||||
std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(),
|
||||
std::inserter(active_sons, active_sons.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
_sso.scheduler.update(active_sons);
|
||||
// similar to witness, produce schedule for sons
|
||||
if(cur_active_sons.at(active_sidechain_type).size() == 0 && new_active_sons.at(active_sidechain_type).size() > 0)
|
||||
{
|
||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
for( size_t i=0; i<new_active_sons.at(active_sidechain_type).size(); ++i )
|
||||
_sso.scheduler.produce_schedule(rng);
|
||||
}
|
||||
});
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const
|
||||
|
|
@ -1335,17 +1463,19 @@ void rolling_period_start(database& db)
|
|||
{
|
||||
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 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();
|
||||
if(now.sec_since_epoch() >= (period_start + vesting_period))
|
||||
const auto now = db.head_block_time();
|
||||
while(now.sec_since_epoch() >= (period_start + vesting_period))
|
||||
{
|
||||
// roll
|
||||
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;
|
||||
});
|
||||
|
||||
period_start = db.get_global_properties().parameters.gpos_period_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1921,7 +2051,7 @@ void database::perform_son_tasks()
|
|||
});
|
||||
}
|
||||
// create BTC asset here because son_account is the issuer of the BTC
|
||||
if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME)
|
||||
if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME)
|
||||
{
|
||||
const asset_dynamic_data_object& dyn_asset =
|
||||
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
||||
|
|
@ -1940,7 +2070,7 @@ void database::perform_son_tasks()
|
|||
asset_issuer_permission_flags::override_authority;
|
||||
a.options.core_exchange_rate.base.amount = 100000;
|
||||
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
|
||||
a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value
|
||||
a.options.core_exchange_rate.quote.amount = 2500;
|
||||
a.options.core_exchange_rate.quote.asset_id = a.id;
|
||||
a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty
|
||||
a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set
|
||||
|
|
@ -1954,8 +2084,42 @@ void database::perform_son_tasks()
|
|||
gpo.pending_parameters->extensions.value.btc_asset = btc_asset.get_id();
|
||||
});
|
||||
}
|
||||
// create ETH asset here because son_account is the issuer of the ETH
|
||||
if (gpo.parameters.eth_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_ETHEREUM_TIME)
|
||||
{
|
||||
const asset_dynamic_data_object& dyn_asset =
|
||||
create<asset_dynamic_data_object>([](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
|
||||
if (gpo.parameters.hbd_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME)
|
||||
if (gpo.parameters.hbd_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_FOR_HIVE_TIME)
|
||||
{
|
||||
const asset_dynamic_data_object& dyn_asset =
|
||||
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
||||
|
|
@ -1974,7 +2138,7 @@ void database::perform_son_tasks()
|
|||
asset_issuer_permission_flags::override_authority;
|
||||
a.options.core_exchange_rate.base.amount = 100000;
|
||||
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
|
||||
a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value
|
||||
a.options.core_exchange_rate.quote.amount = 2500;
|
||||
a.options.core_exchange_rate.quote.asset_id = a.id;
|
||||
a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty
|
||||
a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set
|
||||
|
|
@ -2008,7 +2172,7 @@ void database::perform_son_tasks()
|
|||
asset_issuer_permission_flags::override_authority;
|
||||
a.options.core_exchange_rate.base.amount = 100000;
|
||||
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
|
||||
a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value
|
||||
a.options.core_exchange_rate.quote.amount = 2500;
|
||||
a.options.core_exchange_rate.quote.asset_id = a.id;
|
||||
a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty
|
||||
a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set
|
||||
|
|
@ -2041,6 +2205,13 @@ void update_son_params(database& db)
|
|||
gpo.parameters.extensions.value.maximum_son_count = 7;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& gpo = db.get_global_properties();
|
||||
db.modify( gpo, []( global_property_object& gpo ) {
|
||||
gpo.parameters.extensions.value.maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
|
|
@ -2067,7 +2238,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
d._vote_tally_buffer.resize(props.next_available_vote_id);
|
||||
d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);
|
||||
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
|
||||
d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count() / 2 + 1);
|
||||
for( auto& son_count_histogram_buffer : d._son_count_histogram_buffer ){
|
||||
son_count_histogram_buffer.second.resize(props.parameters.maximum_son_count() / 2 + 1);
|
||||
}
|
||||
d._total_voting_stake = 0;
|
||||
|
||||
auto balance_type = vesting_balance_type::normal;
|
||||
|
|
@ -2167,17 +2340,21 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
// same rationale as for witnesses
|
||||
d._committee_count_histogram_buffer[offset] += voting_stake;
|
||||
}
|
||||
if( opinion_account.options.num_son <= props.parameters.maximum_son_count() )
|
||||
{
|
||||
uint16_t offset = std::min(size_t(opinion_account.options.num_son/2),
|
||||
d._son_count_histogram_buffer.size() - 1);
|
||||
// votes for a number greater than maximum_son_count
|
||||
// are turned into votes for maximum_son_count.
|
||||
//
|
||||
// in particular, this takes care of the case where a
|
||||
// member was voting for a high number, then the
|
||||
// parameter was lowered.
|
||||
d._son_count_histogram_buffer[offset] += voting_stake;
|
||||
FC_ASSERT( opinion_account.options.extensions.value.num_son.valid() , "Invalid son number" );
|
||||
for(const auto& num_sidechain_son : *opinion_account.options.extensions.value.num_son) {
|
||||
const auto sidechain = num_sidechain_son.first;
|
||||
const auto& num_son = num_sidechain_son.second;
|
||||
if (num_son <= props.parameters.maximum_son_count()) {
|
||||
uint16_t offset = std::min(size_t(num_son / 2),
|
||||
d._son_count_histogram_buffer.at(sidechain).size() - 1);
|
||||
// votes for a number greater than maximum_son_count
|
||||
// are turned into votes for maximum_son_count.
|
||||
//
|
||||
// in particular, this takes care of the case where a
|
||||
// member was voting for a high number, then the
|
||||
// parameter was lowered.
|
||||
d._son_count_histogram_buffer.at(sidechain)[offset] += voting_stake;
|
||||
}
|
||||
}
|
||||
|
||||
d._total_voting_stake += voting_stake;
|
||||
|
|
@ -2192,10 +2369,20 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
private:
|
||||
vector<uint64_t>& target;
|
||||
};
|
||||
struct clear_canary_map {
|
||||
clear_canary_map(flat_map<sidechain_type, vector<uint64_t> >& target): target(target){}
|
||||
~clear_canary_map() {
|
||||
for(auto& sidechain_target : target){
|
||||
sidechain_target.second.clear();
|
||||
}
|
||||
}
|
||||
private:
|
||||
flat_map<sidechain_type, vector<uint64_t> >& target;
|
||||
};
|
||||
clear_canary a(_witness_count_histogram_buffer),
|
||||
b(_committee_count_histogram_buffer),
|
||||
d(_son_count_histogram_buffer),
|
||||
c(_vote_tally_buffer);
|
||||
clear_canary_map d{_son_count_histogram_buffer};
|
||||
|
||||
perform_son_tasks();
|
||||
update_top_n_authorities(*this);
|
||||
|
|
@ -2267,13 +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;
|
||||
if( !p.pending_parameters->extensions.value.hive_asset.valid() )
|
||||
p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset;
|
||||
if( !p.pending_parameters->extensions.value.eth_asset.valid() )
|
||||
p.pending_parameters->extensions.value.eth_asset = p.parameters.extensions.value.eth_asset;
|
||||
|
||||
// the following parameters are not allowed to be changed. So take what is in global property
|
||||
p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset;
|
||||
p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset;
|
||||
p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset;
|
||||
p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account;
|
||||
p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start;
|
||||
p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account;
|
||||
p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset;
|
||||
p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count;
|
||||
p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset;
|
||||
p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset;
|
||||
p.pending_parameters->extensions.value.eth_asset = p.parameters.extensions.value.eth_asset;
|
||||
|
||||
p.parameters = std::move(*p.pending_parameters);
|
||||
p.pending_parameters.reset();
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ database::database() :
|
|||
{
|
||||
initialize_indexes();
|
||||
initialize_evaluators();
|
||||
initialize_hardforks();
|
||||
}
|
||||
|
||||
database::~database()
|
||||
|
|
@ -111,6 +112,7 @@ void database::reindex( fc::path data_dir )
|
|||
uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50;
|
||||
|
||||
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);
|
||||
if( head_block_num() >= undo_point )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -203,27 +203,10 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.issuer );
|
||||
}
|
||||
|
||||
void operator()( const transfer_to_blind_operation& op )
|
||||
{
|
||||
_impacted.insert( op.from );
|
||||
for( const auto& out : op.outputs )
|
||||
add_authority_accounts( _impacted, out.owner );
|
||||
}
|
||||
|
||||
void operator()( const blind_transfer_operation& op )
|
||||
{
|
||||
for( const auto& in : op.inputs )
|
||||
add_authority_accounts( _impacted, in.owner );
|
||||
for( const auto& out : op.outputs )
|
||||
add_authority_accounts( _impacted, out.owner );
|
||||
}
|
||||
|
||||
void operator()( const transfer_from_blind_operation& op )
|
||||
{
|
||||
_impacted.insert( op.to );
|
||||
for( const auto& in : op.inputs )
|
||||
add_authority_accounts( _impacted, in.owner );
|
||||
}
|
||||
//! We don't use this operations
|
||||
void operator()( const transfer_to_blind_operation& op ){}
|
||||
void operator()( const blind_transfer_operation& op ){}
|
||||
void operator()( const transfer_from_blind_operation& op ){}
|
||||
|
||||
void operator()( const asset_settle_cancel_operation& op )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,21 +74,32 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
|
|||
return wid;
|
||||
}
|
||||
|
||||
son_id_type database::get_scheduled_son( uint32_t slot_num )const
|
||||
unsigned_int database::get_son_schedule_id( sidechain_type type )const
|
||||
{
|
||||
static const map<sidechain_type, unsigned_int> schedule_map = {
|
||||
{ sidechain_type::bitcoin, 0 },
|
||||
{ sidechain_type::ethereum, 1 },
|
||||
{ sidechain_type::hive, 2 }
|
||||
};
|
||||
|
||||
return schedule_map.at(type);
|
||||
}
|
||||
|
||||
son_id_type database::get_scheduled_son( sidechain_type type, uint32_t slot_num )const
|
||||
{
|
||||
son_id_type sid;
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this);
|
||||
uint64_t current_aslot = dpo.current_aslot + slot_num;
|
||||
return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ];
|
||||
}
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM &&
|
||||
slot_num != 0 )
|
||||
{
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this);
|
||||
// ask the near scheduler who goes in the given slot
|
||||
bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid);
|
||||
if(! slot_is_near)
|
||||
|
|
@ -191,36 +202,41 @@ void database::update_witness_schedule()
|
|||
|
||||
void database::update_son_schedule()
|
||||
{
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
|
||||
if( head_block_num() % gpo.active_sons.size() == 0 )
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
{
|
||||
modify( sso, [&]( son_schedule_object& _sso )
|
||||
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type)));
|
||||
if( head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0)
|
||||
{
|
||||
_sso.current_shuffled_sons.clear();
|
||||
_sso.current_shuffled_sons.reserve( gpo.active_sons.size() );
|
||||
|
||||
for( const son_info& w : gpo.active_sons )
|
||||
_sso.current_shuffled_sons.push_back( w.son_id );
|
||||
|
||||
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
|
||||
for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i )
|
||||
modify( sidechain_sso, [&]( son_schedule_object& _sso )
|
||||
{
|
||||
/// High performance random generator
|
||||
/// http://xorshift.di.unimi.it/
|
||||
uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL;
|
||||
k ^= (k >> 12);
|
||||
k ^= (k << 25);
|
||||
k ^= (k >> 27);
|
||||
k *= 2685821657736338717ULL;
|
||||
_sso.current_shuffled_sons.clear();
|
||||
_sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() );
|
||||
|
||||
uint32_t jmax = _sso.current_shuffled_sons.size() - i;
|
||||
uint32_t j = i + k%jmax;
|
||||
std::swap( _sso.current_shuffled_sons[i],
|
||||
_sso.current_shuffled_sons[j] );
|
||||
}
|
||||
});
|
||||
for ( const son_info &w : gpo.active_sons.at(active_sidechain_type) ) {
|
||||
_sso.current_shuffled_sons.push_back(w.son_id);
|
||||
}
|
||||
|
||||
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
|
||||
|
||||
for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i)
|
||||
{
|
||||
/// High performance random generator
|
||||
/// http://xorshift.di.unimi.it/
|
||||
uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL;
|
||||
k ^= (k >> 12);
|
||||
k ^= (k << 25);
|
||||
k ^= (k >> 27);
|
||||
k *= 2685821657736338717ULL;
|
||||
|
||||
uint32_t jmax = _sso.current_shuffled_sons.size() - i;
|
||||
uint32_t j = i + k % jmax;
|
||||
std::swap(_sso.current_shuffled_sons[i],
|
||||
_sso.current_shuffled_sons[j]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,9 +323,19 @@ void database::update_witness_schedule(const signed_block& next_block)
|
|||
void database::update_son_schedule(const signed_block& next_block)
|
||||
{
|
||||
auto start = fc::time_point::now();
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
#ifndef NDEBUG
|
||||
const son_schedule_object& sso = get(son_schedule_id_type());
|
||||
uint32_t schedule_needs_filled = gpo.active_sons.size();
|
||||
#endif
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const flat_map<sidechain_type, uint32_t> schedule_needs_filled = [&gpo]()
|
||||
{
|
||||
flat_map<sidechain_type, uint32_t> schedule_needs_filled;
|
||||
for(const auto& sidechain_active_sons : gpo.active_sons)
|
||||
{
|
||||
schedule_needs_filled[sidechain_active_sons.first] = sidechain_active_sons.second.size();
|
||||
}
|
||||
return schedule_needs_filled;
|
||||
}();
|
||||
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
|
||||
|
||||
// We shouldn't be able to generate _pending_block with timestamp
|
||||
|
|
@ -319,48 +345,52 @@ void database::update_son_schedule(const signed_block& next_block)
|
|||
|
||||
assert( schedule_slot > 0 );
|
||||
|
||||
son_id_type first_son;
|
||||
bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son );
|
||||
|
||||
son_id_type son;
|
||||
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
|
||||
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
|
||||
assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() );
|
||||
|
||||
modify(sso, [&](son_schedule_object& _sso)
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
{
|
||||
_sso.slots_since_genesis += schedule_slot;
|
||||
witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis);
|
||||
const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type)));
|
||||
son_id_type first_son;
|
||||
bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son );
|
||||
son_id_type son_id;
|
||||
|
||||
_sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1);
|
||||
modify(sidechain_sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
_sso.slots_since_genesis += schedule_slot;
|
||||
witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis);
|
||||
|
||||
_sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(active_sidechain_type).size()) / 2, 1);
|
||||
|
||||
if( slot_is_near )
|
||||
{
|
||||
uint32_t drain = schedule_slot;
|
||||
while( drain > 0 )
|
||||
{
|
||||
if( _sso.scheduler.size() == 0 )
|
||||
break;
|
||||
_sso.scheduler.consume_schedule();
|
||||
--drain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sso.scheduler.reset_schedule( first_son );
|
||||
}
|
||||
while( !_sso.scheduler.get_slot(schedule_needs_filled.at(active_sidechain_type), son_id) )
|
||||
{
|
||||
if( _sso.scheduler.produce_schedule(rng) & emit_turn )
|
||||
memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
|
||||
}
|
||||
_sso.last_scheduling_block = next_block.block_num();
|
||||
_sso.recent_slots_filled = (
|
||||
(_sso.recent_slots_filled << 1)
|
||||
+ 1) << (schedule_slot - 1);
|
||||
});
|
||||
}
|
||||
|
||||
if( slot_is_near )
|
||||
{
|
||||
uint32_t drain = schedule_slot;
|
||||
while( drain > 0 )
|
||||
{
|
||||
if( _sso.scheduler.size() == 0 )
|
||||
break;
|
||||
_sso.scheduler.consume_schedule();
|
||||
--drain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sso.scheduler.reset_schedule( first_son );
|
||||
}
|
||||
while( !_sso.scheduler.get_slot(schedule_needs_filled, son) )
|
||||
{
|
||||
if( _sso.scheduler.produce_schedule(rng) & emit_turn )
|
||||
memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
|
||||
}
|
||||
_sso.last_scheduling_block = next_block.block_num();
|
||||
_sso.recent_slots_filled = (
|
||||
(_sso.recent_slots_filled << 1)
|
||||
+ 1) << (schedule_slot - 1);
|
||||
});
|
||||
auto end = fc::time_point::now();
|
||||
static uint64_t total_time = 0;
|
||||
static uint64_t calls = 0;
|
||||
|
|
|
|||
7
libraries/chain/hardfork.d/SIDECHAIN.hf
Normal file
7
libraries/chain/hardfork.d/SIDECHAIN.hf
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef HARDFORK_SIDECHAIN_DELETE_TIME
|
||||
#ifdef BUILD_PEERPLAYS_TESTNET
|
||||
#define HARDFORK_SIDECHAIN_DELETE_TIME (fc::time_point_sec::from_iso_string("2022-11-16T02:00:00"))
|
||||
#else
|
||||
#define HARDFORK_SIDECHAIN_DELETE_TIME (fc::time_point_sec::from_iso_string("2022-11-16T02:00:00"))
|
||||
#endif
|
||||
#endif
|
||||
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
|
||||
|
|
@ -66,6 +66,8 @@ namespace graphene { namespace chain {
|
|||
database();
|
||||
~database();
|
||||
|
||||
std::vector<fc::time_point_sec> _hardfork_times;
|
||||
|
||||
enum validation_steps
|
||||
{
|
||||
skip_nothing = 0,
|
||||
|
|
@ -243,7 +245,16 @@ namespace graphene { namespace chain {
|
|||
witness_id_type get_scheduled_witness(uint32_t slot_num)const;
|
||||
|
||||
/**
|
||||
* @brief Get the son scheduled for block production in a slot.
|
||||
* @brief Get son schedule id for the given sidechain_type.
|
||||
*
|
||||
* type sidechain_type we getting schedule.
|
||||
*
|
||||
* returns Id of the schedule object.
|
||||
*/
|
||||
unsigned_int get_son_schedule_id(sidechain_type type)const;
|
||||
|
||||
/**
|
||||
* @brief Get the bitcoin or hive son scheduled for block production in a slot.
|
||||
*
|
||||
* slot_num always corresponds to a time in the future.
|
||||
*
|
||||
|
|
@ -256,7 +267,7 @@ namespace graphene { namespace chain {
|
|||
*
|
||||
* Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS
|
||||
*/
|
||||
son_id_type get_scheduled_son(uint32_t slot_num)const;
|
||||
son_id_type get_scheduled_son(sidechain_type type, uint32_t slot_num)const;
|
||||
|
||||
/**
|
||||
* Get the time at which the given slot occurs.
|
||||
|
|
@ -311,7 +322,7 @@ namespace graphene { namespace chain {
|
|||
fc::optional<operation> create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son );
|
||||
signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op );
|
||||
bool is_son_dereg_valid( son_id_type son_id );
|
||||
bool is_son_active( son_id_type son_id );
|
||||
bool is_son_active( sidechain_type type, son_id_type son_id );
|
||||
bool is_asset_creation_allowed(const string& symbol);
|
||||
|
||||
time_point_sec head_block_time()const;
|
||||
|
|
@ -332,6 +343,8 @@ namespace graphene { namespace chain {
|
|||
void initialize_evaluators();
|
||||
/// Reset the object graph in-memory
|
||||
void initialize_indexes();
|
||||
void initialize_hardforks();
|
||||
|
||||
void init_genesis(const genesis_state_type& genesis_state = genesis_state_type());
|
||||
|
||||
template<typename EvaluatorType>
|
||||
|
|
@ -507,12 +520,16 @@ namespace graphene { namespace chain {
|
|||
void notify_changed_objects();
|
||||
|
||||
private:
|
||||
std::mutex _pending_tx_session_mutex;
|
||||
optional<undo_database::session> _pending_tx_session;
|
||||
vector< unique_ptr<op_evaluator> > _operation_evaluators;
|
||||
|
||||
template<class Index>
|
||||
vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(size_t count)const;
|
||||
|
||||
template<class Index>
|
||||
vector<std::reference_wrapper<const typename Index::object_type>> sort_votable_objects(sidechain_type sidechain, size_t count)const;
|
||||
|
||||
//////////////////// db_block.cpp ////////////////////
|
||||
|
||||
public:
|
||||
|
|
@ -567,13 +584,14 @@ namespace graphene { namespace chain {
|
|||
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
|
||||
void update_active_witnesses();
|
||||
void update_active_committee_members();
|
||||
void update_son_metrics( const vector<son_info>& curr_active_sons );
|
||||
void update_son_metrics( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons );
|
||||
void update_active_sons();
|
||||
void remove_son_proposal( const proposal_object& proposal );
|
||||
void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void update_son_statuses( const vector<son_info>& cur_active_sons, const vector<son_info>& new_active_sons );
|
||||
void update_son_wallet( const vector<son_info>& new_active_sons );
|
||||
void update_son_statuses( const flat_map<sidechain_type, vector<son_info> >& curr_active_sons,
|
||||
const flat_map<sidechain_type, vector<son_info> >& new_active_sons );
|
||||
void update_son_wallet( const flat_map<sidechain_type, vector<son_info> >& new_active_sons );
|
||||
void update_worker_votes();
|
||||
|
||||
public:
|
||||
|
|
@ -585,6 +603,7 @@ namespace graphene { namespace chain {
|
|||
///@}
|
||||
///@}
|
||||
|
||||
std::mutex _pending_tx_mutex;
|
||||
vector< processed_transaction > _pending_tx;
|
||||
fork_database _fork_db;
|
||||
|
||||
|
|
@ -612,11 +631,17 @@ namespace graphene { namespace chain {
|
|||
uint16_t _current_op_in_trx = 0;
|
||||
uint32_t _current_virtual_op = 0;
|
||||
|
||||
vector<uint64_t> _vote_tally_buffer;
|
||||
vector<uint64_t> _witness_count_histogram_buffer;
|
||||
vector<uint64_t> _committee_count_histogram_buffer;
|
||||
vector<uint64_t> _son_count_histogram_buffer;
|
||||
uint64_t _total_voting_stake;
|
||||
vector<uint64_t> _vote_tally_buffer;
|
||||
vector<uint64_t> _witness_count_histogram_buffer;
|
||||
vector<uint64_t> _committee_count_histogram_buffer;
|
||||
flat_map<sidechain_type, vector<uint64_t> > _son_count_histogram_buffer = []{
|
||||
flat_map<sidechain_type, vector<uint64_t> > son_count_histogram_buffer;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
son_count_histogram_buffer[active_sidechain_type] = vector<uint64_t>{};
|
||||
}
|
||||
return son_count_histogram_buffer;
|
||||
}();
|
||||
uint64_t _total_voting_stake;
|
||||
|
||||
flat_map<uint32_t,block_id_type> _checkpoints;
|
||||
|
||||
|
|
|
|||
|
|
@ -182,9 +182,6 @@ namespace graphene { namespace chain {
|
|||
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer );
|
||||
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" )
|
||||
|
||||
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer );
|
||||
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" );
|
||||
|
||||
/*
|
||||
FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" )
|
||||
|
|
|
|||
|
|
@ -49,10 +49,18 @@ namespace graphene { namespace chain {
|
|||
chain_parameters parameters;
|
||||
optional<chain_parameters> pending_parameters;
|
||||
|
||||
uint32_t next_available_vote_id = 0;
|
||||
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
|
||||
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
|
||||
vector<son_info> active_sons; // updated once per maintenance interval
|
||||
uint32_t next_available_vote_id = 0;
|
||||
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
|
||||
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
|
||||
flat_map<sidechain_type, vector<son_info> > active_sons = []() // updated once per maintenance interval
|
||||
{
|
||||
flat_map<sidechain_type, vector<son_info> > active_sons;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
{
|
||||
active_sons[active_sidechain_type] = vector<son_info>();
|
||||
}
|
||||
return active_sons;
|
||||
}();
|
||||
// n.b. witness scheduling is done by witness_schedule object
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <graphene/chain/protocol/special_authority.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/protocol/vote.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -37,6 +38,19 @@ namespace graphene { namespace chain {
|
|||
/// These are the fields which can be updated by the active authority.
|
||||
struct account_options
|
||||
{
|
||||
struct ext
|
||||
{
|
||||
/// The number of active son members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of son members voted for in @ref votes
|
||||
optional< flat_map<sidechain_type, uint16_t> > num_son = []{
|
||||
flat_map<sidechain_type, uint16_t> num_son;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types){
|
||||
num_son[active_sidechain_type] = 0;
|
||||
}
|
||||
return num_son;
|
||||
}();
|
||||
};
|
||||
|
||||
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
|
||||
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
|
||||
/// multiple keys in it.
|
||||
|
|
@ -52,14 +66,11 @@ namespace graphene { namespace chain {
|
|||
/// The number of active committee members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of committee members voted for in @ref votes
|
||||
uint16_t num_committee = 0;
|
||||
/// The number of active son members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of son members voted for in @ref votes
|
||||
uint16_t num_son = 0;
|
||||
/// This is the list of vote IDs this account votes for. The weight of these votes is determined by this
|
||||
/// account's balance of core asset.
|
||||
flat_set<vote_id_type> votes;
|
||||
extensions_type extensions;
|
||||
|
||||
extension< ext > extensions;
|
||||
|
||||
/// Whether this account is voting
|
||||
inline bool is_voting() const
|
||||
{
|
||||
|
|
@ -244,7 +255,7 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
struct account_upgrade_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
struct fee_parameters_type {
|
||||
uint64_t membership_annual_fee = 2000 * GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
uint64_t membership_lifetime_fee = 10000 * GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to upgrade to a lifetime member
|
||||
};
|
||||
|
|
@ -289,6 +300,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::account_options::ext, (num_son))
|
||||
FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions))
|
||||
// FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
|
||||
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ namespace graphene { namespace chain {
|
|||
optional < uint16_t > maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS
|
||||
optional < asset_id_type > hbd_asset = asset_id_type();
|
||||
optional < asset_id_type > hive_asset = asset_id_type();
|
||||
optional < asset_id_type > eth_asset = asset_id_type();
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
@ -220,6 +221,9 @@ namespace graphene { namespace chain {
|
|||
inline asset_id_type hive_asset() const {
|
||||
return extensions.value.hive_asset.valid() ? *extensions.value.hive_asset : asset_id_type();
|
||||
}
|
||||
inline asset_id_type eth_asset() const {
|
||||
return extensions.value.eth_asset.valid() ? *extensions.value.eth_asset : asset_id_type();
|
||||
}
|
||||
private:
|
||||
static void safe_copy(chain_parameters& to, const chain_parameters& from);
|
||||
};
|
||||
|
|
@ -257,6 +261,7 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(maximum_son_count)
|
||||
(hbd_asset)
|
||||
(hive_asset)
|
||||
(eth_asset)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::chain_parameters,
|
||||
|
|
|
|||
|
|
@ -111,12 +111,12 @@ struct stealth_confirmation
|
|||
/**
|
||||
* Packs *this then encodes as base58 encoded string.
|
||||
*/
|
||||
operator string()const;
|
||||
//operator string()const;
|
||||
/**
|
||||
* Unpacks from a base58 string
|
||||
*/
|
||||
stealth_confirmation( const std::string& base58 );
|
||||
stealth_confirmation(){}
|
||||
//stealth_confirmation( const std::string& base58 );
|
||||
//stealth_confirmation(){}
|
||||
|
||||
public_key_type one_time_key;
|
||||
optional<public_key_type> to;
|
||||
|
|
@ -152,16 +152,17 @@ struct transfer_to_blind_operation : public base_operation
|
|||
uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
|
||||
asset fee;
|
||||
asset amount;
|
||||
account_id_type from;
|
||||
blind_factor_type blinding_factor;
|
||||
vector<blind_output> outputs;
|
||||
|
||||
account_id_type fee_payer()const { return from; }
|
||||
void validate()const;
|
||||
share_type calculate_fee(const fee_parameters_type& )const;
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
//account_id_type fee_payer()const { return from; }
|
||||
//void validate()const;
|
||||
//share_type calculate_fee(const fee_parameters_type& )const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -180,14 +181,15 @@ struct transfer_from_blind_operation : public base_operation
|
|||
blind_factor_type blinding_factor;
|
||||
vector<blind_input> inputs;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
|
||||
void validate()const;
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
void get_required_authorities( vector<authority>& a )const
|
||||
{
|
||||
for( const auto& in : inputs )
|
||||
a.push_back( in.owner );
|
||||
}
|
||||
//account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
|
||||
//void validate()const;
|
||||
//void get_required_authorities( vector<authority>& a )const
|
||||
//{
|
||||
// for( const auto& in : inputs )
|
||||
// a.push_back( in.owner );
|
||||
//}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -243,17 +245,18 @@ struct blind_transfer_operation : public base_operation
|
|||
asset fee;
|
||||
vector<blind_input> inputs;
|
||||
vector<blind_output> outputs;
|
||||
|
||||
/** graphene TEMP account */
|
||||
account_id_type fee_payer()const;
|
||||
void validate()const;
|
||||
share_type calculate_fee( const fee_parameters_type& k )const;
|
||||
|
||||
void get_required_authorities( vector<authority>& a )const
|
||||
{
|
||||
for( const auto& in : inputs )
|
||||
a.push_back( in.owner );
|
||||
}
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
/** graphene TEMP account */
|
||||
//account_id_type fee_payer()const;
|
||||
//void validate()const;
|
||||
//share_type calculate_fee( const fee_parameters_type& k )const;
|
||||
//void get_required_authorities( vector<authority>& a )const
|
||||
//{
|
||||
// for( const auto& in : inputs )
|
||||
// a.push_back( in.owner );
|
||||
//}
|
||||
};
|
||||
|
||||
///@} endgroup stealth
|
||||
|
|
|
|||
|
|
@ -106,9 +106,9 @@ namespace graphene { namespace chain {
|
|||
assert_operation,
|
||||
balance_claim_operation,
|
||||
override_transfer_operation,
|
||||
transfer_to_blind_operation,
|
||||
blind_transfer_operation,
|
||||
transfer_from_blind_operation,
|
||||
transfer_to_blind_operation, //! We don't use this operation
|
||||
blind_transfer_operation, //! We don't use this operation
|
||||
transfer_from_blind_operation, //! We don't use this operation
|
||||
asset_settle_cancel_operation, // VIRTUAL
|
||||
asset_claim_fees_operation,
|
||||
fba_distribute_operation, // VIRTUAL
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace graphene { namespace chain {
|
|||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
vector<son_info> sons;
|
||||
flat_map<sidechain_type, vector<son_info> > sons;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
|
|
|
|||
|
|
@ -395,6 +395,13 @@ namespace graphene { namespace chain {
|
|||
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 binary_key
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ struct vote_id_type
|
|||
committee,
|
||||
witness,
|
||||
worker,
|
||||
son,
|
||||
son_bitcoin,
|
||||
son_hive,
|
||||
son_ethereum,
|
||||
VOTE_TYPE_COUNT
|
||||
};
|
||||
|
||||
|
|
@ -144,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_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) )
|
||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(son_ethereum)(VOTE_TYPE_COUNT) )
|
||||
FC_REFLECT( graphene::chain::vote_id_type, (content) )
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type )
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace graphene { namespace chain {
|
|||
time_point_sec expires;
|
||||
|
||||
sidechain_address_object() :
|
||||
sidechain(sidechain_type::bitcoin),
|
||||
sidechain(sidechain_type::bitcoin), //! FIXME - bitcoin ???
|
||||
deposit_public_key(""),
|
||||
deposit_address(""),
|
||||
withdraw_public_key(""),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -13,12 +14,14 @@ enum class sidechain_type {
|
|||
hive
|
||||
};
|
||||
|
||||
static const std::set<sidechain_type> active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::ethereum, sidechain_type::hive};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::sidechain_type,
|
||||
(unknown)
|
||||
(bitcoin)
|
||||
(ethereum)
|
||||
(eos)
|
||||
(hive)
|
||||
(peerplays) )
|
||||
(unknown)
|
||||
(bitcoin)
|
||||
(ethereum)
|
||||
(eos)
|
||||
(hive)
|
||||
(peerplays) )
|
||||
|
|
|
|||
|
|
@ -14,26 +14,15 @@ namespace graphene { namespace chain {
|
|||
son_id_type son_id;
|
||||
weight_type weight = 0;
|
||||
public_key_type signing_key;
|
||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||
string public_key;
|
||||
|
||||
bool operator==(const son_info& rhs) {
|
||||
bool operator==(const son_info& rhs) const {
|
||||
bool son_sets_equal =
|
||||
(son_id == rhs.son_id) &&
|
||||
(weight == rhs.weight) &&
|
||||
(signing_key == rhs.signing_key) &&
|
||||
(sidechain_public_keys.size() == rhs.sidechain_public_keys.size());
|
||||
(public_key == rhs.public_key);
|
||||
|
||||
if (son_sets_equal) {
|
||||
bool sidechain_public_keys_equal = true;
|
||||
for (size_t i = 0; i < sidechain_public_keys.size(); i++) {
|
||||
const auto lhs_scpk = sidechain_public_keys.nth(i);
|
||||
const auto rhs_scpk = rhs.sidechain_public_keys.nth(i);
|
||||
sidechain_public_keys_equal = sidechain_public_keys_equal &&
|
||||
(lhs_scpk->first == rhs_scpk->first) &&
|
||||
(lhs_scpk->second == rhs_scpk->second);
|
||||
}
|
||||
son_sets_equal = son_sets_equal && sidechain_public_keys_equal;
|
||||
}
|
||||
return son_sets_equal;
|
||||
}
|
||||
};
|
||||
|
|
@ -44,4 +33,4 @@ FC_REFLECT( graphene::chain::son_info,
|
|||
(son_id)
|
||||
(weight)
|
||||
(signing_key)
|
||||
(sidechain_public_keys) )
|
||||
(public_key) )
|
||||
|
|
|
|||
|
|
@ -35,15 +35,15 @@ namespace graphene { namespace chain {
|
|||
// Transactions signed since the last son payouts
|
||||
flat_map<sidechain_type, uint64_t> txs_signed;
|
||||
// Total Voted Active time i.e. duration selected as part of voted active SONs
|
||||
uint64_t total_voted_time = 0;
|
||||
flat_map<sidechain_type, uint64_t> total_voted_time;
|
||||
// Total Downtime barring the current down time in seconds, used for stats to present to user
|
||||
uint64_t total_downtime = 0;
|
||||
flat_map<sidechain_type, uint64_t> total_downtime;
|
||||
// Current Interval Downtime since last maintenance
|
||||
uint64_t current_interval_downtime = 0;
|
||||
flat_map<sidechain_type, uint64_t> current_interval_downtime;
|
||||
// Down timestamp, if son status is in_maintenance use this
|
||||
fc::time_point_sec last_down_timestamp;
|
||||
flat_map<sidechain_type, fc::time_point_sec> last_down_timestamp;
|
||||
// Last Active heartbeat timestamp
|
||||
fc::time_point_sec last_active_timestamp;
|
||||
flat_map<sidechain_type, fc::time_point_sec> last_active_timestamp;
|
||||
// Deregistered Timestamp
|
||||
fc::time_point_sec deregistered_timestamp;
|
||||
// Total sidechain transactions reported by SON network while SON was active
|
||||
|
|
@ -64,23 +64,38 @@ namespace graphene { namespace chain {
|
|||
static const uint8_t type_id = son_object_type;
|
||||
|
||||
account_id_type son_account;
|
||||
vote_id_type vote_id;
|
||||
uint64_t total_votes = 0;
|
||||
flat_map<sidechain_type, vote_id_type> sidechain_vote_ids;
|
||||
flat_map<sidechain_type, uint64_t> total_votes;
|
||||
string url;
|
||||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
vesting_balance_id_type pay_vb;
|
||||
son_statistics_id_type statistics;
|
||||
son_status status = son_status::inactive;
|
||||
flat_map<sidechain_type, son_status> statuses = []()
|
||||
{
|
||||
flat_map<sidechain_type, son_status> statuses;
|
||||
for(const auto& active_sidechain_type : active_sidechain_types)
|
||||
{
|
||||
statuses[active_sidechain_type] = son_status::inactive;
|
||||
}
|
||||
return statuses;
|
||||
}();
|
||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
void pay_son_fee(share_type pay, database& db);
|
||||
bool has_valid_config()const;
|
||||
bool has_valid_config(time_point_sec head_block_time)const;
|
||||
|
||||
inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); }
|
||||
inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); }
|
||||
inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); }
|
||||
inline vote_id_type get_ethereum_vote_id() const { return get_sidechain_vote_id(sidechain_type::ethereum); }
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
struct by_vote_id;
|
||||
struct by_vote_id_bitcoin;
|
||||
struct by_vote_id_hive;
|
||||
struct by_vote_id_ethereum;
|
||||
using son_multi_index_type = multi_index_container<
|
||||
son_object,
|
||||
indexed_by<
|
||||
|
|
@ -90,8 +105,14 @@ namespace graphene { namespace chain {
|
|||
ordered_unique< tag<by_account>,
|
||||
member<son_object, account_id_type, &son_object::son_account>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id>,
|
||||
member<son_object, vote_id_type, &son_object::vote_id>
|
||||
ordered_unique< tag<by_vote_id_bitcoin>,
|
||||
const_mem_fun<son_object, vote_id_type, &son_object::get_bitcoin_vote_id>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id_hive>,
|
||||
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>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
|
@ -117,14 +138,14 @@ FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintena
|
|||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object),
|
||||
(son_account)
|
||||
(vote_id)
|
||||
(sidechain_vote_ids)
|
||||
(total_votes)
|
||||
(url)
|
||||
(deposit)
|
||||
(signing_key)
|
||||
(pay_vb)
|
||||
(statistics)
|
||||
(status)
|
||||
(statuses)
|
||||
(sidechain_public_keys)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace graphene { namespace chain {
|
|||
time_point_sec expires;
|
||||
|
||||
flat_map<sidechain_type, string> addresses;
|
||||
vector<son_info> sons;
|
||||
flat_map<sidechain_type, vector<son_info> > sons;
|
||||
};
|
||||
|
||||
struct by_valid_from;
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ namespace graphene { namespace chain {
|
|||
* @ingroup object
|
||||
*/
|
||||
struct voters_info {
|
||||
optional<voters_info_object> voters_for_committee_member;
|
||||
optional<voters_info_object> voters_for_witness;
|
||||
optional<vector<voters_info_object> > voters_for_workers;
|
||||
optional<vector<voters_info_object> > voters_against_workers;
|
||||
optional<voters_info_object> voters_for_son;
|
||||
optional<voters_info_object> voters_for_committee_member;
|
||||
optional<voters_info_object> voters_for_witness;
|
||||
optional<vector<voters_info_object> > voters_for_workers;
|
||||
optional<vector<voters_info_object> > voters_against_workers;
|
||||
optional<flat_map<sidechain_type, voters_info_object> > voters_for_son;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -37,4 +37,4 @@ FC_REFLECT( graphene::chain::voters_info,
|
|||
(voters_for_witness)
|
||||
(voters_for_workers)
|
||||
(voters_against_workers)
|
||||
(voters_for_son) )
|
||||
(voters_for_son))
|
||||
|
|
@ -19,11 +19,11 @@ namespace graphene { namespace chain {
|
|||
* @ingroup object
|
||||
*/
|
||||
struct votes_info {
|
||||
optional< vector< votes_info_object > > votes_for_committee_members;
|
||||
optional< vector< votes_info_object > > votes_for_witnesses;
|
||||
optional< vector< votes_info_object > > votes_for_workers;
|
||||
optional< vector< votes_info_object > > votes_against_workers;
|
||||
optional< vector< votes_info_object > > votes_for_sons;
|
||||
optional< vector< votes_info_object > > votes_for_committee_members;
|
||||
optional< vector< votes_info_object > > votes_for_witnesses;
|
||||
optional< vector< votes_info_object > > votes_for_workers;
|
||||
optional< vector< votes_info_object > > votes_against_workers;
|
||||
optional< flat_map<sidechain_type, vector< votes_info_object > > > votes_for_sons;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -37,4 +37,4 @@ FC_REFLECT( graphene::chain::votes_info,
|
|||
(votes_for_witnesses)
|
||||
(votes_for_workers)
|
||||
(votes_against_workers)
|
||||
(votes_for_sons) )
|
||||
(votes_for_sons))
|
||||
|
|
@ -96,7 +96,7 @@ class son_schedule_object : public graphene::db::abstract_object<son_schedule_ob
|
|||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_son_schedule_object_type;
|
||||
|
||||
vector< son_id_type > current_shuffled_sons;
|
||||
vector<son_id_type > current_shuffled_sons;
|
||||
|
||||
son_scheduler scheduler;
|
||||
uint32_t last_scheduling_block;
|
||||
|
|
|
|||
|
|
@ -162,8 +162,12 @@ class generic_witness_scheduler
|
|||
_schedule.pop_front();
|
||||
|
||||
auto it = _lame_duck.find( result );
|
||||
if( it != _lame_duck.end() )
|
||||
_lame_duck.erase( it );
|
||||
if( it != _lame_duck.end() ) {
|
||||
set< WitnessID > removal_set;
|
||||
removal_set.insert(*it);
|
||||
remove_all( removal_set );
|
||||
_lame_duck.erase(it);
|
||||
}
|
||||
if( debug ) check_invariant();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -389,7 +393,7 @@ class generic_witness_scheduler
|
|||
// scheduled
|
||||
std::deque < WitnessID > _schedule;
|
||||
|
||||
// in _schedule, but not to be replaced
|
||||
// in _schedule, but must be removed
|
||||
set< WitnessID > _lame_duck;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -174,22 +174,31 @@ void account_options::validate() const
|
|||
{
|
||||
auto needed_witnesses = num_witness;
|
||||
auto needed_committee = num_committee;
|
||||
auto needed_sons = num_son;
|
||||
FC_ASSERT( extensions.value.num_son.valid() , "Invalid son number" );
|
||||
flat_map<sidechain_type, uint16_t> needed_sons = *extensions.value.num_son;
|
||||
|
||||
for( vote_id_type id : votes )
|
||||
if( id.type() == vote_id_type::witness && needed_witnesses )
|
||||
--needed_witnesses;
|
||||
else if ( id.type() == vote_id_type::committee && needed_committee )
|
||||
--needed_committee;
|
||||
else if ( id.type() == vote_id_type::son && needed_sons )
|
||||
--needed_sons;
|
||||
else if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] )
|
||||
--needed_sons[sidechain_type::bitcoin];
|
||||
else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] )
|
||||
--needed_sons[sidechain_type::hive];
|
||||
else if ( id.type() == vote_id_type::son_ethereum && needed_sons[sidechain_type::ethereum] )
|
||||
--needed_sons[sidechain_type::ethereum];
|
||||
|
||||
FC_ASSERT( needed_witnesses == 0,
|
||||
"May not specify fewer witnesses than the number voted for.");
|
||||
FC_ASSERT( needed_committee == 0,
|
||||
"May not specify fewer committee members than the number voted for.");
|
||||
FC_ASSERT( needed_sons == 0,
|
||||
"May not specify fewer SONs than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0,
|
||||
"May not specify fewer Bitcoin SONs than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::hive] == 0,
|
||||
"May not specify fewer Hive SONs than the number voted for.");
|
||||
FC_ASSERT( needed_sons[sidechain_type::ethereum] == 0,
|
||||
"May not specify fewer Ethereum SONs than the number voted for.");
|
||||
}
|
||||
|
||||
void affiliate_reward_distribution::validate() const
|
||||
|
|
|
|||
|
|
@ -22,12 +22,10 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/confidential.hpp>
|
||||
#include <graphene/chain/confidential_evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
|
||||
/*
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void transfer_to_blind_operation::validate()const
|
||||
|
|
@ -47,19 +45,6 @@ void transfer_to_blind_operation::validate()const
|
|||
FC_ASSERT( !outputs[i].owner.is_impossible() );
|
||||
}
|
||||
FC_ASSERT( out.size(), "there must be at least one output" );
|
||||
|
||||
auto public_c = fc::ecc::blind(blinding_factor,net_public);
|
||||
|
||||
FC_ASSERT( fc::ecc::verify_sum( {public_c}, out, 0 ), "", ("net_public",net_public) );
|
||||
|
||||
if( outputs.size() > 1 )
|
||||
{
|
||||
for( auto out : outputs )
|
||||
{
|
||||
auto info = fc::ecc::range_get_info( out.range_proof );
|
||||
FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
share_type transfer_to_blind_operation::calculate_fee( const fee_parameters_type& k )const
|
||||
|
|
@ -79,31 +64,15 @@ void transfer_from_blind_operation::validate()const
|
|||
vector<commitment_type> in(inputs.size());
|
||||
vector<commitment_type> out;
|
||||
int64_t net_public = fee.amount.value + amount.amount.value;
|
||||
out.push_back( fc::ecc::blind( blinding_factor, net_public ) );
|
||||
for( uint32_t i = 0; i < in.size(); ++i )
|
||||
{
|
||||
in[i] = inputs[i].commitment;
|
||||
/// by requiring all inputs to be sorted we also prevent duplicate commitments on the input
|
||||
if( i > 0 ) FC_ASSERT( in[i-1] < in[i], "all inputs must be sorted by commitment id" );
|
||||
}
|
||||
FC_ASSERT( in.size(), "there must be at least one input" );
|
||||
FC_ASSERT( fc::ecc::verify_sum( in, out, 0 ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If fee_payer = temp_account_id, then the fee is paid by the surplus balance of inputs-outputs and
|
||||
* 100% of the fee goes to the network.
|
||||
*/
|
||||
account_id_type blind_transfer_operation::fee_payer()const
|
||||
{
|
||||
return GRAPHENE_TEMP_ACCOUNT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method can be computationally intensive because it verifies that input commitments - output commitments add up to 0
|
||||
*/
|
||||
void blind_transfer_operation::validate()const
|
||||
{ try {
|
||||
vector<commitment_type> in(inputs.size());
|
||||
|
|
@ -122,17 +91,6 @@ void blind_transfer_operation::validate()const
|
|||
FC_ASSERT( !outputs[i].owner.is_impossible() );
|
||||
}
|
||||
FC_ASSERT( in.size(), "there must be at least one input" );
|
||||
FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), "", ("net_public", net_public) );
|
||||
|
||||
if( outputs.size() > 1 )
|
||||
{
|
||||
for( auto out : outputs )
|
||||
{
|
||||
auto info = fc::ecc::range_get_info( out.range_proof );
|
||||
FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );
|
||||
}
|
||||
}
|
||||
FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), "", ("net_public", net_public) );
|
||||
} FC_CAPTURE_AND_RETHROW( (*this) ) }
|
||||
|
||||
share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k )const
|
||||
|
|
@ -140,16 +98,12 @@ share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k
|
|||
return k.fee + outputs.size() * k.price_per_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs *this then encodes as base58 encoded string.
|
||||
*/
|
||||
|
||||
stealth_confirmation::operator string()const
|
||||
{
|
||||
return fc::to_base58( fc::raw::pack( *this ) );
|
||||
}
|
||||
/**
|
||||
* Unpacks from a base58 string
|
||||
*/
|
||||
|
||||
stealth_confirmation::stealth_confirmation( const std::string& base58 )
|
||||
{
|
||||
*this = fc::raw::unpack<stealth_confirmation>( fc::from_base58( base58 ) );
|
||||
|
|
@ -157,6 +111,8 @@ stealth_confirmation::stealth_confirmation( const std::string& base58 )
|
|||
|
||||
} } // graphene::chain
|
||||
|
||||
*/
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_to_blind_operation::fee_parameters_type )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_from_blind_operation::fee_parameters_type )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blind_transfer_operation::fee_parameters_type )
|
||||
|
|
|
|||
|
|
@ -22,11 +22,14 @@ object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address
|
|||
const auto &sidechain_addresses_idx = db().get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain_and_expires>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(op.sidechain_address_account, op.sidechain, time_point_sec::maximum()));
|
||||
|
||||
if (addr_itr != sidechain_addresses_idx.end())
|
||||
{
|
||||
db().modify(*addr_itr, [&](sidechain_address_object &sao) {
|
||||
sao.expires = db().head_block_time();
|
||||
});
|
||||
if (addr_itr != sidechain_addresses_idx.end()) {
|
||||
if (db().head_block_time() >= HARDFORK_SIDECHAIN_DELETE_TIME) {
|
||||
db().remove(*addr_itr);
|
||||
} else {
|
||||
db().modify(*addr_itr, [&](sidechain_address_object &sao) {
|
||||
sao.expires = db().head_block_time();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const auto& new_sidechain_address_object = db().create<sidechain_address_object>( [&]( sidechain_address_object& obj ){
|
||||
|
|
@ -47,7 +50,7 @@ void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_addr
|
|||
{ try {
|
||||
const auto& sidx = db().get_index_type<son_index>().indices().get<by_account>();
|
||||
const auto& son_obj = sidx.find(op.payer);
|
||||
FC_ASSERT( son_obj != sidx.end() && db().is_son_active(son_obj->id), "Non active SON trying to update deposit address object" );
|
||||
FC_ASSERT( son_obj != sidx.end() && db().is_son_active(op.sidechain, son_obj->id), "Non active SON trying to update deposit address object" );
|
||||
const auto& sdpke_idx = db().get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_public_key_and_expires>();
|
||||
FC_ASSERT( op.deposit_address.valid() && op.deposit_public_key.valid() && op.deposit_address_data.valid(), "Update operation by SON is not valid");
|
||||
FC_ASSERT( (*op.deposit_address).length() > 0 && (*op.deposit_public_key).length() > 0 && (*op.deposit_address_data).length() > 0, "SON should create a valid deposit address with valid deposit public key");
|
||||
|
|
@ -105,10 +108,14 @@ void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address
|
|||
{ try {
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
auto sidechain_address = idx.find(op.sidechain_address_id);
|
||||
if(sidechain_address != idx.end()) {
|
||||
db().modify(*sidechain_address, [&](sidechain_address_object &sao) {
|
||||
sao.expires = db().head_block_time();
|
||||
});
|
||||
if (sidechain_address != idx.end()) {
|
||||
if (db().head_block_time() >= HARDFORK_SIDECHAIN_DELETE_TIME) {
|
||||
db().remove(*sidechain_address);
|
||||
} else {
|
||||
db().modify(*sidechain_address, [&](sidechain_address_object &sao) {
|
||||
sao.expires = db().head_block_time();
|
||||
});
|
||||
}
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -38,14 +38,20 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op)
|
|||
|
||||
object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
|
||||
{ try {
|
||||
vote_id_type vote_id;
|
||||
db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) {
|
||||
vote_id = get_next_vote_id(p, vote_id_type::son);
|
||||
vote_id_type vote_id_bitcoin;
|
||||
vote_id_type vote_id_hive;
|
||||
vote_id_type vote_id_ethereum;
|
||||
db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive, &vote_id_ethereum](global_property_object& p) {
|
||||
vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin);
|
||||
vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive);
|
||||
vote_id_ethereum = get_next_vote_id(p, vote_id_type::son_ethereum);
|
||||
});
|
||||
|
||||
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ){
|
||||
obj.son_account = op.owner_account;
|
||||
obj.vote_id = vote_id;
|
||||
obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin;
|
||||
obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive;
|
||||
obj.sidechain_vote_ids[sidechain_type::ethereum] = vote_id_ethereum;
|
||||
obj.url = op.url;
|
||||
obj.deposit = op.deposit;
|
||||
obj.signing_key = op.signing_key;
|
||||
|
|
@ -94,7 +100,8 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op)
|
|||
if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key;
|
||||
if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys;
|
||||
if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb;
|
||||
if(so.status == son_status::deregistered) so.status = son_status::inactive;
|
||||
for(auto& status : so.statuses)
|
||||
if(status.second == son_status::deregistered) status.second = son_status::inactive;
|
||||
});
|
||||
}
|
||||
return op.son_id;
|
||||
|
|
@ -127,7 +134,8 @@ void_result deregister_son_evaluator::do_apply(const son_deregister_operation& o
|
|||
});
|
||||
|
||||
db().modify(*son, [&op](son_object &so) {
|
||||
so.status = son_status::deregistered;
|
||||
for(auto& status : so.statuses)
|
||||
status.second = son_status::deregistered;
|
||||
});
|
||||
|
||||
auto stats_obj = ss_idx.find(son->statistics);
|
||||
|
|
@ -144,18 +152,28 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation&
|
|||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
const auto itr = idx.find(op.son_id);
|
||||
FC_ASSERT( itr != idx.end() );
|
||||
FC_ASSERT(itr->son_account == op.owner_account);
|
||||
auto stats = itr->statistics( db() );
|
||||
// Inactive SONs need not send heartbeats
|
||||
FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats");
|
||||
bool status_need_to_send_heartbeats = false;
|
||||
for(const auto& status : itr->statuses)
|
||||
{
|
||||
if( (status.second == son_status::active) || (status.second == son_status::in_maintenance) || (status.second == son_status::request_maintenance) )
|
||||
status_need_to_send_heartbeats = true;
|
||||
}
|
||||
FC_ASSERT(status_need_to_send_heartbeats, "Inactive SONs need not send heartbeats");
|
||||
// Account for network delays
|
||||
fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval());
|
||||
// Account for server ntp sync difference
|
||||
fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval());
|
||||
FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time");
|
||||
FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp");
|
||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
||||
if(stats.last_active_timestamp.contains(active_sidechain_type))
|
||||
FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type));
|
||||
if(stats.last_down_timestamp.contains(active_sidechain_type))
|
||||
FC_ASSERT(op.ts > stats.last_down_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} is invalid can't be <= last down timestamp", ("sidechain", active_sidechain_type));
|
||||
}
|
||||
FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold");
|
||||
FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold");
|
||||
return void_result();
|
||||
|
|
@ -164,44 +182,48 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation&
|
|||
object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
const auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
const global_property_object& gpo = db().get_global_properties();
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id);
|
||||
bool is_son_active = true;
|
||||
for(const auto& active_sidechain_sons : gpo.active_sons) {
|
||||
const auto& sidechain = active_sidechain_sons.first;
|
||||
const auto& active_sons = active_sidechain_sons.second;
|
||||
|
||||
if(it_son == active_son_ids.end()) {
|
||||
is_son_active = false;
|
||||
}
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(active_sons.size());
|
||||
std::transform(active_sons.cbegin(), active_sons.cend(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
if(itr->status == son_status::in_maintenance) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch();
|
||||
sso.last_active_timestamp = op.ts;
|
||||
} );
|
||||
const auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id);
|
||||
bool is_son_active = true;
|
||||
|
||||
db().modify(*itr, [&is_son_active](son_object &so) {
|
||||
if(is_son_active) {
|
||||
so.status = son_status::active;
|
||||
} else {
|
||||
so.status = son_status::inactive;
|
||||
}
|
||||
});
|
||||
} else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.last_active_timestamp = op.ts;
|
||||
} );
|
||||
if (it_son == active_son_ids.end()) {
|
||||
is_son_active = false;
|
||||
}
|
||||
|
||||
if (itr->statuses.at(sidechain) == son_status::in_maintenance) {
|
||||
db().modify(itr->statistics(db()), [&](son_statistics_object &sso) {
|
||||
sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - sso.last_down_timestamp.at(sidechain).sec_since_epoch();
|
||||
sso.last_active_timestamp[sidechain] = op.ts;
|
||||
});
|
||||
|
||||
db().modify(*itr, [&is_son_active, &sidechain](son_object &so) {
|
||||
if (is_son_active) {
|
||||
so.statuses[sidechain] = son_status::active;
|
||||
} else {
|
||||
so.statuses[sidechain] = son_status::inactive;
|
||||
}
|
||||
});
|
||||
} else if ((itr->statuses.at(sidechain) == son_status::active) || (itr->statuses.at(sidechain) == son_status::request_maintenance)) {
|
||||
db().modify(itr->statistics(db()), [&](son_statistics_object &sso) {
|
||||
sso.last_active_timestamp[sidechain] = op.ts;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
|
|
@ -213,29 +235,40 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati
|
|||
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
auto itr = idx.find(op.son_id);
|
||||
auto stats = itr->statistics( db() );
|
||||
FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
|
||||
FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp");
|
||||
const auto itr = idx.find(op.son_id);
|
||||
const auto stats = itr->statistics( db() );
|
||||
bool status_need_to_report_down = false;
|
||||
for(const auto& status : itr->statuses)
|
||||
{
|
||||
if( (status.second == son_status::active) || (status.second == son_status::request_maintenance) )
|
||||
status_need_to_report_down = true;
|
||||
}
|
||||
FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
|
||||
for(const auto& active_sidechain_type : active_sidechain_types) {
|
||||
FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type));
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
const auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.last_down_timestamp = op.down_ts;
|
||||
});
|
||||
for( const auto& status : itr->statuses ) {
|
||||
const auto& sidechain = status.first;
|
||||
|
||||
db().modify(*itr, [&op](son_object &so) {
|
||||
so.status = son_status::in_maintenance;
|
||||
});
|
||||
}
|
||||
if ((status.second == son_status::active) || (status.second == son_status::request_maintenance)) {
|
||||
db().modify(*itr, [&sidechain](son_object &so) {
|
||||
so.statuses[sidechain] = son_status::in_maintenance;
|
||||
});
|
||||
|
||||
db().modify(itr->statistics(db()), [&](son_statistics_object &sso) {
|
||||
sso.last_down_timestamp[sidechain] = op.down_ts;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
@ -249,9 +282,19 @@ void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operati
|
|||
FC_ASSERT( itr != idx.end() );
|
||||
// Inactive SONs can't go to maintenance, toggle between active and request_maintenance states
|
||||
if(op.request_type == son_maintenance_request_type::request_maintenance) {
|
||||
FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance");
|
||||
} else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request");
|
||||
bool status_active = false;
|
||||
for(const auto& status : itr->statuses) {
|
||||
if( (status.second == son_status::active) )
|
||||
status_active = true;
|
||||
}
|
||||
FC_ASSERT(status_active, "Inactive SONs can't request for maintenance");
|
||||
} else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
bool status_request_maintenance = false;
|
||||
for(const auto& status : itr->statuses) {
|
||||
if( (status.second == son_status::request_maintenance) )
|
||||
status_request_maintenance = true;
|
||||
}
|
||||
FC_ASSERT(status_request_maintenance, "Only maintenance requested SONs can cancel the request");
|
||||
} else {
|
||||
FC_ASSERT(false, "Invalid maintenance operation");
|
||||
}
|
||||
|
|
@ -264,15 +307,33 @@ object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operati
|
|||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
so.status = son_status::request_maintenance;
|
||||
});
|
||||
} else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
so.status = son_status::active;
|
||||
});
|
||||
}
|
||||
bool status_active = false;
|
||||
for(const auto& status : itr->statuses) {
|
||||
if( (status.second == son_status::active) )
|
||||
status_active = true;
|
||||
}
|
||||
if(status_active && op.request_type == son_maintenance_request_type::request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
for(auto& status : so.statuses) {
|
||||
status.second = son_status::request_maintenance;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
bool status_request_maintenance = false;
|
||||
for(const auto& status : itr->statuses) {
|
||||
if( (status.second == son_status::request_maintenance) )
|
||||
status_request_maintenance = true;
|
||||
}
|
||||
if(status_request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
for(auto& status : so.statuses) {
|
||||
status.second = son_status::active;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ namespace graphene { namespace chain {
|
|||
retval = retval &&
|
||||
(sidechain_public_keys.find( sidechain_type::hive ) != sidechain_public_keys.end()) &&
|
||||
(sidechain_public_keys.at(sidechain_type::hive).length() > 0);
|
||||
|
||||
retval = retval &&
|
||||
(sidechain_public_keys.find( sidechain_type::ethereum ) != sidechain_public_keys.end()) &&
|
||||
(sidechain_public_keys.at(sidechain_type::ethereum).length() > 0);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_de
|
|||
const auto &swdo_idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_uid>();
|
||||
const auto swdo = swdo_idx.find(op.sidechain_uid);
|
||||
if (swdo == swdo_idx.end()) {
|
||||
auto &gpo = db().get_global_properties();
|
||||
const auto &gpo = db().get_global_properties();
|
||||
bool expected = false;
|
||||
for (auto &si : gpo.active_sons) {
|
||||
for (auto &si : gpo.active_sons.at(op.sidechain)) {
|
||||
if (op.son_id == si.son_id) {
|
||||
expected = true;
|
||||
break;
|
||||
|
|
@ -78,8 +78,8 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de
|
|||
swdo.peerplays_to = op.peerplays_to;
|
||||
swdo.peerplays_asset = op.peerplays_asset;
|
||||
|
||||
auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons) {
|
||||
const auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons.at(op.sidechain)) {
|
||||
swdo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
|
||||
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
|
||||
|
|
@ -142,11 +142,11 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d
|
|||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
const auto& itr = idx.find(op.son_wallet_deposit_id);
|
||||
FC_ASSERT(itr != idx.end(), "Son wallet deposit not found");
|
||||
FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
FC_ASSERT(!itr->processed, "Son wallet deposit is already processed");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -20,8 +20,16 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate
|
|||
|
||||
bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size());
|
||||
if (son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i);
|
||||
for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) {
|
||||
const auto& sidechain = cur_wallet_sidechain_sons.first;
|
||||
const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second;
|
||||
|
||||
son_sets_equal = son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_wallet_sons.at(sidechain).size());
|
||||
if (son_sets_equal) {
|
||||
for (size_t i = 0; i < cur_wallet_sons.size(); i++) {
|
||||
son_sets_equal = son_sets_equal && _cur_wallet_sidechain_sons.at(i) == new_wallet_sons.at(sidechain).at(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,15 +23,15 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w
|
|||
const auto &swwo_idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>();
|
||||
const auto swwo = swwo_idx.find(op.peerplays_uid);
|
||||
if (swwo == swwo_idx.end()) {
|
||||
auto &gpo = db().get_global_properties();
|
||||
const auto &gpo = db().get_global_properties();
|
||||
bool expected = false;
|
||||
for (auto &si : gpo.active_sons) {
|
||||
for (auto &si : gpo.active_sons.at(op.sidechain)) {
|
||||
if (op.son_id == si.son_id) {
|
||||
expected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FC_ASSERT(expected, "Only active SON can create deposit");
|
||||
FC_ASSERT(expected, "Only active SON can create withdraw");
|
||||
} else {
|
||||
bool exactly_the_same = true;
|
||||
exactly_the_same = exactly_the_same && (swwo->sidechain == op.sidechain);
|
||||
|
|
@ -76,8 +76,8 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w
|
|||
swwo.withdraw_currency = op.withdraw_currency;
|
||||
swwo.withdraw_amount = op.withdraw_amount;
|
||||
|
||||
auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons) {
|
||||
const auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons.at(op.sidechain)) {
|
||||
swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
|
||||
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
|
||||
|
|
@ -140,11 +140,11 @@ void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_
|
|||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
const auto& itr = idx.find(op.son_wallet_withdraw_id);
|
||||
FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found");
|
||||
FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <fc/log/logger.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace graphene { namespace db {
|
||||
|
||||
|
|
@ -144,6 +145,7 @@ namespace graphene { namespace db {
|
|||
fc::path get_data_dir()const { return _data_dir; }
|
||||
|
||||
/** public for testing purposes only... should be private in practice. */
|
||||
mutable std::mutex _undo_db_mutex;
|
||||
undo_database _undo_db;
|
||||
protected:
|
||||
template<typename IndexType>
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit e7369949bea26f3201d8442ba78286a88df74762
|
||||
Subproject commit 156b0c4e41c9215eadb2af8009b05e0f38c16dda
|
||||
|
|
@ -47,4 +47,3 @@ namespace graphene { namespace net {
|
|||
const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type;
|
||||
|
||||
} } // graphene::net
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define GRAPHENE_NET_PROTOCOL_VERSION 106
|
||||
|
||||
/**
|
||||
|
|
@ -110,3 +112,6 @@
|
|||
#define GRAPHENE_NET_MAX_NESTED_OBJECTS (250)
|
||||
|
||||
#define MAXIMUM_PEERDB_SIZE 1000
|
||||
|
||||
constexpr size_t MAX_BLOCKS_TO_HANDLE_AT_ONCE = 200;
|
||||
constexpr size_t MAX_SYNC_BLOCKS_TO_PREFETCH = 10 * MAX_BLOCKS_TO_HANDLE_AT_ONCE;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace graphene { namespace net {
|
|||
class node_delegate
|
||||
{
|
||||
public:
|
||||
virtual ~node_delegate(){}
|
||||
virtual ~node_delegate() = default;
|
||||
|
||||
/**
|
||||
* If delegate has the item, the network has no need to fetch it.
|
||||
|
|
@ -71,7 +71,9 @@ namespace graphene { namespace net {
|
|||
/**
|
||||
* @brief Called when a new block comes in from the network
|
||||
*
|
||||
* @param blk_msg the message which contains the block
|
||||
* @param sync_mode true if the message was fetched through the sync process, false during normal operation
|
||||
* @param contained_transaction_msg_ids container for the transactions to write back into
|
||||
* @returns true if this message caused the blockchain to switch forks, false if it did not
|
||||
*
|
||||
* @throws exception if error validating the item, otherwise the item is
|
||||
|
|
@ -152,6 +154,8 @@ namespace graphene { namespace net {
|
|||
|
||||
virtual uint32_t get_block_number(const item_hash_t& block_id) = 0;
|
||||
|
||||
virtual fc::time_point_sec get_last_known_hardfork_time() = 0;
|
||||
|
||||
/**
|
||||
* Returns the time a block was produced (if block_id = 0, returns genesis time).
|
||||
* If we don't know about the block, returns time_point_sec::min()
|
||||
|
|
@ -193,7 +197,7 @@ namespace graphene { namespace net {
|
|||
{
|
||||
public:
|
||||
node(const std::string& user_agent);
|
||||
~node();
|
||||
virtual ~node();
|
||||
|
||||
void close();
|
||||
|
||||
|
|
@ -211,11 +215,34 @@ namespace graphene { namespace net {
|
|||
*/
|
||||
void add_node( const fc::ip::endpoint& ep );
|
||||
|
||||
/*****
|
||||
* @brief add a list of nodes to seed the p2p network
|
||||
* @param seeds a vector of url strings
|
||||
*/
|
||||
void add_seed_nodes( std::vector<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.
|
||||
*/
|
||||
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
|
||||
* connections should be accepted.
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ namespace graphene { namespace net
|
|||
class peer_connection_delegate
|
||||
{
|
||||
public:
|
||||
virtual ~peer_connection_delegate() = default;
|
||||
virtual void on_message(peer_connection* originating_peer,
|
||||
const message& received_message) = 0;
|
||||
virtual void on_connection_closed(peer_connection* originating_peer) = 0;
|
||||
|
|
@ -125,7 +126,7 @@ namespace graphene { namespace net
|
|||
* it is sitting on the queue
|
||||
*/
|
||||
virtual size_t get_size_in_queue() = 0;
|
||||
virtual ~queued_message() {}
|
||||
virtual ~queued_message() = default;
|
||||
};
|
||||
|
||||
/* when you queue up a 'real_queued_message', a full copy of the message is
|
||||
|
|
@ -258,6 +259,8 @@ namespace graphene { namespace net
|
|||
|
||||
uint32_t last_known_fork_block_number = 0;
|
||||
|
||||
fc::time_point_sec last_known_hardfork_time;
|
||||
|
||||
fc::future<void> accept_or_connect_task_done;
|
||||
|
||||
firewall_check_state_data *firewall_check_state = nullptr;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ namespace graphene { namespace net {
|
|||
{
|
||||
public:
|
||||
peer_database();
|
||||
~peer_database();
|
||||
virtual ~peer_database();
|
||||
|
||||
void open(const fc::path& databaseFilename);
|
||||
void close();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <deque>
|
||||
|
|
@ -71,6 +72,7 @@
|
|||
#include <fc/io/raw_fwd.hpp>
|
||||
#include <fc/network/rate_limiting.hpp>
|
||||
#include <fc/network/ip.hpp>
|
||||
#include <fc/network/resolve.hpp>
|
||||
|
||||
#include <graphene/net/node.hpp>
|
||||
#include <graphene/net/peer_database.hpp>
|
||||
|
|
@ -298,6 +300,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
(sync_status) \
|
||||
(connection_count_changed) \
|
||||
(get_block_number) \
|
||||
(get_last_known_hardfork_time) \
|
||||
(get_block_time) \
|
||||
(get_head_block_id) \
|
||||
(estimate_last_known_fork_from_git_revision_timestamp) \
|
||||
|
|
@ -397,6 +400,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
void sync_status( uint32_t item_type, uint32_t item_count ) override;
|
||||
void connection_count_changed( uint32_t c ) override;
|
||||
uint32_t get_block_number(const item_hash_t& block_id) override;
|
||||
fc::time_point_sec get_last_known_hardfork_time() override;
|
||||
fc::time_point_sec get_block_time(const item_hash_t& block_id) override;
|
||||
item_hash_t get_head_block_id() const override;
|
||||
uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const override;
|
||||
|
|
@ -552,6 +556,10 @@ namespace graphene { namespace net { namespace detail {
|
|||
fc::future<void> _bandwidth_monitor_loop_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
|
||||
* uses a mutex to prevent one fiber from adding items to the queue while another is deleting
|
||||
|
|
@ -572,7 +580,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
std::set<node_id_t> _allowed_peers;
|
||||
#endif // ENABLE_P2P_DEBUGGING_API
|
||||
|
||||
bool _node_is_shutting_down; // set to true when we begin our destructor, used to prevent us from starting new tasks while we're shutting down
|
||||
std::atomic_bool _node_is_shutting_down {false};
|
||||
|
||||
unsigned _maximum_number_of_blocks_to_handle_at_one_time;
|
||||
unsigned _maximum_number_of_sync_blocks_to_prefetch;
|
||||
|
|
@ -725,6 +733,11 @@ namespace graphene { namespace net { namespace detail {
|
|||
void listen_to_p2p_network();
|
||||
void connect_to_p2p_network();
|
||||
void add_node( const fc::ip::endpoint& ep );
|
||||
void add_seed_node( const std::string& in);
|
||||
void add_seed_nodes( std::vector<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 connect_to_endpoint(const fc::ip::endpoint& ep);
|
||||
void listen_on_endpoint(const fc::ip::endpoint& ep , bool wait_if_not_available);
|
||||
|
|
@ -823,7 +836,6 @@ namespace graphene { namespace net { namespace detail {
|
|||
_average_network_write_speed_hours(72),
|
||||
_average_network_usage_second_counter(0),
|
||||
_average_network_usage_minute_counter(0),
|
||||
_node_is_shutting_down(false),
|
||||
_maximum_number_of_blocks_to_handle_at_one_time(MAXIMUM_NUMBER_OF_BLOCKS_TO_HANDLE_AT_ONE_TIME),
|
||||
_maximum_number_of_sync_blocks_to_prefetch(MAXIMUM_NUMBER_OF_BLOCKS_TO_PREFETCH),
|
||||
_maximum_blocks_per_peer_during_syncing(GRAPHENE_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING)
|
||||
|
|
@ -840,7 +852,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
ilog( "cleaning up node" );
|
||||
_node_is_shutting_down = true;
|
||||
_node_is_shutting_down.store(true);
|
||||
|
||||
for (const peer_connection_ptr& active_peer : _active_connections)
|
||||
{
|
||||
|
|
@ -948,6 +960,11 @@ namespace graphene { namespace net { namespace detail {
|
|||
}
|
||||
|
||||
display_current_connections();
|
||||
if(_node_is_shutting_down)
|
||||
{
|
||||
ilog("Breaking p2p_network_connect_loop loop because node is shutting down");
|
||||
break;
|
||||
}
|
||||
|
||||
// if we broke out of the while loop, that means either we have connected to enough nodes, or
|
||||
// we don't have any good candidates to connect to right now.
|
||||
|
|
@ -1030,7 +1047,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
void node_impl::fetch_sync_items_loop()
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
while( !_fetch_sync_items_loop_done.canceled() )
|
||||
while( !_fetch_sync_items_loop_done.canceled() && !_node_is_shutting_down )
|
||||
{
|
||||
_sync_items_to_fetch_updated = false;
|
||||
dlog( "beginning another iteration of the sync items loop" );
|
||||
|
|
@ -1337,7 +1354,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
// reconnect with the rest of the network, or it might just futher isolate us.
|
||||
{
|
||||
// As usual, the first step is to walk through all our peers and figure out which
|
||||
// peers need action (disconneting, sending keepalives, etc), then we walk through
|
||||
// peers need action (disconnecting, sending keepalives, etc), then we walk through
|
||||
// those lists yielding at our leisure later.
|
||||
ASSERT_TASK_NOT_PREEMPTED();
|
||||
|
||||
|
|
@ -1674,13 +1691,15 @@ namespace graphene { namespace net { namespace detail {
|
|||
bool node_impl::is_accepting_new_connections()
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
return !_p2p_network_connect_loop_done.canceled() && get_number_of_connections() <= _maximum_number_of_connections;
|
||||
return !_node_is_shutting_down && (!_p2p_network_connect_loop_done.valid() || !_p2p_network_connect_loop_done.canceled()) &&
|
||||
get_number_of_connections() <= _maximum_number_of_connections;
|
||||
}
|
||||
|
||||
bool node_impl::is_wanting_new_connections()
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
return !_p2p_network_connect_loop_done.canceled() && get_number_of_connections() < _desired_number_of_connections;
|
||||
return !_node_is_shutting_down && !_p2p_network_connect_loop_done.canceled() &&
|
||||
get_number_of_connections() < _desired_number_of_connections;
|
||||
}
|
||||
|
||||
uint32_t node_impl::get_number_of_connections()
|
||||
|
|
@ -1870,6 +1889,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
user_data["last_known_block_hash"] = fc::variant( head_block_id, 1 );
|
||||
user_data["last_known_block_number"] = _delegate->get_block_number(head_block_id);
|
||||
user_data["last_known_block_time"] = _delegate->get_block_time(head_block_id);
|
||||
user_data["last_known_hardfork_time"] = _delegate->get_last_known_hardfork_time().sec_since_epoch();
|
||||
|
||||
if (!_hard_fork_block_numbers.empty())
|
||||
user_data["last_known_fork_block_number"] = _hard_fork_block_numbers.back();
|
||||
|
|
@ -1896,6 +1916,17 @@ namespace graphene { namespace net { namespace detail {
|
|||
originating_peer->node_id = user_data["node_id"].as<node_id_t>(1);
|
||||
if (user_data.contains("last_known_fork_block_number"))
|
||||
originating_peer->last_known_fork_block_number = user_data["last_known_fork_block_number"].as<uint32_t>(1);
|
||||
if (user_data.contains("last_known_hardfork_time")){
|
||||
originating_peer->last_known_hardfork_time = fc::time_point_sec(user_data["last_known_hardfork_time"].as<uint32_t>(1));
|
||||
}else{
|
||||
// this state is invalid when node which wants to connect doesn't provide
|
||||
// last hardfork time. We are setting to 0 which will disconnect the node
|
||||
// on hello message
|
||||
originating_peer->last_known_hardfork_time = fc::time_point_sec(0);
|
||||
if(DISABLE_WITNESS_HF_CHECK) {
|
||||
originating_peer->last_known_hardfork_time = _delegate->get_last_known_hardfork_time();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void node_impl::on_hello_message( peer_connection* originating_peer, const hello_message& hello_message_received )
|
||||
|
|
@ -1975,23 +2006,11 @@ namespace graphene { namespace net { namespace detail {
|
|||
disconnect_from_peer(originating_peer, "You are on a different chain from me");
|
||||
return;
|
||||
}
|
||||
if (originating_peer->last_known_fork_block_number != 0)
|
||||
{
|
||||
uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(originating_peer->last_known_fork_block_number);
|
||||
if (next_fork_block_number != 0)
|
||||
{
|
||||
// we know about a fork they don't. See if we've already passed that block. If we have, don't let them
|
||||
// connect because we won't be able to give them anything useful
|
||||
uint32_t head_block_num = _delegate->get_block_number(_delegate->get_head_block_id());
|
||||
if (next_fork_block_number < head_block_num)
|
||||
{
|
||||
#ifdef ENABLE_DEBUG_ULOGS
|
||||
|
||||
auto disconnect_peer = [&](const std::ostringstream& rejection_message) {
|
||||
#ifdef ENABLE_DEBUG_ULOGS
|
||||
ulog("Rejecting connection from peer because their version is too old. Their version date: ${date}", ("date", originating_peer->graphene_git_revision_unix_timestamp));
|
||||
#endif
|
||||
wlog("Received hello message from peer running a version of that can only understand blocks up to #${their_hard_fork}, but I'm at head block number #${my_block_number}",
|
||||
("their_hard_fork", next_fork_block_number)("my_block_number", head_block_num));
|
||||
std::ostringstream rejection_message;
|
||||
rejection_message << "Your client is outdated -- you can only understand blocks up to #" << next_fork_block_number << ", but I'm already on block #" << head_block_num;
|
||||
#endif
|
||||
connection_rejected_message connection_rejected(_user_agent_string, core_protocol_version,
|
||||
originating_peer->get_socket().remote_endpoint(),
|
||||
rejection_reason_code::unspecified,
|
||||
|
|
@ -2003,10 +2022,42 @@ namespace graphene { namespace net { namespace detail {
|
|||
// allowing her to ask us for peers (any of our peers will be on the same chain as us, so there's no
|
||||
// benefit of sharing them)
|
||||
disconnect_from_peer(originating_peer, "Your client is too old, please upgrade");
|
||||
};
|
||||
|
||||
if (originating_peer->last_known_fork_block_number != 0)
|
||||
{
|
||||
uint32_t next_fork_block_number = get_next_known_hard_fork_block_number(originating_peer->last_known_fork_block_number);
|
||||
if (next_fork_block_number != 0)
|
||||
{
|
||||
// we know about a fork they don't. See if we've already passed that block. If we have, don't let them
|
||||
// connect because we won't be able to give them anything useful
|
||||
uint32_t head_block_num = _delegate->get_block_number(_delegate->get_head_block_id());
|
||||
if (next_fork_block_number < head_block_num)
|
||||
{
|
||||
wlog("Received hello message from peer running a version of that can only understand blocks up to #${their_hard_fork}, but I'm at head block number #${my_block_number}",
|
||||
("their_hard_fork", next_fork_block_number)("my_block_number", head_block_num));
|
||||
std::ostringstream rejection_message;
|
||||
rejection_message << "Your client is outdated -- you can only understand blocks up to #" << next_fork_block_number << ", but I'm already on block #" << head_block_num;
|
||||
disconnect_peer(rejection_message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we wan't to disconnect from the peer that didn't updated the software. With the last hardforks we could
|
||||
// indetify if peer's are not compatible due the hardforks
|
||||
if ( originating_peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time())
|
||||
{
|
||||
if ((_delegate->get_block_time(_delegate->get_head_block_id()).sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch())
|
||||
|| originating_peer->last_known_hardfork_time.sec_since_epoch() == 0)
|
||||
{
|
||||
std::ostringstream rejection_message;
|
||||
rejection_message << "Your client is outdated -- you can only understand blocks up to #" << originating_peer->last_known_hardfork_time.to_iso_string() << ", but I'm already on block #" << _delegate->get_block_time(_delegate->get_head_block_id()).to_iso_string();
|
||||
disconnect_peer(rejection_message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (already_connected_to_this_peer)
|
||||
{
|
||||
|
||||
|
|
@ -3108,11 +3159,37 @@ namespace graphene { namespace net { namespace detail {
|
|||
--_total_number_of_unfetched_items;
|
||||
dlog("sync: client accpted the block, we now have only ${count} items left to fetch before we're in sync",
|
||||
("count", _total_number_of_unfetched_items));
|
||||
|
||||
|
||||
auto disconnect_peer = [&](const std::ostringstream& disconnect_reason_stream, const peer_connection_ptr& peer, bool& disconnecting_this_peer)
|
||||
{
|
||||
peers_to_disconnect[peer] = std::make_pair(disconnect_reason_stream.str(),
|
||||
fc::oexception(fc::exception(FC_LOG_MESSAGE(error, "You need to upgrade your client"))));
|
||||
#ifdef ENABLE_DEBUG_ULOGS
|
||||
ulog("Disconnecting from peer during sync because their version is too old. Their version date: ${date}", ("date", peer->graphene_git_revision_unix_timestamp));
|
||||
#endif
|
||||
disconnecting_this_peer = true;
|
||||
};
|
||||
|
||||
bool is_fork_block = is_hard_fork_block(block_message_to_send.block.block_num());
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
{
|
||||
ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections
|
||||
bool disconnecting_this_peer = false;
|
||||
|
||||
// if connected peer doesn't have the same version of witness which is fully indetified
|
||||
// with last hardfork time received and block timestamp is grater than peers last known hardfork
|
||||
// time disconnect that peer, since he will not be capable of handling already pushed block
|
||||
if( peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time() )
|
||||
{
|
||||
if( block_message_to_send.block.timestamp.sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch() )
|
||||
{
|
||||
std::ostringstream disconnect_reason_stream;
|
||||
disconnect_reason_stream << "You need to upgrade your client due to hard fork at block " << block_message_to_send.block.timestamp.to_iso_string();
|
||||
disconnect_peer(disconnect_reason_stream, peer, disconnecting_this_peer);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_fork_block)
|
||||
{
|
||||
// we just pushed a hard fork block. Find out if this peer is running a client
|
||||
|
|
@ -3125,16 +3202,11 @@ namespace graphene { namespace net { namespace detail {
|
|||
{
|
||||
std::ostringstream disconnect_reason_stream;
|
||||
disconnect_reason_stream << "You need to upgrade your client due to hard fork at block " << block_message_to_send.block.block_num();
|
||||
peers_to_disconnect[peer] = std::make_pair(disconnect_reason_stream.str(),
|
||||
fc::oexception(fc::exception(FC_LOG_MESSAGE(error, "You need to upgrade your client due to hard fork at block ${block_number}",
|
||||
("block_number", block_message_to_send.block.block_num())))));
|
||||
#ifdef ENABLE_DEBUG_ULOGS
|
||||
ulog("Disconnecting from peer during sync because their version is too old. Their version date: ${date}", ("date", peer->graphene_git_revision_unix_timestamp));
|
||||
#endif
|
||||
disconnecting_this_peer = true;
|
||||
disconnect_peer(disconnect_reason_stream, peer, disconnecting_this_peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!disconnecting_this_peer &&
|
||||
peer->ids_of_items_to_get.empty() && peer->ids_of_items_being_processed.empty())
|
||||
{
|
||||
|
|
@ -3363,7 +3435,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
|
||||
dlog("leaving process_backlog_of_sync_blocks, ${count} processed", ("count", blocks_processed));
|
||||
|
||||
if (!_suspend_fetching_sync_blocks)
|
||||
if (!_suspend_fetching_sync_blocks && !_node_is_shutting_down)
|
||||
trigger_fetch_sync_items_loop();
|
||||
}
|
||||
|
||||
|
|
@ -3397,8 +3469,16 @@ namespace graphene { namespace net { namespace detail {
|
|||
std::string disconnect_reason;
|
||||
fc::oexception disconnect_exception;
|
||||
fc::oexception restart_sync_exception;
|
||||
bool rejecting_block_due_hf = false;
|
||||
|
||||
try
|
||||
{
|
||||
if(_delegate->get_last_known_hardfork_time().sec_since_epoch() < originating_peer->last_known_hardfork_time.sec_since_epoch()
|
||||
&& block_message_to_process.block.timestamp.sec_since_epoch() >= originating_peer->last_known_hardfork_time.sec_since_epoch() )
|
||||
{
|
||||
rejecting_block_due_hf = true;
|
||||
}
|
||||
|
||||
// we can get into an intersting situation near the end of synchronization. We can be in
|
||||
// sync with one peer who is sending us the last block on the chain via a regular inventory
|
||||
// message, while at the same time still be synchronizing with a peer who is sending us the
|
||||
|
|
@ -3407,7 +3487,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
// message id, for the peer in the sync case we only known the block_id).
|
||||
fc::time_point message_validated_time;
|
||||
if (std::find(_most_recent_blocks_accepted.begin(), _most_recent_blocks_accepted.end(),
|
||||
block_message_to_process.block_id) == _most_recent_blocks_accepted.end())
|
||||
block_message_to_process.block_id) == _most_recent_blocks_accepted.end() && !rejecting_block_due_hf)
|
||||
{
|
||||
std::vector<fc::uint160_t> contained_transaction_message_ids;
|
||||
_delegate->handle_block(block_message_to_process, false, contained_transaction_message_ids);
|
||||
|
|
@ -3436,14 +3516,16 @@ namespace graphene { namespace net { namespace detail {
|
|||
if (new_transaction_discovered)
|
||||
trigger_advertise_inventory_loop();
|
||||
}
|
||||
else
|
||||
dlog( "Already received and accepted this block (presumably through sync mechanism), treating it as accepted" );
|
||||
else {
|
||||
dlog( "Already received and accepted this block (presumably through sync mechanism), treating it as accepted or non compatible node witness");
|
||||
}
|
||||
|
||||
dlog( "client validated the block, advertising it to other peers" );
|
||||
|
||||
item_id block_message_item_id(core_message_type_enum::block_message_type, message_hash);
|
||||
uint32_t block_number = block_message_to_process.block.block_num();
|
||||
fc::time_point_sec block_time = block_message_to_process.block.timestamp;
|
||||
bool disconnect_this_peer = false;
|
||||
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
{
|
||||
|
|
@ -3465,11 +3547,9 @@ namespace graphene { namespace net { namespace detail {
|
|||
broadcast( block_message_to_process, propagation_data );
|
||||
_message_cache.block_accepted();
|
||||
|
||||
if (is_hard_fork_block(block_number))
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
{
|
||||
// we just pushed a hard fork block. Find out if any of our peers are running clients
|
||||
// that will be unable to process future blocks
|
||||
for (const peer_connection_ptr& peer : _active_connections)
|
||||
if (is_hard_fork_block(block_number) )
|
||||
{
|
||||
if (peer->last_known_fork_block_number != 0)
|
||||
{
|
||||
|
|
@ -3477,21 +3557,42 @@ namespace graphene { namespace net { namespace detail {
|
|||
if (next_fork_block_number != 0 &&
|
||||
next_fork_block_number <= block_number)
|
||||
{
|
||||
peers_to_disconnect.insert(peer);
|
||||
#ifdef ENABLE_DEBUG_ULOGS
|
||||
ulog("Disconnecting from peer because their version is too old. Their version date: ${date}", ("date", peer->graphene_git_revision_unix_timestamp));
|
||||
#endif
|
||||
disconnect_this_peer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!peers_to_disconnect.empty())
|
||||
|
||||
if(peer->last_known_hardfork_time < _delegate->get_last_known_hardfork_time())
|
||||
{
|
||||
std::ostringstream disconnect_reason_stream;
|
||||
disconnect_reason_stream << "You need to upgrade your client due to hard fork at block " << block_number;
|
||||
disconnect_reason = disconnect_reason_stream.str();
|
||||
disconnect_exception = fc::exception(FC_LOG_MESSAGE(error, "You need to upgrade your client due to hard fork at block ${block_number}",
|
||||
("block_number", block_number)));
|
||||
if(block_message_to_process.block.timestamp.sec_since_epoch() >= _delegate->get_last_known_hardfork_time().sec_since_epoch())
|
||||
{
|
||||
disconnect_this_peer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( disconnect_this_peer )
|
||||
{
|
||||
peers_to_disconnect.insert(peer);
|
||||
#ifdef ENABLE_DEBUG_ULOGS
|
||||
ulog("Disconnecting from peer because their version is too old. Their version date: ${date}", ("date", peer->graphene_git_revision_unix_timestamp));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(rejecting_block_due_hf)
|
||||
{
|
||||
// disconnect originated peer since we rejected the block from him due
|
||||
// the not anymore compatible witness nodes
|
||||
peers_to_disconnect.insert(originating_peer->shared_from_this());
|
||||
}
|
||||
|
||||
if (!peers_to_disconnect.empty())
|
||||
{
|
||||
std::ostringstream disconnect_reason_stream;
|
||||
disconnect_reason_stream << "You need to upgrade your client due to hard fork at block " << block_number;
|
||||
disconnect_reason = disconnect_reason_stream.str();
|
||||
disconnect_exception = fc::exception(FC_LOG_MESSAGE(error, "You need to upgrade your client due to hard fork at block ${block_number}",
|
||||
("block_number", block_number)));
|
||||
}
|
||||
}
|
||||
catch (const fc::canceled_exception&)
|
||||
|
|
@ -3967,7 +4068,7 @@ namespace graphene { namespace net { namespace detail {
|
|||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
|
||||
_node_is_shutting_down = true;
|
||||
_node_is_shutting_down.store(true);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -4666,7 +4767,69 @@ namespace graphene { namespace net { namespace detail {
|
|||
_potential_peer_db.update_entry(updated_peer_record);
|
||||
trigger_p2p_network_connect_loop();
|
||||
}
|
||||
void node_impl::add_seed_node(const std::string& endpoint_string)
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
_seed_nodes.insert( endpoint_string );
|
||||
resolve_seed_node_and_add( endpoint_string );
|
||||
}
|
||||
|
||||
void node_impl::resolve_seed_node_and_add(const std::string& endpoint_string)
|
||||
{
|
||||
VERIFY_CORRECT_THREAD();
|
||||
std::vector<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)
|
||||
{
|
||||
new_peer->get_socket().open();
|
||||
|
|
@ -5205,6 +5368,11 @@ namespace graphene { namespace net { namespace detail {
|
|||
INVOKE_IN_IMPL(add_node, ep);
|
||||
}
|
||||
|
||||
void node::add_seed_node(const std::string& in)
|
||||
{
|
||||
INVOKE_IN_IMPL(add_seed_node, in);
|
||||
}
|
||||
|
||||
void node::connect_to_endpoint( const fc::ip::endpoint& remote_endpoint )
|
||||
{
|
||||
INVOKE_IN_IMPL(connect_to_endpoint, remote_endpoint);
|
||||
|
|
@ -5550,6 +5718,14 @@ namespace graphene { namespace net { namespace detail {
|
|||
return _node_delegate->get_block_number(block_id);
|
||||
}
|
||||
|
||||
fc::time_point_sec statistics_gathering_node_delegate_wrapper::get_last_known_hardfork_time()
|
||||
{
|
||||
// this function doesn't need to block,
|
||||
ASSERT_TASK_NOT_PREEMPTED();
|
||||
return _node_delegate->get_last_known_hardfork_time();
|
||||
}
|
||||
|
||||
|
||||
fc::time_point_sec statistics_gathering_node_delegate_wrapper::get_block_time(const item_hash_t& block_id)
|
||||
{
|
||||
INVOKE_AND_COLLECT_STATISTICS(get_block_time, block_id);
|
||||
|
|
@ -5578,5 +5754,45 @@ namespace graphene { namespace net { namespace detail {
|
|||
#undef INVOKE_AND_COLLECT_STATISTICS
|
||||
|
||||
} // 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
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ namespace graphene { namespace net {
|
|||
indexed_by<ordered_non_unique<tag<last_seen_time_index>,
|
||||
member<potential_peer_record,
|
||||
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>,
|
||||
member<potential_peer_record,
|
||||
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();
|
||||
bool is_first = true;
|
||||
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
|
||||
{
|
||||
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );
|
||||
|
|
|
|||
|
|
@ -16,3 +16,4 @@ install( TARGETS
|
|||
LIBRARY 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 )
|
||||
{}
|
||||
{
|
||||
// Nothing else to do
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
debug_witness_plugin::~debug_witness_plugin() {}
|
||||
debug_witness_plugin::~debug_witness_plugin()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void debug_witness_plugin::plugin_set_program_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");
|
||||
_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>>();
|
||||
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); });
|
||||
_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 )
|
||||
|
|
@ -155,11 +157,15 @@ void debug_witness_plugin::flush_json_object_stream()
|
|||
}
|
||||
|
||||
void debug_witness_plugin::plugin_shutdown()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void debug_witness_plugin::cleanup()
|
||||
{
|
||||
if( _json_object_stream )
|
||||
{
|
||||
_json_object_stream->close();
|
||||
_json_object_stream.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,23 +34,25 @@ namespace graphene { namespace debug_witness_plugin {
|
|||
|
||||
class debug_witness_plugin : public graphene::app::plugin {
|
||||
public:
|
||||
~debug_witness_plugin();
|
||||
using graphene::app::plugin::plugin;
|
||||
~debug_witness_plugin() 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 &config_file_options
|
||||
) override;
|
||||
|
||||
virtual void plugin_initialize( const boost::program_options::variables_map& options ) override;
|
||||
virtual void plugin_startup() override;
|
||||
virtual void plugin_shutdown() override;
|
||||
void plugin_initialize( const boost::program_options::variables_map& options ) override;
|
||||
void plugin_startup() override;
|
||||
void plugin_shutdown() override;
|
||||
|
||||
void set_json_object_stream( const std::string& filename );
|
||||
void flush_json_object_stream();
|
||||
|
||||
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_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;
|
||||
|
||||
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;
|
||||
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()
|
||||
{
|
||||
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->set_block_applied_callback([this]( const fc::variant& block_id )
|
||||
{
|
||||
fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
} );
|
||||
my->client_connection_closed = my->client_connection->closed.connect([this] {
|
||||
connection_failed();
|
||||
});
|
||||
|
|
@ -73,7 +89,9 @@ void delayed_node_plugin::connect()
|
|||
void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{
|
||||
FC_ASSERT(options.count("trusted-node") > 0);
|
||||
ilog("delayed_node_plugin: plugin_initialize() begin");
|
||||
my->remote_endpoint = "ws://" + options.at("trusted-node").as<std::string>();
|
||||
ilog("delayed_node_plugin: plugin_initialize() end");
|
||||
}
|
||||
|
||||
void delayed_node_plugin::sync_with_trusted_node()
|
||||
|
|
@ -100,8 +118,11 @@ void delayed_node_plugin::sync_with_trusted_node()
|
|||
while( remote_dpo.last_irreversible_block_num > db.head_block_num() )
|
||||
{
|
||||
fc::optional<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.");
|
||||
ilog("Pushing block #${n}", ("n", block->block_num()));
|
||||
// timur: failed to merge from bitshares, API n/a in peerplays
|
||||
// db.precompute_parallel( *block, graphene::chain::database::skip_nothing ).wait();
|
||||
db.push_block(*block);
|
||||
synced_blocks++;
|
||||
}
|
||||
|
|
@ -136,24 +157,12 @@ void delayed_node_plugin::plugin_startup()
|
|||
mainloop();
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
connect();
|
||||
my->database_api->set_block_applied_callback([this]( const fc::variant& block_id )
|
||||
{
|
||||
fc::from_variant( block_id, my->last_received_remote_head, GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
} );
|
||||
return;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
elog("Error during connection: ${e}", ("e", e.to_detail_string()));
|
||||
}
|
||||
fc::async([this]{connection_failed();});
|
||||
connect();
|
||||
}
|
||||
|
||||
void delayed_node_plugin::connection_failed()
|
||||
{
|
||||
my->last_received_remote_head = my->last_processed_remote_head;
|
||||
elog("Connection to trusted node failed; retrying in 5 seconds...");
|
||||
fc::schedule([this]{connect();}, fc::time_point::now() + fc::seconds(5));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
bool is_first = true;
|
||||
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
|
||||
{
|
||||
db.remove( db.create<operation_history_object>( []( operation_history_object& obj) {} ) );
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp")
|
|||
add_library( peerplays_sidechain
|
||||
peerplays_sidechain_plugin.cpp
|
||||
sidechain_api.cpp
|
||||
sidechain_net_manager.cpp
|
||||
sidechain_net_handler_factory.cpp
|
||||
sidechain_net_handler.cpp
|
||||
sidechain_net_handler_bitcoin.cpp
|
||||
sidechain_net_handler_ethereum.cpp
|
||||
sidechain_net_handler_hive.cpp
|
||||
sidechain_net_handler_peerplays.cpp
|
||||
bitcoin/bech32.cpp
|
||||
|
|
@ -17,6 +18,11 @@ add_library( peerplays_sidechain
|
|||
bitcoin/sign_bitcoin_transaction.cpp
|
||||
common/rpc_client.cpp
|
||||
common/utils.cpp
|
||||
ethereum/encoders.cpp
|
||||
ethereum/decoders.cpp
|
||||
ethereum/transaction.cpp
|
||||
ethereum/types.cpp
|
||||
ethereum/utils.cpp
|
||||
hive/asset.cpp
|
||||
hive/operations.cpp
|
||||
hive/transaction.cpp
|
||||
|
|
@ -36,7 +42,7 @@ endif()
|
|||
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
|
||||
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE)
|
||||
|
||||
target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin zmq )
|
||||
target_link_libraries( peerplays_sidechain PRIVATE graphene_plugin sha3 zmq )
|
||||
target_include_directories( peerplays_sidechain
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
#include <fc/io/raw.hpp>
|
||||
|
||||
|
|
@ -6,8 +7,8 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
const secp256k1_context_t *btc_context() {
|
||||
static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
const secp256k1_context *btc_context() {
|
||||
static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
@ -31,20 +32,14 @@ fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &script
|
|||
return fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
}
|
||||
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign) {
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context *context_sign) {
|
||||
bytes sig;
|
||||
sig.resize(72);
|
||||
int sig_len = sig.size();
|
||||
|
||||
FC_ASSERT(secp256k1_ecdsa_sign(
|
||||
context_sign,
|
||||
reinterpret_cast<unsigned char *>(hash.data()),
|
||||
reinterpret_cast<unsigned char *>(sig.data()),
|
||||
&sig_len,
|
||||
reinterpret_cast<const unsigned char *>(privkey.data()),
|
||||
secp256k1_nonce_function_rfc6979,
|
||||
nullptr)); // TODO: replace assert with exception
|
||||
|
||||
secp256k1_ecdsa_signature sign;
|
||||
FC_ASSERT(secp256k1_ecdsa_sign(context_sign, &sign, (const unsigned char *)hash.data(), (const unsigned char *)privkey.data(), secp256k1_nonce_function_rfc6979, nullptr));
|
||||
FC_ASSERT(secp256k1_ecdsa_signature_serialize_der(context_sign, (unsigned char *)sig.data(), (size_t *)&sig_len, &sign));
|
||||
sig.resize(sig_len);
|
||||
|
||||
return sig;
|
||||
|
|
@ -52,7 +47,7 @@ std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, con
|
|||
|
||||
std::vector<bytes> sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const bytes &privkey,
|
||||
const secp256k1_context_t *context_sign, int hash_type) {
|
||||
const secp256k1_context *context_sign, int hash_type) {
|
||||
FC_ASSERT(tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size());
|
||||
FC_ASSERT(!privkey.empty());
|
||||
|
||||
|
|
@ -77,17 +72,34 @@ void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vecto
|
|||
}
|
||||
}
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context) {
|
||||
std::vector<unsigned char> sig_temp(sig.begin(), sig.end());
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context *context) {
|
||||
//! Get sig_temp
|
||||
FC_ASSERT(sig.size() > 70);
|
||||
FC_ASSERT(sig[0] == 0x30);
|
||||
FC_ASSERT(sig[1] == static_cast<char>(sig.size() - 3));
|
||||
FC_ASSERT(sig[2] == 0x02);
|
||||
const uint r_size = sig[3];
|
||||
std::vector<unsigned char> sig_temp(sig.begin() + 4 + (r_size - 32), sig.begin() + 4 + r_size);
|
||||
FC_ASSERT(sig[4 + r_size] == 0x02);
|
||||
const uint s_size = sig[5 + r_size];
|
||||
FC_ASSERT(sig.size() == r_size + s_size + 7);
|
||||
sig_temp.insert(sig_temp.end(), sig.begin() + 6 + r_size, sig.end());
|
||||
|
||||
std::vector<unsigned char> pubkey_temp(pubkey.begin(), pubkey.end());
|
||||
std::vector<unsigned char> msg_temp(msg.begin(), msg.end());
|
||||
|
||||
int result = secp256k1_ecdsa_verify(context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size());
|
||||
secp256k1_pubkey pub_key;
|
||||
FC_ASSERT(secp256k1_ec_pubkey_parse(context, &pub_key, (const unsigned char *)pubkey_temp.data(), pubkey_temp.size()));
|
||||
|
||||
secp256k1_ecdsa_signature sign;
|
||||
FC_ASSERT(secp256k1_ecdsa_signature_parse_compact(context, &sign, (const unsigned char *)sig_temp.data()));
|
||||
|
||||
int result = secp256k1_ecdsa_verify(context, &sign, (const unsigned char *)msg_temp.data(), &pub_key);
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context_t *context) {
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context *context) {
|
||||
FC_ASSERT(redeem_scripts.size() == amounts.size());
|
||||
|
||||
using data = std::pair<size_t, bytes>;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,50 @@
|
|||
#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 = fc::to_string(id.space()) + "." +
|
||||
fc::to_string(id.type()) + "." +
|
||||
fc::to_string(id.instance());
|
||||
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));
|
||||
|
||||
unsigned int v = recid + from_hex<unsigned 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 = to_hex(v);
|
||||
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);
|
||||
const int recid = from_hex<unsigned int>(v) - from_hex<unsigned int>(chain_id) * 2 - 35;
|
||||
fc::from_hex(std::to_string(recid), (char *)&input64.at(0), 1);
|
||||
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
|
||||
|
|
@ -8,23 +8,23 @@ namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
|||
|
||||
class bitcoin_transaction;
|
||||
|
||||
const secp256k1_context_t *btc_context();
|
||||
const secp256k1_context *btc_context();
|
||||
|
||||
fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptPubKey, int64_t amount,
|
||||
size_t in_index, int hash_type, bool is_witness);
|
||||
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign = nullptr);
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context *context_sign = nullptr);
|
||||
|
||||
std::vector<bytes> sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const bytes &privkey,
|
||||
const secp256k1_context_t *context_sign = nullptr, int hash_type = 1);
|
||||
const secp256k1_context *context_sign = nullptr, int hash_type = 1);
|
||||
|
||||
void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts, bool use_mulisig_workaround = true);
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context);
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context *context);
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context_t *context);
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context *context);
|
||||
|
||||
void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,134 +3,44 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
//#include <fc/network/http/connection.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
enum class url_schema_type { unknown,
|
||||
http,
|
||||
https,
|
||||
};
|
||||
|
||||
// utl
|
||||
|
||||
url_schema_type identify_url_schema_type(const std::string &schema_name);
|
||||
|
||||
struct url_data {
|
||||
|
||||
url_schema_type schema_type;
|
||||
std::string schema;
|
||||
std::string host;
|
||||
uint16_t port;
|
||||
std::string path;
|
||||
|
||||
url_data() :
|
||||
schema_type(url_schema_type::unknown),
|
||||
port(0) {
|
||||
}
|
||||
|
||||
url_data(const std::string &url);
|
||||
|
||||
void clear();
|
||||
|
||||
bool parse(const std::string &url);
|
||||
};
|
||||
|
||||
struct http_request {
|
||||
|
||||
struct rpc_reply {
|
||||
uint16_t status;
|
||||
std::string body;
|
||||
std::string content_type;
|
||||
|
||||
http_request(const std::string &body_, const std::string &content_type_) :
|
||||
body(body_),
|
||||
content_type(content_type_) {
|
||||
}
|
||||
};
|
||||
|
||||
struct http_response {
|
||||
|
||||
uint16_t status_code;
|
||||
std::string body;
|
||||
|
||||
void clear() {
|
||||
status_code = 0;
|
||||
body = decltype(body)();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <class>
|
||||
class http_call_impl;
|
||||
class tcp_socket;
|
||||
class ssl_socket;
|
||||
} // namespace detail
|
||||
|
||||
class http_call {
|
||||
public:
|
||||
http_call(const url_data &url, const std::string &method = std::string(), const std::string &headers = std::string());
|
||||
~http_call();
|
||||
|
||||
bool is_ssl() const;
|
||||
|
||||
const std::string &path() const;
|
||||
void set_path(const std::string &path);
|
||||
void set_method(const std::string &method);
|
||||
void set_headers(const std::string &headers);
|
||||
const std::string &host() const;
|
||||
void set_host(const std::string &host);
|
||||
|
||||
uint16_t port() const;
|
||||
void set_port(uint16_t port);
|
||||
|
||||
bool exec(const http_request &request, http_response *response);
|
||||
|
||||
const std::string &error_what() const;
|
||||
|
||||
private:
|
||||
template <class>
|
||||
friend class detail::http_call_impl;
|
||||
friend detail::tcp_socket;
|
||||
friend detail::ssl_socket;
|
||||
static constexpr auto response_size_limit_bytes = 16 * 1024 * 1024;
|
||||
static constexpr auto response_first_alloc_bytes = 32 * 1024;
|
||||
static constexpr auto response_next_alloc_bytes = 256 * 1024;
|
||||
std::string m_host;
|
||||
uint16_t m_port_default;
|
||||
uint16_t m_port;
|
||||
std::string m_path;
|
||||
std::string m_method;
|
||||
std::string m_headers;
|
||||
std::string m_error_what;
|
||||
|
||||
boost::asio::io_service m_service;
|
||||
boost::asio::ssl::context *m_context;
|
||||
boost::asio::ip::tcp::endpoint m_endpoint;
|
||||
|
||||
void ctor_priv();
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class rpc_client {
|
||||
public:
|
||||
rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug);
|
||||
rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls);
|
||||
|
||||
protected:
|
||||
std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx);
|
||||
std::string retrieve_value_from_reply(std::string reply_str, std::string value_path);
|
||||
std::string send_post_request(std::string method, std::string params, bool show_log);
|
||||
|
||||
std::string url;
|
||||
std::string user;
|
||||
std::string password;
|
||||
bool debug_rpc_calls;
|
||||
|
||||
std::string protocol;
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::string target;
|
||||
std::string authorization;
|
||||
|
||||
uint32_t request_id;
|
||||
|
||||
private:
|
||||
http_call client;
|
||||
http_response send_post_request(const std::string &body, bool show_log);
|
||||
rpc_reply send_post_request(std::string body, bool show_log);
|
||||
|
||||
boost::beast::net::io_context ioc;
|
||||
boost::beast::net::ip::tcp::resolver resolver;
|
||||
boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp> results;
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -2,4 +2,11 @@
|
|||
|
||||
#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);
|
||||
|
||||
}} // 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,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
|
||||
|
||||
bytes parse_hex(const std::string &str);
|
||||
|
||||
std::string bytes2hex(const std::string &s);
|
||||
|
||||
std::string uchar2Hex(unsigned char n);
|
||||
|
||||
std::string add_0x(const std::string &s);
|
||||
|
||||
std::string remove_0x(const std::string &s);
|
||||
|
||||
template <typename T>
|
||||
std::string to_hex(const T &val, bool add_front_zero = true) {
|
||||
std::stringstream stream;
|
||||
stream << std::hex << val;
|
||||
std::string result(stream.str());
|
||||
if(add_front_zero) {
|
||||
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
|
||||
|
|
@ -13,16 +13,18 @@ class peerplays_sidechain_plugin_impl;
|
|||
}
|
||||
|
||||
struct son_proposal_type {
|
||||
son_proposal_type(int op, son_id_type son, object_id_type object) :
|
||||
son_proposal_type(int op, sidechain_type sid, son_id_type son, object_id_type object) :
|
||||
op_type(op),
|
||||
sidechain(sid),
|
||||
son_id(son),
|
||||
object_id(object) {
|
||||
}
|
||||
int op_type;
|
||||
sidechain_type sidechain;
|
||||
son_id_type son_id;
|
||||
object_id_type object_id;
|
||||
bool operator<(const son_proposal_type &other) const {
|
||||
return std::tie(op_type, son_id, object_id) < std::tie(other.op_type, other.son_id, other.object_id);
|
||||
return std::tie(op_type, sidechain, son_id, object_id) < std::tie(other.op_type, other.sidechain, other.son_id, other.object_id);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -42,16 +44,17 @@ public:
|
|||
std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my;
|
||||
|
||||
std::set<chain::son_id_type> &get_sons();
|
||||
const son_id_type get_current_son_id();
|
||||
const son_object get_current_son_object();
|
||||
const son_id_type get_current_son_id(sidechain_type sidechain);
|
||||
const son_object get_current_son_object(sidechain_type sidechain);
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
bool is_active_son(sidechain_type sidechain, son_id_type son_id);
|
||||
bool is_son_deregistered(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
|
||||
void log_son_proposal_retry(int op_type, object_id_type object_id);
|
||||
bool can_son_participate(int op_type, object_id_type object_id);
|
||||
void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id);
|
||||
bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id);
|
||||
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
|
||||
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -26,9 +26,10 @@ public:
|
|||
std::shared_ptr<detail::sidechain_api_impl> my;
|
||||
|
||||
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
|
||||
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
||||
FC_API(graphene::peerplays_sidechain::sidechain_api,
|
||||
(get_son_listener_log))
|
||||
(get_son_listener_log)(estimate_withdrawal_transaction_fee))
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public:
|
|||
|
||||
void add_to_son_listener_log(std::string trx_id);
|
||||
std::vector<std::string> get_son_listener_log();
|
||||
virtual optional<asset> estimate_withdrawal_transaction_fee() const = 0;
|
||||
|
||||
protected:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
|
|
@ -21,7 +23,7 @@ public:
|
|||
uint64_t amount_;
|
||||
};
|
||||
|
||||
class bitcoin_rpc_client {
|
||||
class bitcoin_rpc_client : public rpc_client {
|
||||
public:
|
||||
enum class multi_type {
|
||||
script,
|
||||
|
|
@ -40,49 +42,29 @@ public:
|
|||
};
|
||||
|
||||
public:
|
||||
bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password, bool _debug_rpc_calls);
|
||||
bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls);
|
||||
|
||||
std::string addmultisigaddress(const uint32_t nrequired, const std::vector<std::string> public_keys);
|
||||
std::string combinepsbt(const vector<std::string> &psbts);
|
||||
std::string createmultisig(const uint32_t nrequired, const std::vector<std::string> public_keys);
|
||||
std::string createpsbt(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
|
||||
std::string createrawtransaction(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
|
||||
std::string createwallet(const std::string &wallet_name);
|
||||
std::string decodepsbt(std::string const &tx_psbt);
|
||||
std::string decoderawtransaction(std::string const &tx_hex);
|
||||
std::string encryptwallet(const std::string &passphrase);
|
||||
uint64_t estimatesmartfee(uint16_t conf_target = 128);
|
||||
std::string finalizepsbt(std::string const &tx_psbt);
|
||||
std::string getaddressinfo(const std::string &address);
|
||||
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
|
||||
std::string getrawtransaction(const std::string &txid, const bool verbose = false);
|
||||
std::string getnetworkinfo();
|
||||
std::string gettransaction(const std::string &txid, const bool include_watch_only = false);
|
||||
std::string getblockchaininfo();
|
||||
void importaddress(const std::string &address_or_script, const std::string &label = "", const bool rescan = true, const bool p2sh = false);
|
||||
void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true);
|
||||
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
|
||||
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
|
||||
std::string loadwallet(const std::string &filename);
|
||||
std::string sendrawtransaction(const std::string &tx_hex);
|
||||
std::string signrawtransactionwithwallet(const std::string &tx_hash);
|
||||
std::string unloadwallet(const std::string &filename);
|
||||
std::string walletlock();
|
||||
std::string walletprocesspsbt(std::string const &tx_psbt);
|
||||
bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
|
||||
|
||||
private:
|
||||
fc::http::reply send_post_request(std::string body, bool show_log);
|
||||
|
||||
std::string ip;
|
||||
uint32_t rpc_port;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string wallet;
|
||||
std::string wallet_name;
|
||||
std::string wallet_password;
|
||||
bool debug_rpc_calls;
|
||||
|
||||
fc::http::header authorization;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
|
@ -90,7 +72,9 @@ private:
|
|||
class zmq_listener {
|
||||
public:
|
||||
zmq_listener(std::string _ip, uint32_t _zmq);
|
||||
virtual ~zmq_listener();
|
||||
|
||||
void start();
|
||||
boost::signals2::signal<void(const std::string &)> event_received;
|
||||
|
||||
private:
|
||||
|
|
@ -102,6 +86,9 @@ private:
|
|||
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t socket;
|
||||
|
||||
std::atomic_bool stopped;
|
||||
std::thread thr;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
|
@ -119,22 +106,24 @@ public:
|
|||
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);
|
||||
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
|
||||
|
||||
private:
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
uint32_t rpc_port;
|
||||
uint32_t bitcoin_major_version;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::string wallet;
|
||||
std::string wallet_name;
|
||||
std::string wallet_password;
|
||||
|
||||
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
|
||||
std::unique_ptr<zmq_listener> listener;
|
||||
|
||||
fc::future<void> on_changed_objects_task;
|
||||
|
||||
bitcoin::bitcoin_address::network network_type;
|
||||
uint32_t bitcoin_major_version;
|
||||
|
||||
std::mutex event_handler_mutex;
|
||||
typedef std::lock_guard<decltype(event_handler_mutex)> scoped_lock;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
#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 eth_blockNumber();
|
||||
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 eth_chainId();
|
||||
std::string net_version();
|
||||
std::string eth_get_transaction_count(const std::string ¶ms);
|
||||
std::string eth_gas_price();
|
||||
std::string eth_estimateGas(const std::string ¶ms);
|
||||
|
||||
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 get_estimate_gas(const std::string ¶ms);
|
||||
|
||||
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);
|
||||
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
|
||||
|
||||
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 &block_number);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_handler_factory {
|
||||
public:
|
||||
sidechain_net_handler_factory(peerplays_sidechain_plugin &_plugin);
|
||||
|
||||
std::unique_ptr<sidechain_net_handler> create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) const;
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -6,15 +6,14 @@
|
|||
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#include <fc/network/http/connection.hpp>
|
||||
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
|
||||
#include <graphene/peerplays_sidechain/hive/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class hive_node_rpc_client : public rpc_client {
|
||||
class hive_rpc_client : public rpc_client {
|
||||
public:
|
||||
hive_node_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls);
|
||||
hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls);
|
||||
|
||||
std::string account_history_api_get_transaction(std::string transaction_id);
|
||||
std::string block_api_get_block(uint32_t block_number);
|
||||
|
|
@ -46,12 +45,15 @@ public:
|
|||
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);
|
||||
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
|
||||
|
||||
private:
|
||||
std::string node_rpc_url;
|
||||
std::string node_rpc_user;
|
||||
std::string node_rpc_password;
|
||||
hive_node_rpc_client *node_rpc_client;
|
||||
std::string rpc_url;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::string wallet_account_name;
|
||||
|
||||
hive_rpc_client *rpc_client;
|
||||
|
||||
hive::chain_id_type chain_id;
|
||||
hive::network network_type;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public:
|
|||
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);
|
||||
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_manager {
|
||||
public:
|
||||
sidechain_net_manager(peerplays_sidechain_plugin &_plugin);
|
||||
virtual ~sidechain_net_manager();
|
||||
|
||||
bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options);
|
||||
void process_proposals();
|
||||
void process_active_sons_change();
|
||||
void create_deposit_addresses();
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
void settle_sidechain_transactions();
|
||||
|
||||
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
graphene::chain::database &database;
|
||||
std::vector<std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||
|
||||
void on_applied_block(const signed_block &b);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/range/algorithm_ext/insert.hpp>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
|
|
@ -11,7 +13,7 @@
|
|||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_api.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
|
@ -33,36 +35,37 @@ public:
|
|||
void plugin_shutdown();
|
||||
|
||||
std::set<chain::son_id_type> &get_sons();
|
||||
const son_id_type get_current_son_id();
|
||||
const son_object get_current_son_object();
|
||||
const son_id_type get_current_son_id(sidechain_type sidechain);
|
||||
const son_object get_current_son_object(sidechain_type sidechain);
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
bool is_active_son(sidechain_type sidechain, son_id_type son_id);
|
||||
bool is_son_deregistered(son_id_type son_id);
|
||||
bool is_son_deregister_op_valid(const chain::operation &op);
|
||||
bool is_son_down_op_valid(const chain::operation &op);
|
||||
bool is_valid_son_proposal(const chain::proposal_object &proposal);
|
||||
fc::ecc::private_key get_private_key(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
|
||||
void log_son_proposal_retry(int op_type, object_id_type object_id);
|
||||
bool can_son_participate(int op_type, object_id_type object_id);
|
||||
void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id);
|
||||
bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id);
|
||||
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
|
||||
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
|
||||
|
||||
void schedule_heartbeat_loop();
|
||||
void heartbeat_loop();
|
||||
void schedule_son_processing();
|
||||
void son_processing();
|
||||
void approve_proposals();
|
||||
void create_son_down_proposals();
|
||||
void create_son_deregister_proposals();
|
||||
void son_processing(sidechain_type sidechain);
|
||||
void approve_proposals(sidechain_type sidechain);
|
||||
void create_son_down_proposals(sidechain_type sidechain);
|
||||
void create_son_deregister_proposals(sidechain_type sidechain);
|
||||
|
||||
void process_proposals();
|
||||
void process_active_sons_change();
|
||||
void create_deposit_addresses();
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
void settle_sidechain_transactions();
|
||||
void process_proposals(sidechain_type sidechain);
|
||||
void process_active_sons_change(sidechain_type sidechain);
|
||||
void create_deposit_addresses(sidechain_type sidechain);
|
||||
void process_deposits(sidechain_type sidechain);
|
||||
void process_withdrawals(sidechain_type sidechain);
|
||||
void process_sidechain_transactions(sidechain_type sidechain);
|
||||
void send_sidechain_transactions(sidechain_type sidechain);
|
||||
void settle_sidechain_transactions(sidechain_type sidechain);
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
|
|
@ -80,15 +83,21 @@ private:
|
|||
bool sidechain_enabled_hive;
|
||||
bool sidechain_enabled_peerplays;
|
||||
|
||||
son_id_type current_son_id;
|
||||
std::map<sidechain_type, son_id_type> current_son_id;
|
||||
std::mutex current_son_id_mutex;
|
||||
std::mutex access_db_mutex;
|
||||
std::mutex access_approve_prop_mutex;
|
||||
std::mutex access_son_down_prop_mutex;
|
||||
std::mutex access_son_deregister_prop_mutex;
|
||||
|
||||
std::unique_ptr<peerplays_sidechain::sidechain_net_manager> net_manager;
|
||||
std::map<sidechain_type, bool> sidechain_enabled;
|
||||
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||
std::set<chain::son_id_type> sons;
|
||||
std::map<chain::public_key_type, fc::ecc::private_key> private_keys;
|
||||
fc::future<void> _heartbeat_task;
|
||||
fc::future<void> _son_processing_task;
|
||||
std::map<sidechain_type, std::future<void>> _son_processing_task;
|
||||
std::map<son_proposal_type, uint16_t> son_retry_count;
|
||||
uint16_t retries_threshold;
|
||||
uint16_t retries_threshold = 150;
|
||||
|
||||
bool first_block_skipped;
|
||||
void on_applied_block(const signed_block &b);
|
||||
|
|
@ -105,8 +114,27 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec
|
|||
sidechain_enabled_ethereum(false),
|
||||
sidechain_enabled_hive(false),
|
||||
sidechain_enabled_peerplays(false),
|
||||
current_son_id(son_id_type(std::numeric_limits<uint32_t>().max())),
|
||||
net_manager(nullptr),
|
||||
current_son_id([] {
|
||||
std::map<sidechain_type, son_id_type> current_son_id;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits<uint32_t>().max()));
|
||||
}
|
||||
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([] {
|
||||
std::map<sidechain_type, std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
net_handlers.emplace(active_sidechain_type, nullptr);
|
||||
}
|
||||
return net_handlers;
|
||||
}()),
|
||||
first_block_skipped(false) {
|
||||
}
|
||||
|
||||
|
|
@ -121,8 +149,10 @@ peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
|
|||
}
|
||||
|
||||
try {
|
||||
if (_son_processing_task.valid())
|
||||
_son_processing_task.cancel_and_wait(__FUNCTION__);
|
||||
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).valid())
|
||||
_son_processing_task.at(active_sidechain_type).wait();
|
||||
}
|
||||
} catch (fc::canceled_exception &) {
|
||||
//Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
|
|
@ -152,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-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-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-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)");
|
||||
|
||||
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-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-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")),
|
||||
"Tuple of [Hive public key, Hive private key] (may specify multiple times)");
|
||||
|
||||
|
|
@ -211,45 +250,39 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
|
|||
config_ready_bitcoin = options.count("bitcoin-node-ip") &&
|
||||
options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") &&
|
||||
options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") &&
|
||||
/*options.count("bitcoin-wallet") && options.count("bitcoin-wallet-password") &&*/
|
||||
options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") &&
|
||||
options.count("bitcoin-private-key");
|
||||
if (!config_ready_bitcoin) {
|
||||
if (sidechain_enabled_bitcoin && !config_ready_bitcoin) {
|
||||
wlog("Haven't set up Bitcoin sidechain parameters");
|
||||
}
|
||||
|
||||
//sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as<bool>();
|
||||
//config_ready_ethereum = options.count("ethereum-node-ip") &&
|
||||
// options.count("ethereum-address") &&
|
||||
// options.count("ethereum-public-key") && options.count("ethereum-private-key");
|
||||
//if (!config_ready_ethereum) {
|
||||
// wlog("Haven't set up Ethereum sidechain parameters");
|
||||
//}
|
||||
sidechain_enabled_ethereum = options.at("ethereum-sidechain-enabled").as<bool>();
|
||||
config_ready_ethereum = options.count("ethereum-node-rpc-url") &&
|
||||
/*options.count("ethereum-node-rpc-user") && options.count("ethereum-node-rpc-password") &&*/
|
||||
options.count("ethereum-wallet-contract-address") &&
|
||||
options.count("ethereum-private-key");
|
||||
if (sidechain_enabled_ethereum && !config_ready_ethereum) {
|
||||
wlog("Haven't set up Ethereum sidechain parameters");
|
||||
}
|
||||
|
||||
sidechain_enabled_hive = options.at("hive-sidechain-enabled").as<bool>();
|
||||
config_ready_hive = options.count("hive-node-rpc-url") &&
|
||||
/*options.count("hive-node-rpc-user") && options.count("hive-node-rpc-password") &&*/
|
||||
options.count("hive-wallet-account-name") &&
|
||||
options.count("hive-private-key");
|
||||
if (!config_ready_hive) {
|
||||
if (sidechain_enabled_hive && !config_ready_hive) {
|
||||
wlog("Haven't set up Hive sidechain parameters");
|
||||
}
|
||||
|
||||
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
|
||||
sidechain_enabled_peerplays = true; //options.at("peerplays-sidechain-enabled").as<bool>();
|
||||
sidechain_enabled_peerplays = true;
|
||||
#else
|
||||
sidechain_enabled_peerplays = false;
|
||||
#endif
|
||||
config_ready_peerplays = true;
|
||||
if (!config_ready_peerplays) {
|
||||
if (sidechain_enabled_peerplays && !config_ready_peerplays) {
|
||||
wlog("Haven't set up Peerplays sidechain parameters");
|
||||
}
|
||||
|
||||
if (!(config_ready_bitcoin &&
|
||||
/*config_ready_ethereum &&*/
|
||||
config_ready_hive &&
|
||||
config_ready_peerplays)) {
|
||||
wlog("Haven't set up any sidechain parameters");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_startup() {
|
||||
|
|
@ -262,25 +295,29 @@ void peerplays_sidechain_plugin_impl::plugin_startup() {
|
|||
elog("No sons configured! Please add SON IDs and private keys to configuration.");
|
||||
}
|
||||
|
||||
net_manager = std::unique_ptr<sidechain_net_manager>(new sidechain_net_manager(plugin));
|
||||
sidechain_net_handler_factory net_handler_factory(plugin);
|
||||
|
||||
if (sidechain_enabled_bitcoin && config_ready_bitcoin) {
|
||||
net_manager->create_handler(sidechain_type::bitcoin, options);
|
||||
sidechain_enabled.at(sidechain_type::bitcoin) = true;
|
||||
net_handlers.at(sidechain_type::bitcoin) = net_handler_factory.create_handler(sidechain_type::bitcoin, options);
|
||||
ilog("Bitcoin sidechain handler running");
|
||||
}
|
||||
|
||||
//if (sidechain_enabled_ethereum && config_ready_ethereum) {
|
||||
// net_manager->create_handler(sidechain_type::ethereum, options);
|
||||
// ilog("Ethereum sidechain handler running");
|
||||
//}
|
||||
if (sidechain_enabled_ethereum && config_ready_ethereum) {
|
||||
sidechain_enabled.at(sidechain_type::ethereum) = true;
|
||||
net_handlers.at(sidechain_type::ethereum) = net_handler_factory.create_handler(sidechain_type::ethereum, options);
|
||||
ilog("Ethereum sidechain handler running");
|
||||
}
|
||||
|
||||
if (sidechain_enabled_hive && config_ready_hive) {
|
||||
net_manager->create_handler(sidechain_type::hive, options);
|
||||
sidechain_enabled.at(sidechain_type::hive) = true;
|
||||
net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options);
|
||||
ilog("Hive sidechain handler running");
|
||||
}
|
||||
|
||||
if (sidechain_enabled_peerplays && config_ready_peerplays) {
|
||||
net_manager->create_handler(sidechain_type::peerplays, options);
|
||||
sidechain_enabled.at(sidechain_type::peerplays) = true;
|
||||
net_handlers.at(sidechain_type::peerplays) = net_handler_factory.create_handler(sidechain_type::peerplays, options);
|
||||
ilog("Peerplays sidechain handler running");
|
||||
}
|
||||
|
||||
|
|
@ -296,12 +333,13 @@ std::set<chain::son_id_type> &peerplays_sidechain_plugin_impl::get_sons() {
|
|||
return sons;
|
||||
}
|
||||
|
||||
const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() {
|
||||
return current_son_id;
|
||||
const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id(sidechain_type sidechain) {
|
||||
const std::lock_guard<std::mutex> lock(current_son_id_mutex);
|
||||
return current_son_id.at(sidechain);
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin_impl::get_current_son_object() {
|
||||
return get_son_object(current_son_id);
|
||||
const son_object peerplays_sidechain_plugin_impl::get_current_son_object(sidechain_type sidechain) {
|
||||
return get_son_object(get_current_son_id(sidechain));
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) {
|
||||
|
|
@ -312,16 +350,15 @@ const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son
|
|||
return *son_obj;
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) {
|
||||
bool peerplays_sidechain_plugin_impl::is_active_son(sidechain_type sidechain, son_id_type son_id) {
|
||||
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(son_id);
|
||||
if (son_obj == idx.end())
|
||||
return false;
|
||||
|
||||
const chain::global_property_object &gpo = plugin.database().get_global_properties();
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
set<son_id_type> active_son_ids;
|
||||
std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
|
|
@ -338,7 +375,13 @@ bool peerplays_sidechain_plugin_impl::is_son_deregistered(son_id_type son_id) {
|
|||
if (son_obj == idx.end())
|
||||
return true;
|
||||
|
||||
if (son_obj->status == chain::son_status::deregistered) {
|
||||
bool status_deregistered = true;
|
||||
for (const auto &status : son_obj->statuses) {
|
||||
if ((status.second != son_status::deregistered))
|
||||
status_deregistered = false;
|
||||
}
|
||||
|
||||
if (status_deregistered) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -362,13 +405,23 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio
|
|||
}
|
||||
auto stats = son_obj->statistics(d);
|
||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time);
|
||||
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) &&
|
||||
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
|
||||
return true;
|
||||
|
||||
bool status_son_down_op_valid = true;
|
||||
for (const auto &status : son_obj->statuses) {
|
||||
if ((status.second != son_status::active) && (status.second != son_status::request_maintenance))
|
||||
status_son_down_op_valid = false;
|
||||
}
|
||||
return false;
|
||||
if (status_son_down_op_valid) {
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time);
|
||||
if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) {
|
||||
status_son_down_op_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status_son_down_op_valid;
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) {
|
||||
|
|
@ -400,7 +453,25 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
|
|||
chain::database &d = plugin.database();
|
||||
|
||||
for (son_id_type son_id : sons) {
|
||||
if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) {
|
||||
const auto &son_obj = get_son_object(son_id);
|
||||
|
||||
//! Check that son is in_maintenance
|
||||
bool status_in_maintenance = false;
|
||||
for (const auto &status : son_obj.statuses) {
|
||||
if ((status.second == son_status::in_maintenance))
|
||||
status_in_maintenance = true;
|
||||
}
|
||||
|
||||
//! Check that son is active (at least for one sidechain_type)
|
||||
bool is_son_active = false;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
if (sidechain_enabled.at(active_sidechain_type)) {
|
||||
if (is_active_son(active_sidechain_type, son_id))
|
||||
is_son_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_son_active || status_in_maintenance) {
|
||||
|
||||
ilog("Sending heartbeat for SON ${son}", ("son", son_id));
|
||||
chain::son_heartbeat_operation op;
|
||||
|
|
@ -426,19 +497,30 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() {
|
|||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::schedule_son_processing() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next_son_processing = 500000;
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
static const int64_t time_to_next_son_processing = 500000;
|
||||
|
||||
fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing));
|
||||
const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing);
|
||||
|
||||
_son_processing_task = fc::schedule([this] {
|
||||
son_processing();
|
||||
},
|
||||
next_wakeup, "SON Processing");
|
||||
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] {
|
||||
if (sidechain_enabled.at(active_sidechain_type)) {
|
||||
std::this_thread::sleep_until(next_wakeup);
|
||||
son_processing(active_sidechain_type);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::son_processing() {
|
||||
if (plugin.database().get_global_properties().active_sons.size() <= 0) {
|
||||
void peerplays_sidechain_plugin_impl::son_processing(sidechain_type sidechain) {
|
||||
//! Check whether we have active SONs
|
||||
if (plugin.database().get_global_properties().active_sons.count(sidechain) == 0 ||
|
||||
plugin.database().get_global_properties().active_sons.at(sidechain).empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -448,50 +530,55 @@ void peerplays_sidechain_plugin_impl::son_processing() {
|
|||
// return; // Not synced
|
||||
//}
|
||||
|
||||
fc::time_point now_fine = fc::time_point::now();
|
||||
fc::time_point_sec now = now_fine - fc::milliseconds(3000);
|
||||
const fc::time_point now_fine = fc::time_point::now();
|
||||
const fc::time_point_sec now = now_fine - fc::milliseconds(3000);
|
||||
if (plugin.database().head_block_time() < now) {
|
||||
return; // Not synced
|
||||
}
|
||||
|
||||
chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1);
|
||||
ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ",
|
||||
("scheduled_son_id", scheduled_son_id)("now", now));
|
||||
//! Get scheduled_son_id according to sidechain_type
|
||||
const chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(sidechain, 1);
|
||||
ilog("Scheduled SON: ${scheduled_son_id} Sidechain: ${sidechain} Now: ${now}",
|
||||
("scheduled_son_id", scheduled_son_id)("sidechain", sidechain)("now", now));
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_son_deregistered(son_id)) {
|
||||
continue;
|
||||
}
|
||||
current_son_id = son_id;
|
||||
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(current_son_id_mutex);
|
||||
current_son_id.at(sidechain) = son_id;
|
||||
}
|
||||
|
||||
// These tasks are executed by
|
||||
// - All active SONs, no matter if scheduled
|
||||
// - All previously active SONs
|
||||
approve_proposals();
|
||||
process_proposals();
|
||||
process_sidechain_transactions();
|
||||
approve_proposals(sidechain);
|
||||
process_proposals(sidechain);
|
||||
process_sidechain_transactions(sidechain);
|
||||
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
if (plugin.is_active_son(sidechain, son_id)) {
|
||||
// Tasks that are executed by scheduled and active SON only
|
||||
if (current_son_id == scheduled_son_id) {
|
||||
if (get_current_son_id(sidechain) == scheduled_son_id) {
|
||||
|
||||
create_son_down_proposals();
|
||||
create_son_down_proposals(sidechain);
|
||||
|
||||
create_son_deregister_proposals();
|
||||
create_son_deregister_proposals(sidechain);
|
||||
|
||||
process_active_sons_change();
|
||||
process_active_sons_change(sidechain);
|
||||
|
||||
create_deposit_addresses();
|
||||
create_deposit_addresses(sidechain);
|
||||
|
||||
process_deposits();
|
||||
process_deposits(sidechain);
|
||||
|
||||
process_withdrawals();
|
||||
process_withdrawals(sidechain);
|
||||
|
||||
process_sidechain_transactions();
|
||||
process_sidechain_transactions(sidechain);
|
||||
|
||||
send_sidechain_transactions();
|
||||
send_sidechain_transactions(sidechain);
|
||||
|
||||
settle_sidechain_transactions();
|
||||
settle_sidechain_transactions(sidechain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -514,8 +601,8 @@ bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposa
|
|||
return false;
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object_id_type object_id) {
|
||||
son_proposal_type prop_type(op_type, get_current_son_id(), object_id);
|
||||
void peerplays_sidechain_plugin_impl::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) {
|
||||
son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id);
|
||||
auto itr = son_retry_count.find(prop_type);
|
||||
if (itr != son_retry_count.end()) {
|
||||
itr->second++;
|
||||
|
|
@ -524,18 +611,38 @@ void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object
|
|||
}
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::can_son_participate(int op_type, object_id_type object_id) {
|
||||
son_proposal_type prop_type(op_type, get_current_son_id(), object_id);
|
||||
bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) {
|
||||
son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id);
|
||||
auto itr = son_retry_count.find(prop_type);
|
||||
return (itr == son_retry_count.end() || itr->second < retries_threshold);
|
||||
}
|
||||
|
||||
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_impl::get_son_listener_log() {
|
||||
return net_manager->get_son_listener_log();
|
||||
std::map<sidechain_type, std::vector<std::string>> result;
|
||||
for (const auto &active_sidechain_type : active_sidechain_types) {
|
||||
if (net_handlers.at(active_sidechain_type)) {
|
||||
result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::approve_proposals() {
|
||||
optional<asset> peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
|
||||
if (!net_handlers.at(sidechain)) {
|
||||
wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain));
|
||||
return optional<asset>();
|
||||
}
|
||||
|
||||
return net_handlers.at(sidechain)->estimate_withdrawal_transaction_fee();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) {
|
||||
// prevent approving duplicate proposals with lock for parallel execution.
|
||||
// We can have the same propsals, but in the case of parallel execution we can run
|
||||
// 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
|
||||
// active proposals which is consulted here in the code.
|
||||
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) {
|
||||
if (!is_valid_son_proposal(proposal)) {
|
||||
return;
|
||||
|
|
@ -549,6 +656,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() {
|
|||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
trx.validate();
|
||||
std::lock_guard<std::mutex> lck(access_db_mutex);
|
||||
plugin.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));
|
||||
|
|
@ -568,7 +676,6 @@ void peerplays_sidechain_plugin_impl::approve_proposals() {
|
|||
}
|
||||
|
||||
for (const auto proposal_id : proposals) {
|
||||
|
||||
const object *obj = plugin.database().find_object(proposal_id);
|
||||
const chain::proposal_object *proposal_ptr = dynamic_cast<const chain::proposal_object *>(obj);
|
||||
if (proposal_ptr == nullptr) {
|
||||
|
|
@ -576,15 +683,16 @@ void peerplays_sidechain_plugin_impl::approve_proposals() {
|
|||
}
|
||||
const proposal_object proposal = *proposal_ptr;
|
||||
|
||||
if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) {
|
||||
if (proposal.available_active_approvals.find(get_current_son_object(sidechain).son_account) != proposal.available_active_approvals.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
check_approve_proposal(get_current_son_id(), proposal);
|
||||
check_approve_proposal(get_current_son_id(sidechain), proposal);
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
|
||||
void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type sidechain) {
|
||||
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) {
|
||||
chain::database &d = plugin.database();
|
||||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
|
|
@ -595,7 +703,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
|
|||
son_down_op.down_ts = last_active_ts;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = get_current_son_object().son_account;
|
||||
proposal_op.fee_paying_account = get_current_son_object(sidechain).son_account;
|
||||
proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op));
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
|
@ -607,24 +715,32 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
|
|||
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
|
||||
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
std::set<son_id_type> sons_being_reported_down = d.get_sons_being_reported_down();
|
||||
chain::son_id_type my_son_id = get_current_son_id();
|
||||
for (auto son_inf : gpo.active_sons) {
|
||||
chain::son_id_type my_son_id = get_current_son_id(sidechain);
|
||||
|
||||
//! Fixme - check this part of the code
|
||||
for (auto son_inf : gpo.active_sons.at(sidechain)) {
|
||||
if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) {
|
||||
continue;
|
||||
}
|
||||
auto son_obj = idx.find(son_inf.son_id);
|
||||
auto stats = son_obj->statistics(d);
|
||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time);
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time);
|
||||
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) &&
|
||||
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
|
||||
|
||||
bool status_son_down_valid = true;
|
||||
for (const auto &status : son_obj->statuses) {
|
||||
if ((status.second != son_status::active) && (status.second != son_status::request_maintenance))
|
||||
status_son_down_valid = false;
|
||||
}
|
||||
if ((status_son_down_valid) && ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
|
||||
ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id))));
|
||||
chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts);
|
||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
trx.validate();
|
||||
std::lock_guard<std::mutex> lck(access_db_mutex);
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
|
|
@ -639,10 +755,11 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
|
|||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
|
||||
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();
|
||||
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();
|
||||
chain::son_id_type my_son_id = get_current_son_id(sidechain);
|
||||
|
||||
if (sons_to_be_dereg.size() > 0) {
|
||||
// We shouldn't raise proposals for the SONs for which a de-reg
|
||||
|
|
@ -660,6 +777,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
|
|||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
trx.validate();
|
||||
std::lock_guard<std::mutex> lck(access_db_mutex);
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
|
|
@ -676,36 +794,52 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
|
|||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_proposals() {
|
||||
net_manager->process_proposals();
|
||||
void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->process_proposals();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_active_sons_change() {
|
||||
net_manager->process_active_sons_change();
|
||||
void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->process_active_sons_change();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_deposit_addresses() {
|
||||
net_manager->create_deposit_addresses();
|
||||
void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->create_deposit_addresses();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_deposits() {
|
||||
net_manager->process_deposits();
|
||||
void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->process_deposits();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_withdrawals() {
|
||||
net_manager->process_withdrawals();
|
||||
void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->process_withdrawals();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_sidechain_transactions() {
|
||||
net_manager->process_sidechain_transactions();
|
||||
void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->process_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::send_sidechain_transactions() {
|
||||
net_manager->send_sidechain_transactions();
|
||||
void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->send_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() {
|
||||
net_manager->settle_sidechain_transactions();
|
||||
void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) {
|
||||
if (net_handlers.at(sidechain)) {
|
||||
net_handlers.at(sidechain)->settle_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
|
||||
|
|
@ -758,20 +892,20 @@ std::set<chain::son_id_type> &peerplays_sidechain_plugin::get_sons() {
|
|||
return my->get_sons();
|
||||
}
|
||||
|
||||
const son_id_type peerplays_sidechain_plugin::get_current_son_id() {
|
||||
return my->get_current_son_id();
|
||||
const son_id_type peerplays_sidechain_plugin::get_current_son_id(sidechain_type sidechain) {
|
||||
return my->get_current_son_id(sidechain);
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin::get_current_son_object() {
|
||||
return my->get_current_son_object();
|
||||
const son_object peerplays_sidechain_plugin::get_current_son_object(sidechain_type sidechain) {
|
||||
return my->get_current_son_object(sidechain);
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) {
|
||||
return my->get_son_object(son_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) {
|
||||
return my->is_active_son(son_id);
|
||||
bool peerplays_sidechain_plugin::is_active_son(sidechain_type sidechain, son_id_type son_id) {
|
||||
return my->is_active_son(sidechain, son_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::is_son_deregistered(son_id_type son_id) {
|
||||
|
|
@ -786,16 +920,20 @@ fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_k
|
|||
return my->get_private_key(public_key);
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::log_son_proposal_retry(int op_type, object_id_type object_id) {
|
||||
my->log_son_proposal_retry(op_type, object_id);
|
||||
void peerplays_sidechain_plugin::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) {
|
||||
my->log_son_proposal_retry(sidechain, op_type, object_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::can_son_participate(int op_type, object_id_type object_id) {
|
||||
return my->can_son_participate(op_type, object_id);
|
||||
bool peerplays_sidechain_plugin::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) {
|
||||
return my->can_son_participate(sidechain, op_type, object_id);
|
||||
}
|
||||
|
||||
std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin::get_son_listener_log() {
|
||||
return my->get_son_listener_log();
|
||||
}
|
||||
|
||||
optional<asset> peerplays_sidechain_plugin::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
|
||||
return my->estimate_withdrawal_transaction_fee(sidechain);
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public:
|
|||
std::shared_ptr<graphene::peerplays_sidechain::peerplays_sidechain_plugin> get_plugin();
|
||||
|
||||
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
|
||||
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
|
||||
|
||||
private:
|
||||
app::application &app;
|
||||
|
|
@ -32,6 +33,10 @@ std::map<sidechain_type, std::vector<std::string>> sidechain_api_impl::get_son_l
|
|||
return get_plugin()->get_son_listener_log();
|
||||
}
|
||||
|
||||
optional<asset> sidechain_api_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
|
||||
return get_plugin()->estimate_withdrawal_transaction_fee(sidechain);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
sidechain_api::sidechain_api(graphene::app::application &_app) :
|
||||
|
|
@ -45,4 +50,8 @@ std::map<sidechain_type, std::vector<std::string>> sidechain_api::get_son_listen
|
|||
return my->get_son_listener_log();
|
||||
}
|
||||
|
||||
optional<asset> sidechain_api::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
|
||||
return my->estimate_withdrawal_transaction_fee(sidechain);
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -172,18 +172,21 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
|
|||
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
|
||||
//enable_peerplays_asset_deposits = (sed.sidechain == sidechain_type::peerplays) &&
|
||||
// (sed.sidechain_currency.compare("BTC") != 0) &&
|
||||
// (sed.sidechain_currency.compare("ETH") != 0) &&
|
||||
// (sed.sidechain_currency.compare("HBD") != 0) &&
|
||||
// (sed.sidechain_currency.compare("HIVE") != 0);
|
||||
#endif
|
||||
|
||||
bool deposit_condition = (sed.peerplays_to == gpo.parameters.son_account()) &&
|
||||
(((sed.sidechain == sidechain_type::bitcoin) && (sed.sidechain_currency.compare("BTC") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::ethereum) && (sed.sidechain_currency.compare("ETH") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HBD") == 0)) ||
|
||||
((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) ||
|
||||
enable_peerplays_asset_deposits);
|
||||
|
||||
bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain_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.eth_asset())) ||
|
||||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) ||
|
||||
(sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset())));
|
||||
|
||||
|
|
@ -191,7 +194,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
|
|||
if (deposit_condition) {
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
if (plugin.is_active_son(sidechain, son_id)) {
|
||||
|
||||
son_wallet_deposit_create_operation op;
|
||||
op.payer = plugin.get_son_object(son_id).son_account;
|
||||
|
|
@ -240,6 +243,10 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
|
|||
withdraw_currency = "BTC";
|
||||
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())) {
|
||||
withdraw_currency = "HBD";
|
||||
withdraw_currency_price = database.get<asset_object>(database.get_global_properties().parameters.hbd_asset()).options.core_exchange_rate;
|
||||
|
|
@ -253,7 +260,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
|
|||
}
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
if (plugin.is_active_son(sidechain, son_id)) {
|
||||
|
||||
son_wallet_withdraw_create_operation op;
|
||||
op.payer = plugin.get_son_object(son_id).son_account;
|
||||
|
|
@ -297,7 +304,7 @@ void sidechain_net_handler::process_proposals() {
|
|||
const auto po = idx.find(proposal_id);
|
||||
if (po != idx.end()) {
|
||||
|
||||
if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) {
|
||||
if (po->available_active_approvals.find(plugin.get_current_son_object(sidechain).son_account) != po->available_active_approvals.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -380,12 +387,12 @@ void sidechain_net_handler::process_proposals() {
|
|||
elog("==================================================");
|
||||
}
|
||||
|
||||
if (should_process && (op_idx_0 == chain::operation::tag<chain::sidechain_transaction_sign_operation>::value || plugin.can_son_participate(op_idx_0, object_id))) {
|
||||
if (should_process && (op_idx_0 == chain::operation::tag<chain::sidechain_transaction_sign_operation>::value || plugin.can_son_participate(sidechain, op_idx_0, object_id))) {
|
||||
bool should_approve = process_proposal(*po);
|
||||
if (should_approve) {
|
||||
if (approve_proposal(po->id, plugin.get_current_son_id())) {
|
||||
if (approve_proposal(po->id, plugin.get_current_son_id(sidechain))) {
|
||||
if (op_idx_0 != chain::operation::tag<chain::sidechain_transaction_sign_operation>::value) {
|
||||
plugin.log_son_proposal_retry(op_idx_0, object_id);
|
||||
plugin.log_son_proposal_retry(sidechain, op_idx_0, object_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -399,14 +406,14 @@ void sidechain_net_handler::process_active_sons_change() {
|
|||
}
|
||||
|
||||
void sidechain_net_handler::create_deposit_addresses() {
|
||||
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
return;
|
||||
}
|
||||
process_sidechain_addresses();
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_deposits() {
|
||||
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -414,7 +421,7 @@ void sidechain_net_handler::process_deposits() {
|
|||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) {
|
||||
if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) {
|
||||
if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id)) {
|
||||
return;
|
||||
}
|
||||
//Ignore the deposits which are not valid anymore, considered refunds.
|
||||
|
|
@ -436,12 +443,12 @@ void sidechain_net_handler::process_deposits() {
|
|||
wlog("Deposit not processed: ${swdo}", ("swdo", swdo));
|
||||
return;
|
||||
}
|
||||
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id);
|
||||
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_deposit_process_operation>::value, swdo.id);
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_withdrawals() {
|
||||
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -449,7 +456,7 @@ void sidechain_net_handler::process_withdrawals() {
|
|||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) {
|
||||
if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id)) {
|
||||
if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -461,7 +468,7 @@ void sidechain_net_handler::process_withdrawals() {
|
|||
wlog("Withdraw not processed: ${swwo}", ("swwo", swwo));
|
||||
return;
|
||||
}
|
||||
plugin.log_son_proposal_retry(chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id);
|
||||
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value, swwo.id);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -470,7 +477,7 @@ void sidechain_net_handler::process_sidechain_transactions() {
|
|||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id())) {
|
||||
if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id(sidechain))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -485,13 +492,13 @@ void sidechain_net_handler::process_sidechain_transactions() {
|
|||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
sidechain_transaction_sign_operation sts_op;
|
||||
sts_op.signer = plugin.get_current_son_id();
|
||||
sts_op.signer = plugin.get_current_son_id(sidechain);
|
||||
sts_op.payer = gpo.parameters.son_account();
|
||||
sts_op.sidechain_transaction_id = sto.id;
|
||||
sts_op.signature = processed_sidechain_tx;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
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);
|
||||
proposal_op.proposed_ops.emplace_back(sts_op);
|
||||
|
|
@ -500,7 +507,7 @@ void sidechain_net_handler::process_sidechain_transactions() {
|
|||
return;
|
||||
}
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_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);
|
||||
|
|
@ -531,11 +538,11 @@ void sidechain_net_handler::send_sidechain_transactions() {
|
|||
}
|
||||
|
||||
sidechain_transaction_send_operation sts_op;
|
||||
sts_op.payer = plugin.get_current_son_object().son_account;
|
||||
sts_op.payer = plugin.get_current_son_object(sidechain).son_account;
|
||||
sts_op.sidechain_transaction_id = sto.id;
|
||||
sts_op.sidechain_transaction = sidechain_transaction;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op);
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), sts_op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
|
|
@ -560,7 +567,7 @@ void sidechain_net_handler::settle_sidechain_transactions() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!plugin.can_son_participate(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id)) {
|
||||
if (!plugin.can_son_participate(sidechain, chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -577,7 +584,7 @@ void sidechain_net_handler::settle_sidechain_transactions() {
|
|||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
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);
|
||||
|
||||
|
|
@ -605,13 +612,13 @@ void sidechain_net_handler::settle_sidechain_transactions() {
|
|||
}
|
||||
}
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_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(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id);
|
||||
plugin.log_son_proposal_retry(sidechain, chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.object_id);
|
||||
} catch (fc::exception &e) {
|
||||
elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
|
|
@ -648,6 +655,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
|
|||
|
||||
bool is_tracked_asset =
|
||||
((sidechain == sidechain_type::bitcoin) && (transfer_op.amount.asset_id == gpo.parameters.btc_asset())) ||
|
||||
((sidechain == sidechain_type::ethereum) && (transfer_op.amount.asset_id == gpo.parameters.eth_asset())) ||
|
||||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hbd_asset())) ||
|
||||
((sidechain == sidechain_type::hive) && (transfer_op.amount.asset_id == gpo.parameters.hive_asset()));
|
||||
|
||||
|
|
@ -673,7 +681,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) {
|
|||
sidechain_event_data sed;
|
||||
sed.timestamp = database.head_block_time();
|
||||
sed.block_num = database.head_block_num();
|
||||
sed.sidechain = sidechain_type::peerplays;
|
||||
sed.sidechain = sidechain; //! Fixme -> sidechain_type::peerplays
|
||||
sed.sidechain_uid = sidechain_uid;
|
||||
sed.sidechain_transaction_id = trx.id().str();
|
||||
sed.sidechain_from = sidechain_from;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,854 @@
|
|||
#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::eth_blockNumber() {
|
||||
const std::string reply_str = send_post_request("eth_blockNumber", "", debug_rpc_calls);
|
||||
return retrieve_value_from_reply(reply_str, "");
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::eth_get_block_by_number(std::string block_number, bool full_block) {
|
||||
const 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) {
|
||||
const std::string params = "[{\"address\": \"" + wallet_contract_address + "\"}]";
|
||||
const 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::eth_chainId() {
|
||||
return send_post_request("eth_chainId", "", debug_rpc_calls);
|
||||
}
|
||||
|
||||
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::eth_estimateGas(const std::string ¶ms) {
|
||||
return send_post_request("eth_estimateGas", params, debug_rpc_calls);
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::get_chain_id() {
|
||||
const std::string reply_str = eth_chainId();
|
||||
const auto chain_id_string = retrieve_value_from_reply(reply_str, "");
|
||||
return chain_id_string.empty() ? "" : std::to_string(ethereum::from_hex<long long>(chain_id_string));
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::get_network_id() {
|
||||
const std::string reply_str = net_version();
|
||||
return retrieve_value_from_reply(reply_str, "");
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::get_nonce(const std::string &address) {
|
||||
const std::string reply_str = eth_get_transaction_count("[\"" + address + "\", \"latest\"]");
|
||||
const auto nonce_string = retrieve_value_from_reply(reply_str, "");
|
||||
if (!nonce_string.empty()) {
|
||||
const auto nonce_val = ethereum::from_hex<boost::multiprecision::uint256_t>(nonce_string);
|
||||
return nonce_val == 0 ? ethereum::add_0x("0") : ethereum::add_0x(ethereum::to_hex(nonce_val));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::get_gas_price() {
|
||||
const std::string reply_str = eth_gas_price();
|
||||
return retrieve_value_from_reply(reply_str, "");
|
||||
}
|
||||
|
||||
std::string ethereum_rpc_client::get_gas_limit() {
|
||||
const 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::get_estimate_gas(const std::string ¶ms) {
|
||||
const std::string reply_str = eth_estimateGas(params);
|
||||
return retrieve_value_from_reply(reply_str, "");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const 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);
|
||||
|
||||
const std::string network_id_str = rpc_client->get_network_id();
|
||||
if (network_id_str.empty()) {
|
||||
elog("No Ethereum node running at ${url}", ("url", rpc_url));
|
||||
FC_ASSERT(false);
|
||||
}
|
||||
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));
|
||||
|
||||
const auto block_number = rpc_client->eth_blockNumber();
|
||||
last_block_received = !block_number.empty() ? ethereum::from_hex<uint64_t>(block_number) : 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()) {
|
||||
|
||||
const auto &prev_sw = std::next(active_sw);
|
||||
if (prev_sw != swi.rend() && active_sw->sons.at(sidechain) == prev_sw->sons.at(sidechain))
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
const auto signers = [this, &prev_sw, &active_sw, &swi] {
|
||||
std::vector<son_info> signers;
|
||||
//! Check if we don't have any previous set of active SONs use the current one
|
||||
if (prev_sw != swi.rend()) {
|
||||
if (!prev_sw->sons.at(sidechain).empty())
|
||||
signers = prev_sw->sons.at(sidechain);
|
||||
else
|
||||
signers = active_sw->sons.at(sidechain);
|
||||
} else {
|
||||
signers = active_sw->sons.at(sidechain);
|
||||
}
|
||||
|
||||
return signers;
|
||||
}();
|
||||
|
||||
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 = signers;
|
||||
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;
|
||||
}
|
||||
|
||||
optional<asset> sidechain_net_handler_ethereum::estimate_withdrawal_transaction_fee() const {
|
||||
const auto &gpo = database.get_global_properties();
|
||||
if (gpo.active_sons.at(sidechain).empty()) {
|
||||
wlog("No active sons for sidechain: ${sidechain}", ("sidechain", sidechain));
|
||||
return optional<asset>{};
|
||||
}
|
||||
|
||||
const auto &active_son = gpo.active_sons.at(sidechain).at(0);
|
||||
const auto &s_idx = database.get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto son = s_idx.find(active_son.son_id);
|
||||
if (son == s_idx.end()) {
|
||||
wlog("Can't find son for id: ${son_id}", ("son_id", active_son.son_id));
|
||||
return optional<asset>{};
|
||||
}
|
||||
|
||||
if (!son->sidechain_public_keys.contains(sidechain)) {
|
||||
wlog("No public keys for current son: ${account_id}", ("account_id", son->son_account));
|
||||
return optional<asset>{};
|
||||
}
|
||||
|
||||
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
auto asset_itr = assets_by_symbol.find("ETH");
|
||||
if (asset_itr == assets_by_symbol.end()) {
|
||||
wlog("Could not find asset matching ETH");
|
||||
return optional<asset>{};
|
||||
}
|
||||
|
||||
const auto &public_key = son->sidechain_public_keys.at(sidechain);
|
||||
const ethereum::withdrawal_encoder encoder;
|
||||
const auto data = encoder.encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string());
|
||||
const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]";
|
||||
|
||||
const auto estimate_gas = ethereum::from_hex<int64_t>(rpc_client->get_estimate_gas(params));
|
||||
const auto gas_price = ethereum::from_hex<int64_t>(rpc_client->get_gas_price());
|
||||
const auto eth_gas_fee = double(estimate_gas * gas_price) / double{1000000000000000000};
|
||||
return asset_itr->amount_from_string(std::to_string(eth_gas_fee));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
const auto reply = rpc_client->eth_blockNumber();
|
||||
//std::string reply = rpc_client->eth_get_logs(wallet_contract_address);
|
||||
|
||||
if (!reply.empty()) {
|
||||
uint64_t head_block_number = ethereum::from_hex<uint64_t>(reply);
|
||||
|
||||
if (head_block_number != last_block_received) {
|
||||
//! Check that current block number is greater than last one
|
||||
if (head_block_number < last_block_received) {
|
||||
wlog("Head block ${head_block_number} is greater than last received block ${last_block_received}", ("head_block_number", head_block_number)("last_block_received", last_block_received));
|
||||
return;
|
||||
}
|
||||
|
||||
//! Send event data for all blocks that passed
|
||||
for (uint64_t i = last_block_received + 1; i <= head_block_number; ++i) {
|
||||
const std::string block_number = ethereum::add_0x(ethereum::to_hex(i, false));
|
||||
handle_event(block_number);
|
||||
}
|
||||
|
||||
last_block_received = head_block_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_handler_ethereum::handle_event(const std::string &block_number) {
|
||||
const std::string block = rpc_client->eth_get_block_by_number(block_number, true);
|
||||
if (block != "") {
|
||||
add_to_son_listener_log("BLOCK : " + block_number);
|
||||
std::stringstream ss(block);
|
||||
boost::property_tree::ptree block_json;
|
||||
boost::property_tree::read_json(ss, block_json);
|
||||
|
||||
if (block_json.get<string>("result") == "null") {
|
||||
wlog("No data for block ${block_number}", ("block_number", block_number));
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#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_ethereum.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler_factory::sidechain_net_handler_factory(peerplays_sidechain_plugin &_plugin) :
|
||||
plugin(_plugin) {
|
||||
}
|
||||
|
||||
std::unique_ptr<sidechain_net_handler> sidechain_net_handler_factory::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) const {
|
||||
switch (sidechain) {
|
||||
case sidechain_type::bitcoin: {
|
||||
return std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_bitcoin(plugin, options));
|
||||
}
|
||||
case sidechain_type::hive: {
|
||||
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: {
|
||||
return std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_peerplays(plugin, options));
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -28,25 +28,23 @@
|
|||
#include <graphene/peerplays_sidechain/hive/transaction.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
hive_node_rpc_client::hive_node_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) :
|
||||
hive_rpc_client::hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) :
|
||||
rpc_client(url, user_name, password, debug_rpc_calls) {
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::account_history_api_get_transaction(std::string transaction_id) {
|
||||
std::string params = "{ \"id\": \"" + transaction_id + "\" }";
|
||||
std::string hive_rpc_client::account_history_api_get_transaction(std::string transaction_id) {
|
||||
const std::string params = "{ \"id\": \"" + transaction_id + "\" }";
|
||||
return send_post_request("account_history_api.get_transaction", params, debug_rpc_calls);
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::block_api_get_block(uint32_t block_number) {
|
||||
std::string params = "{ \"block_num\": " + std::to_string(block_number) + " }";
|
||||
std::string hive_rpc_client::block_api_get_block(uint32_t block_number) {
|
||||
const std::string params = "{ \"block_num\": " + std::to_string(block_number) + " }";
|
||||
return send_post_request("block_api.get_block", params, debug_rpc_calls);
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::condenser_api_get_accounts(std::vector<std::string> accounts) {
|
||||
std::string hive_rpc_client::condenser_api_get_accounts(std::vector<std::string> accounts) {
|
||||
std::string params = "";
|
||||
for (auto account : accounts) {
|
||||
if (!params.empty()) {
|
||||
|
|
@ -58,59 +56,59 @@ 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);
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::condenser_api_get_config() {
|
||||
std::string params = "[]";
|
||||
std::string hive_rpc_client::condenser_api_get_config() {
|
||||
static const std::string params = "[]";
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::network_broadcast_api_broadcast_transaction(std::string htrx) {
|
||||
std::string params = "{ \"trx\": " + htrx + ", \"max_block_age\": -1 }";
|
||||
std::string hive_rpc_client::network_broadcast_api_broadcast_transaction(std::string htrx) {
|
||||
const std::string params = "{ \"trx\": " + htrx + ", \"max_block_age\": -1 }";
|
||||
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;
|
||||
accounts.push_back(account);
|
||||
std::string reply_str = condenser_api_get_accounts(accounts);
|
||||
return retrieve_array_value_from_reply(reply_str, "", 0);
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::get_account_memo_key(std::string account) {
|
||||
std::string hive_rpc_client::get_account_memo_key(std::string account) {
|
||||
std::string reply_str = get_account(account);
|
||||
reply_str = "{\"result\":" + reply_str + "}";
|
||||
return retrieve_value_from_reply(reply_str, "memo_key");
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::get_chain_id() {
|
||||
std::string reply_str = database_api_get_version();
|
||||
std::string hive_rpc_client::get_chain_id() {
|
||||
const std::string reply_str = database_api_get_version();
|
||||
return retrieve_value_from_reply(reply_str, "chain_id");
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::get_head_block_id() {
|
||||
std::string reply_str = database_api_get_dynamic_global_properties();
|
||||
std::string hive_rpc_client::get_head_block_id() {
|
||||
const std::string reply_str = database_api_get_dynamic_global_properties();
|
||||
return retrieve_value_from_reply(reply_str, "head_block_id");
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::get_head_block_time() {
|
||||
std::string reply_str = database_api_get_dynamic_global_properties();
|
||||
std::string hive_rpc_client::get_head_block_time() {
|
||||
const std::string reply_str = database_api_get_dynamic_global_properties();
|
||||
return retrieve_value_from_reply(reply_str, "time");
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::get_is_test_net() {
|
||||
std::string reply_str = condenser_api_get_config();
|
||||
std::string hive_rpc_client::get_is_test_net() {
|
||||
const std::string reply_str = condenser_api_get_config();
|
||||
return retrieve_value_from_reply(reply_str, "IS_TEST_NET");
|
||||
}
|
||||
|
||||
std::string hive_node_rpc_client::get_last_irreversible_block_num() {
|
||||
std::string reply_str = database_api_get_dynamic_global_properties();
|
||||
std::string hive_rpc_client::get_last_irreversible_block_num() {
|
||||
const std::string reply_str = database_api_get_dynamic_global_properties();
|
||||
return retrieve_value_from_reply(reply_str, "last_irreversible_block_num");
|
||||
}
|
||||
|
||||
|
|
@ -122,18 +120,20 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi
|
|||
debug_rpc_calls = options.at("debug-rpc-calls").as<bool>();
|
||||
}
|
||||
|
||||
node_rpc_url = options.at("hive-node-rpc-url").as<std::string>();
|
||||
if (options.count("hive-node-rpc-user")) {
|
||||
node_rpc_user = options.at("hive-node-rpc-user").as<std::string>();
|
||||
rpc_url = options.at("hive-node-rpc-url").as<std::string>();
|
||||
if (options.count("hive-rpc-user")) {
|
||||
rpc_user = options.at("hive-rpc-user").as<std::string>();
|
||||
} else {
|
||||
node_rpc_user = "";
|
||||
rpc_user = "";
|
||||
}
|
||||
if (options.count("hive-node-rpc-password")) {
|
||||
node_rpc_password = options.at("hive-node-rpc-password").as<std::string>();
|
||||
if (options.count("hive-rpc-password")) {
|
||||
rpc_password = options.at("hive-rpc-password").as<std::string>();
|
||||
} else {
|
||||
node_rpc_password = "";
|
||||
rpc_password = "";
|
||||
}
|
||||
|
||||
wallet_account_name = options.at("hive-wallet-account-name").as<std::string>();
|
||||
|
||||
if (options.count("hive-private-key")) {
|
||||
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) {
|
||||
|
|
@ -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();
|
||||
const std::string chain_id_str = rpc_client->get_chain_id();
|
||||
if (chain_id_str.empty()) {
|
||||
elog("No Hive node running at ${url}", ("url", node_rpc_url));
|
||||
elog("No Hive node running at ${url}", ("url", rpc_url));
|
||||
FC_ASSERT(false);
|
||||
}
|
||||
chain_id = chain_id_type(chain_id_str);
|
||||
|
||||
std::string is_test_net = node_rpc_client->get_is_test_net();
|
||||
const std::string is_test_net = rpc_client->get_is_test_net();
|
||||
network_type = is_test_net.compare("true") == 0 ? hive::network::testnet : hive::network::mainnet;
|
||||
if (network_type == hive::network::mainnet) {
|
||||
ilog("Running on Hive mainnet, chain id ${chain_id_str}", ("chain_id_str", chain_id_str));
|
||||
|
|
@ -180,7 +180,7 @@ sidechain_net_handler_hive::~sidechain_net_handler_hive() {
|
|||
}
|
||||
|
||||
bool sidechain_net_handler_hive::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()));
|
||||
//ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain)));
|
||||
|
||||
bool should_approve = false;
|
||||
|
||||
|
|
@ -213,8 +213,8 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
const auto swo = idx.find(swo_id);
|
||||
if (swo != idx.end()) {
|
||||
|
||||
auto active_sons = gpo.active_sons;
|
||||
vector<son_info> wallet_sons = swo->sons;
|
||||
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());
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
}
|
||||
|
||||
if (son_sets_equal) {
|
||||
address_ok = (op_obj_idx_0.get<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) {
|
||||
|
|
@ -251,17 +251,17 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
uint32_t total_weight = 0;
|
||||
for (const auto &wallet_son : wallet_sons) {
|
||||
total_weight = total_weight + wallet_son.weight;
|
||||
account_auths[wallet_son.sidechain_public_keys.at(sidechain)] = 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");
|
||||
const std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name);
|
||||
|
||||
hive::authority active;
|
||||
active.weight_threshold = total_weight * 2 / 3 + 1;
|
||||
active.account_auths = account_auths;
|
||||
|
||||
hive::account_update_operation auo;
|
||||
auo.account = "son-account";
|
||||
auo.account = wallet_account_name;
|
||||
auo.active = active;
|
||||
auo.memo_key = op_trx.operations[0].get<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_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);
|
||||
const std::string tx_str = rpc_client->account_history_api_get_transaction(swdo_txid);
|
||||
if (tx_str != "") {
|
||||
|
||||
std::stringstream ss_tx(tx_str);
|
||||
|
|
@ -408,7 +408,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) {
|
|||
}
|
||||
|
||||
hive::transfer_operation t_op;
|
||||
t_op.from = "son-account";
|
||||
t_op.from = wallet_account_name;
|
||||
t_op.to = swwo->withdraw_address;
|
||||
t_op.amount.amount = swwo->withdraw_amount;
|
||||
t_op.amount.symbol = symbol;
|
||||
|
|
@ -478,6 +478,10 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
const auto &active_sw = swi.rbegin();
|
||||
if (active_sw != swi.rend()) {
|
||||
|
||||
const auto &prev_sw = std::next(active_sw);
|
||||
if (prev_sw != swi.rend() && active_sw->sons.at(sidechain) == prev_sw->sons.at(sidechain))
|
||||
return;
|
||||
|
||||
if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) ||
|
||||
(active_sw->addresses.at(sidechain).empty())) {
|
||||
|
||||
|
|
@ -487,15 +491,15 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
auto active_sons = gpo.active_sons;
|
||||
const auto &active_sons = gpo.active_sons.at(sidechain);
|
||||
fc::flat_map<std::string, uint16_t> account_auths;
|
||||
uint32_t total_weight = 0;
|
||||
for (const auto &active_son : active_sons) {
|
||||
total_weight = total_weight + active_son.weight;
|
||||
account_auths[active_son.sidechain_public_keys.at(sidechain)] = 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");
|
||||
const std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name);
|
||||
|
||||
if (memo_key.empty()) {
|
||||
return;
|
||||
|
|
@ -506,14 +510,14 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
active.account_auths = account_auths;
|
||||
|
||||
hive::account_update_operation auo;
|
||||
auo.account = "son-account";
|
||||
auo.account = wallet_account_name;
|
||||
auo.active = active;
|
||||
auo.memo_key = hive::public_key_type(memo_key);
|
||||
|
||||
std::string block_id_str = node_rpc_client->get_head_block_id();
|
||||
const std::string block_id_str = rpc_client->get_head_block_id();
|
||||
hive::block_id_type head_block_id(block_id_str);
|
||||
|
||||
std::string head_block_time_str = node_rpc_client->get_head_block_time();
|
||||
const std::string head_block_time_str = rpc_client->get_head_block_time();
|
||||
time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str);
|
||||
|
||||
hive::signed_transaction htrx;
|
||||
|
|
@ -530,7 +534,7 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
}
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
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);
|
||||
|
||||
|
|
@ -538,7 +542,7 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
swu_op.payer = gpo.parameters.son_account();
|
||||
swu_op.son_wallet_id = active_sw->id;
|
||||
swu_op.sidechain = sidechain;
|
||||
swu_op.address = "son-account";
|
||||
swu_op.address = wallet_account_name;
|
||||
|
||||
proposal_op.proposed_ops.emplace_back(swu_op);
|
||||
|
||||
|
|
@ -547,11 +551,11 @@ void sidechain_net_handler_hive::process_primary_wallet() {
|
|||
stc_op.object_id = active_sw->id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons;
|
||||
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()), proposal_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);
|
||||
|
|
@ -575,7 +579,7 @@ void sidechain_net_handler_hive::process_sidechain_addresses() {
|
|||
if (sao.expires == time_point_sec::maximum()) {
|
||||
if (sao.deposit_address == "") {
|
||||
sidechain_address_update_operation op;
|
||||
op.payer = plugin.get_current_son_object().son_account;
|
||||
op.payer = plugin.get_current_son_object(sidechain).son_account;
|
||||
op.sidechain_address_id = sao.id;
|
||||
op.sidechain_address_account = sao.sidechain_address_account;
|
||||
op.sidechain = sao.sidechain;
|
||||
|
|
@ -585,7 +589,7 @@ void sidechain_net_handler_hive::process_sidechain_addresses() {
|
|||
op.withdraw_public_key = sao.withdraw_public_key;
|
||||
op.withdraw_address = sao.withdraw_address;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op);
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
|
|
@ -617,7 +621,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object
|
|||
}
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
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);
|
||||
|
||||
|
|
@ -633,7 +637,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object
|
|||
ai_op.issue_to_account = swdo.peerplays_from;
|
||||
proposal_op.proposed_ops.emplace_back(ai_op);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
|
||||
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);
|
||||
|
|
@ -662,16 +666,16 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob
|
|||
}
|
||||
|
||||
hive::transfer_operation t_op;
|
||||
t_op.from = "son-account";
|
||||
t_op.from = wallet_account_name;
|
||||
t_op.to = swwo.withdraw_address;
|
||||
t_op.amount.amount = swwo.withdraw_amount;
|
||||
t_op.amount.symbol = symbol;
|
||||
t_op.memo = "";
|
||||
|
||||
std::string block_id_str = node_rpc_client->get_head_block_id();
|
||||
const std::string block_id_str = rpc_client->get_head_block_id();
|
||||
hive::block_id_type head_block_id(block_id_str);
|
||||
|
||||
std::string head_block_time_str = node_rpc_client->get_head_block_time();
|
||||
const std::string head_block_time_str = rpc_client->get_head_block_time();
|
||||
time_point head_block_time = fc::time_point_sec::from_iso_string(head_block_time_str);
|
||||
|
||||
hive::signed_transaction htrx;
|
||||
|
|
@ -690,7 +694,7 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob
|
|||
//=====
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
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);
|
||||
|
||||
|
|
@ -704,10 +708,10 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob
|
|||
stc_op.object_id = swwo.id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons;
|
||||
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()), proposal_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);
|
||||
|
|
@ -727,10 +731,10 @@ std::string sidechain_net_handler_hive::process_sidechain_transaction(const side
|
|||
hive::signed_transaction htrx;
|
||||
fc::raw::unpack(ss_trx, htrx, 1000);
|
||||
|
||||
std::string chain_id_str = node_rpc_client->get_chain_id();
|
||||
const std::string chain_id_str = rpc_client->get_chain_id();
|
||||
const hive::chain_id_type chain_id(chain_id_str);
|
||||
|
||||
fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain)));
|
||||
fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain)));
|
||||
signature_type st = htrx.sign(*privkey, chain_id);
|
||||
|
||||
std::stringstream ss_st;
|
||||
|
|
@ -755,7 +759,7 @@ std::string sidechain_net_handler_hive::send_sidechain_transaction(const sidecha
|
|||
}
|
||||
|
||||
std::string params = fc::json::to_string(htrx);
|
||||
node_rpc_client->network_broadcast_api_broadcast_transaction(params);
|
||||
rpc_client->network_broadcast_api_broadcast_transaction(params);
|
||||
|
||||
return htrx.id().str();
|
||||
}
|
||||
|
|
@ -770,7 +774,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string tx_str = node_rpc_client->account_history_api_get_transaction(sto.sidechain_transaction);
|
||||
const std::string tx_str = rpc_client->account_history_api_get_transaction(sto.sidechain_transaction);
|
||||
if (tx_str != "") {
|
||||
|
||||
std::stringstream ss_tx(tx_str);
|
||||
|
|
@ -781,7 +785,7 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr
|
|||
|
||||
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 last_irreversible_block = std::stoul(node_rpc_client->get_last_irreversible_block_num());
|
||||
const uint32_t last_irreversible_block = std::stoul(rpc_client->get_last_irreversible_block_num());
|
||||
|
||||
//std::string tx_address = addr.get_address();
|
||||
//int64_t tx_amount = -1;
|
||||
|
|
@ -802,6 +806,11 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr
|
|||
return false;
|
||||
}
|
||||
|
||||
optional<asset> sidechain_net_handler_hive::estimate_withdrawal_transaction_fee() const {
|
||||
wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain));
|
||||
return optional<asset>{};
|
||||
}
|
||||
|
||||
void sidechain_net_handler_hive::schedule_hive_listener() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next = 1000;
|
||||
|
|
@ -817,7 +826,7 @@ void sidechain_net_handler_hive::schedule_hive_listener() {
|
|||
void sidechain_net_handler_hive::hive_listener_loop() {
|
||||
schedule_hive_listener();
|
||||
|
||||
std::string reply = node_rpc_client->database_api_get_dynamic_global_properties();
|
||||
const std::string reply = rpc_client->database_api_get_dynamic_global_properties();
|
||||
if (!reply.empty()) {
|
||||
std::stringstream ss(reply);
|
||||
boost::property_tree::ptree json;
|
||||
|
|
@ -832,7 +841,7 @@ void sidechain_net_handler_hive::hive_listener_loop() {
|
|||
}
|
||||
}
|
||||
|
||||
//std::string reply = node_rpc_client->get_last_irreversible_block_num();
|
||||
//std::string reply = rpc_client->get_last_irreversible_block_num();
|
||||
//if (!reply.empty()) {
|
||||
// uint64_t last_irreversible_block = std::stoul(reply);
|
||||
// if (last_irreversible_block != last_block_received) {
|
||||
|
|
@ -844,7 +853,7 @@ void sidechain_net_handler_hive::hive_listener_loop() {
|
|||
}
|
||||
|
||||
void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
|
||||
std::string block = node_rpc_client->block_api_get_block(std::atoll(event_data.c_str()));
|
||||
const std::string block = rpc_client->block_api_get_block(std::atoll(event_data.c_str()));
|
||||
if (block != "") {
|
||||
add_to_son_listener_log("BLOCK : " + event_data);
|
||||
std::stringstream ss(block);
|
||||
|
|
@ -869,7 +878,7 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) {
|
|||
std::string from = op_value.get<std::string>("from");
|
||||
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");
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() {
|
|||
|
||||
bool sidechain_net_handler_peerplays::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()));
|
||||
ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain)));
|
||||
|
||||
bool should_approve = false;
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() {
|
|||
if (sao.expires == time_point_sec::maximum()) {
|
||||
if (sao.deposit_address == "") {
|
||||
sidechain_address_update_operation op;
|
||||
op.payer = plugin.get_current_son_object().son_account;
|
||||
op.payer = plugin.get_current_son_object(sidechain).son_account;
|
||||
op.sidechain_address_id = sao.id;
|
||||
op.sidechain_address_account = sao.sidechain_address_account;
|
||||
op.sidechain = sao.sidechain;
|
||||
|
|
@ -150,7 +150,7 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() {
|
|||
op.withdraw_public_key = sao.withdraw_public_key;
|
||||
op.withdraw_address = sao.withdraw_address;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op);
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
|
|
@ -197,15 +197,15 @@ bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_o
|
|||
stc_op.object_id = swdo.id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons;
|
||||
stc_op.signers = gpo.active_sons.at(sidechain);
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account;
|
||||
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||
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);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_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);
|
||||
|
|
@ -230,7 +230,7 @@ std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const
|
|||
signed_transaction trx;
|
||||
fc::raw::unpack(ss_trx, trx, 1000);
|
||||
|
||||
fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain)));
|
||||
fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain)));
|
||||
signature_type st = trx.sign(*privkey, database.get_chain_id());
|
||||
|
||||
std::stringstream ss_st;
|
||||
|
|
@ -290,4 +290,9 @@ bool sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidecha
|
|||
return true;
|
||||
}
|
||||
|
||||
optional<asset> sidechain_net_handler_peerplays::estimate_withdrawal_transaction_fee() const {
|
||||
wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain));
|
||||
return optional<asset>{};
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin) :
|
||||
plugin(_plugin),
|
||||
database(_plugin.database()) {
|
||||
|
||||
//database.applied_block.connect([&](const signed_block &b) {
|
||||
// on_applied_block(b);
|
||||
//});
|
||||
}
|
||||
|
||||
sidechain_net_manager::~sidechain_net_manager() {
|
||||
}
|
||||
|
||||
bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) {
|
||||
|
||||
bool ret_val = false;
|
||||
|
||||
switch (sidechain) {
|
||||
case sidechain_type::bitcoin: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_bitcoin(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
case sidechain_type::hive: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_hive(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
case sidechain_type::peerplays: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_peerplays(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_proposals() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_proposals();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_active_sons_change() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_active_sons_change();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::create_deposit_addresses() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->create_deposit_addresses();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_deposits() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_deposits();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_withdrawals() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_withdrawals();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::send_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->send_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::settle_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->settle_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
std::map<sidechain_type, std::vector<std::string>> sidechain_net_manager::get_son_listener_log() {
|
||||
std::map<sidechain_type, std::vector<std::string>> result;
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
result[net_handlers.at(i)->get_sidechain()] = net_handlers.at(i)->get_son_listener_log();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void sidechain_net_manager::on_applied_block(const signed_block &b) {
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue