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

Initial Transaction API #130

Merged
merged 9 commits into from
Aug 8, 2017
1 change: 1 addition & 0 deletions libraries/chain/include/eos/chain/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace eos { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_scope, eos::chain::transaction_exception, 3030008, "missing required scope" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_recipient, eos::chain::transaction_exception, 3030009, "missing required recipient" )
FC_DECLARE_DERIVED_EXCEPTION( checktime_exceeded, eos::chain::transaction_exception, 3030010, "allotted processing time was exceeded" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction_exception, eos::chain::transaction_exception, 3030011, "unknown transaction" )

FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, eos::chain::utility_exception, 3060001, "invalid pts address" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, eos::chain::chain_exception, 37006, "insufficient feeds" )
Expand Down
2 changes: 2 additions & 0 deletions libraries/chain/include/eos/chain/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ namespace eos { namespace chain {
transaction_object_type,
producer_object_type,
chain_property_object_type,
transaction_history_object_type, ///< Defined by account history plugin library
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Defined by account_history_plugin"

balance_object_type, ///< Defined by native_contract library
staked_balance_object_type, ///< Defined by native_contract library
producer_votes_object_type, ///< Defined by native_contract library
Expand Down Expand Up @@ -216,6 +217,7 @@ FC_REFLECT_ENUM(eos::chain::object_type,
(transaction_object_type)
(producer_object_type)
(chain_property_object_type)
(transaction_history_object_type)
(balance_object_type)
(staked_balance_object_type)
(producer_votes_object_type)
Expand Down
2 changes: 1 addition & 1 deletion libraries/chainbase
2 changes: 2 additions & 0 deletions plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ add_subdirectory(database_plugin)
add_subdirectory(chain_plugin)
add_subdirectory(chain_api_plugin)
add_subdirectory(producer_plugin)
add_subdirectory(account_history_plugin)
add_subdirectory(account_history_api_plugin)
16 changes: 16 additions & 0 deletions plugins/account_history_api_plugin/CMakeLists.txt
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" )
48 changes: 48 additions & 0 deletions plugins/account_history_api_plugin/account_history_api_plugin.cpp
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:
};

}
16 changes: 16 additions & 0 deletions plugins/account_history_plugin/CMakeLists.txt
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" )
136 changes: 136 additions & 0 deletions plugins/account_history_plugin/account_history_plugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include <eos/account_history_plugin/account_history_plugin.hpp>
#include <eos/account_history_plugin/account_history_object.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 fc { class variant; }

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 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( [&]() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not compile:

../../plugins/account_history_plugin/account_history_plugin.cpp:42:7: error: no matching member function for call to 'with_read_lock'
   db.with_read_lock( [&]() {
   ~~~^~~~~~~~~~~~~~
../../libraries/chainbase/include/chainbase/chainbase.hpp:881:15: note: candidate function not viable: 'this' argument has type 'const chainbase::database', but method is not marked const
         auto with_read_lock( Lambda&& callback, uint64_t wait_micro = 1000000 ) -> decltype( (*(Lambda*)nullptr)() )
              ^

db is a const ref, but with_read_lock is not a const method. I wonder... should with_read_lock be const?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I "fixed" that (Otherwise Travis would complain). see commit d48ebab.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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 ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use a std::runtime_error -- we want to exclusively use our own exception types in this codebase.

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:

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

}

#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
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <chainbase/chainbase.hpp>

namespace eos {
using chain::block_id_type;
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;

}

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) )
Loading