Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Added retrieving transactions for a given account and also added conf…
Browse files Browse the repository at this point in the history
…iguration to limit transaction tracking to specific accounts.
  • Loading branch information
brianjohnson5972 committed Aug 11, 2017
1 parent 27281ca commit d11212e
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ void account_history_api_plugin::plugin_startup() {
auto rw_api = app().get_plugin<account_history_plugin>().get_read_write_api();

app().get_plugin<http_plugin>().add_api({
CHAIN_RO_CALL(get_transaction)
CHAIN_RO_CALL(get_transaction),
CHAIN_RO_CALL(get_transactions)
});
}

Expand Down
121 changes: 102 additions & 19 deletions plugins/account_history_plugin/account_history_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,20 @@ using namespace boost::multi_index;
class account_history_plugin_impl {
public:
ProcessedTransaction get_transaction(const chain::transaction_id_type& transaction_id) const;
vector<account_history_apis::read_only::get_transaction_results> get_transactions(const AccountName& account_name, const optional<chain::transaction_id_type>& after_transaction_id) const;
void applied_block(const signed_block&);

chain_plugin* chain_plug;
static const int DEFAULT_TRANSACTION_LIMIT;
int transactions_limit = DEFAULT_TRANSACTION_LIMIT;
std::set<AccountName> filter_on;
private:

optional<block_id_type> find_block_id(const transaction_id_type& transaction_id) const;
ProcessedTransaction find_transaction(const chain::transaction_id_type& transaction_id, const block_id_type& block_id) const;
bool scope_relevant(const eos::types::Vector<AccountName>& scope);
};
const int account_history_plugin_impl::DEFAULT_TRANSACTION_LIMIT = 100;

optional<block_id_type> account_history_plugin_impl::find_block_id(const transaction_id_type& transaction_id) const
{
Expand All @@ -48,45 +56,101 @@ optional<block_id_type> account_history_plugin_impl::find_block_id(const transac
return block_id;
}

ProcessedTransaction account_history_plugin_impl::find_transaction(const chain::transaction_id_type& transaction_id, const chain::block_id_type& block_id) const
{
auto block = chain_plug->chain().fetch_block_by_id(block_id);
FC_ASSERT(block, "Transaction with ID ${tid} was indexed as being in block ID ${bid}, but no such block was found", ("tid", transaction_id)("bid", block_id));

for (const auto& cycle : block->cycles)
for (const auto& thread : cycle)
for (const auto& trx : thread.user_input)
if (trx.id() == transaction_id)
return trx;

// ERROR in indexing logic
FC_THROW("Transaction with ID ${tid} was indexed as being in block ID ${bid}, but was not found in that block", ("tid", transaction_id)("bid", block_id));
}

ProcessedTransaction account_history_plugin_impl::get_transaction(const chain::transaction_id_type& transaction_id) const
{
auto block_id = find_block_id(transaction_id);
if( block_id.valid() )
{
auto block = chain_plug->chain().fetch_block_by_id(*block_id);
if (block.valid())
{
for (const auto& cycle : block->cycles)
for (const auto& thread : cycle)
for (const auto& trx : thread.user_input)
if (trx.id() == transaction_id)
return trx;
}

// ERROR in indexing logic
FC_ASSERT(block, "Transaction with ID ${tid} was indexed as being in block ID ${bid}, but no such block was found", ("tid", transaction_id)("bid", block_id));
FC_THROW("Transaction with ID ${tid} was indexed as being in block ID ${bid}, but was not found in that block", ("tid", transaction_id)("bid", block_id));
return find_transaction(transaction_id, *block_id);
}

#warning TODO: lookup of recent transactions
FC_THROW_EXCEPTION(chain::unknown_transaction_exception,
"Could not find transaction for: ${id}", ("id", transaction_id.str()));
}

vector<account_history_apis::read_only::get_transaction_results> account_history_plugin_impl::get_transactions(const AccountName& account_name, const optional<chain::transaction_id_type>& after_transaction_id) const
{
const auto& db = chain_plug->chain().get_database();
typedef std::map<transaction_id_type, block_id_type> transaction_map;
transaction_map transactions;

db.with_read_lock( [&]() {
const auto& account_idx = db.get_index<transaction_history_multi_index, by_account_name>();
auto range = account_idx.equal_range( account_name );
for (auto transaction_history = range.first; transaction_history != range.second; ++transaction_history)
{
transactions.emplace(std::make_pair(transaction_history->transaction_id, transaction_history->block_id));
}
} );
vector<account_history_apis::read_only::get_transaction_results> results;
transaction_map::const_iterator trans;
if (after_transaction_id.valid())
{
trans = transactions.find(*after_transaction_id);
if (trans != transactions.cend())
++trans;
}
else
trans = transactions.cbegin();

for(; trans != transactions.end() && results.size() < transactions_limit; ++trans)
{
const auto trx = find_transaction(trans->first, trans->second);
const auto pretty_trx = chain_plug->chain().transaction_to_variant(trx);
results.emplace_back(account_history_apis::read_only::get_transaction_results{trans->first, pretty_trx});
}

return results;
}

void account_history_plugin_impl::applied_block(const signed_block& block)
{
const auto block_id = block.id();
auto& db = chain_plug->chain().get_mutable_database();
const bool check_relevance = filter_on.size();
for (const auto& cycle : block.cycles)
for (const auto& thread : cycle)
for (const auto& trx : thread.user_input) {
db.create<transaction_history_object>([&block_id,&trx](transaction_history_object& transaction_history) {
transaction_history.block_id = block_id;
transaction_history.transaction_id = trx.id();
});
for (const auto& trx : thread.user_input)
{
if (check_relevance && !scope_relevant(trx.scope))
continue;

for (const auto& account_name : trx.scope)
{
db.create<transaction_history_object>([&block_id,&trx,&account_name](transaction_history_object& transaction_history) {
transaction_history.block_id = block_id;
transaction_history.transaction_id = trx.id();
transaction_history.account_name = account_name;
});
}
}
}

bool account_history_plugin_impl::scope_relevant(const eos::types::Vector<AccountName>& scope)
{
for (const AccountName& account_name : scope)
if (filter_on.count(account_name))
return true;

return false;
}


account_history_plugin::account_history_plugin()
:my(new account_history_plugin_impl())
Expand All @@ -99,10 +163,24 @@ account_history_plugin::~account_history_plugin()

void account_history_plugin::set_program_options(options_description& cli, options_description& cfg)
{
cfg.add_options()
("filter_on_accounts,f", bpo::value<vector<string>>()->composing(),
"Track only transactions whose scopes involve the listed accounts. Default is to track all transactions.")
("get-transactions-limit", bpo::value<int>()->default_value(account_history_plugin_impl::DEFAULT_TRANSACTION_LIMIT),
"Limits the number of transactions returned for get_transactions")
;
}

void account_history_plugin::plugin_initialize(const variables_map& options)
{
my->transactions_limit = options.at("get-transactions-limit").as<int>();

if(options.count("filter_on_accounts"))
{
auto foa = options.at("filter_on_accounts").as<vector<string>>();
for(auto filter_account : foa)
my->filter_on.emplace(filter_account);
}
}

void account_history_plugin::plugin_startup()
Expand All @@ -125,7 +203,12 @@ namespace account_history_apis {
read_only::get_transaction_results read_only::get_transaction(const read_only::get_transaction_params& params) const
{
auto trx = account_history->get_transaction(params.transaction_id);
return { account_history->chain_plug->chain().transaction_to_variant(trx) };
return { params.transaction_id, account_history->chain_plug->chain().transaction_to_variant(trx) };
}

read_only::get_transactions_results read_only::get_transactions(const read_only::get_transactions_params& params) const
{
return { account_history->get_transactions(params.account_name, params.after_transaction_id) };
}

} // namespace account_history_apis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@

#include <chainbase/chainbase.hpp>

namespace std {

template<>
struct hash<eos::chain::AccountName>
{
size_t operator()( const eos::chain::AccountName& name )const
{
return (uint64_t)name;
}
};
}

namespace eos {
using chain::AccountName;
using chain::block_id_type;
using chain::transaction_id_type;
using namespace boost::multi_index;
Expand All @@ -13,15 +26,18 @@ class transaction_history_object : public chainbase::object<chain::transaction_h
id_type id;
block_id_type block_id;
transaction_id_type transaction_id;
AccountName account_name;
};

struct by_id;
struct by_trx_id;
struct by_account_name;
using transaction_history_multi_index = chainbase::shared_multi_index_container<
transaction_history_object,
indexed_by<
ordered_unique<tag<by_id>, BOOST_MULTI_INDEX_MEMBER(transaction_history_object, transaction_history_object::id_type, id)>,
hashed_unique<tag<by_trx_id>, BOOST_MULTI_INDEX_MEMBER(transaction_history_object, transaction_id_type, transaction_id), std::hash<transaction_id_type>>
hashed_non_unique<tag<by_trx_id>, BOOST_MULTI_INDEX_MEMBER(transaction_history_object, transaction_id_type, transaction_id), std::hash<transaction_id_type>>,
hashed_non_unique<tag<by_account_name>, BOOST_MULTI_INDEX_MEMBER(transaction_history_object, AccountName, account_name), std::hash<AccountName>>
>
>;

Expand All @@ -31,5 +47,5 @@ typedef chainbase::generic_index<transaction_history_multi_index> transaction_hi

CHAINBASE_SET_INDEX_TYPE( eos::transaction_history_object, eos::transaction_history_multi_index )

FC_REFLECT( eos::transaction_history_object, (block_id)(transaction_id) )
FC_REFLECT( eos::transaction_history_object, (block_id)(transaction_id)(account_name) )

Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,21 @@ class read_only {
chain::transaction_id_type transaction_id;
};
struct get_transaction_results {
fc::variant transaction;
chain::transaction_id_type transaction_id;
fc::variant transaction;
};

get_transaction_results get_transaction(const get_transaction_params& params) const;

struct get_transactions_params {
chain::AccountName account_name;
optional<chain::transaction_id_type> after_transaction_id;
};
struct get_transactions_results {
vector<get_transaction_results> transactions;
};

get_transactions_results get_transactions(const get_transactions_params& params) const;
};

class read_write {
Expand Down Expand Up @@ -69,4 +79,6 @@ class account_history_plugin : public plugin<account_history_plugin> {

FC_REFLECT(eos::account_history_apis::empty, )
FC_REFLECT(eos::account_history_apis::read_only::get_transaction_params, (transaction_id) )
FC_REFLECT(eos::account_history_apis::read_only::get_transaction_results, (transaction) )
FC_REFLECT(eos::account_history_apis::read_only::get_transaction_results, (transaction_id)(transaction) )
FC_REFLECT(eos::account_history_apis::read_only::get_transactions_params, (account_name)(after_transaction_id) )
FC_REFLECT(eos::account_history_apis::read_only::get_transactions_results, (transactions) )
19 changes: 17 additions & 2 deletions programs/eosc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const string get_account_func = chain_func_base + "/get_account";

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";

inline std::vector<Name> sort_names( std::vector<Name>&& names ) {
std::sort( names.begin(), names.end() );
Expand Down Expand Up @@ -366,9 +366,24 @@ int send_command (const vector<string> &cmd_line)
} else if( command == "do" ) {

} else if( command == "transaction" ) {
FC_ASSERT( cmd_line.size() == 2 );
if( cmd_line.size() != 2 )
{
std::cerr << "usage: " << program << " transaction TRANSACTION_ID\n";
return -1;
}
auto arg= fc::mutable_variant_object( "transaction_id", cmd_line[1]);
std::cout << fc::json::to_pretty_string( call( get_transaction_func, arg) ) << std::endl;
} else if( command == "transactions" ) {
if( cmd_line.size() < 2 || cmd_line.size() > 3 )
{
std::cerr << "usage: " << program << " transactions ACCOUNT_TO_LOOKUP [AFTER_TRANSACTION_ID]\n";
return -1;
}
chain::AccountName account_name(cmd_line[1]);
auto arg = (cmd_line.size() == 3)
? fc::mutable_variant_object( "account_name", account_name)("after_transaction_id", cmd_line[2])
: fc::mutable_variant_object( "account_name", account_name);
std::cout << fc::json::to_pretty_string( call( get_transactions_func, arg) ) << std::endl;
}
return 0;
}
Expand Down

0 comments on commit d11212e

Please sign in to comment.