-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Initial Transaction API #130
Changes from 6 commits
1476206
991165e
65c1ceb
a251af2
3122cab
69f0bb1
4a4026e
91da030
809bb82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
file(GLOB HEADERS "include/eos/account_history_api_plugin/*.hpp") | ||
add_library( account_history_api_plugin | ||
account_history_api_plugin.cpp | ||
${HEADERS} ) | ||
|
||
target_link_libraries( account_history_api_plugin account_history_plugin chain_plugin http_plugin appbase ) | ||
target_include_directories( account_history_api_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) | ||
|
||
install( TARGETS | ||
account_history_api_plugin | ||
|
||
RUNTIME DESTINATION bin | ||
LIBRARY DESTINATION lib | ||
ARCHIVE DESTINATION lib | ||
) | ||
install( FILES ${HEADERS} DESTINATION "include/eos/account_history_api_plugin" ) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#include <eos/account_history_api_plugin/account_history_api_plugin.hpp> | ||
#include <eos/chain/chain_controller.hpp> | ||
#include <eos/chain/exceptions.hpp> | ||
|
||
#include <fc/io/json.hpp> | ||
|
||
namespace eos { | ||
|
||
using namespace eos; | ||
|
||
account_history_api_plugin::account_history_api_plugin(){} | ||
account_history_api_plugin::~account_history_api_plugin(){} | ||
|
||
void account_history_api_plugin::set_program_options(options_description&, options_description&) {} | ||
void account_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) { \ | ||
cb(400, "Invalid arguments"); \ | ||
elog("Unable to parse arguments: ${args}", ("args", body)); \ | ||
} catch (fc::exception& e) { \ | ||
cb(500, e.to_detail_string()); \ | ||
elog("Exception encountered while processing ${call}: ${e}", ("call", #api_name "." #call_name)("e", e)); \ | ||
} \ | ||
}} | ||
|
||
#define CHAIN_RO_CALL(call_name) CALL(account_history, ro_api, account_history_apis::read_only, call_name) | ||
#define CHAIN_RW_CALL(call_name) CALL(account_history, rw_api, account_history_apis::read_write, call_name) | ||
|
||
void account_history_api_plugin::plugin_startup() { | ||
ilog( "starting account_history_api_plugin" ); | ||
auto ro_api = app().get_plugin<account_history_plugin>().get_read_only_api(); | ||
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) | ||
}); | ||
} | ||
|
||
void account_history_api_plugin::plugin_shutdown() {} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#pragma once | ||
#include <eos/account_history_plugin/account_history_plugin.hpp> | ||
#include <eos/chain_plugin/chain_plugin.hpp> | ||
#include <eos/http_plugin/http_plugin.hpp> | ||
|
||
#include <appbase/application.hpp> | ||
|
||
namespace eos { | ||
|
||
using namespace appbase; | ||
|
||
class account_history_api_plugin : public plugin<account_history_api_plugin> { | ||
public: | ||
APPBASE_PLUGIN_REQUIRES((account_history_plugin)(chain_plugin)(http_plugin)) | ||
|
||
account_history_api_plugin(); | ||
virtual ~account_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: | ||
}; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
file(GLOB HEADERS "include/eos/account_history_plugin/*.hpp") | ||
add_library( account_history_plugin | ||
account_history_plugin.cpp | ||
${HEADERS} ) | ||
|
||
target_link_libraries( account_history_plugin chain_plugin eos_chain appbase ) | ||
target_include_directories( account_history_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) | ||
|
||
install( TARGETS | ||
account_history_plugin | ||
|
||
RUNTIME DESTINATION bin | ||
LIBRARY DESTINATION lib | ||
ARCHIVE DESTINATION lib | ||
) | ||
install( FILES ${HEADERS} DESTINATION "include/eos/account_history_plugin" ) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
#include <eos/account_history_plugin/account_history_plugin.hpp> | ||
#include <eos/chain/chain_controller.hpp> | ||
#include <eos/chain/config.hpp> | ||
#include <eos/chain/exceptions.hpp> | ||
#include <eos/chain/transaction.hpp> | ||
#include <eos/chain/types.hpp> | ||
|
||
#include <fc/crypto/sha256.hpp> | ||
#include <fc/io/json.hpp> | ||
#include <fc/variant.hpp> | ||
|
||
#include <boost/multi_index/hashed_index.hpp> | ||
#include <boost/multi_index/mem_fun.hpp> | ||
#include <eos/chain/multi_index_includes.hpp> | ||
|
||
namespace eos { | ||
|
||
using chain::block_id_type; | ||
using chain::ProcessedTransaction; | ||
using chain::signed_block; | ||
using boost::multi_index_container; | ||
using chain::transaction_id_type; | ||
using namespace boost::multi_index; | ||
|
||
class transaction_history_object : public chainbase::object<chain::transaction_history_object_type, transaction_history_object> { | ||
OBJECT_CTOR(transaction_history_object) | ||
|
||
id_type id; | ||
block_id_type block_id; | ||
transaction_id_type transaction_id; | ||
}; | ||
|
||
struct by_id; | ||
struct by_trx_id; | ||
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>> | ||
> | ||
>; | ||
|
||
typedef chainbase::generic_index<transaction_history_multi_index> transaction_history_index; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typically we keep the object and index definitions in a header file. Please move to a header for consistency |
||
|
||
class account_history_plugin_impl { | ||
public: | ||
ProcessedTransaction get_transaction(const chain::transaction_id_type& transaction_id) const; | ||
void applied_block(const signed_block&); | ||
chain_plugin* chain_plug; | ||
private: | ||
|
||
optional<block_id_type> find_block_id(const transaction_id_type& transaction_id) const; | ||
}; | ||
|
||
optional<block_id_type> account_history_plugin_impl::find_block_id(const transaction_id_type& transaction_id) const | ||
{ | ||
const auto& db = chain_plug->chain().get_database(); | ||
optional<block_id_type> block_id; | ||
db.with_read_lock( [&]() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does not compile:
db is a const ref, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe I "fixed" that (Otherwise Travis would complain). see commit d48ebab. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Were you able to get it to work, or is there an issue with my commit? I had a heck of a time getting the submodule commit correct, so it would not be a big shocker if it really didn't work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, just an old submodule. Oops :] |
||
const auto& trx_idx = db.get_index<transaction_history_multi_index, by_trx_id>(); | ||
auto transaction_history = trx_idx.find( transaction_id ); | ||
if (transaction_history != trx_idx.end()) | ||
block_id = transaction_history->block_id; | ||
} ); | ||
return 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 | ||
std::string msg = "transaction_id=" + transaction_id.str() + " indexed with block_id=" + block_id->str() + ", but "; | ||
if (!block) | ||
msg += "block was not found"; | ||
else | ||
msg += "transaction was not found in the block"; | ||
BOOST_THROW_EXCEPTION( std::runtime_error( msg ) ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't use a This is throwing for a pretty weird case that, theoretically, should never actually occur, so we don't need to define an exception type for it specifically. We can just use assertions. Something like this should suffice:
|
||
} | ||
|
||
#warning TODO: lookup of recent transactions | ||
FC_THROW_EXCEPTION(chain::unknown_transaction_exception, | ||
"Could not find transaction for: ${id}", ("id", transaction_id.str())); | ||
} | ||
|
||
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(); | ||
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(); | ||
}); | ||
} | ||
} | ||
|
||
|
||
account_history_plugin::account_history_plugin() | ||
:my(new account_history_plugin_impl()) | ||
{ | ||
} | ||
|
||
account_history_plugin::~account_history_plugin() | ||
{ | ||
} | ||
|
||
void account_history_plugin::set_program_options(options_description& cli, options_description& cfg) | ||
{ | ||
} | ||
|
||
void account_history_plugin::plugin_initialize(const variables_map& options) | ||
{ | ||
} | ||
|
||
void account_history_plugin::plugin_startup() | ||
{ | ||
my->chain_plug = app().find_plugin<chain_plugin>(); | ||
auto& db = my->chain_plug->chain().get_mutable_database(); | ||
db.add_index<transaction_history_multi_index>(); | ||
|
||
my->chain_plug->chain().applied_block.connect ([&impl = my](const signed_block& block) { | ||
impl->applied_block(block); | ||
}); | ||
} | ||
|
||
void account_history_plugin::plugin_shutdown() | ||
{ | ||
} | ||
|
||
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) }; | ||
} | ||
|
||
} // namespace account_history_apis | ||
} // namespace eos | ||
|
||
CHAINBASE_SET_INDEX_TYPE( eos::transaction_history_object, eos::transaction_history_multi_index ) | ||
|
||
FC_REFLECT( eos::transaction_history_object, (block_id)(transaction_id) ) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#pragma once | ||
#include <appbase/application.hpp> | ||
|
||
#include <eos/chain_plugin/chain_plugin.hpp> | ||
|
||
namespace fc { class variant; } | ||
|
||
namespace eos { | ||
using chain::transaction_id_type; | ||
using std::shared_ptr; | ||
using namespace appbase; | ||
using chain::Name; | ||
using fc::optional; | ||
using chain::uint128_t; | ||
|
||
typedef shared_ptr<class account_history_plugin_impl> account_history_ptr; | ||
typedef shared_ptr<const class account_history_plugin_impl> account_history_const_ptr; | ||
|
||
namespace account_history_apis { | ||
struct empty{}; | ||
|
||
class read_only { | ||
account_history_const_ptr account_history; | ||
|
||
public: | ||
read_only(account_history_const_ptr&& account_history) | ||
: account_history(account_history) {} | ||
|
||
struct get_transaction_params { | ||
chain::transaction_id_type transaction_id; | ||
}; | ||
struct get_transaction_results { | ||
fc::variant transaction; | ||
}; | ||
|
||
get_transaction_results get_transaction(const get_transaction_params& params) const; | ||
|
||
}; | ||
|
||
class read_write { | ||
account_history_ptr account_history; | ||
|
||
public: | ||
read_write(account_history_ptr account_history) : account_history(account_history) {} | ||
}; | ||
} // namespace account_history_apis | ||
|
||
class account_history_plugin : public plugin<account_history_plugin> { | ||
public: | ||
APPBASE_PLUGIN_REQUIRES((chain_plugin)) | ||
|
||
account_history_plugin(); | ||
virtual ~account_history_plugin(); | ||
|
||
virtual void set_program_options(options_description& cli, options_description& cfg) override; | ||
|
||
void plugin_initialize(const variables_map& options); | ||
void plugin_startup(); | ||
void plugin_shutdown(); | ||
|
||
account_history_apis::read_only get_read_only_api() const { return account_history_apis::read_only(account_history_const_ptr(my)); } | ||
account_history_apis::read_write get_read_write_api() { return account_history_apis::read_write(my); } | ||
|
||
private: | ||
account_history_ptr my; | ||
}; | ||
|
||
} | ||
|
||
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) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment here noting that this object is defined in the account_history_plugin would be good (like in the lines below). With modern IDEs, it's not as necessary, but it's still helpful to note which object types are declared here, but implemented in a separate library/plugin.