diff --git a/libraries/appbase b/libraries/appbase index 31a02293a303782a97963a2e67815703f1123914..3bfd8b5fdf8df9774f116fb2014770053df73a35 160000 --- a/libraries/appbase +++ b/libraries/appbase @@ -1 +1 @@ -Subproject commit 31a02293a303782a97963a2e67815703f1123914 +Subproject commit 3bfd8b5fdf8df9774f116fb2014770053df73a35 diff --git a/libraries/chain/include/eosio/chain/trace.hpp b/libraries/chain/include/eosio/chain/trace.hpp index 06c13d6526de4956aae623cf215fb4a1db47243a..9a711297d08e0cce08a4b471e226c3353cb6a5b3 100644 --- a/libraries/chain/include/eosio/chain/trace.hpp +++ b/libraries/chain/include/eosio/chain/trace.hpp @@ -10,9 +10,9 @@ namespace eosio { namespace chain { - struct action_trace { - action_trace( const action_receipt& r ):receipt(r){} - action_trace(){} + struct base_action_trace { + base_action_trace( const action_receipt& r ):receipt(r){} + base_action_trace(){} action_receipt receipt; action act; @@ -21,6 +21,12 @@ namespace eosio { namespace chain { string console; uint64_t total_inline_cpu_usage = 0; /// total of inline_traces[x].cpu_usage + cpu_usage + + }; + + struct action_trace : public base_action_trace { + using base_action_trace::base_action_trace; + vector inline_traces; }; @@ -50,7 +56,11 @@ namespace eosio { namespace chain { } } /// namespace eosio::chain -FC_REFLECT( eosio::chain::action_trace, - (receipt)(act)(elapsed)(cpu_usage)(console)(total_inline_cpu_usage)(inline_traces) ) +FC_REFLECT( eosio::chain::base_action_trace, + (receipt)(act)(elapsed)(cpu_usage)(console)(total_inline_cpu_usage) ) + +FC_REFLECT_DERIVED( eosio::chain::action_trace, + (eosio::chain::base_action_trace), (inline_traces) ) + FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(cpu_usage)(scheduled)(action_traces)(soft_except)(hard_except) ) FC_REFLECT( eosio::chain::block_trace, (elapsed)(cpu_usage)(trx_traces) ) diff --git a/libraries/chain/include/eosio/chain/types.hpp b/libraries/chain/include/eosio/chain/types.hpp index e97f734d613693aa63e1ed6c5ab01e514a60988a..8325fcffcce814ee2a25853f6c9ba768aa1b2920 100644 --- a/libraries/chain/include/eosio/chain/types.hpp +++ b/libraries/chain/include/eosio/chain/types.hpp @@ -147,6 +147,8 @@ namespace eosio { namespace chain { resource_usage_object_type, resource_limits_state_object_type, resource_limits_config_object_type, + account_history_object_type, + action_history_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -205,6 +207,8 @@ FC_REFLECT_ENUM(eosio::chain::object_type, (resource_usage_object_type) (resource_limits_state_object_type) (resource_limits_config_object_type) + (account_history_object_type) + (action_history_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT( eosio::chain::void_t, ) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 812aca11b11b142e4fcd10e74b3bc0dc1e8a6fcb..96e666696e04275ea6388a0644948c0850485c5d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(chain_plugin) add_subdirectory(chain_api_plugin) add_subdirectory(producer_plugin) add_subdirectory(history_plugin) +add_subdirectory(history_api_plugin) #add_subdirectory(account_history_api_plugin) add_subdirectory(wallet_plugin) diff --git a/plugins/history_api_plugin/CMakeLists.txt b/plugins/history_api_plugin/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1858af33b0254cfad0fbc1466f295bae92d900d5 --- /dev/null +++ b/plugins/history_api_plugin/CMakeLists.txt @@ -0,0 +1,7 @@ +file( GLOB HEADERS "include/eosio/history_api_plugin/*.hpp" ) +add_library( history_api_plugin + history_api_plugin.cpp + ${HEADERS} ) + +target_link_libraries( history_api_plugin history_plugin chain_plugin http_plugin appbase ) +target_include_directories( history_api_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/plugins/history_api_plugin/history_api_plugin.cpp b/plugins/history_api_plugin/history_api_plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc0d621c4f40b2be4818b486da96d9cc2c836e58 --- /dev/null +++ b/plugins/history_api_plugin/history_api_plugin.cpp @@ -0,0 +1,58 @@ +/** + * @file + * @copyright defined in eos/LICENSE.txt + */ +#include +#include + +#include + +namespace eosio { + +using namespace eosio; + +static appbase::abstract_plugin& _history_api_plugin = app().register_plugin(); + +history_api_plugin::history_api_plugin(){} +history_api_plugin::~history_api_plugin(){} + +void history_api_plugin::set_program_options(options_description&, options_description&) {} +void history_api_plugin::plugin_initialize(const variables_map&) {} + +#define CALL(api_name, api_handle, api_namespace, call_name) \ +{std::string("/v1/" #api_name "/" #call_name), \ + [this, api_handle](string, string body, url_response_callback cb) mutable { \ + try { \ + if (body.empty()) body = "{}"; \ + auto result = api_handle.call_name(fc::json::from_string(body).as()); \ + cb(200, fc::json::to_string(result)); \ + } catch (fc::eof_exception& e) { \ + error_results results{400, "Bad Request", e}; \ + cb(400, fc::json::to_string(results)); \ + elog("Unable to parse arguments: ${args}", ("args", body)); \ + } catch (fc::exception& e) { \ + error_results results{500, "Internal Service Error", e}; \ + cb(500, fc::json::to_string(results)); \ + elog("Exception encountered while processing ${call}: ${e}", ("call", #api_name "." #call_name)("e", e)); \ + } \ + }} + +#define CHAIN_RO_CALL(call_name) CALL(history, ro_api, history_apis::read_only, call_name) +//#define CHAIN_RW_CALL(call_name) CALL(history, rw_api, history_apis::read_write, call_name) + +void history_api_plugin::plugin_startup() { + ilog( "starting history_api_plugin" ); + auto ro_api = app().get_plugin().get_read_only_api(); + //auto rw_api = app().get_plugin().get_read_write_api(); + + app().get_plugin().add_api({ +// CHAIN_RO_CALL(get_transaction), + CHAIN_RO_CALL(get_actions), +// CHAIN_RO_CALL(get_key_accounts), +// CHAIN_RO_CALL(get_controlled_accounts) + }); +} + +void history_api_plugin::plugin_shutdown() {} + +} diff --git a/plugins/history_api_plugin/include/eosio/history_api_plugin/history_api_plugin.hpp b/plugins/history_api_plugin/include/eosio/history_api_plugin/history_api_plugin.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b50cea51d881a08da0b4ea6adf24dc0073a58cdb --- /dev/null +++ b/plugins/history_api_plugin/include/eosio/history_api_plugin/history_api_plugin.hpp @@ -0,0 +1,33 @@ +/** + * @file + * @copyright defined in eos/LICENSE.txt + */ + +#pragma once +#include +#include +#include + +#include + +namespace eosio { + + using namespace appbase; + + class history_api_plugin : public plugin { + public: + APPBASE_PLUGIN_REQUIRES((history_plugin)(chain_plugin)(http_plugin)) + + history_api_plugin(); + virtual ~history_api_plugin(); + + virtual void set_program_options(options_description&, options_description&) override; + + void plugin_initialize(const variables_map&); + void plugin_startup(); + void plugin_shutdown(); + + private: + }; + +} diff --git a/plugins/history_plugin/history_plugin.cpp b/plugins/history_plugin/history_plugin.cpp index 4507d59d98f292fad075a46ea1417ea6bb4789d8..803e5597b87442250f6d72a6272c667a2dd2b3d2 100644 --- a/plugins/history_plugin/history_plugin.cpp +++ b/plugins/history_plugin/history_plugin.cpp @@ -8,13 +8,69 @@ namespace eosio { using namespace chain; + static appbase::abstract_plugin& _history_plugin = app().register_plugin(); + + + struct account_history_object : public chainbase::object { + OBJECT_CTOR( account_history_object ); + + id_type id; + account_name account; ///< the name of the account which has this action in its history + uint64_t action_sequence_num = 0; ///< the sequence number of the relevant action (global) + int32_t account_sequence_num = 0; ///< the sequence number for this account (per-account) + }; + + struct action_history_object : public chainbase::object { + + OBJECT_CTOR( action_history_object, (packed_action_trace) ); + + id_type id; + uint64_t action_sequence_num; ///< the sequence number of the relevant action + + shared_vector packed_action_trace; + }; + using account_history_id_type = account_history_object::id_type; + using action_history_id_type = action_history_object::id_type; + + + struct by_action_sequence_num; + struct by_account_action_seq; + + using action_history_index = chainbase::shared_multi_index_container< + action_history_object, + indexed_by< + ordered_unique, member>, + ordered_unique, member> + > + >; + + using account_history_index = chainbase::shared_multi_index_container< + account_history_object, + indexed_by< + ordered_unique, member>, + ordered_unique, + composite_key< account_history_object, + member, + member + > + > + > + >; + +} /// namespace eosio + +CHAINBASE_SET_INDEX_TYPE(eosio::account_history_object, eosio::account_history_index) +CHAINBASE_SET_INDEX_TYPE(eosio::action_history_object, eosio::action_history_index) + +namespace eosio { + class history_plugin_impl { public: std::set filter_on; chain_plugin* chain_plug = nullptr; bool is_filtered( const account_name& n ) { - return filter_on.find(n) != filter_on.end(); + return !filter_on.size() || filter_on.find(n) != filter_on.end(); } bool filter( const action_trace& act ) { if( filter_on.size() == 0 ) return true; @@ -25,9 +81,58 @@ namespace eosio { return false; } + set account_set( const action_trace& act ) { + set result; + + if( is_filtered( act.receipt.receiver ) ) + result.insert( act.receipt.receiver ); + for( const auto& a : act.act.authorization ) { + if( is_filtered( a.actor ) ) result.insert( a.actor ); + } + return result; + } + + void record_account_action( account_name n, const base_action_trace& act ) { + auto& chain = chain_plug->chain(); + auto& db = chain.db(); + + const auto& idx = db.get_index(); + auto itr = idx.lower_bound( boost::make_tuple( name(n.value+1), 0 ) ); + + uint64_t asn = 0; + if( itr != idx.begin() ) --itr; + if( itr->account == n ) + asn = itr->account_sequence_num + 1; + + idump((n)(act.receipt.global_sequence)(asn)); + const auto& a = db.create( [&]( auto& aho ) { + aho.account = n; + aho.action_sequence_num = act.receipt.global_sequence; + aho.account_sequence_num = asn; + }); + idump((a.account)(a.action_sequence_num)(a.action_sequence_num)); + } + void on_action_trace( const action_trace& at ) { if( filter( at ) ) { - idump((fc::json::to_pretty_string(at.act))); + idump((fc::json::to_pretty_string(at))); + auto& chain = chain_plug->chain(); + auto& db = chain.db(); + + idump((at.receipt.global_sequence)); + db.create( [&]( auto& aho ) { + auto ps = fc::raw::pack_size( at ); + aho.packed_action_trace.resize(ps); + datastream ds( aho.packed_action_trace.data(), ps ); + fc::raw::pack( ds, at ); + aho.action_sequence_num = at.receipt.global_sequence; + }); + + auto aset = account_set( at ); + idump((aset)); + for( auto a : aset ) { + record_account_action( a, at ); + } } for( const auto& iline : at.inline_traces ) { on_action_trace( iline ); @@ -49,6 +154,7 @@ namespace eosio { } + void history_plugin::set_program_options(options_description& cli, options_description& cfg) { cfg.add_options() ("filter_on_accounts,f", bpo::value>()->composing(), @@ -65,7 +171,12 @@ namespace eosio { } my->chain_plug = app().find_plugin(); - my->chain_plug->chain().applied_transaction.connect( [&]( const transaction_trace_ptr& p ){ + auto& chain = my->chain_plug->chain(); + + chain.db().add_index(); + chain.db().add_index(); + + chain.applied_transaction.connect( [&]( const transaction_trace_ptr& p ){ my->on_applied_transaction(p); }); @@ -78,4 +189,76 @@ namespace eosio { } + + + namespace history_apis { + read_only::get_actions_result read_only::get_actions( const read_only::get_actions_params& params )const { + edump((params)); + auto& chain = history->chain_plug->chain(); + const auto& db = chain.db(); + + const auto& idx = db.get_index(); + + int32_t start = 0; + int32_t pos = params.pos ? *params.pos : -1; + int32_t end = 0; + int32_t offset = params.offset ? *params.offset : -20; + auto n = params.account_name; + idump((pos)); + if( pos == -1 ) { + auto itr = idx.lower_bound( boost::make_tuple( name(n.value+1), 0 ) ); + if( itr == idx.begin() ) { + if( itr->account == n ) + pos = itr->account_sequence_num+1; + } else if( itr != idx.begin() ) --itr; + + if( itr->account == n ) + pos = itr->account_sequence_num + 1; + } + + if( pos== -1 ) pos = 0xffffff; + + if( offset > 0 ) { + start = pos; + end = start + offset; + } else { + start = pos + offset; + if( start > pos ) start = 0; + end = pos; + } + FC_ASSERT( end >= start ); + + idump((start)(end)); + + auto start_itr = idx.lower_bound( boost::make_tuple( n, start ) ); + auto end_itr = idx.lower_bound( boost::make_tuple( n, end+1) ); + + auto start_time = fc::time_point::now(); + auto end_time = start_time; + + get_actions_result result; + while( start_itr != end_itr ) { + const auto& a = db.get( start_itr->action_sequence_num ); + fc::datastream ds( a.packed_action_trace.data(), a.packed_action_trace.size() ); + action_trace t; + fc::raw::unpack( ds, t ); + result.actions.emplace_back( ordered_action_result{ + start_itr->action_sequence_num, + start_itr->account_sequence_num, + fc::variant(t) + }); + + end_time = fc::time_point::now(); + if( end_time - start_time > fc::microseconds(100000) ) { + result.time_limit_exceeded_error = true; + break; + } + ++start_itr; + } + + return result; + } + } /// history_apis + + } /// namespace eosio diff --git a/plugins/history_plugin/include/eosio/history_plugin.hpp b/plugins/history_plugin/include/eosio/history_plugin.hpp index dce35c5d4d8431d1e7b96a828f075ed17712d920..ba555bc3795e440c9bf4e91cf2b62d7d56840a45 100644 --- a/plugins/history_plugin/include/eosio/history_plugin.hpp +++ b/plugins/history_plugin/include/eosio/history_plugin.hpp @@ -30,6 +30,7 @@ class read_only { : history(history) {} + /* struct get_transaction_params { chain::transaction_id_type transaction_id; }; @@ -38,24 +39,39 @@ class read_only { fc::variant transaction; }; get_transaction_results get_transaction(const get_transaction_params& params) const; + */ - struct get_transactions_params { + struct get_actions_params { chain::account_name account_name; - optional skip_seq; - optional num_seq; + optional pos; /// a absolute sequence positon -1 is the end/last action + optional offset; ///< the number of actions relative to pos, negative numbers return [pos-offset,pos), positive numbers return [pos,pos+offset) }; + + struct ordered_action_result { + uint64_t global_action_seq = 0; + int32_t account_action_seq = 0; + fc::variant action_trace; + }; + + struct get_actions_result { + vector actions; + optional time_limit_exceeded_error; + }; + + + get_actions_result get_actions( const get_actions_params& )const; + + + /* struct ordered_transaction_results { uint32_t seq_num; chain::transaction_id_type transaction_id; fc::variant transaction; }; - struct get_transactions_results { - vector transactions; - optional time_limit_exceeded_error; - }; get_transactions_results get_transactions(const get_transactions_params& params) const; + */ struct get_key_accounts_params { @@ -111,6 +127,10 @@ class history_plugin : public plugin { } /// namespace eosio +FC_REFLECT( eosio::history_apis::read_only::get_actions_params, (account_name)(pos)(offset) ) +FC_REFLECT( eosio::history_apis::read_only::get_actions_result, (actions)(time_limit_exceeded_error) ) +FC_REFLECT( eosio::history_apis::read_only::ordered_action_result, (global_action_seq)(account_action_seq)(action_trace) ) +/* FC_REFLECT(eosio::history_apis::read_only::get_transaction_params, (transaction_id) ) FC_REFLECT(eosio::history_apis::read_only::get_transaction_results, (transaction_id)(transaction) ) FC_REFLECT(eosio::history_apis::read_only::get_transactions_params, (account_name)(skip_seq)(num_seq) ) @@ -120,5 +140,6 @@ FC_REFLECT(eosio::history_apis::read_only::get_key_accounts_params, (public_key) FC_REFLECT(eosio::history_apis::read_only::get_key_accounts_results, (account_names) ) FC_REFLECT(eosio::history_apis::read_only::get_controlled_accounts_params, (controlling_account) ) FC_REFLECT(eosio::history_apis::read_only::get_controlled_accounts_results, (controlled_accounts) ) +*/ diff --git a/programs/cleos/httpc.hpp b/programs/cleos/httpc.hpp index 31870bf3937d18de65d1bee6fda1885a8725ac37..3023857b6b7655b1236d85c7d6a884abb82b9378 100644 --- a/programs/cleos/httpc.hpp +++ b/programs/cleos/httpc.hpp @@ -22,6 +22,10 @@ namespace eosio { namespace client { namespace http { const string get_currency_stats_func = chain_func_base + "/get_currency_stats"; const string get_required_keys = chain_func_base + "/get_required_keys"; + + const string history_func_base = "/v1/history"; + const string get_actions_func = history_func_base + "/get_actions"; + const string account_history_func_base = "/v1/account_history"; const string get_transaction_func = account_history_func_base + "/get_transaction"; const string get_transactions_func = account_history_func_base + "/get_transactions"; @@ -48,4 +52,4 @@ namespace eosio { namespace client { namespace http { const string wallet_sign_trx = wallet_func_base + "/sign_transaction"; FC_DECLARE_EXCEPTION( connection_exception, 1100000, "Connection Exception" ); - }}} \ No newline at end of file + }}} diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index 17314a41e7f52b55abbd77720ab7a808e94929d6..9aecee58d116e760800cc271f63084b75d3fa372 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -1081,10 +1081,32 @@ int main( int argc, char** argv ) { std::cout << fc::json::to_pretty_string(call(get_transaction_func, arg)) << std::endl; }); - // get transactions + // get actions string skip_seq_str; string num_seq_str; bool printjson = false; + + int32_t pos_seq = -1; + int32_t offset = -20; + auto getActions = get->add_subcommand("actions", localized("Retrieve all actions with specific account name referenced in authorization or receiver"), false); + getActions->add_option("account_name", account_name, localized("name of account to query on"))->required(); + getActions->add_option("pos", pos_seq, localized("sequence number of action for this account, -1 for last")); + getActions->add_option("offset", offset, localized("get actions [pos,pos+offset) for positive offset or [pos-offset,pos) for negative offset")); + getActions->add_flag("--json,-j", printjson, localized("print full json")); + getActions->set_callback([&] { + fc::mutable_variant_object arg; + arg( "account_name", account_name ); + arg( "pos", pos_seq ); + arg( "offset", offset); + + edump((get_actions_func)(arg)); + auto result = call(get_actions_func, arg); + //if( printjson ) { + std::cout << fc::json::to_pretty_string(result) << std::endl; + }); + + + /* auto getTransactions = get->add_subcommand("transactions", localized("Retrieve all transactions with specific account name referenced in their scope"), false); getTransactions->add_option("account_name", account_name, localized("name of account to query on"))->required(); getTransactions->add_option("skip_seq", skip_seq_str, localized("Number of most recent transactions to skip (0 would start at most recent transaction)")); @@ -1137,6 +1159,7 @@ int main( int argc, char** argv ) { } }); + */ // set subcommand auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state")); diff --git a/programs/nodeos/CMakeLists.txt b/programs/nodeos/CMakeLists.txt index 558f1a4c2fc06691aed90506939c4b03a3c490c7..7d5f0bd0cfde22e1251d1db93714d9088de1b3cd 100644 --- a/programs/nodeos/CMakeLists.txt +++ b/programs/nodeos/CMakeLists.txt @@ -46,12 +46,13 @@ endif() target_link_libraries( nodeos PRIVATE appbase PRIVATE -Wl,${whole_archive_flag} history_plugin -Wl,${no_whole_archive_flag} + PRIVATE -Wl,${whole_archive_flag} history_api_plugin -Wl,${no_whole_archive_flag} PRIVATE -Wl,${whole_archive_flag} chain_api_plugin -Wl,${no_whole_archive_flag} PRIVATE -Wl,${whole_archive_flag} wallet_api_plugin -Wl,${no_whole_archive_flag} # PRIVATE -Wl,${whole_archive_flag} net_api_plugin -Wl,${no_whole_archive_flag} # PRIVATE -Wl,${whole_archive_flag} faucet_testnet_plugin -Wl,${no_whole_archive_flag} # PRIVATE -Wl,${whole_archive_flag} txn_test_gen_plugin -Wl,${no_whole_archive_flag} - PRIVATE producer_plugin chain_plugin http_plugin history_plugin + PRIVATE producer_plugin chain_plugin http_plugin history_api_plugin history_plugin PRIVATE eosio_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) if(BUILD_MONGO_DB_PLUGIN)