提交 20d9c9b8 编写于 作者: N Nathan Hourt

Move system contract impl to plugin

The system contract C++ implementation is now in a plugin rather than
directly on database.

Note that testing_database installs this implementation on the testing
database, even though the test framework does not use appbase or appbase
plugins.
上级 0175c308
......@@ -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>
......@@ -468,13 +466,17 @@ try {
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 {
......@@ -482,7 +484,8 @@ void database::validate_uniqueness( const signed_transaction& trx )const {
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 {
......@@ -505,25 +508,25 @@ void database::validate_referenced_accounts( const signed_transaction& trx )cons
}
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 );
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_handler_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handler_itr != contract_handlers_itr->second.end() ) {
message_handler_itr->second(mvc);
continue;
}
}
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 );
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_handler_itr = contract_handlers_itr->second.find( {m.recipient, m.type} );
if( message_handler_itr != contract_handlers_itr->second.end() ) {
message_handler_itr->second(mvc);
continue;
}
}
/// TODO: dispatch to script if not handled above
}
/// TODO: dispatch to script if not handled above
}
} FC_CAPTURE_AND_RETHROW( (trx) ) }
void database::validate_message_precondition( precondition_validate_context& context )const {
......@@ -712,7 +715,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) {
......
......@@ -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)
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, database& db) {
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, database& 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, database& 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); }); \
db.set_precondition_validate_handler("sys", "sys", #name, \
[&db](chain::precondition_validate_context& c) mutable { name ## _validate_preconditions(c, db); }); \
db.set_apply_handler("sys", "sys", #name, \
[&db](chain::apply_context& c) mutable { name ## _apply(c, db); })
SET_HANDLERS(Transfer);
}
}
......@@ -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} )
......@@ -30,6 +30,8 @@
#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>
......@@ -90,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() {
......
......@@ -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>
......
......@@ -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,24 +65,24 @@ 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 = "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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册