提交 b6292de4 编写于 作者: D Daniel Larimer

implement action history api

上级 1122a082
Subproject commit 31a02293a303782a97963a2e67815703f1123914
Subproject commit 3bfd8b5fdf8df9774f116fb2014770053df73a35
......@@ -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<action_trace> 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) )
......@@ -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, )
......@@ -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)
......
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" )
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/history_api_plugin/history_api_plugin.hpp>
#include <eosio/chain/exceptions.hpp>
#include <fc/io/json.hpp>
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::~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<api_namespace::call_name ## _params>()); \
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<history_plugin>().get_read_only_api();
//auto rw_api = app().get_plugin<history_plugin>().get_read_write_api();
app().get_plugin<http_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() {}
}
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <eosio/history_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <appbase/application.hpp>
namespace eosio {
using namespace appbase;
class history_api_plugin : public plugin<history_api_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:
};
}
......@@ -8,13 +8,69 @@
namespace eosio {
using namespace chain;
static appbase::abstract_plugin& _history_plugin = app().register_plugin<history_plugin>();
struct account_history_object : public chainbase::object<account_history_object_type, account_history_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<action_history_object_type, action_history_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<char> 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<tag<by_id>, member<action_history_object, action_history_object::id_type, &action_history_object::id>>,
ordered_unique<tag<by_action_sequence_num>, member<action_history_object, uint64_t, &action_history_object::action_sequence_num>>
>
>;
using account_history_index = chainbase::shared_multi_index_container<
account_history_object,
indexed_by<
ordered_unique<tag<by_id>, member<account_history_object, account_history_object::id_type, &account_history_object::id>>,
ordered_unique<tag<by_account_action_seq>,
composite_key< account_history_object,
member<account_history_object, account_name, &account_history_object::account >,
member<account_history_object, int32_t, &account_history_object::account_sequence_num >
>
>
>
>;
} /// 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<account_name> 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_name> account_set( const action_trace& act ) {
set<account_name> 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<account_history_index, by_account_action_seq>();
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<account_history_object>( [&]( 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<action_history_object>( [&]( auto& aho ) {
auto ps = fc::raw::pack_size( at );
aho.packed_action_trace.resize(ps);
datastream<char*> 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<vector<string>>()->composing(),
......@@ -65,7 +171,12 @@ namespace eosio {
}
my->chain_plug = app().find_plugin<chain_plugin>();
my->chain_plug->chain().applied_transaction.connect( [&]( const transaction_trace_ptr& p ){
auto& chain = my->chain_plug->chain();
chain.db().add_index<account_history_index>();
chain.db().add_index<action_history_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<account_history_index, by_account_action_seq>();
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<action_history_object, by_action_sequence_num>( start_itr->action_sequence_num );
fc::datastream<const char*> 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
......@@ -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<uint32_t> skip_seq;
optional<uint32_t> num_seq;
optional<int32_t> pos; /// a absolute sequence positon -1 is the end/last action
optional<int32_t> 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<ordered_action_result> actions;
optional<bool> 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<ordered_transaction_results> transactions;
optional<bool> 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<history_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) )
*/
......@@ -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
}}}
......@@ -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"));
......
......@@ -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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册