From a5a78e2e1ae742d0c7df4183f4af036ee0c1a782 Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Tue, 3 Jul 2018 13:14:07 -0400 Subject: [PATCH 1/4] Add an RPC endpoint to fetch the scheduled transactions present in the head chain state --- plugins/chain_api_plugin/chain_api_plugin.cpp | 1 + plugins/chain_plugin/chain_plugin.cpp | 75 +++++++++++++++++++ .../eosio/chain_plugin/chain_plugin.hpp | 16 ++++ 3 files changed, 92 insertions(+) diff --git a/plugins/chain_api_plugin/chain_api_plugin.cpp b/plugins/chain_api_plugin/chain_api_plugin.cpp index de8770f5ab7..7b8954cf6f6 100644 --- a/plugins/chain_api_plugin/chain_api_plugin.cpp +++ b/plugins/chain_api_plugin/chain_api_plugin.cpp @@ -88,6 +88,7 @@ void chain_api_plugin::plugin_startup() { CHAIN_RO_CALL(get_currency_balance, 200), CHAIN_RO_CALL(get_currency_stats, 200), CHAIN_RO_CALL(get_producers, 200), + CHAIN_RO_CALL(get_scheduled_transactions, 200), CHAIN_RO_CALL(abi_json_to_bin, 200), CHAIN_RO_CALL(abi_bin_to_json, 200), CHAIN_RO_CALL(get_required_keys, 200), diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index ba4e4ab76bb..220b1b104eb 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -863,6 +864,80 @@ auto make_resolver(const Api *api) { return resolver_factory::make(api); } + +read_only::get_scheduled_transactions_result +read_only::get_scheduled_transactions( const read_only::get_scheduled_transactions_params& p ) const { + const auto& d = db.db(); + + const auto& idx_by_delay = d.get_index(); + auto itr = ([&](){ + if (!p.lower_bound.empty()) { + try { + auto when = time_point::from_iso_string( p.lower_bound ); + return idx_by_delay.lower_bound(boost::make_tuple(when)); + } catch (...) { + try { + auto txid = transaction_id_type(p.lower_bound); + const auto& by_txid = d.get_index(); + auto itr = by_txid.find( txid ); + if (itr == by_txid.end()) { + EOS_THROW(transaction_exception, "Unknown Transaction ID: ${txid}", ("txid", txid)); + } + + return d.get_index().indices().project(itr); + + } catch (...) { + return idx_by_delay.end(); + } + } + } else { + return idx_by_delay.begin(); + } + })(); + + read_only::get_scheduled_transactions_result result; + result.transactions.reserve(p.limit); + + auto resolver = make_resolver(this); + + uint32_t remaining = p.limit; + while (itr != idx_by_delay.end() && remaining > 0) { + auto row = fc::mutable_variant_object() + ("trx_id", itr->trx_id) + ("sender", itr->sender) + ("sender_id", itr->sender_id) + ("payer", itr->payer) + ("delay_until", itr->delay_until) + ("expiration", itr->expiration) + ("published", itr->published) + ; + + if (p.json) { + fc::variant pretty_transaction; + + transaction trx; + fc::datastream ds( itr->packed_trx.data(), itr->packed_trx.size() ); + fc::raw::unpack(ds,trx); + + abi_serializer::to_variant(trx, pretty_transaction, resolver); + row("transaction", pretty_transaction); + } else { + auto packed_transaction = bytes(itr->packed_trx.begin(), itr->packed_trx.end()); + row("transaction", packed_transaction); + } + + result.transactions.emplace_back(std::move(row)); + ++itr; + remaining--; + } + + if (itr != idx_by_delay.end()) { + result.more = string(itr->trx_id); + } + + return result; +} + fc::variant read_only::get_block(const read_only::get_block_params& params) const { signed_block_ptr block; EOS_ASSERT(!params.block_num_or_id.empty() && params.block_num_or_id.size() <= 64, chain::block_id_type_exception, "Invalid Block number or ID, must be greater than 0 and less than 64 characters" ); diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index c74bfa0ddac..1553afed7cd 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -258,6 +258,19 @@ class read_only { get_producers_result get_producers( const get_producers_params& params )const; + struct get_scheduled_transactions_params { + bool json = false; + string lower_bound; /// timestamp OR transaction ID + uint32_t limit = 50; + }; + + struct get_scheduled_transactions_result { + fc::variants transactions; + string more; ///< fill lower_bound with this to fetch next set of transactions + }; + + get_scheduled_transactions_result get_scheduled_transactions( const get_scheduled_transactions_params& params ) const; + static void copy_inline_row(const chain::key_value_object& obj, vector& data) { data.resize( obj.value.size() ); memcpy( data.data(), obj.value.data(), obj.value.size() ); @@ -422,6 +435,9 @@ FC_REFLECT( eosio::chain_apis::read_only::get_currency_stats_result, (supply)(ma FC_REFLECT( eosio::chain_apis::read_only::get_producers_params, (json)(lower_bound)(limit) ) FC_REFLECT( eosio::chain_apis::read_only::get_producers_result, (rows)(total_producer_vote_weight)(more) ); +FC_REFLECT( eosio::chain_apis::read_only::get_scheduled_transactions_params, (json)(lower_bound)(limit) ) +FC_REFLECT( eosio::chain_apis::read_only::get_scheduled_transactions_result, (transactions)(more) ); + FC_REFLECT( eosio::chain_apis::read_only::get_account_results, (account_name)(head_block_num)(head_block_time)(privileged)(last_code_update)(created) (core_liquid_balance)(ram_quota)(net_weight)(cpu_weight)(net_limit)(cpu_limit)(ram_usage)(permissions) From feb62f216d82e26cf504c72afa2445a6f9851b5b Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Sat, 14 Jul 2018 10:58:34 -0400 Subject: [PATCH 2/4] add abi_serializer_max_time appropriately --- plugins/chain_plugin/chain_plugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index ce8c0fd31e4..697f76288ba 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1114,7 +1114,7 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio read_only::get_scheduled_transactions_result result; result.transactions.reserve(p.limit); - auto resolver = make_resolver(this); + auto resolver = make_resolver(this, abi_serializer_max_time); uint32_t remaining = p.limit; while (itr != idx_by_delay.end() && remaining > 0) { @@ -1135,7 +1135,7 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio fc::datastream ds( itr->packed_trx.data(), itr->packed_trx.size() ); fc::raw::unpack(ds,trx); - abi_serializer::to_variant(trx, pretty_transaction, resolver); + abi_serializer::to_variant(trx, pretty_transaction, resolver, abi_serializer_max_time); row("transaction", pretty_transaction); } else { auto packed_transaction = bytes(itr->packed_trx.begin(), itr->packed_trx.end()); From 284f7aa0c0f0a07cfb9c51c32a819922785cba7d Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Sat, 14 Jul 2018 11:02:27 -0400 Subject: [PATCH 3/4] remove reserve call that depended on user input --- plugins/chain_plugin/chain_plugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 697f76288ba..176ccd3048e 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1112,7 +1112,6 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio })(); read_only::get_scheduled_transactions_result result; - result.transactions.reserve(p.limit); auto resolver = make_resolver(this, abi_serializer_max_time); From 257badfced872bac4bd56eb9fdfdf90c2fea0fa6 Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Sat, 14 Jul 2018 11:56:42 -0400 Subject: [PATCH 4/4] limit the time spent traversing scheduled transactions --- plugins/chain_plugin/chain_plugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 176ccd3048e..ae265ee49d3 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1116,7 +1116,8 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio auto resolver = make_resolver(this, abi_serializer_max_time); uint32_t remaining = p.limit; - while (itr != idx_by_delay.end() && remaining > 0) { + auto time_limit = fc::time_point::now() + fc::microseconds(1000 * 10); /// 10ms max time + while (itr != idx_by_delay.end() && remaining > 0 && time_limit > fc::time_point::now()) { auto row = fc::mutable_variant_object() ("trx_id", itr->trx_id) ("sender", itr->sender)