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

Added retrieving transactions for a given account… #155

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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