提交 4e10a2db 编写于 作者: D Daniel Larimer

clean up system contract

......@@ -5,7 +5,6 @@ file(GLOB PROTOCOL_HEADERS "include/eos/chain/protocol/*.hpp")
## SORT .cpp by most likely to change / break compile
add_library( eos_chain
database.cpp
sys_contract.cpp
fork_database.cpp
protocol/types.cpp
......
......@@ -32,8 +32,6 @@
#include <eos/chain/transaction_object.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/chain/sys_contract.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/uint128.hpp>
#include <fc/crypto/digest.hpp>
......@@ -128,11 +126,6 @@ std::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of
*/
bool database::push_block(const signed_block& new_block, uint32_t skip)
{ try {
/// TODO: the simulated network code will cause push_block to be called
/// recursively which in turn will cause the write lock to hang
if( new_block.block_num() == head_block_num() && new_block.id() == head_block_id() )
return false;
return with_skip_flags( skip, [&](){
return without_pending_transactions( [&]() {
return with_write_lock( [&]() {
......@@ -467,27 +460,32 @@ void database::apply_transaction(const signed_transaction& trx, uint32_t skip)
void database::validate_tapos( const signed_transaction& trx )const {
try {
if( !check_tapos() ) return;
if( !should_check_tapos() ) return;
const chain_parameters& chain_parameters = get_global_properties().parameters;
const auto& tapos_block_summary = get<block_summary_object>(trx.ref_block_num);
//Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
FC_ASSERT(trx.ref_block_prefix == tapos_block_summary.block_id._hash[1]);
EOS_ASSERT(trx.ref_block_prefix == tapos_block_summary.block_id._hash[1], transaction_exception,
"Transaction's reference block did not match. Is this transaction from a different fork?");
fc::time_point_sec now = head_block_time();
FC_ASSERT(trx.expiration <= now + chain_parameters.maximum_time_until_expiration, "",
("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration));
FC_ASSERT(now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration));
EOS_ASSERT(trx.expiration <= now + chain_parameters.maximum_time_until_expiration,
transaction_exception, "Transaction expiration is too far in the future",
("trx.expiration",trx.expiration)("now",now)
("max_til_exp",chain_parameters.maximum_time_until_expiration));
EOS_ASSERT(now <= trx.expiration, transaction_exception, "Transaction is expired",
("now",now)("trx.exp",trx.expiration));
} FC_CAPTURE_AND_RETHROW( (trx) ) }
void database::validate_uniqueness( const signed_transaction& trx )const {
if( !check_for_duplicate_transactions() ) return;
if( !should_check_for_duplicate_transactions() ) return;
auto trx_id = trx.id();
auto& trx_idx = get_index<transaction_multi_index>();
FC_ASSERT( trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end());
EOS_ASSERT(trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end(),
transaction_exception, "Transaction is not unique");
}
void database::validate_referenced_accounts( const signed_transaction& trx )const {
......@@ -520,35 +518,35 @@ try {
void database::validate_transaction( const signed_transaction& trx )const {
try {
FC_ASSERT( trx.messages.size() > 0, "A transaction must have at least one message" );
validate_uniqueness( trx );
validate_tapos( trx );
validate_referenced_accounts( trx );
validate_message_types( trx );
for( const auto& m : trx.messages ) { /// TODO: this loop can be processed in parallel
message_validate_context mvc( trx, m );
auto contract_handlers_itr = message_validate_handlers.find( m.recipient );
if( contract_handlers_itr != message_validate_handlers.end() ) {
auto message_handelr_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handelr_itr != contract_handlers_itr->second.end() ) {
message_handelr_itr->second(mvc);
continue;
}
}
/// TODO: dispatch to script if not handled above
}
EOS_ASSERT(trx.messages.size() > 0, transaction_exception, "A transaction must have at least one message");
validate_uniqueness( trx );
validate_tapos( trx );
validate_referenced_accounts( trx );
validate_message_types( trx );
for( const auto& m : trx.messages ) { /// TODO: this loop can be processed in parallel
message_validate_context mvc( trx, m );
auto contract_handlers_itr = message_validate_handlers.find( m.recipient );
if( contract_handlers_itr != message_validate_handlers.end() ) {
auto message_handelr_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handelr_itr != contract_handlers_itr->second.end() ) {
message_handelr_itr->second(mvc);
continue;
}
}
/// TODO: dispatch to script if not handled above
}
} FC_CAPTURE_AND_RETHROW( (trx) ) }
void database::validate_message_precondition( precondition_validate_context& context )const {
const auto& m = context.msg;
auto contract_handlers_itr = precondition_validate_handlers.find( context.receiver );
if( contract_handlers_itr != precondition_validate_handlers.end() ) {
auto message_handelr_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handelr_itr != contract_handlers_itr->second.end() ) {
message_handelr_itr->second(context);
auto message_handler_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handler_itr != contract_handlers_itr->second.end() ) {
message_handler_itr->second(context);
return;
}
}
......@@ -559,9 +557,9 @@ void database::apply_message( apply_context& context ) {
const auto& m = context.msg;
auto contract_handlers_itr = apply_handlers.find( context.receiver );
if( contract_handlers_itr != apply_handlers.end() ) {
auto message_handelr_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handelr_itr != contract_handlers_itr->second.end() ) {
message_handelr_itr->second(context);
auto message_handler_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handler_itr != contract_handlers_itr->second.end() ) {
message_handler_itr->second(context);
return;
}
}
......@@ -589,7 +587,7 @@ void database::_apply_transaction(const signed_transaction& trx)
}
//Insert transaction into unique transactions database.
if( check_for_duplicate_transactions() )
if( should_check_for_duplicate_transactions() )
{
create<transaction_object>([&](transaction_object& transaction) {
transaction.trx_id = trx.id(); /// TODO: consider caching ID
......@@ -729,7 +727,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
create<account_object>([&](account_object& a) {
a.name = "sys";
});
init_sys_contract();
// Create initial accounts
for (const auto& acct : genesis_state.initial_accounts) {
......
......@@ -336,8 +336,8 @@ namespace eos { namespace chain {
bool check_for_duplicate_transactions()const { return !(_skip_flags&skip_transaction_dupe_check); }
bool check_tapos()const { return !(_skip_flags&skip_tapos_check); }
bool should_check_for_duplicate_transactions()const { return !(_skip_flags&skip_transaction_dupe_check); }
bool should_check_tapos()const { return !(_skip_flags&skip_tapos_check); }
///Steps involved in applying a new block
///@{
......
......@@ -36,13 +36,13 @@
#define EOS_DECLARE_OP_BASE_EXCEPTIONS( op_name ) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _validate_exception, \
eos::chain::operation_validate_exception, \
eos::chain::message_validate_exception, \
3040000 + 100 * operation::tag< op_name ## _operation >::value, \
#op_name "_operation validation exception" \
) \
FC_DECLARE_DERIVED_EXCEPTION( \
op_name ## _evaluate_exception, \
eos::chain::operation_evaluate_exception, \
eos::chain::message_evaluate_exception, \
3050000 + 100 * operation::tag< op_name ## _operation >::value, \
#op_name "_operation evaluation exception" \
)
......@@ -71,13 +71,14 @@ namespace eos { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( database_query_exception, eos::chain::chain_exception, 3010000, "database query exception" )
FC_DECLARE_DERIVED_EXCEPTION( block_validate_exception, eos::chain::chain_exception, 3020000, "block validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( transaction_exception, eos::chain::chain_exception, 3030000, "transaction validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( operation_validate_exception, eos::chain::chain_exception, 3040000, "operation validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( operation_evaluate_exception, eos::chain::chain_exception, 3050000, "operation evaluation exception" )
FC_DECLARE_DERIVED_EXCEPTION( utility_exception, eos::chain::chain_exception, 3060000, "utility method exception" )
FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception, eos::chain::chain_exception, 3070000, "undo database exception" )
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, eos::chain::chain_exception, 3080000, "unlinkable block" )
FC_DECLARE_DERIVED_EXCEPTION( black_swan_exception, eos::chain::chain_exception, 3090000, "black swan" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_block_exception, eos::chain::chain_exception, 3100000, "unknown block" )
FC_DECLARE_DERIVED_EXCEPTION( message_validate_exception, eos::chain::chain_exception, 3040000, "message validation exception" )
FC_DECLARE_DERIVED_EXCEPTION( message_precondition_exception, eos::chain::chain_exception, 3050000, "message precondition exception" )
FC_DECLARE_DERIVED_EXCEPTION( message_evaluate_exception, eos::chain::chain_exception, 3060000, "message evaluation exception" )
FC_DECLARE_DERIVED_EXCEPTION( utility_exception, eos::chain::chain_exception, 3070000, "utility method exception" )
FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception, eos::chain::chain_exception, 3080000, "undo database exception" )
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, eos::chain::chain_exception, 3090000, "unlinkable block" )
FC_DECLARE_DERIVED_EXCEPTION( black_swan_exception, eos::chain::chain_exception, 3100000, "black swan" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_block_exception, eos::chain::chain_exception, 3110000, "unknown block" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth, eos::chain::transaction_exception, 3030001, "missing required active authority" )
FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth, eos::chain::transaction_exception, 3030002, "missing required owner authority" )
......
#pragma once
#include <eos/chain/protocol/types.hpp>
#include <eos/chain/protocol/authority.hpp>
/**
* @file sys_contract
* @brief defines logic for the native eos system contract
*/
namespace eos { namespace chain {
struct Transfer {
account_name to;
uint64_t amount = 0;
string memo;
};
struct CreateAccount {
account_name new_account;
Authority owner;
Authority active;
uint64_t initial_balance = 0;
};
} }
FC_REFLECT( eos::chain::Transfer, (to)(amount)(memo) )
FC_REFLECT( eos::chain::CreateAccount, (new_account)(owner)(active)(initial_balance) )
#include <eos/chain/database.hpp>
#include <eos/chain/sys_contract.hpp>
#include <eos/chain/account_object.hpp>
namespace eos { namespace chain {
void database::init_sys_contract() {
wlog( "init sys contract" );
set_validate_handler( "sys", "sys", "Transfer", [&]( message_validate_context& context ) {
auto transfer = context.msg.as<Transfer>();
FC_ASSERT( context.msg.has_notify( transfer.to ), "Must notify recipient of transfer" );
});
set_precondition_validate_handler( "sys", "sys", "Transfer", [&]( precondition_validate_context& context ) {
auto transfer = context.msg.as<Transfer>();
const auto& from = get_account( context.msg.sender );
FC_ASSERT( from.balance > transfer.amount, "Insufficient Funds",
("from.balance",from.balance)("transfer.amount",transfer.amount) );
});
set_apply_handler( "sys", "sys", "Transfer", [&]( apply_context& context ) {
auto transfer = context.msg.as<Transfer>();
const auto& from = get_account( context.msg.sender );
const auto& to = get_account( transfer.to );
modify( from, [&]( account_object& a ) {
a.balance -= transfer.amount;
});
modify( to, [&]( account_object& a ) {
a.balance += transfer.amount;
});
});
}
} } // namespace eos::chain
......@@ -4,3 +4,4 @@ add_subdirectory(http_plugin)
add_subdirectory(chain_plugin)
add_subdirectory(chain_api_plugin)
add_subdirectory(producer_plugin)
add_subdirectory(native_system_contract_plugin)
......@@ -12,20 +12,6 @@ public:
chain_api_plugin_impl(database& db)
: db(db) {}
void get_info(url_response_callback& cb) {
fc::mutable_variant_object response;
response["head_block_num"] = db.head_block_num();
response["head_block_id"] = db.head_block_id();
response["head_block_time"] = db.head_block_time();
response["head_block_producer"] = db.head_block_producer();
response["recent_slots"] = std::bitset<64>(db.get_dynamic_global_properties().recent_slots_filled).to_string();
response["participation_rate"] =
__builtin_popcountll(db.get_dynamic_global_properties().recent_slots_filled) / 64.0;
cb(200, fc::json::to_string(response));
}
database& db;
};
......@@ -37,9 +23,29 @@ chain_api_plugin::~chain_api_plugin(){}
void chain_api_plugin::set_program_options(options_description&, options_description&) {}
void chain_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) { \
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.what()); \
elog("Exception encountered while processing ${call}: ${e}", ("call", #api_name "." #call_name)("e", e)); \
} \
}}
#define CHAIN_RO_CALL(call_name) CALL(chain, ro_api, chain_apis::read_only, call_name)
void chain_api_plugin::plugin_startup() {
auto ro_api = app().get_plugin<chain_plugin>().get_read_only_api();
app().get_plugin<http_plugin>().add_api({
{std::string("/v1/chain/get_info"), [this](string, string, url_response_callback cb) {my->get_info(cb);}}
CHAIN_RO_CALL(get_info),
CHAIN_RO_CALL(get_block)
});
}
......
......@@ -30,6 +30,7 @@ chain_plugin::chain_plugin()
chain_plugin::~chain_plugin(){}
database& chain_plugin::db() { return my->db; }
const chain::database& chain_plugin::db() const { return my->db; }
void chain_plugin::set_program_options(options_description& cli, options_description& cfg)
{
......@@ -145,4 +146,32 @@ bool chain_plugin::block_is_on_preferred_chain(const chain::block_id_type& block
return db().get_block_id_for_num(chain::block_header::num_from_id(block_id)) == block_id;
}
namespace chain_apis {
read_only::get_info_results read_only::get_info(const read_only::get_info_params&) const {
return {
db.head_block_num(),
db.head_block_id(),
db.head_block_time(),
db.head_block_producer(),
std::bitset<64>(db.get_dynamic_global_properties().recent_slots_filled).to_string(),
__builtin_popcountll(db.get_dynamic_global_properties().recent_slots_filled) / 64.0
};
}
read_only::get_block_results read_only::get_block(const read_only::get_block_params& params) const {
try {
if (auto block = db.fetch_block_by_id(fc::json::from_string(params.block_num_or_id).as<chain::block_id_type>()))
return *block;
} catch (fc::bad_cast_exception) {/* do nothing */}
try {
if (auto block = db.fetch_block_by_number(fc::to_uint64(params.block_num_or_id)))
return *block;
} catch (fc::bad_cast_exception) {/* do nothing */}
FC_THROW_EXCEPTION(chain::unknown_block_exception,
"Could not find block: ${block}", ("block", params.block_num_or_id));
}
} // namespace chain_apis
} // namespace eos
......@@ -7,29 +7,68 @@ namespace eos {
using std::unique_ptr;
using namespace appbase;
class chain_plugin : public plugin<chain_plugin>
{
public:
APPBASE_PLUGIN_REQUIRES()
namespace chain_apis {
struct empty{};
chain_plugin();
virtual ~chain_plugin();
class read_only {
const database& db;
virtual void set_program_options(options_description& cli, options_description& cfg) override;
public:
read_only(const database& db)
: db(db) {}
void plugin_initialize(const variables_map& options);
void plugin_startup();
void plugin_shutdown();
using get_info_params = empty;
struct get_info_results {
uint32_t head_block_num;
chain::block_id_type head_block_id;
fc::time_point_sec head_block_time;
chain::producer_id_type head_block_producer;
string recent_slots;
double participation_rate;
};
get_info_results get_info(const get_info_params&) const;
bool accept_block(const chain::signed_block& block, bool currently_syncing);
void accept_transaction(const chain::signed_transaction& trx);
struct get_block_params {
string block_num_or_id;
};
using get_block_results = chain::signed_block;
get_block_results get_block(const get_block_params& params) const;
};
} // namespace chain_apis
bool block_is_on_preferred_chain(const chain::block_id_type& block_id);
class chain_plugin : public plugin<chain_plugin> {
public:
APPBASE_PLUGIN_REQUIRES()
database& db();
chain_plugin();
virtual ~chain_plugin();
private:
unique_ptr<class chain_plugin_impl> my;
};
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();
chain_apis::read_only get_read_only_api() const {
return chain_apis::read_only(db());
}
bool accept_block(const chain::signed_block& block, bool currently_syncing);
void accept_transaction(const chain::signed_transaction& trx);
bool block_is_on_preferred_chain(const chain::block_id_type& block_id);
database& db();
const database& db() const;
private:
unique_ptr<class chain_plugin_impl> my;
};
}
FC_REFLECT(eos::chain_apis::empty, )
FC_REFLECT(eos::chain_apis::read_only::get_info_results,
(head_block_num)(head_block_id)(head_block_time)(head_block_producer)
(recent_slots)(participation_rate))
FC_REFLECT(eos::chain_apis::read_only::get_block_params, (block_num_or_id))
#!/bin/bash
if [ $# -ne 1 ]; then
echo Usage: $0 my_new_plugin
echo ... where my_new_plugin is the name of the plugin you want to create
exit 1
fi
pluginName=$1
echo Copying template...
cp -r template_plugin $pluginName
echo Renaming files/directories...
mv $pluginName/include/eos/template_plugin $pluginName/include/eos/$pluginName
for file in `find $pluginName -type f -name '*template_plugin*'`; do mv $file `sed s/template_plugin/$pluginName/g <<< $file`; done;
echo Renaming in files...
find $pluginName -type f -exec sed -i "s/template_plugin/$pluginName/g" {} \;
echo "Done! $pluginName is ready. Don't forget to add it to CMakeLists.txt!"
file(GLOB HEADERS "include/eos/native_system_contract_plugin/*.hpp")
add_library( native_system_contract_plugin
native_system_contract_plugin.cpp
${HEADERS} )
target_link_libraries( native_system_contract_plugin chain_plugin appbase fc )
target_include_directories( native_system_contract_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
native_system_contract_plugin
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install( FILES ${HEADERS} DESTINATION "include/eos/native_system_contract_plugin" )
#pragma once
#include <appbase/application.hpp>
#include <eos/chain_plugin/chain_plugin.hpp>
#include <eos/chain/protocol/authority.hpp>
#include <eos/chain/protocol/types.hpp>
namespace eos {
using namespace appbase;
using chain::account_name;
using chain::Authority;
struct Transfer {
account_name to;
uint64_t amount = 0;
string memo;
};
struct CreateAccount {
account_name new_account;
Authority owner;
Authority active;
uint64_t initial_balance = 0;
};
/**
* @brief This class is a native C++ implementation of the system contract.
*/
class native_system_contract_plugin : public appbase::plugin<native_system_contract_plugin> {
public:
native_system_contract_plugin();
virtual ~native_system_contract_plugin();
APPBASE_PLUGIN_REQUIRES((chain_plugin))
virtual void set_program_options(options_description&, options_description&) override {}
void plugin_initialize(const variables_map& options);
void plugin_startup();
void plugin_shutdown();
/**
* @brief Install the system contract implementation on the provided database
*
* Installs the native system contract on the provided database. This method is static, and may be used without a
* native_system_contract_plugin or even AppBase. All that is required is a database to install the implementation
* on.
*/
static void install(chain::database& db);
private:
std::unique_ptr<class native_system_contract_plugin_impl> my;
};
}
FC_REFLECT(eos::Transfer, (to)(amount)(memo))
FC_REFLECT(eos::CreateAccount, (new_account)(owner)(active)(initial_balance))
#include <eos/native_system_contract_plugin/native_system_contract_plugin.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/chain/exceptions.hpp>
namespace eos {
using namespace chain;
void Transfer_validate(chain::message_validate_context& context) {
auto transfer = context.msg.as<Transfer>();
EOS_ASSERT(context.msg.has_notify(transfer.to), message_validate_exception, "Must notify recipient of transfer");
}
void Transfer_validate_preconditions(chain::precondition_validate_context& context) {
const auto& db = context.db;
auto transfer = context.msg.as<Transfer>();
const auto& from = db.get_account(context.msg.sender);
EOS_ASSERT(from.balance > transfer.amount, message_precondition_exception, "Insufficient Funds",
("from.balance",from.balance)("transfer.amount",transfer.amount));
}
void Transfer_apply(chain::apply_context& context) {
auto& db = context.mutable_db;
auto transfer = context.msg.as<Transfer>();
const auto& from = db.get_account(context.msg.sender);
const auto& to = db.get_account(transfer.to);
db.modify(from, [&](account_object& a) {
a.balance -= transfer.amount;
});
db.modify(to, [&](account_object& a) {
a.balance += transfer.amount;
});
}
class native_system_contract_plugin_impl {
public:
native_system_contract_plugin_impl(database& db)
: db(db) {}
database& db;
};
native_system_contract_plugin::native_system_contract_plugin()
: my(new native_system_contract_plugin_impl(app().get_plugin<chain_plugin>().db())){}
native_system_contract_plugin::~native_system_contract_plugin(){}
void native_system_contract_plugin::plugin_initialize(const variables_map& options) {
install(my->db);
}
void native_system_contract_plugin::plugin_startup() {
// Make the magic happen
}
void native_system_contract_plugin::plugin_shutdown() {
// OK, that's enough magic
}
void native_system_contract_plugin::install(database& db) {
#define SET_HANDLERS(name) \
db.set_validate_handler("sys", "sys", #name, \
[&db](chain::message_validate_context& c) mutable { name ## _validate(c); }); \
db.set_precondition_validate_handler("sys", "sys", #name, \
[&db](chain::precondition_validate_context& c) mutable { name ## _validate_preconditions(c); }); \
db.set_apply_handler("sys", "sys", #name, \
[&db](chain::apply_context& c) mutable { name ## _apply(c); })
SET_HANDLERS(Transfer);
}
}
......@@ -2,6 +2,7 @@ file(GLOB HEADERS "include/eos/p2p_plugin/*.hpp")
add_library( p2p_plugin
p2p_plugin.cpp
${HEADERS}
)
target_link_libraries( p2p_plugin chain_plugin appbase eos_chain eos_utilities eos_net )
......
file(GLOB HEADERS "include/eos/template_plugin/*.hpp")
add_library( template_plugin
template_plugin.cpp
${HEADERS} )
target_link_libraries( template_plugin appbase fc )
target_include_directories( template_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
template_plugin
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install( FILES ${HEADERS} DESTINATION "include/eos/template_plugin" )
#pragma once
#include <appbase/application.hpp>
namespace eos {
using namespace appbase;
/**
* This is a template plugin, intended to serve as a starting point for making new plugins
*/
class template_plugin : public appbase::plugin<template_plugin>
{
public:
template_plugin();
virtual ~template_plugin();
APPBASE_PLUGIN_REQUIRES()
virtual void set_program_options(options_description&, options_description& cfg) override;
void plugin_initialize(const variables_map& options);
void plugin_startup();
void plugin_shutdown();
private:
std::unique_ptr<class template_plugin_impl> my;
};
}
#include <eos/template_plugin/template_plugin.hpp>
namespace eos {
class template_plugin_impl {
public:
};
template_plugin::template_plugin():my(new template_plugin_impl()){}
template_plugin::~template_plugin(){}
void template_plugin::set_program_options(options_description&, options_description& cfg) {
cfg.add_options()
("option-name", bpo::value<string>()->default_value("default value"),
"Option Description")
;
}
void template_plugin::plugin_initialize(const variables_map& options) {
if(options.count("option-name")) {
// Handle the option
}
}
void template_plugin::plugin_startup() {
// Make the magic happen
}
void template_plugin::plugin_shutdown() {
// OK, that's enough magic
}
}
......@@ -8,4 +8,4 @@ endif()
file(GLOB UNIT_TESTS "tests/*.cpp")
add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} )
target_link_libraries( chain_test eos_chain eos_utilities eos_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
target_link_libraries( chain_test native_system_contract_plugin eos_chain eos_utilities eos_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
......@@ -23,12 +23,15 @@
*/
#include <boost/test/unit_test.hpp>
#include <boost/program_options.hpp>
#include <boost/signals2/shared_connection_block.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/utilities/tempdir.hpp>
#include <eos/native_system_contract_plugin/native_system_contract_plugin.hpp>
#include <fc/crypto/digest.hpp>
#include <fc/smart_ref_impl.hpp>
......@@ -89,6 +92,8 @@ testing_database::testing_database(testing_fixture& fixture, std::string id,
: genesis_state(override_genesis_state? *override_genesis_state : fixture.genesis_state()),
fixture(fixture) {
data_dir = fixture.get_temp_dir(id);
// Install the system contract implementation
native_system_contract_plugin::install(*this);
}
void testing_database::open() {
......@@ -151,9 +156,8 @@ void testing_network::connect_database(testing_database& new_database) {
}
// The new database is now in sync with any old ones; go ahead and connect the propagation signal.
databases[&new_database] = new_database.applied_block.connect([this](const signed_block& block) {
if (!currently_propagating_block)
propagate_block(block);
databases[&new_database] = new_database.applied_block.connect([this, &new_database](const signed_block& block) {
propagate_block(block, new_database);
});
}
......@@ -165,11 +169,12 @@ void testing_network::disconnect_all() {
databases.clear();
}
void testing_network::propagate_block(const signed_block& block) {
currently_propagating_block = true;
for (const auto& pair : databases)
void testing_network::propagate_block(const signed_block& block, const testing_database& skip_db) {
for (const auto& pair : databases) {
if (pair.first == &skip_db) continue;
boost::signals2::shared_connection_block blocker(pair.second);
pair.first->push_block(block);
currently_propagating_block = false;
}
}
} } // eos::chain
......@@ -26,7 +26,9 @@
#include <eos/chain/database.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/chain/exceptions.hpp>
#include <eos/utilities/tempdir.hpp>
#include <fc/io/json.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/signals.hpp>
......@@ -240,11 +242,10 @@ public:
* @brief Send a block to all databases in this network
* @param block The block to send
*/
void propagate_block(const signed_block& block);
void propagate_block(const signed_block& block, const testing_database& skip_db);
protected:
std::map<testing_database*, fc::scoped_connection> databases;
bool currently_propagating_block = false;
};
/// Some helpful macros to reduce boilerplate when making a testing_network and connecting some testing_databases @{
......
......@@ -27,7 +27,8 @@
#include <eos/chain/database.hpp>
#include <eos/chain/exceptions.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/chain/sys_contract.hpp>
#include <eos/native_system_contract_plugin/native_system_contract_plugin.hpp>
#include <eos/utilities/tempdir.hpp>
......@@ -64,27 +65,27 @@ BOOST_FIXTURE_TEST_CASE(transfer, testing_fixture)
BOOST_CHECK_EQUAL(db.head_block_num(), 10);
signed_transaction trx;
BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::assert_exception ); /// no messages
BOOST_REQUIRE_THROW(db.push_transaction(trx), transaction_exception); // no messages
trx.messages.resize(1);
trx.set_reference_block( db.head_block_id() );
trx.set_expiration( db.head_block_time() );
trx.set_reference_block(db.head_block_id());
trx.set_expiration(db.head_block_time() + 100);
trx.messages[0].sender = "init1";
trx.messages[0].recipient = "sys";
trx.messages[0].type = "Undefined";
BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::assert_exception ); // "Type Undefined is not defined"
trx.messages[0].type = "Transfer";
trx.messages[0].set( "Transfer", eos::chain::Transfer{ "init2", 100, "memo" } );
BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::assert_exception ); // "fail to notify receiver, init2"
trx.messages[0].set("Transfer", Transfer{ "init2", 100, "memo" });
BOOST_REQUIRE_THROW(db.push_transaction(trx), message_validate_exception); // "fail to notify receiver, init2"
trx.messages[0].notify = {"init2"};
trx.messages[0].set( "Transfer", eos::chain::Transfer{ "init2", 100, "memo" } );
trx.messages[0].set("Transfer", Transfer{ "init2", 100, "memo" });
db.push_transaction(trx);
BOOST_CHECK_EQUAL( db.get_account( "init1" ).balance, 100000 - 100 );
BOOST_CHECK_EQUAL( db.get_account( "init2" ).balance, 100000 + 100 );
BOOST_CHECK_EQUAL(db.get_account("init1").balance, 100000 - 100);
BOOST_CHECK_EQUAL(db.get_account("init2").balance, 100000 + 100);
db.produce_blocks(1);
BOOST_REQUIRE_THROW( db.push_transaction(trx), fc::assert_exception ); /// no messages
BOOST_REQUIRE_THROW(db.push_transaction(trx), transaction_exception); // not unique
} FC_LOG_AND_RETHROW() }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册