diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 1de84800c74..8f42ae56ccc 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory( chain ) add_subdirectory( egenesis ) add_subdirectory( utilities ) add_subdirectory( appbase ) +add_subdirectory( native_contract ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 31f3cfcc7a5..c49cefbeb6a 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -13,7 +13,6 @@ add_library( eos_chain block_log.cpp BlockchainConfiguration.cpp - chain_model.cpp ${HEADERS} ) diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 98ce7710889..face4d7fa36 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -153,10 +152,6 @@ std::vector chain_controller::get_block_ids_on_fork(block_id_type return result; } -chain_model chain_controller::get_model() const { - return chain_model(_db); -} - /** * Push block "may fail" in which case every partial change is unwound. After * push block is successful the block is appended to the chain database on disk. @@ -286,7 +281,7 @@ void chain_controller::_push_transaction(const SignedTransaction& trx) { signed_block chain_controller::generate_block( fc::time_point_sec when, - producer_id_type producer_id, + const AccountName& producer, const fc::ecc::private_key& block_signing_private_key, uint32_t skip /* = 0 */ ) @@ -294,7 +289,7 @@ signed_block chain_controller::generate_block( return with_producing( [&]() { return with_skip_flags( skip, [&](){ auto b = _db.with_write_lock( [&](){ - return _generate_block( when, producer_id, block_signing_private_key ); + return _generate_block( when, producer, block_signing_private_key ); }); push_block(b); return b; @@ -304,7 +299,7 @@ signed_block chain_controller::generate_block( signed_block chain_controller::_generate_block( fc::time_point_sec when, - producer_id_type producer_id, + const AccountName& producer, const fc::ecc::private_key& block_signing_private_key ) { @@ -312,10 +307,10 @@ signed_block chain_controller::_generate_block( uint32_t skip = get_node_properties().skip_flags; uint32_t slot_num = get_slot_at_time( when ); FC_ASSERT( slot_num > 0 ); - producer_id_type scheduled_producer = get_scheduled_producer( slot_num ); - FC_ASSERT( scheduled_producer == producer_id ); + AccountName scheduled_producer = get_scheduled_producer( slot_num ); + FC_ASSERT( scheduled_producer == producer ); - const auto& producer_obj = _db.get(scheduled_producer); + const auto& producer_obj = get_producer(scheduled_producer); if( !(skip & skip_producer_signature) ) FC_ASSERT( producer_obj.signing_key == block_signing_private_key.get_public_key() ); @@ -391,7 +386,7 @@ signed_block chain_controller::_generate_block( pending_block.timestamp = when; pending_block.transaction_merkle_root = pending_block.calculate_merkle_root(); - pending_block.producer = static_cast(producer_id._id); //pa.name.c_str(); //producer_id; + pending_block.producer = static_cast(producer_obj.id._id); if( !(skip & skip_producer_signature) ) pending_block.sign( block_signing_private_key ); @@ -407,7 +402,7 @@ signed_block chain_controller::_generate_block( // push_block( pending_block, skip ); return pending_block; -} FC_CAPTURE_AND_RETHROW( (producer_id) ) } +} FC_CAPTURE_AND_RETHROW( (producer) ) } /** * Removes the most recent block from the database and undoes any changes it made. @@ -512,7 +507,7 @@ try { for (const auto& tm : trx.messages) { /// TODO: this loop can be processed in parallel Message m(tm); - message_validate_context mvc(trx, m); + message_validate_context mvc(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}); @@ -547,16 +542,15 @@ void chain_controller::validate_tapos(const SignedTransaction& trx)const { } void chain_controller::validate_referenced_accounts(const SignedTransaction& trx)const { - auto model = get_model(); for(const auto& auth : trx.authorizations) { - model.get_account(auth.account); + require_account(auth.account); } for(const auto& msg : trx.messages) { - model.get_account(msg.sender); - model.get_account(msg.recipient); + require_account(msg.sender); + require_account(msg.recipient); const AccountName* previous_notify_account = nullptr; for(const auto& current_notify_account : msg.notify) { - model.get_account(current_notify_account); + require_account(current_notify_account); if(previous_notify_account) { EOS_ASSERT(current_notify_account < *previous_notify_account, message_validate_exception, "Message notify accounts out of order. Possibly a bug in the wallet?"); @@ -599,23 +593,41 @@ try { void chain_controller::validate_message_precondition( precondition_validate_context& context )const { try { const auto& m = context.msg; - auto contract_handlers_itr = precondition_validate_handlers.find( context.recipient ); + auto contract_handlers_itr = precondition_validate_handlers.find( m.recipient ); if( contract_handlers_itr != precondition_validate_handlers.end() ) { - auto message_handler_itr = contract_handlers_itr->second.find( {m.recipient, m.type} ); + auto message_handler_itr = contract_handlers_itr->second.find( {context.recipient, m.type} ); if( message_handler_itr != contract_handlers_itr->second.end() ) { message_handler_itr->second(context); return; } } /// TODO: dispatch to script if not handled above -} FC_CAPTURE_AND_RETHROW() } + } FC_CAPTURE_AND_RETHROW() } + +void chain_controller::process_message(Message message) { + apply_context apply_ctx(_db, message, message.recipient); + + /** TODO: pre condition validation and application can occur in parallel */ + /** TODO: verify that message is fully authorized + (check that @ref SignedTransaction::authorizations are all present) */ + validate_message_precondition(apply_ctx); + apply_message(apply_ctx); + + for (const auto& notify_account : message.notify) { + try { + apply_context notify_ctx(_db, message, notify_account); + validate_message_precondition(notify_ctx); + apply_message(notify_ctx); + } FC_CAPTURE_AND_RETHROW((notify_account)(message)) + } +} void chain_controller::apply_message( apply_context& context ) { try { const auto& m = context.msg; - auto contract_handlers_itr = apply_handlers.find( context.recipient ); + auto contract_handlers_itr = apply_handlers.find( m.recipient ); if( contract_handlers_itr != apply_handlers.end() ) { - auto message_handler_itr = contract_handlers_itr->second.find( {m.recipient, m.type} ); + auto message_handler_itr = contract_handlers_itr->second.find( {context.recipient, m.type} ); if( message_handler_itr != contract_handlers_itr->second.end() ) { message_handler_itr->second(context); return; @@ -641,50 +653,38 @@ void chain_controller::apply_message( apply_context& context ) apply_method( &context, 1 ); } /// TODO: dispatch to script if not handled above -} FC_CAPTURE_AND_RETHROW() } +} FC_CAPTURE_AND_RETHROW((context.msg)) } void chain_controller::_apply_transaction(const SignedTransaction& trx) { try { validate_transaction(trx); - // trx.messages is a vector, but we want a chain::Message, so go ahead and copy it into one. - for( Message m : trx.messages ) { - // apply_context will store a reference to the chain::Message argument. m must *not* be a types::Message here, or - // the compiler will create a temporary chain::Message out of the types::Message and pass a reference to that to - // ac. ac will keep the temporary reference, which will be invalid as soon as this next line finishes running. - apply_context ac( _db, trx, m, m.recipient ); - - /** TODO: pre condition validation and application can occur in parallel */ - validate_message_precondition( ac ); - apply_message( ac ); - - for( const auto& n : m.notify ) { - try { - apply_context c( _db, trx, m, n ); - validate_message_precondition( c ); - apply_message( c ); - } FC_CAPTURE_AND_RETHROW( (n) ) - } - + for (const auto& message : trx.messages) { + process_message(message); } //Insert transaction into unique transactions database. - if( should_check_for_duplicate_transactions() ) + if (should_check_for_duplicate_transactions()) { _db.create([&](transaction_object& transaction) { transaction.trx_id = trx.id(); /// TODO: consider caching ID transaction.trx = trx; }); } -} FC_CAPTURE_AND_RETHROW((trx)) } + } FC_CAPTURE_AND_RETHROW((trx)) } + +void chain_controller::require_account(const types::AccountName& name) const { + auto account = _db.find(name); + FC_ASSERT(account != nullptr, "Account not found: ${name}", ("name", name)); +} const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const { FC_ASSERT(head_block_id() == next_block.previous, "", ("head_block_id",head_block_id())("next.prev",next_block.previous)); FC_ASSERT(head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num())); - const producer_object& producer = _db.get(get_scheduled_producer(get_slot_at_time(next_block.timestamp))); + const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp))); if(!(skip&skip_producer_signature)) FC_ASSERT(next_block.validate_signee(producer.signing_key), @@ -693,7 +693,7 @@ const producer_object& chain_controller::validate_block_header(uint32_t skip, co if(!(skip&skip_producer_schedule_check)) { FC_ASSERT(next_block.producer == producer.id, "Producer produced block at wrong time", - ("block producer",next_block.producer)("scheduled producer",producer.id)); + ("block producer",next_block.producer)("scheduled producer",producer.id._id)); } return producer; @@ -751,20 +751,20 @@ block_id_type chain_controller::head_block_id()const { return get_dynamic_global_properties().head_block_id; } -producer_id_type chain_controller::head_block_producer() const { +types::AccountName chain_controller::head_block_producer() const { if (auto head_block = fetch_block_by_id(head_block_id())) - return head_block->producer; + return _db.get((producer_object::id_type)head_block->producer).owner; return {}; } -const chain_id_type& chain_controller::get_chain_id()const { - return _db.get().chain_id; -} - const node_property_object& chain_controller::get_node_properties()const { return _node_property_object; } +const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const { + return _db.get(ownerName); +} + node_property_object& chain_controller::node_properties() { return _node_property_object; } @@ -786,17 +786,12 @@ void chain_controller::initialize_indexes() { _db.add_index(); _db.add_index(); _db.add_index(); - _db.add_index(); } -void chain_controller::initialize_genesis(std::function genesis_loader) +void chain_controller::initialize_chain(chain_initializer& starter) { try { if (!_db.find()) { - _db.with_write_lock([this, genesis_state = genesis_loader()] { - FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." ); - FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % config::BlockIntervalSeconds == 0, - "Genesis timestamp must be divisible by config::BlockIntervalSeconds." ); - + _db.with_write_lock([this, &starter] { struct auth_inhibitor { auth_inhibitor(chain_controller& db) : db(db), old_flags(db.node_properties().skip_flags) { db.node_properties().skip_flags |= skip_authority_check; } @@ -807,76 +802,34 @@ void chain_controller::initialize_genesis(std::function ge uint32_t old_flags; } inhibitor(*this); + auto messages = starter.prepare_database(*this, _db); - /// create the native contract accounts - _db.create([&](account_object& a) { - a.name = config::SystemContractName; - }); - _db.create([&](account_object& a) { - a.name = config::EosContractName; - }); - _db.create([&](account_object& a) { - a.name = config::StakedBalanceContractName; - }); - - // Register native contract message types -#define MACRO(r, data, elem) register_type(data); - BOOST_PP_SEQ_FOR_EACH(MACRO, config::SystemContractName, EOS_SYSTEM_CONTRACT_FUNCTIONS) - BOOST_PP_SEQ_FOR_EACH(MACRO, config::EosContractName, EOS_CONTRACT_FUNCTIONS) - BOOST_PP_SEQ_FOR_EACH(MACRO, config::StakedBalanceContractName, EOS_STAKED_BALANCE_CONTRACT_FUNCTIONS) -#undef MACRO - - // Create initial accounts - for (const auto& acct : genesis_state.initial_accounts) { - _db.create([&acct](account_object& a) { - a.name = acct.name.c_str(); - a.balance = acct.balance; - // idump((acct.name)(a.balance)); - // a.active_key = acct.active_key; - // a.owner_key = acct.owner_key; - }); - } - // Create initial producers - std::vector initial_producers; - for (const auto& producer : genesis_state.initial_producers) { - const auto& owner = _db.get(producer.owner_name); - auto id = _db.create([&](producer_object& w) { - w.signing_key = producer.block_signing_key; - w.owner = owner.id; - }).id; - initial_producers.push_back(id); - } - - // Initialize block summary index -#warning TODO: Figure out how to do this - - chain_id_type chain_id = genesis_state.compute_chain_id(); + auto initial_timestamp = starter.get_chain_start_time(); + FC_ASSERT(initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." ); + FC_ASSERT(initial_timestamp.sec_since_epoch() % config::BlockIntervalSeconds == 0, + "Genesis timestamp must be divisible by config::BlockIntervalSeconds." ); // Create global properties - _db.create([&](global_property_object& p) { - p.configuration = genesis_state.initial_configuration; - std::copy(initial_producers.begin(), initial_producers.end(), p.active_producers.begin()); + _db.create([&starter](global_property_object& p) { + p.configuration = starter.get_chain_start_configuration(); + p.active_producers = starter.get_chain_start_producers(); }); _db.create([&](dynamic_global_property_object& p) { - p.time = genesis_state.initial_timestamp; + p.time = initial_timestamp; p.recent_slots_filled = uint64_t(-1); }); - FC_ASSERT((genesis_state.immutable_parameters.min_producer_count & 1) == 1, "min_producer_count must be odd"); - - _db.create([&](chain_property_object& p) { - p.chain_id = chain_id; - p.immutable_parameters = genesis_state.immutable_parameters; - }); - + // Initialize block summary index for (int i = 0; i < 0x10000; i++) _db.create([&](block_summary_object&) {}); + + std::for_each(messages.begin(), messages.end(), [this](const auto& m) { process_message(m); }); }); } } FC_CAPTURE_AND_RETHROW() } -chain_controller::chain_controller(database& database, fork_database& fork_db, block_log& blocklog, - std::function genesis_loader) +chain_controller::chain_controller(database& database, fork_database& fork_db, + block_log& blocklog, chain_initializer& starter) : _db(database), _fork_db(fork_db), _block_log(blocklog) { static bool bound_apply = [](){ wrenpp::beginModule( "main" ) @@ -889,7 +842,7 @@ chain_controller::chain_controller(database& database, fork_database& fork_db, b }(); initialize_indexes(); - initialize_genesis(genesis_loader); + initialize_chain(starter); spinup_db(); spinup_fork_db(); @@ -967,7 +920,7 @@ void chain_controller::update_global_dynamic_data(const signed_block& b) { // wlog("Blockchain continuing after gap of ${b} missed blocks", ("b", missed_blocks)); for(uint32_t i = 0; i < missed_blocks; ++i) { - const auto& producer_missed = _db.get(get_scheduled_producer(i+1)); + const auto& producer_missed = get_producer(get_scheduled_producer(i+1)); if(producer_missed.id != b.producer) { /* const auto& producer_account = producer_missed.producer_account(*this); @@ -986,7 +939,7 @@ void chain_controller::update_global_dynamic_data(const signed_block& b) { dgp.head_block_number = b.block_num(); dgp.head_block_id = b.id(); dgp.time = b.timestamp; - dgp.current_producer = b.producer; + dgp.current_producer = _db.get(producer_object::id_type(b.producer)).owner; dgp.current_absolute_slot += missed_blocks+1; // If we've missed more blocks than the bitmap stores, skip calculations and simply reset the bitmap @@ -1021,7 +974,7 @@ void chain_controller::update_last_irreversible_block() vector producer_objs; producer_objs.reserve(gpo.active_producers.size()); std::transform(gpo.active_producers.begin(), gpo.active_producers.end(), std::back_inserter(producer_objs), - [this](producer_id_type id) { return &_db.get(id); }); + [this](const AccountName& owner) { return &get_producer(owner); }); static_assert(config::IrreversibleThresholdPercent > 0, "irreversible threshold must be nonzero"); @@ -1071,7 +1024,7 @@ void chain_controller::clear_expired_transactions() } FC_CAPTURE_AND_RETHROW() } void chain_controller::update_blockchain_configuration() { - auto get_producer = [this](producer_id_type id) { return _db.get(id); }; + auto get_producer = [this](const AccountName& owner) { return this->get_producer(owner); }; auto get_votes = [](const producer_object& p) { return p.configuration; }; using boost::adaptors::transformed; @@ -1085,7 +1038,7 @@ void chain_controller::update_blockchain_configuration() { using boost::container::flat_set; -producer_id_type chain_controller::get_scheduled_producer(uint32_t slot_num)const +types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)const { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); uint64_t current_aslot = dpo.current_absolute_slot + slot_num; @@ -1142,4 +1095,6 @@ void chain_controller::set_apply_handler( const AccountName& contract, const Acc apply_handlers[contract][std::make_pair(scope,action)] = v; } +chain_initializer::~chain_initializer() {} + } } diff --git a/libraries/chain/chain_model.cpp b/libraries/chain/chain_model.cpp deleted file mode 100644 index 3af5c2f7200..00000000000 --- a/libraries/chain/chain_model.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include - -namespace eos { namespace chain { - -const account_object& chain_model::get_account(const AccountName& name) const { - return db.get(name); -} - -const producer_object& chain_model::get_producer(const AccountName& name) const { - return db.get(get_account(name).id); -} - -} } // namespace eos::chain diff --git a/libraries/chain/include/eos/chain/account_object.hpp b/libraries/chain/include/eos/chain/account_object.hpp index 0f98696ecd9..4e787e5d7a4 100644 --- a/libraries/chain/include/eos/chain/account_object.hpp +++ b/libraries/chain/include/eos/chain/account_object.hpp @@ -55,15 +55,15 @@ namespace eos { namespace chain { shared_vector accounts; shared_vector keys; }; - - class account_object : public chainbase::object - { + + class account_object : public chainbase::object { OBJECT_CTOR(account_object) id_type id; AccountName name; - Asset balance; + Time creation_date; }; + using account_id_type = account_object::id_type; struct by_name; using account_index = chainbase::shared_multi_index_container< @@ -74,39 +74,41 @@ namespace eos { namespace chain { > >; - class permission_object : public chainbase::object - { + class permission_object : public chainbase::object { OBJECT_CTOR(permission_object, (auth) ) id_type id; - account_id_type owner; ///< the account this permission belongs to + AccountName owner; ///< the account this permission belongs to id_type parent; ///< parent permission PermissionName name; shared_authority auth; ///< TODO }; - - struct by_parent; struct by_owner; using permission_index = chainbase::shared_multi_index_container< permission_object, indexed_by< ordered_unique, member>, - ordered_unique, - composite_key< permission_object, + ordered_unique, + composite_key, member > >, - ordered_unique, - composite_key< permission_object, - member, + ordered_unique, + composite_key, member, member > >, - ordered_unique, member > + ordered_unique, + composite_key, + member + > + > > >; @@ -115,5 +117,9 @@ namespace eos { namespace chain { CHAINBASE_SET_INDEX_TYPE(eos::chain::account_object, eos::chain::account_index) CHAINBASE_SET_INDEX_TYPE(eos::chain::permission_object, eos::chain::permission_index) -FC_REFLECT(eos::chain::account_object, (id)(name)(balance)) +FC_REFLECT(chainbase::oid, (_id)) +FC_REFLECT(chainbase::oid, (_id)) + +FC_REFLECT(eos::chain::account_object, (id)(name)(creation_date)) +// TODO: Reflect permission_object::auth FC_REFLECT(eos::chain::permission_object, (id)(owner)(parent)(name)) diff --git a/libraries/chain/include/eos/chain/chain_controller.hpp b/libraries/chain/include/eos/chain/chain_controller.hpp index 0c6fe6de33d..7260fb2de6a 100644 --- a/libraries/chain/include/eos/chain/chain_controller.hpp +++ b/libraries/chain/include/eos/chain/chain_controller.hpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -49,6 +48,39 @@ namespace eos { namespace chain { using database = chainbase::database; using boost::signals2::signal; + /** + * @brief This class defines an interface allowing + */ + class chain_initializer { + public: + virtual ~chain_initializer(); + /** + * @brief Prepare the database, creating objects and defining state which should exist before the first block + * @param chain A reference to the @ref chain_controller + * @param db A reference to the @ref chainbase::database + * @param A list of @ref Message "Messages" to be applied before the first block + * + * This method creates the @ref account_object "account_objects" and @ref producer_object "producer_objects" for + * at least the initial block producers. + * + * This method also provides an opportunity to create objects and setup the database to the state it should be in + * prior to the first block. This method should only initialize state that the @ref chain_controller itself does + * not understand. The other methods will be called to retrieve the data necessary to initialize chain state the + * controller does understand. + * + * Finally, this method may perform any necessary initializations on the chain and/or database, such as + * installing indexes and message handlers that should be defined before the first block is processed. This may + * be necessary in order for the returned list of messages to be processed successfully. + */ + virtual vector prepare_database(chain_controller& chain, database& db) = 0; + /// Retrieve the timestamp to use as the blockchain start time + virtual types::Time get_chain_start_time() = 0; + /// Retrieve the BlockchainConfiguration to use at blockchain start + virtual BlockchainConfiguration get_chain_start_configuration() = 0; + /// Retrieve the first round of block producers + virtual std::array get_chain_start_producers() = 0; + }; + /** * @class database * @brief tracks the blockchain state in an extensible manner @@ -56,8 +88,7 @@ namespace eos { namespace chain { class chain_controller { public: - chain_controller(database& database, fork_database& fork_db, block_log& blocklog, - std::function genesis_loader); + chain_controller(database& database, fork_database& fork_db, block_log& blocklog, chain_initializer& starter); chain_controller(chain_controller&&) = default; ~chain_controller(); @@ -130,8 +161,6 @@ namespace eos { namespace chain { const SignedTransaction& get_recent_transaction( const transaction_id_type& trx_id )const; std::vector get_block_ids_on_fork(block_id_type head_of_fork) const; - chain_model get_model()const; - /** * Calculate the percent of block production slots that were missed in the * past 128 blocks, not including the current block. @@ -148,14 +177,14 @@ namespace eos { namespace chain { void _push_transaction( const SignedTransaction& trx ); signed_block generate_block( - const fc::time_point_sec when, - producer_id_type producer, + fc::time_point_sec when, + const AccountName& producer, const fc::ecc::private_key& block_signing_private_key, uint32_t skip ); signed_block _generate_block( - const fc::time_point_sec when, - producer_id_type producer, + fc::time_point_sec when, + const AccountName& producer, const fc::ecc::private_key& block_signing_private_key ); @@ -215,7 +244,7 @@ namespace eos { namespace chain { * * Passing slot_num == 0 returns EOS_NULL_PRODUCER */ - producer_id_type get_scheduled_producer(uint32_t slot_num)const; + AccountName get_scheduled_producer(uint32_t slot_num)const; /** * Get the time at which the given slot occurs. @@ -239,15 +268,15 @@ namespace eos { namespace chain { void update_producer_schedule(); - const chain_id_type& get_chain_id()const; const global_property_object& get_global_properties()const; const dynamic_global_property_object& get_dynamic_global_properties()const; const node_property_object& get_node_properties()const; + const producer_object& get_producer(const AccountName& ownerName)const; time_point_sec head_block_time()const; uint32_t head_block_num()const; block_id_type head_block_id()const; - producer_id_type head_block_producer()const; + AccountName head_block_producer()const; uint32_t block_interval()const { return config::BlockIntervalSeconds; } @@ -262,17 +291,22 @@ namespace eos { namespace chain { // these were formerly private, but they have a fairly well-defined API, so let's make them public void apply_block(const signed_block& next_block, uint32_t skip = skip_nothing); void apply_transaction(const SignedTransaction& trx, uint32_t skip = skip_nothing); + + protected: + const chainbase::database& get_database() const { return _db; } private: /// Reset the object graph in-memory void initialize_indexes(); - void initialize_genesis(std::function genesis_loader); + void initialize_chain(chain_initializer& starter); void replay(); void _apply_block(const signed_block& next_block); void _apply_transaction(const SignedTransaction& trx); + void require_account(const AccountName& name) const; + /** * This method validates transactions without adding it to the pending state. * @return true if the transaction would validate @@ -287,6 +321,7 @@ namespace eos { namespace chain { /// @} void validate_message_precondition(precondition_validate_context& c)const; + void process_message(Message message); void apply_message(apply_context& c); bool should_check_for_duplicate_transactions()const { return !(_skip_flags&skip_transaction_dupe_check); } diff --git a/libraries/chain/include/eos/chain/chain_model.hpp b/libraries/chain/include/eos/chain/chain_model.hpp deleted file mode 100644 index 83b20c6d79b..00000000000 --- a/libraries/chain/include/eos/chain/chain_model.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include - -namespace eos { namespace chain { - -class account_object; -class producer_object; -using types::AccountName; - -/** - * @brief The chain_model class provides read-only access to blockchain state - * - * The raw chainbase::database API is a lower level of abstraction, dealing with indexes and revisions and undo - * semantics, than clients to the blockchain ought to consume. This class wraps the database with a higher-level, - * read-only API. - * - * @note This class has reference-semantics only. This reference does not imply ownership of the database, and will - * dangle if it outlives the underlying database. - */ -class chain_model { - const chainbase::database& db; - -public: - chain_model(const chainbase::database& db) : db(db) {} - - const account_object& get_account(const AccountName& name) const; - const producer_object& get_producer(const AccountName& name) const; - - template - const ObjectType* find(CompatibleKey&& key) const { - return db.find(std::forward(key)); - } - - template - const ObjectType* find(chainbase::oid key = chainbase::oid()) const { return db.find(key); } - - template - const ObjectType& get(CompatibleKey&& key) const { - return db.get(std::forward(key)); - } - - template - const ObjectType& get(const chainbase::oid& key = chainbase::oid()) const { - return db.get(key); - } -}; - -} } // namespace eos::chain - diff --git a/libraries/chain/include/eos/chain/chain_property_object.hpp b/libraries/chain/include/eos/chain/chain_property_object.hpp deleted file mode 100644 index 5ad1c3b1855..00000000000 --- a/libraries/chain/include/eos/chain/chain_property_object.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017, Respective Authors. - * - * The MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#pragma once - -#include -#include - -#include "multi_index_includes.hpp" - -namespace eos { namespace chain { - -/** - * Contains invariants which are set at genesis and never changed. - */ -class chain_property_object : public chainbase::object -{ - OBJECT_CTOR(chain_property_object) - - id_type id; - chain_id_type chain_id; - immutable_chain_parameters immutable_parameters; -}; - -using chain_property_multi_index = chainbase::shared_multi_index_container< - chain_property_object, - indexed_by< - ordered_unique, BOOST_MULTI_INDEX_MEMBER(chain_property_object, chain_property_object::id_type, id)> - > ->; - -} } - -CHAINBASE_SET_INDEX_TYPE(eos::chain::chain_property_object, eos::chain::chain_property_multi_index) - -FC_REFLECT(eos::chain::chain_property_object, (chain_id)(immutable_parameters)) diff --git a/libraries/chain/include/eos/chain/config.hpp b/libraries/chain/include/eos/chain/config.hpp index 7bc52adeb8c..7a1330e5bdf 100644 --- a/libraries/chain/include/eos/chain/config.hpp +++ b/libraries/chain/include/eos/chain/config.hpp @@ -38,6 +38,8 @@ const static char SystemContractName[] = "sys"; const static char EosContractName[] = "eos"; const static char StakedBalanceContractName[] = "sbc"; +const static ShareType InitialTokenSupply = Asset(90'000'000).amount; + const static int BlockIntervalSeconds = 3; /** Percentages are fixed point with a denominator of 10,000 */ diff --git a/libraries/chain/include/eos/chain/genesis_state.hpp b/libraries/chain/include/eos/chain/genesis_state.hpp index c72ee7ce554..5db38d2d7b8 100644 --- a/libraries/chain/include/eos/chain/genesis_state.hpp +++ b/libraries/chain/include/eos/chain/genesis_state.hpp @@ -39,15 +39,17 @@ using std::vector; struct genesis_state_type { struct initial_account_type { initial_account_type(const string& name = string(), - uint64_t bal = 0, + uint64_t staking_bal = 0, + uint64_t liquid_bal = 0, const public_key_type& owner_key = public_key_type(), const public_key_type& active_key = public_key_type()) - : name(name), balance(bal), + : name(name), staking_balance(staking_bal), liquid_balance(liquid_bal), owner_key(owner_key), active_key(active_key == public_key_type()? owner_key : active_key) {} string name; - Asset balance; + Asset staking_balance; + Asset liquid_balance; public_key_type owner_key; public_key_type active_key; }; @@ -72,7 +74,6 @@ struct genesis_state_type { config::DefaultMinEosBalance, config::DefaultMaxTrxLifetime }; - immutable_chain_parameters immutable_parameters; vector initial_accounts; vector initial_producers; @@ -91,10 +92,11 @@ struct genesis_state_type { } } // namespace eos::chain -FC_REFLECT(eos::chain::genesis_state_type::initial_account_type, (name)(balance)(owner_key)(active_key)) +FC_REFLECT(eos::chain::genesis_state_type::initial_account_type, + (name)(staking_balance)(liquid_balance)(owner_key)(active_key)) FC_REFLECT(eos::chain::genesis_state_type::initial_producer_type, (owner_name)(block_signing_key)) FC_REFLECT(eos::chain::genesis_state_type, - (initial_timestamp)(initial_configuration)(immutable_parameters)(initial_accounts) + (initial_timestamp)(initial_configuration)(initial_accounts) (initial_producers)(initial_chain_id)) diff --git a/libraries/chain/include/eos/chain/global_property_object.hpp b/libraries/chain/include/eos/chain/global_property_object.hpp index dea6549878e..822eb3ea5ac 100644 --- a/libraries/chain/include/eos/chain/global_property_object.hpp +++ b/libraries/chain/include/eos/chain/global_property_object.hpp @@ -49,7 +49,7 @@ namespace eos { namespace chain { id_type id; BlockchainConfiguration configuration; - std::array active_producers; + std::array active_producers; }; @@ -71,7 +71,7 @@ namespace eos { namespace chain { uint32_t head_block_number = 0; block_id_type head_block_id; time_point_sec time; - producer_id_type current_producer; + AccountName current_producer; uint32_t accounts_registered_this_interval = 0; /** diff --git a/libraries/chain/include/eos/chain/immutable_chain_parameters.hpp b/libraries/chain/include/eos/chain/immutable_chain_parameters.hpp index d93762bcdb9..29331eaa720 100644 --- a/libraries/chain/include/eos/chain/immutable_chain_parameters.hpp +++ b/libraries/chain/include/eos/chain/immutable_chain_parameters.hpp @@ -31,13 +31,5 @@ namespace eos { namespace chain { -struct immutable_chain_parameters -{ - uint16_t min_producer_count = config::ProducerCount; -}; - } } // eos::chain -FC_REFLECT( eos::chain::immutable_chain_parameters, - (min_producer_count) -) diff --git a/libraries/chain/include/eos/chain/message.hpp b/libraries/chain/include/eos/chain/message.hpp index e4d0bff06d5..ffb461568fa 100644 --- a/libraries/chain/include/eos/chain/message.hpp +++ b/libraries/chain/include/eos/chain/message.hpp @@ -50,4 +50,4 @@ struct Message : public types::Message { } } // namespace eos::chain -FC_REFLECT(eos::chain::Message, (sender)(recipient)(notify)(type)(data)) +FC_REFLECT_DERIVED(eos::chain::Message, (eos::types::Message), ) diff --git a/libraries/chain/include/eos/chain/message_handling_contexts.hpp b/libraries/chain/include/eos/chain/message_handling_contexts.hpp index f4923332d24..b515cec7c1d 100644 --- a/libraries/chain/include/eos/chain/message_handling_contexts.hpp +++ b/libraries/chain/include/eos/chain/message_handling_contexts.hpp @@ -10,18 +10,16 @@ namespace eos { namespace chain { class message_validate_context { public: - message_validate_context(const types::Transaction& t, const chain::Message& m) - :trx(t),msg(m){} + explicit message_validate_context(const chain::Message& m) + :msg(m){} - const types::Transaction& trx; const chain::Message& msg; }; class precondition_validate_context : public message_validate_context { public: - precondition_validate_context(const chainbase::database& db, const types::Transaction& t, - const chain::Message& m, const types::AccountName& r) - :message_validate_context(t,m),recipient(r),db(db){} + precondition_validate_context(const chainbase::database& db, const chain::Message& m, const types::AccountName& r) + :message_validate_context(m),recipient(r),db(db){} const types::AccountName& recipient; @@ -30,9 +28,8 @@ class precondition_validate_context : public message_validate_context { class apply_context : public precondition_validate_context { public: - apply_context(chainbase::database& db, const types::Transaction& t, - const chain::Message& m, const types::AccountName& recipient) - :precondition_validate_context(db,t,m,recipient),mutable_db(db){} + apply_context(chainbase::database& db, const chain::Message& m, const types::AccountName& recipient) + :precondition_validate_context(db,m,recipient),mutable_db(db){} types::String get(types::String key)const; void set(types::String key, types::String value); diff --git a/libraries/chain/include/eos/chain/producer_object.hpp b/libraries/chain/include/eos/chain/producer_object.hpp index 634324796b7..5ceef11c116 100644 --- a/libraries/chain/include/eos/chain/producer_object.hpp +++ b/libraries/chain/include/eos/chain/producer_object.hpp @@ -32,7 +32,7 @@ class producer_object : public chainbase::object, member>, - ordered_unique, member>, + ordered_unique, member>, ordered_non_unique, member> > >; diff --git a/libraries/chain/include/eos/chain/types.hpp b/libraries/chain/include/eos/chain/types.hpp index cdab7a8c5f4..64852f1c033 100644 --- a/libraries/chain/include/eos/chain/types.hpp +++ b/libraries/chain/include/eos/chain/types.hpp @@ -158,6 +158,7 @@ namespace eos { namespace chain { transaction_object_type, producer_object_type, chain_property_object_type, + balance_object_type, ///< Defined by native_system_contract_plugin staked_balance_object_type, ///< Defined by native_system_contract_plugin producer_votes_object_type, ///< Defined by native_system_contract_plugin OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types @@ -166,9 +167,6 @@ namespace eos { namespace chain { class account_object; class producer_object; - using account_id_type = chainbase::oid; - using producer_id_type = chainbase::oid; - using block_id_type = fc::sha256; using checksum_type = fc::sha256; using transaction_id_type = fc::sha256; @@ -181,25 +179,23 @@ namespace eos { namespace chain { } } // eos::chain -FC_REFLECT(eos::chain::account_id_type, (_id)) -FC_REFLECT(eos::chain::producer_id_type, (_id)) - -FC_REFLECT_ENUM( eos::chain::object_type, - (null_object_type) - (account_object_type) - (permission_object_type) - (action_code_object_type) - (type_object_type) - (key_value_object_type) - (action_permission_object_type) - (global_property_object_type) - (dynamic_global_property_object_type) - (block_summary_object_type) - (transaction_object_type) - (producer_object_type) - (chain_property_object_type) - (staked_balance_object_type) - (producer_votes_object_type) - (OBJECT_TYPE_COUNT) +FC_REFLECT_ENUM(eos::chain::object_type, + (null_object_type) + (account_object_type) + (permission_object_type) + (action_code_object_type) + (type_object_type) + (key_value_object_type) + (action_permission_object_type) + (global_property_object_type) + (dynamic_global_property_object_type) + (block_summary_object_type) + (transaction_object_type) + (producer_object_type) + (chain_property_object_type) + (balance_object_type) + (staked_balance_object_type) + (producer_votes_object_type) + (OBJECT_TYPE_COUNT) ) FC_REFLECT( eos::chain::void_t, ) diff --git a/libraries/native_contract/CMakeLists.txt b/libraries/native_contract/CMakeLists.txt new file mode 100644 index 00000000000..daf73f7b254 --- /dev/null +++ b/libraries/native_contract/CMakeLists.txt @@ -0,0 +1,25 @@ +file(GLOB HEADERS "include/eos/native_contract/*.hpp") + +## SORT .cpp by most likely to change / break compile +add_library( eos_native_contract + eos_contract.cpp + staked_balance_contract.cpp + staked_balance_objects.cpp + system_contract.cpp + native_contract_chain_initializer.cpp + + ${HEADERS} + ) + +target_link_libraries( eos_native_contract eos_chain fc ) +target_include_directories( eos_native_contract + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" ) + +INSTALL( TARGETS + eos_native_contract + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) +INSTALL( FILES ${HEADERS} DESTINATION "include/eos/native_contract" ) diff --git a/libraries/native_contract/eos_contract.cpp b/libraries/native_contract/eos_contract.cpp new file mode 100644 index 00000000000..80862ef4f49 --- /dev/null +++ b/libraries/native_contract/eos_contract.cpp @@ -0,0 +1,102 @@ +#include +#include + +#include +#include +#include + +namespace eos { +using namespace chain; + +void CreateAccount_Notify_Eos::validate_preconditions(precondition_validate_context& context) { + auto create = context.msg.as(); + const auto& creatorBalance = context.db.get(create.creator); + EOS_ASSERT(creatorBalance.balance >= create.deposit.amount, message_validate_exception, + "Creator '${c}' has insufficient funds to make account creation deposit of ${a}", + ("c", create.creator)("a", create.deposit)); +} + +void ClaimUnlockedEos_Notify_Eos::apply(apply_context& context) { + auto claim = context.msg.as(); + const auto& claimant = context.db.get(claim.account); + context.mutable_db.modify(claimant, [&claim](BalanceObject& a) { + a.balance += claim.amount; + }); +} + +void CreateAccount_Notify_Eos::apply(apply_context& context) { + auto create = context.msg.as(); + context.mutable_db.create([&create](BalanceObject& b) { + b.ownerName = create.name; + b.balance = create.deposit.amount; + }); + const auto& creatorBalance = context.mutable_db.get(create.creator); + context.mutable_db.modify(creatorBalance, [&create](BalanceObject& b) { + b.balance -= create.deposit.amount; + }); +} + +void Transfer::validate(message_validate_context& context) { + auto transfer = context.msg.as(); + try { + EOS_ASSERT(transfer.amount > Asset(0), message_validate_exception, "Must transfer a positive amount"); + EOS_ASSERT(context.msg.has_notify(transfer.to), message_validate_exception, "Must notify recipient of transfer"); + } FC_CAPTURE_AND_RETHROW((transfer)) +} + +void Transfer::validate_preconditions(precondition_validate_context& context) { + const auto& db = context.db; + auto transfer = context.msg.as(); + + try { + db.get(transfer.to); ///< make sure this exists + const auto& from = db.get(transfer.from); + EOS_ASSERT(from.balance >= transfer.amount.amount, message_precondition_exception, "Insufficient Funds", + ("from.balance",from.balance)("transfer.amount",transfer.amount)); + } FC_CAPTURE_AND_RETHROW((transfer)) +} + +void Transfer::apply(apply_context& context) { + auto& db = context.mutable_db; + auto transfer = context.msg.as(); + const auto& from = db.get(transfer.from); + const auto& to = db.get(transfer.to); +#warning TODO: move balance from account_object to an EOS-constract-specific object + db.modify(from, [&](BalanceObject& a) { + a.balance -= transfer.amount.amount; + }); + db.modify(to, [&](BalanceObject& a) { + a.balance += transfer.amount.amount; + }); +} + +void TransferToLocked::validate(message_validate_context& context) { + auto lock = context.msg.as(); + EOS_ASSERT(lock.amount > 0, message_validate_exception, "Locked amount must be positive"); + EOS_ASSERT(lock.to == lock.from || context.msg.has_notify(lock.to), + message_validate_exception, "Recipient account must be notified"); + EOS_ASSERT(context.msg.has_notify(config::StakedBalanceContractName), message_validate_exception, + "Staked Balance Contract (${name}) must be notified", ("name", config::StakedBalanceContractName)); +} + +void TransferToLocked::validate_preconditions(precondition_validate_context& context) { + auto lock = context.msg.as(); + ShareType balance; + try { + const auto& sender = context.db.get(lock.from); + context.db.get(lock.to); + balance = sender.balance; + } EOS_RECODE_EXC(fc::exception, message_precondition_exception) + EOS_ASSERT(balance >= lock.amount, message_precondition_exception, + "Account ${a} lacks sufficient funds to lock ${amt} EOS", ("a", lock.from)("amt", lock.amount)); +} + +void TransferToLocked::apply(apply_context& context) { + auto lock = context.msg.as(); + const auto& locker = context.db.get(lock.from); + context.mutable_db.modify(locker, [&lock](BalanceObject& a) { + a.balance -= lock.amount; + }); +} + +} // namespace eos diff --git a/libraries/native_contract/include/eos/native_contract/balance_object.hpp b/libraries/native_contract/include/eos/native_contract/balance_object.hpp new file mode 100644 index 00000000000..f2ba583976f --- /dev/null +++ b/libraries/native_contract/include/eos/native_contract/balance_object.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace eos { + +/** + * @brief The BalanceObject class tracks the EOS balance for accounts + */ +class BalanceObject : public chainbase::object { + OBJECT_CTOR(BalanceObject) + + id_type id; + types::AccountName ownerName; + types::ShareType balance = 0; +}; + +struct byOwnerName; + +using BalanceMultiIndex = chainbase::shared_multi_index_container< + BalanceObject, + indexed_by< + ordered_unique, + member + >, + ordered_unique, + member + > + > +>; + +} // namespace eos + +CHAINBASE_SET_INDEX_TYPE(eos::BalanceObject, eos::BalanceMultiIndex) diff --git a/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/eos_contract.hpp b/libraries/native_contract/include/eos/native_contract/eos_contract.hpp similarity index 65% rename from plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/eos_contract.hpp rename to libraries/native_contract/include/eos/native_contract/eos_contract.hpp index 527a29bd123..61341ac1171 100644 --- a/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/eos_contract.hpp +++ b/libraries/native_contract/include/eos/native_contract/eos_contract.hpp @@ -6,6 +6,16 @@ namespace eos { +struct CreateAccount_Notify_Eos { + static void validate_preconditions(chain::precondition_validate_context& context); + static void apply(chain::apply_context& context); +}; + +struct ClaimUnlockedEos_Notify_Eos { + static void validate_preconditions(chain::precondition_validate_context&) {} + static void apply(chain::apply_context& context); +}; + struct Transfer { static void validate(chain::message_validate_context& context); static void validate_preconditions(chain::precondition_validate_context& context); diff --git a/libraries/native_contract/include/eos/native_contract/native_contract_chain_initializer.hpp b/libraries/native_contract/include/eos/native_contract/native_contract_chain_initializer.hpp new file mode 100644 index 00000000000..cf95d1c8526 --- /dev/null +++ b/libraries/native_contract/include/eos/native_contract/native_contract_chain_initializer.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace eos { namespace native_contract { + +class native_contract_chain_initializer : public chain::chain_initializer { + chain::genesis_state_type genesis; +public: + native_contract_chain_initializer(const chain::genesis_state_type& genesis) : genesis(genesis) {} + virtual ~native_contract_chain_initializer() {} + + virtual std::vector prepare_database(chain::chain_controller& chain, chainbase::database& db); + virtual types::Time get_chain_start_time(); + virtual chain::BlockchainConfiguration get_chain_start_configuration(); + virtual std::array get_chain_start_producers(); +}; + +} } // namespace eos::native_contract + diff --git a/libraries/native_contract/include/eos/native_contract/objects.hpp b/libraries/native_contract/include/eos/native_contract/objects.hpp new file mode 100644 index 00000000000..fdd33c38c52 --- /dev/null +++ b/libraries/native_contract/include/eos/native_contract/objects.hpp @@ -0,0 +1,6 @@ +#pragma once + +/// @file This file #include's all database objects/indices used by the C++ native contract implementation + +#include +#include diff --git a/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/staked_balance_contract.hpp b/libraries/native_contract/include/eos/native_contract/staked_balance_contract.hpp similarity index 78% rename from plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/staked_balance_contract.hpp rename to libraries/native_contract/include/eos/native_contract/staked_balance_contract.hpp index ce6aafa4c5b..f27e13068e1 100644 --- a/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/staked_balance_contract.hpp +++ b/libraries/native_contract/include/eos/native_contract/staked_balance_contract.hpp @@ -6,7 +6,15 @@ namespace eos { -void TransferToLocked_Notify_Staked(chain::apply_context& context); +struct CreateAccount_Notify_Staked { + static void validate_preconditions(chain::precondition_validate_context& context); + static void apply(chain::apply_context& context); +}; + +struct TransferToLocked_Notify_Staked { + static void validate_preconditions(chain::precondition_validate_context&) {} + static void apply(chain::apply_context& context); +}; struct StartUnlockEos { static void validate(chain::message_validate_context& context); diff --git a/plugins/native_system_contract_plugin/staked_balance_objects.hpp b/libraries/native_contract/include/eos/native_contract/staked_balance_objects.hpp similarity index 100% rename from plugins/native_system_contract_plugin/staked_balance_objects.hpp rename to libraries/native_contract/include/eos/native_contract/staked_balance_objects.hpp diff --git a/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/system_contract.hpp b/libraries/native_contract/include/eos/native_contract/system_contract.hpp similarity index 100% rename from plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/system_contract.hpp rename to libraries/native_contract/include/eos/native_contract/system_contract.hpp diff --git a/libraries/native_contract/native_contract_chain_initializer.cpp b/libraries/native_contract/native_contract_chain_initializer.cpp new file mode 100644 index 00000000000..b082480dbee --- /dev/null +++ b/libraries/native_contract/native_contract_chain_initializer.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#include + +namespace eos { namespace native_contract { +using namespace chain; + +std::vector native_contract_chain_initializer::prepare_database(chain_controller& chain, + chainbase::database& db) { + std::vector messages_to_process; + + // Install the native contract's indexes; we can't do anything until our objects are recognized + db.add_index(); + db.add_index(); + db.add_index(); + + /// Create the native contract accounts manually; sadly, we can't run their contracts to make them create themselves + auto CreateNativeAccount = [this, &db](auto name, auto liquidBalance) { + db.create([this, &name](account_object& a) { + a.name = name; + a.creation_date = genesis.initial_timestamp; + }); + db.create([&name, liquidBalance](BalanceObject& b) { + b.ownerName = name; + b.balance = liquidBalance; + }); + db.create([&name](StakedBalanceObject& sb) { sb.ownerName = name; }); + }; + CreateNativeAccount(config::SystemContractName, config::InitialTokenSupply); + CreateNativeAccount(config::EosContractName, 0); + CreateNativeAccount(config::StakedBalanceContractName, 0); + + // Install the native contract's message handlers + // First, set message handlers +#define SET_HANDLERS(contractname, handlername) \ + chain.set_validate_handler(contractname, contractname, #handlername, &handlername::validate); \ + chain.set_precondition_validate_handler(contractname, contractname, #handlername, &handlername::validate_preconditions); \ + chain.set_apply_handler(contractname, contractname, #handlername, &handlername::apply); +#define FWD_SET_HANDLERS(r, data, elem) SET_HANDLERS(data, elem) + BOOST_PP_SEQ_FOR_EACH(FWD_SET_HANDLERS, config::SystemContractName, EOS_SYSTEM_CONTRACT_FUNCTIONS) + BOOST_PP_SEQ_FOR_EACH(FWD_SET_HANDLERS, config::EosContractName, EOS_CONTRACT_FUNCTIONS) + BOOST_PP_SEQ_FOR_EACH(FWD_SET_HANDLERS, config::StakedBalanceContractName, EOS_STAKED_BALANCE_CONTRACT_FUNCTIONS) +#undef FWD_SET_HANDLERS +#undef SET_HANDLERS + + // Second, set notify handlers + auto SetNotifyHandlers = [&chain](auto recipient, auto scope, auto message, auto validate, auto apply) { + chain.set_precondition_validate_handler(recipient, scope, message, validate); + chain.set_apply_handler(recipient, scope, message, apply); + }; + SetNotifyHandlers(config::EosContractName, config::StakedBalanceContractName, "TransferToLocked", + &TransferToLocked_Notify_Staked::validate_preconditions, &TransferToLocked_Notify_Staked::apply); + SetNotifyHandlers(config::StakedBalanceContractName, config::EosContractName, "ClaimUnlockedEos", + &ClaimUnlockedEos_Notify_Eos::validate_preconditions, &ClaimUnlockedEos_Notify_Eos::apply); + SetNotifyHandlers(config::SystemContractName, config::EosContractName, "CreateAccount", + &CreateAccount_Notify_Eos::validate_preconditions, &CreateAccount_Notify_Eos::apply); + SetNotifyHandlers(config::SystemContractName, config::StakedBalanceContractName, "CreateAccount", + &CreateAccount_Notify_Staked::validate_preconditions, &CreateAccount_Notify_Staked::apply); + + // Register native contract message types +#define MACRO(r, data, elem) chain.register_type(data); + BOOST_PP_SEQ_FOR_EACH(MACRO, config::SystemContractName, EOS_SYSTEM_CONTRACT_FUNCTIONS) + BOOST_PP_SEQ_FOR_EACH(MACRO, config::EosContractName, EOS_CONTRACT_FUNCTIONS) + BOOST_PP_SEQ_FOR_EACH(MACRO, config::StakedBalanceContractName, EOS_STAKED_BALANCE_CONTRACT_FUNCTIONS) +#undef MACRO + + // Queue up messages which will run contracts to create the initial accounts + auto KeyAuthority = [](PublicKey k) { + return types::Authority(1, {{k, 1}}, {}); + }; + for (const auto& acct : genesis.initial_accounts) { + chain::Message message(config::SystemContractName, config::SystemContractName, + {config::EosContractName, config::StakedBalanceContractName}, + "CreateAccount", types::CreateAccount(config::SystemContractName, acct.name, + KeyAuthority(acct.owner_key), + KeyAuthority(acct.active_key), + KeyAuthority(acct.owner_key), + acct.staking_balance)); + messages_to_process.emplace_back(std::move(message)); + if (acct.liquid_balance > 0) { + message = chain::Message(config::SystemContractName, config::EosContractName, {}, + "Transfer", types::Transfer(config::SystemContractName, acct.name, + acct.liquid_balance, "Genesis Allocation")); + messages_to_process.emplace_back(std::move(message)); + } + } + + // Create initial producers + std::vector initial_producers; + for (const auto& producer : genesis.initial_producers) { + db.create([&](producer_object& p) { + p.signing_key = producer.block_signing_key; + p.owner = producer.owner_name; + }); + initial_producers.push_back(producer.owner_name); + } + + return messages_to_process; +} + +types::Time native_contract_chain_initializer::get_chain_start_time() { + return genesis.initial_timestamp; +} + +chain::BlockchainConfiguration native_contract_chain_initializer::get_chain_start_configuration() { + return genesis.initial_configuration; +} + +std::array native_contract_chain_initializer::get_chain_start_producers() { + std::array result; + std::transform(genesis.initial_producers.begin(), genesis.initial_producers.end(), result.begin(), + [](const auto& p) { return p.owner_name; }); + return result; +} + +} } // namespace eos::native_contract diff --git a/plugins/native_system_contract_plugin/staked_balance_contract.cpp b/libraries/native_contract/staked_balance_contract.cpp similarity index 87% rename from plugins/native_system_contract_plugin/staked_balance_contract.cpp rename to libraries/native_contract/staked_balance_contract.cpp index fd566660784..61f212bf945 100644 --- a/plugins/native_system_contract_plugin/staked_balance_contract.cpp +++ b/libraries/native_contract/staked_balance_contract.cpp @@ -1,16 +1,26 @@ -#include +#include +#include #include #include #include #include -#include "staked_balance_objects.hpp" - namespace eos { using namespace chain; -void TransferToLocked_Notify_Staked(apply_context& context) { +void CreateAccount_Notify_Staked::validate_preconditions(precondition_validate_context& context) { + +} + +void CreateAccount_Notify_Staked::apply(apply_context& context) { + auto create = context.msg.as(); + context.mutable_db.create([&create](StakedBalanceObject& sbo) { + sbo.ownerName = create.name; + }); +} + +void TransferToLocked_Notify_Staked::apply(apply_context& context) { auto lock = context.msg.as(); const auto& balance = context.db.get(lock.to); context.mutable_db.modify(balance, [&lock](StakedBalanceObject& sbo) { @@ -86,8 +96,7 @@ void CreateProducer::validate(message_validate_context& context) { void CreateProducer::validate_preconditions(precondition_validate_context& context) { auto create = context.msg.as(); const auto& db = context.db; - const auto& owner = db.get(create.name); - auto producer = db.find(owner.id); + auto producer = db.find(create.name); EOS_ASSERT(producer == nullptr, message_precondition_exception, "Account ${name} already has a block producer", ("name", create.name)); } @@ -95,9 +104,8 @@ void CreateProducer::validate_preconditions(precondition_validate_context& conte void CreateProducer::apply(apply_context& context) { auto create = context.msg.as(); auto& db = context.mutable_db; - const auto& owner = db.get(create.name); - db.create([&create, &owner](producer_object& p) { - p.owner = owner.id; + db.create([&create](producer_object& p) { + p.owner = create.name; p.signing_key = create.key; }); } @@ -110,7 +118,7 @@ void UpdateProducer::validate(message_validate_context& context) { void UpdateProducer::validate_preconditions(precondition_validate_context& context) { const auto& db = context.db; auto update = context.msg.as(); - const auto& producer = db.get(db.get(update.name).id); + const auto& producer = db.get(update.name); EOS_ASSERT(producer.signing_key != update.newKey || producer.configuration != update.configuration, message_validate_exception, "Producer's new settings may not be identical to old settings"); } @@ -118,7 +126,7 @@ void UpdateProducer::validate_preconditions(precondition_validate_context& conte void UpdateProducer::apply(apply_context& context) { auto& db = context.mutable_db; auto update = context.msg.as(); - const auto& producer = db.get(db.get(update.name).id); + const auto& producer = db.get(update.name); db.modify(producer, [&update](producer_object& p) { p.signing_key = update.newKey; diff --git a/plugins/native_system_contract_plugin/staked_balance_objects.cpp b/libraries/native_contract/staked_balance_objects.cpp similarity index 91% rename from plugins/native_system_contract_plugin/staked_balance_objects.cpp rename to libraries/native_contract/staked_balance_objects.cpp index 4f1ed10775e..950700d06d6 100644 --- a/plugins/native_system_contract_plugin/staked_balance_objects.cpp +++ b/libraries/native_contract/staked_balance_objects.cpp @@ -1,4 +1,4 @@ -#include "staked_balance_objects.hpp" +#include namespace eos { using namespace chain; diff --git a/plugins/native_system_contract_plugin/system_contract.cpp b/libraries/native_contract/system_contract.cpp similarity index 84% rename from plugins/native_system_contract_plugin/system_contract.cpp rename to libraries/native_contract/system_contract.cpp index 0a2e6601125..d9f10b0e571 100644 --- a/plugins/native_system_contract_plugin/system_contract.cpp +++ b/libraries/native_contract/system_contract.cpp @@ -1,17 +1,18 @@ -#include +#include #include #include #include #include #include +#include namespace eos { using namespace chain; void DefineStruct::validate(message_validate_context& context) { auto msg = context.msg.as(); - FC_ASSERT(msg.definition.name != TypeName(), "must define a type name"); + EOS_ASSERT(msg.definition.name != TypeName(), message_validate_exception, "must define a type name"); // TODO: validate_type_name( msg.definition.name) // validate_type_name( msg.definition.base) } @@ -72,6 +73,11 @@ void SetMessageHandler::apply(apply_context& context) { void CreateAccount::validate(message_validate_context& context) { auto create = context.msg.as(); + EOS_ASSERT(context.msg.has_notify(config::EosContractName), message_validate_exception, + "Must notify EOS Contract (${name})", ("name", config::EosContractName)); + EOS_ASSERT(context.msg.has_notify(config::StakedBalanceContractName), message_validate_exception, + "Must notify Staked Balance Contract (${name})", ("name", config::StakedBalanceContractName)); + EOS_ASSERT( eos::validate(create.owner), message_validate_exception, "Invalid owner authority"); EOS_ASSERT( eos::validate(create.active), message_validate_exception, "Invalid active authority"); EOS_ASSERT( eos::validate(create.recovery), message_validate_exception, "Invalid recovery authority"); @@ -88,9 +94,6 @@ void CreateAccount::validate_preconditions(precondition_validate_context& contex "Cannot create account named ${name}, as that name is already taken", ("name", create.name)); - const auto& creator = db.get(context.msg.sender); - EOS_ASSERT(creator.balance >= create.deposit, message_precondition_exception, "Insufficient Funds"); - #warning TODO: make sure creation deposit is greater than min account balance auto validate_authority_preconditions = [&context](const auto& auth) { @@ -105,17 +108,14 @@ void CreateAccount::validate_preconditions(precondition_validate_context& contex void CreateAccount::apply(apply_context& context) { auto& db = context.mutable_db; auto create = context.msg.as(); - db.modify(db.get(context.msg.sender), [&create](account_object& a) { - a.balance -= create.deposit; + const auto& new_account = db.create([&create, &db](account_object& a) { + a.name = create.name; + a.creation_date = db.get(dynamic_global_property_object::id_type()).time; }); - const auto& new_account = db.create([&create](account_object& a) { - a.name = create.name; - a.balance = create.deposit; -}); const auto& owner_permission = db.create([&create, &new_account](permission_object& p) { p.name = "owner"; p.parent = 0; - p.owner = new_account.id; + p.owner = new_account.name; p.auth = std::move(create.owner); }); db.create([&create, &owner_permission](permission_object& p) { diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 5389732b749..9b0480effa2 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -4,4 +4,3 @@ add_subdirectory(database_plugin) add_subdirectory(chain_plugin) add_subdirectory(chain_api_plugin) add_subdirectory(producer_plugin) -add_subdirectory(native_system_contract_plugin) diff --git a/plugins/chain_plugin/CMakeLists.txt b/plugins/chain_plugin/CMakeLists.txt index 7c9df9a5bdb..5e0f33ef551 100644 --- a/plugins/chain_plugin/CMakeLists.txt +++ b/plugins/chain_plugin/CMakeLists.txt @@ -3,7 +3,7 @@ add_library( chain_plugin chain_plugin.cpp ${HEADERS} ) -target_link_libraries( chain_plugin database_plugin eos_chain appbase ) +target_link_libraries( chain_plugin database_plugin eos_native_contract eos_chain appbase ) target_include_directories( chain_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) install( TARGETS diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 35c348f1230..3a7ed938db8 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -2,6 +2,11 @@ #include #include #include +#include +#include + +#include + #include namespace eos { @@ -84,16 +89,14 @@ void chain_plugin::plugin_initialize(const variables_map& options) { } void chain_plugin::plugin_startup() { - auto genesis_loader = [this] { - if (my->genesis_file.empty()) - return eos::chain::genesis_state_type(); - return fc::json::from_file(my->genesis_file).as(); - }; auto& db = app().get_plugin().db(); + auto genesis = fc::json::from_file(my->genesis_file).as(); + native_contract::native_contract_chain_initializer initializer(genesis); + my->fork_db = fork_database(); my->block_logger = block_log(my->block_log_dir); - my->chain = chain_controller(db, *my->fork_db, *my->block_logger, genesis_loader); + my->chain = chain_controller(db, *my->fork_db, *my->block_logger, initializer); if(!my->readonly) { ilog("starting chain in read/write mode"); diff --git a/plugins/chain_plugin/include/eos/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eos/chain_plugin/chain_plugin.hpp index 06791d2a397..7a64b991b48 100644 --- a/plugins/chain_plugin/include/eos/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eos/chain_plugin/chain_plugin.hpp @@ -24,7 +24,7 @@ class read_only { 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; + types::AccountName head_block_producer; string recent_slots; double participation_rate; }; diff --git a/plugins/native_system_contract_plugin/CMakeLists.txt b/plugins/native_system_contract_plugin/CMakeLists.txt deleted file mode 100644 index 2df127124bb..00000000000 --- a/plugins/native_system_contract_plugin/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -file(GLOB HEADERS "include/eos/native_system_contract_plugin/*.hpp") -add_library( native_system_contract_plugin - native_system_contract_plugin.cpp - staked_balance_contract.cpp - staked_balance_objects.hpp - staked_balance_objects.cpp - system_contract.cpp - eos_contract.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" ) diff --git a/plugins/native_system_contract_plugin/eos_contract.cpp b/plugins/native_system_contract_plugin/eos_contract.cpp deleted file mode 100644 index 0d65b72242d..00000000000 --- a/plugins/native_system_contract_plugin/eos_contract.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include - -#include -#include -#include - -namespace eos { -using namespace chain; - -void Transfer::validate(message_validate_context& context) { - auto transfer = context.msg.as(); - try { - EOS_ASSERT(transfer.amount > Asset(0), message_validate_exception, "Must transfer a positive amount"); - EOS_ASSERT(context.msg.has_notify(transfer.to), message_validate_exception, "Must notify recipient of transfer"); - } FC_CAPTURE_AND_RETHROW((transfer)) -} - -void Transfer::validate_preconditions(precondition_validate_context& context) { - const auto& db = context.db; - auto transfer = context.msg.as(); - - db.get(transfer.to); ///< make sure this exists - const auto& from = db.get(transfer.from); - EOS_ASSERT(from.balance >= transfer.amount.amount, message_precondition_exception, "Insufficient Funds", - ("from.balance",from.balance)("transfer.amount",transfer.amount)); -} - -void Transfer::apply(apply_context& context) { - auto& db = context.mutable_db; - auto transfer = context.msg.as(); - const auto& from = db.get(transfer.from); - const auto& to = db.get(transfer.to); -#warning TODO: move balance from account_object to an EOS-constract-specific object - db.modify(from, [&](account_object& a) { - a.balance -= transfer.amount; - }); - db.modify(to, [&](account_object& a) { - a.balance += transfer.amount; - }); -} - -void TransferToLocked::validate(message_validate_context& context) { - auto lock = context.msg.as(); - EOS_ASSERT(lock.amount > 0, message_validate_exception, "Locked amount must be positive"); - EOS_ASSERT(lock.to == lock.from || context.msg.has_notify(lock.to), - message_validate_exception, "Recipient account must be notified"); - EOS_ASSERT(context.msg.has_notify(config::StakedBalanceContractName), message_validate_exception, - "Staked Balance Contract (${name}) must be notified", ("name", config::StakedBalanceContractName)); -} - -void TransferToLocked::validate_preconditions(precondition_validate_context& context) { - auto lock = context.msg.as(); - ShareType balance; - try { - const auto& sender = context.db.get(lock.from); - context.db.get(lock.to); - balance = sender.balance.amount; - } EOS_RECODE_EXC(fc::exception, message_precondition_exception) - EOS_ASSERT(balance >= lock.amount, message_precondition_exception, - "Account ${a} lacks sufficient funds to lock ${amt} EOS", ("a", lock.from)("amt", lock.amount)); -} - -void TransferToLocked::apply(apply_context& context) { - auto lock = context.msg.as(); - const auto& locker = context.db.get(lock.from); - context.mutable_db.modify(locker, [&lock](account_object& a) { - a.balance.amount -= lock.amount; - }); -} - -void ClaimUnlockedEos_Notify_Eos(apply_context& context) { - auto claim = context.msg.as(); - const auto& claimant = context.db.get(claim.account); - context.mutable_db.modify(claimant, [&claim](account_object& a) { - a.balance.amount += claim.amount; - }); -} - -} // namespace eos diff --git a/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/native_system_contract_plugin.hpp b/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/native_system_contract_plugin.hpp deleted file mode 100644 index 6f4427448ba..00000000000 --- a/plugins/native_system_contract_plugin/include/eos/native_system_contract_plugin/native_system_contract_plugin.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include - -#include - -#include -#include - -namespace eos { -using namespace appbase; - -/** - * @brief This class is a native C++ implementation of the system contract. - */ -class native_system_contract_plugin : public appbase::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&); - 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::chain_controller& db); - -private: - std::unique_ptr my; -}; - -} - diff --git a/plugins/native_system_contract_plugin/native_system_contract_plugin.cpp b/plugins/native_system_contract_plugin/native_system_contract_plugin.cpp deleted file mode 100644 index 2edb81b58a2..00000000000 --- a/plugins/native_system_contract_plugin/native_system_contract_plugin.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace eos { - -class native_system_contract_plugin_impl { -public: - native_system_contract_plugin_impl(chain_controller& chain) - : chain(chain) {} - - chain_controller& chain; -}; - -native_system_contract_plugin::native_system_contract_plugin() - : my(new native_system_contract_plugin_impl(app().get_plugin().chain())){} -native_system_contract_plugin::~native_system_contract_plugin(){} - -void native_system_contract_plugin::plugin_initialize(const variables_map&) { - install(my->chain); -} - -void native_system_contract_plugin::plugin_startup() { -} - -void native_system_contract_plugin::plugin_shutdown() { -} - -void native_system_contract_plugin::install(chain_controller& db) { -#define SET_HANDLERS(contractname, handlername) \ - db.set_validate_handler(contractname, contractname, #handlername, &handlername::validate); \ - db.set_precondition_validate_handler(contractname, contractname, #handlername, &handlername::validate_preconditions); \ - db.set_apply_handler(contractname, contractname, #handlername, &handlername::apply); -#define FWD_SET_HANDLERS(r, data, elem) SET_HANDLERS(data, elem) - - // Set message handlers - BOOST_PP_SEQ_FOR_EACH(FWD_SET_HANDLERS, config::SystemContractName, EOS_SYSTEM_CONTRACT_FUNCTIONS) - BOOST_PP_SEQ_FOR_EACH(FWD_SET_HANDLERS, config::EosContractName, EOS_CONTRACT_FUNCTIONS) - BOOST_PP_SEQ_FOR_EACH(FWD_SET_HANDLERS, config::StakedBalanceContractName, EOS_STAKED_BALANCE_CONTRACT_FUNCTIONS) - - // Set notify handlers - db.set_apply_handler(config::EosContractName, config::StakedBalanceContractName, - "TransferToLocked", &TransferToLocked_Notify_Staked); - db.set_apply_handler(config::StakedBalanceContractName, config::EosContractName, - "ClaimUnlockedEos", &ClaimUnlockedEos_Notify_Eos); -#warning TODO: Notify eos and sbc when an account is created -} - -} diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index f6d9ca8558c..f3110b7bdc0 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -57,7 +57,7 @@ class producer_plugin_impl { uint32_t _production_skip_flags = eos::chain::chain_controller::skip_nothing; std::map _private_keys; - std::set _producers; + std::set _producers; boost::asio::deadline_timer _timer; }; @@ -92,7 +92,7 @@ void producer_plugin::set_program_options( boost::program_options::options_description& config_file_options) { auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan"))); - string producer_id_example = fc::json::to_string(chain::producer_id_type(5)); + string producer_id_example = fc::json::to_string("init0"); auto private_key_default = std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), eos::utilities::key_to_wif(default_priv_key)); @@ -100,7 +100,7 @@ void producer_plugin::set_program_options( command_line_options.add_options() ("enable-stale-production", bpo::bool_switch()->notifier([this](bool e){my->_production_enabled = e;}), "Enable block production, even if the chain is stale.") ("required-participation", bpo::bool_switch()->notifier([this](int e){my->_required_producer_participation = uint32_t(e*config::Percent1);}), "Percent of producers (0-99) that must be participating in order to produce blocks") - ("producer-id,w", bpo::value>()->composing()->multitoken(), + ("producer-name,p", bpo::value>()->composing()->multitoken(), ("ID of producer controlled by this node (e.g. " + producer_id_example + ", quotes are required, may specify multiple times)").c_str()) ("private-key", bpo::value>()->composing()->multitoken()->default_value({fc::json::to_string(private_key_default)}, fc::json::to_string(private_key_default)), @@ -117,13 +117,13 @@ T dejsonify(const string& s) { #define LOAD_VALUE_SET(options, name, container, type) \ if( options.count(name) ) { \ const std::vector& ops = options[name].as>(); \ - std::transform(ops.begin(), ops.end(), std::inserter(container, container.end()), &dejsonify); \ + std::copy(ops.begin(), ops.end(), std::inserter(container, container.end())); \ } void producer_plugin::plugin_initialize(const boost::program_options::variables_map& options) { try { my->_options = &options; - LOAD_VALUE_SET(options, "producer-id", my->_producers, chain::producer_id_type) + LOAD_VALUE_SET(options, "producer-name", my->_producers, types::AccountName) if( options.count("private-key") ) { @@ -237,7 +237,6 @@ block_production_condition::block_production_condition_enum producer_plugin_impl block_production_condition::block_production_condition_enum producer_plugin_impl::maybe_produce_block(fc::mutable_variant_object& capture) { chain::chain_controller& chain = app().get_plugin().chain(); - const auto& db = app().get_plugin().db(); fc::time_point now_fine = fc::time_point::now(); fc::time_point_sec now = now_fine + fc::microseconds(500000); @@ -268,7 +267,7 @@ block_production_condition::block_production_condition_enum producer_plugin_impl // assert( now > chain.head_block_time() ); - eos::chain::producer_id_type scheduled_producer = chain.get_scheduled_producer( slot ); + eos::types::AccountName scheduled_producer = chain.get_scheduled_producer( slot ); // we must control the producer scheduled to produce the next block. if( _producers.find( scheduled_producer ) == _producers.end() ) { @@ -277,7 +276,7 @@ block_production_condition::block_production_condition_enum producer_plugin_impl } fc::time_point_sec scheduled_time = chain.get_slot_time( slot ); - eos::chain::public_key_type scheduled_key = db.get(scheduled_producer).signing_key; + eos::chain::public_key_type scheduled_key = chain.get_producer(scheduled_producer).signing_key; auto private_key_itr = _private_keys.find( scheduled_key ); if( private_key_itr == _private_keys.end() ) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 33108ea2755..5bfe608deed 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,4 +8,4 @@ endif() file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( chain_test native_system_contract_plugin eos_chain chainbase eos_utilities eos_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_test eos_native_contract eos_chain chainbase eos_utilities eos_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 093cf2d0b7a..a20ff0e84fc 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -30,7 +30,8 @@ #include -#include +#include +#include #include #include @@ -47,12 +48,11 @@ namespace eos { namespace chain { testing_fixture::testing_fixture() { default_genesis_state.initial_timestamp = fc::time_point_sec(EOS_TESTING_GENESIS_TIMESTAMP); - default_genesis_state.immutable_parameters.min_producer_count = config::ProducerCount; - for (int i = 0; i < default_genesis_state.immutable_parameters.min_producer_count; ++i) { + for (int i = 0; i < config::ProducerCount; ++i) { auto name = std::string("init") + fc::to_string(i); auto private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(name)); public_key_type public_key = private_key.get_public_key(); - default_genesis_state.initial_accounts.emplace_back(name, 100000, public_key, public_key); + default_genesis_state.initial_accounts.emplace_back(name, 0, 100000, public_key, public_key); key_ring[public_key] = private_key; private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(name + ".producer")); @@ -87,16 +87,10 @@ private_key_type testing_fixture::get_private_key(const public_key_type& public_ return itr->second; } -testing_database::testing_database(chainbase::database& db, fork_database& fork_db, block_log& blocklog, - testing_fixture& fixture, fc::optional override_genesis_state) - : chain_controller(db, fork_db, blocklog, [&override_genesis_state, &fixture] { - if (override_genesis_state) return *override_genesis_state; - return fixture.genesis_state(); - }), - fixture(fixture) { - // Install the system contract implementation - native_system_contract_plugin::install(*this); -} +testing_database::testing_database(chainbase::database& db, fork_database& fork_db, + block_log& blocklog, chain_initializer& initializer, testing_fixture& fixture) + : chain_controller(db, fork_db, blocklog, initializer), + fixture(fixture) {} void testing_database::produce_blocks(uint32_t count, uint32_t blocks_to_miss) { if (count == 0) @@ -104,10 +98,9 @@ void testing_database::produce_blocks(uint32_t count, uint32_t blocks_to_miss) { for (int i = 0; i < count; ++i) { auto slot = blocks_to_miss + 1; - auto producer_id = get_scheduled_producer(slot); - const auto& producer = get_model().get(producer_id); + auto producer = get_producer(get_scheduled_producer(slot)); auto private_key = fixture.get_private_key(producer.signing_key); - generate_block(get_slot_time(slot), producer_id, private_key, 0); + generate_block(get_slot_time(slot), producer.owner, private_key, 0); } } @@ -132,6 +125,18 @@ void testing_database::sync_with(testing_database& other) { sync_dbs(other, *this); } +types::Asset testing_database::get_liquid_balance(const types::AccountName& account) { + return get_database().get(account).balance; +} + +types::Asset testing_database::get_staked_balance(const types::AccountName& account) { + return get_database().get(account).stakedBalance; +} + +types::Asset testing_database::get_unstaking_balance(const types::AccountName& account) { + return get_database().get(account).unstakingBalance; +} + void testing_network::connect_database(testing_database& new_database) { if (databases.count(&new_database)) return; diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 5f7f02b06cc..b822ffff04d 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -27,6 +27,8 @@ #include #include +#include + #include #include @@ -133,8 +135,8 @@ class testing_fixture { */ class testing_database : public chain_controller { public: - testing_database(chainbase::database& db, fork_database& fork_db, block_log& blocklog, testing_fixture& fixture, - fc::optional override_genesis_state = {}); + testing_database(chainbase::database& db, fork_database& fork_db, block_log& blocklog, + chain_initializer& initializer, testing_fixture& fixture); /** * @brief Produce new blocks, adding them to the database, optionally following a gap of missed blocks @@ -157,6 +159,13 @@ class testing_database : public chain_controller { */ void sync_with(testing_database& other); + /// @brief Get the liquid balance belonging to the named account + Asset get_liquid_balance(const types::AccountName& account); + /// @brief Get the staked balance belonging to the named account + Asset get_staked_balance(const types::AccountName& account); + /// @brief Get the unstaking balance belonging to the named account + Asset get_unstaking_balance(const types::AccountName& account); + protected: testing_fixture& fixture; }; @@ -199,8 +208,6 @@ class testing_network { std::map databases; }; - - /// Some helpful macros to reduce boilerplate when making testcases /// @{ #include "macro_support.hpp" diff --git a/tests/common/macro_support.hpp b/tests/common/macro_support.hpp index ea9944c5bdf..528b9770624 100644 --- a/tests/common/macro_support.hpp +++ b/tests/common/macro_support.hpp @@ -7,12 +7,14 @@ chainbase::database name ## _db(get_temp_dir(), chainbase::database::read_write, TEST_DB_SIZE); \ block_log name ## _log(get_temp_dir() / "blocklog"); \ fork_database name ## _fdb; \ - testing_database name(name ## _db, name ## _fdb, name ## _log, *this); + native_contract::native_contract_chain_initializer name ## _initializer(genesis_state()); \ + testing_database name(name ## _db, name ## _fdb, name ## _log, name ## _initializer, *this); #define MKDB2(name, id) \ chainbase::database name ## _db(get_temp_dir(#id), chainbase::database::read_write, TEST_DB_SIZE); \ block_log name ## _log(get_temp_dir(#id) / "blocklog"); \ fork_database name ## _fdb; \ - testing_database name(name ## _db, name ## _fdb, name ## _log, *this); + native_contract::native_contract_chain_initializer name ## _initializer(genesis_state()); \ + testing_database name(name ## _db, name ## _fdb, name ## _log, name ## _initializer, *this); #define MKDBS_MACRO(x, y, name) Make_Database(name) #define MKNET1(name) testing_network name; @@ -22,7 +24,9 @@ #define MKACCT_IMPL(db, name, creator, active, owner, recovery, deposit) \ { \ eos::chain::SignedTransaction trx; \ - trx.emplaceMessage(#creator, config::SystemContractName, vector{}, "CreateAccount", \ + trx.emplaceMessage(#creator, config::SystemContractName, \ + vector{config::StakedBalanceContractName, \ + config::EosContractName}, "CreateAccount", \ types::CreateAccount{#creator, #name, owner, active, recovery, deposit}); \ trx.expiration = db.head_block_time() + 100; \ trx.set_reference_block(db.head_block_id()); \ @@ -79,4 +83,4 @@ trx.set_reference_block(db.head_block_id()); \ db.push_transaction(trx); \ } -#define UPPDCR3(db, owner, key) UPPDCR4(db, owner, key, db.get_model().get_producer(owner).configuration) +#define UPPDCR3(db, owner, key) UPPDCR4(db, owner, key, db.get_producer(owner).configuration) diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 975bf9ed033..841593f38ff 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -29,8 +29,6 @@ #include #include -#include - #include #include @@ -59,16 +57,13 @@ BOOST_FIXTURE_TEST_CASE(produce_blocks, testing_fixture) BOOST_FIXTURE_TEST_CASE(order_dependent_transactions, testing_fixture) { try { Make_Database(db); - auto model = db.get_model(); db.produce_blocks(10); Make_Account(db, newguy); - auto newguy = model.find("newguy"); - BOOST_CHECK(newguy != nullptr); Transfer_Asset(db, newguy, init0, Asset(1)); - BOOST_CHECK_EQUAL(model.get_account("newguy").balance, Asset(99)); - BOOST_CHECK_EQUAL(model.get_account("init0").balance, Asset(100000-99)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("newguy"), Asset(99)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init0"), Asset(100000-99)); db.produce_blocks(); BOOST_CHECK_EQUAL(db.head_block_num(), 11); @@ -76,15 +71,14 @@ BOOST_FIXTURE_TEST_CASE(order_dependent_transactions, testing_fixture) BOOST_CHECK(!db.fetch_block_by_number(11)->cycles.empty()); BOOST_CHECK(!db.fetch_block_by_number(11)->cycles.front().empty()); BOOST_CHECK_EQUAL(db.fetch_block_by_number(11)->cycles.front().front().user_input.size(), 2); - BOOST_CHECK_EQUAL(model.get_account("newguy").balance, Asset(99)); - BOOST_CHECK_EQUAL(model.get_account("init0").balance, Asset(100000-99)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("newguy"), Asset(99)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init0"), Asset(100000-99)); } FC_LOG_AND_RETHROW() } //Test account script processing BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture) { try { Make_Database(db); - auto model = db.get_model(); db.produce_blocks(10); SignedTransaction trx; @@ -115,14 +109,14 @@ BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture) trx.setMessage(0, "SetMessageHandler", handler); - idump((trx)); db.push_transaction(trx); db.produce_blocks(1); Transfer_Asset(db, init3, init1, Asset(100), "transfer 100"); db.produce_blocks(1); - const auto& world = model.get(boost::make_tuple(AccountName("init1"), String("hello"))); + const auto& world = db_db.get(boost::make_tuple(AccountName("init1"), + String("hello"))); BOOST_CHECK_EQUAL( string(world.value.c_str()), "world" ); } FC_LOG_AND_RETHROW() } @@ -131,14 +125,13 @@ BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture) BOOST_FIXTURE_TEST_CASE(missed_blocks, testing_fixture) { try { Make_Database(db) - auto model = db.get_model(); db.produce_blocks(); BOOST_CHECK_EQUAL(db.head_block_num(), 1); - producer_id_type skipped_producers[3] = {db.get_scheduled_producer(1), - db.get_scheduled_producer(2), - db.get_scheduled_producer(3)}; + AccountName skipped_producers[3] = {db.get_scheduled_producer(1), + db.get_scheduled_producer(2), + db.get_scheduled_producer(3)}; auto next_block_time = db.get_slot_time(4); auto next_producer = db.get_scheduled_producer(4); @@ -146,11 +139,11 @@ BOOST_FIXTURE_TEST_CASE(missed_blocks, testing_fixture) db.produce_blocks(1, 3); BOOST_CHECK_EQUAL(db.head_block_num(), 2); BOOST_CHECK_EQUAL(db.head_block_time().to_iso_string(), next_block_time.to_iso_string()); - BOOST_CHECK_EQUAL(db.head_block_producer()._id, next_producer._id); - BOOST_CHECK_EQUAL(model.get(next_producer).total_missed, 0); + BOOST_CHECK_EQUAL(db.head_block_producer(), next_producer); + BOOST_CHECK_EQUAL(db.get_producer(next_producer).total_missed, 0); for (auto producer : skipped_producers) { - BOOST_CHECK_EQUAL(model.get(producer).total_missed, 1); + BOOST_CHECK_EQUAL(db.get_producer(producer).total_missed, 1); } } FC_LOG_AND_RETHROW() } @@ -403,7 +396,8 @@ BOOST_FIXTURE_TEST_CASE(reindex, testing_fixture) chainbase::database db(get_temp_dir(), chainbase::database::read_write, TEST_DB_SIZE); block_log log(get_temp_dir("log")); fork_database fdb; - testing_database chain(db, fdb, log, *this); + native_contract::native_contract_chain_initializer initr(genesis_state()); + testing_database chain(db, fdb, log, initr, *this); chain.produce_blocks(100); @@ -414,7 +408,8 @@ BOOST_FIXTURE_TEST_CASE(reindex, testing_fixture) chainbase::database db(get_temp_dir(), chainbase::database::read_write, TEST_DB_SIZE); block_log log(get_temp_dir("log")); fork_database fdb; - testing_database chain(db, fdb, log, *this); + native_contract::native_contract_chain_initializer initr(genesis_state()); + testing_database chain(db, fdb, log, initr, *this); BOOST_CHECK_EQUAL(chain.head_block_num(), 100 - lag); chain.produce_blocks(20); diff --git a/tests/tests/database_tests.cpp b/tests/tests/database_tests.cpp index a8f399408cf..4aa30237c28 100644 --- a/tests/tests/database_tests.cpp +++ b/tests/tests/database_tests.cpp @@ -123,7 +123,7 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_parameters, testing_fixture, * boost::un for (int i = 0; i < votes.size(); ++i) { auto name = std::string("init") + fc::to_string(i); - Update_Producer(db, name, db.get_model().get_producer(name).signing_key, votes[i]); + Update_Producer(db, name, db.get_producer(name).signing_key, votes[i]); } BOOST_CHECK_NE(db.get_global_properties().configuration, medians); diff --git a/tests/tests/system_contract_tests.cpp b/tests/tests/system_contract_tests.cpp index cf11cb64320..0caaa8914f7 100644 --- a/tests/tests/system_contract_tests.cpp +++ b/tests/tests/system_contract_tests.cpp @@ -5,8 +5,6 @@ #include #include -#include - #include #include @@ -23,26 +21,23 @@ BOOST_FIXTURE_TEST_CASE(create_account, testing_fixture) { try { Make_Database(db); db.produce_blocks(10); - auto model = db.get_model(); - const auto& init1_account = model.get_account("init1"); - BOOST_CHECK_EQUAL(init1_account.balance, Asset(100000)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init1"), Asset(100000)); Make_Account(db, joe, init1, Asset(1000)); { // test in the pending state - const auto& joe_account = model.get_account("joe"); - BOOST_CHECK_EQUAL(joe_account.balance, Asset(1000)); - BOOST_CHECK_EQUAL(init1_account.balance, Asset(100000 - 1000)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("joe"), Asset(1000)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init1"), Asset(100000 - 1000)); - const auto& joe_owner_authority = model.get(boost::make_tuple(joe_account.id, "owner")); + const auto& joe_owner_authority = db_db.get(boost::make_tuple("joe", "owner")); BOOST_CHECK_EQUAL(joe_owner_authority.auth.threshold, 1); BOOST_CHECK_EQUAL(joe_owner_authority.auth.accounts.size(), 0); BOOST_CHECK_EQUAL(joe_owner_authority.auth.keys.size(), 1); BOOST_CHECK_EQUAL(string(joe_owner_authority.auth.keys[0].key), string(joe_public_key)); BOOST_CHECK_EQUAL(joe_owner_authority.auth.keys[0].weight, 1); - const auto& joe_active_authority = model.get(boost::make_tuple(joe_account.id, "active")); + const auto& joe_active_authority = db_db.get(boost::make_tuple("joe", "active")); BOOST_CHECK_EQUAL(joe_active_authority.auth.threshold, 1); BOOST_CHECK_EQUAL(joe_active_authority.auth.accounts.size(), 0); BOOST_CHECK_EQUAL(joe_active_authority.auth.keys.size(), 1); @@ -52,18 +47,17 @@ BOOST_FIXTURE_TEST_CASE(create_account, testing_fixture) db.produce_blocks(1); /// verify changes survived creating a new block { - const auto& joe_account = model.get_account("joe"); - BOOST_CHECK_EQUAL(joe_account.balance, Asset(1000)); - BOOST_CHECK_EQUAL(init1_account.balance, Asset(100000 - 1000)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("joe"), Asset(1000)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init1"), Asset(100000 - 1000)); - const auto& joe_owner_authority = model.get(boost::make_tuple(joe_account.id, "owner")); + const auto& joe_owner_authority = db_db.get(boost::make_tuple("joe", "owner")); BOOST_CHECK_EQUAL(joe_owner_authority.auth.threshold, 1); BOOST_CHECK_EQUAL(joe_owner_authority.auth.accounts.size(), 0); BOOST_CHECK_EQUAL(joe_owner_authority.auth.keys.size(), 1); BOOST_CHECK_EQUAL(string(joe_owner_authority.auth.keys[0].key), string(joe_public_key)); BOOST_CHECK_EQUAL(joe_owner_authority.auth.keys[0].weight, 1); - const auto& joe_active_authority = model.get(boost::make_tuple(joe_account.id, "active")); + const auto& joe_active_authority = db_db.get(boost::make_tuple("joe", "active")); BOOST_CHECK_EQUAL(joe_active_authority.auth.threshold, 1); BOOST_CHECK_EQUAL(joe_active_authority.auth.accounts.size(), 0); BOOST_CHECK_EQUAL(joe_active_authority.auth.keys.size(), 1); @@ -76,7 +70,6 @@ BOOST_FIXTURE_TEST_CASE(create_account, testing_fixture) BOOST_FIXTURE_TEST_CASE(transfer, testing_fixture) { try { Make_Database(db) - auto model = db.get_model(); BOOST_CHECK_EQUAL(db.head_block_num(), 0); db.produce_blocks(10); @@ -108,22 +101,21 @@ BOOST_FIXTURE_TEST_CASE(transfer, testing_fixture) trx.setMessage(0, "Transfer", trans); db.push_transaction(trx); - BOOST_CHECK_EQUAL(model.get_account("init1").balance, Asset(100000 - 100)); - BOOST_CHECK_EQUAL(model.get_account("init2").balance, Asset(100000 + 100)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init1"), Asset(100000 - 100)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init2"), Asset(100000 + 100)); db.produce_blocks(1); BOOST_REQUIRE_THROW(db.push_transaction(trx), transaction_exception); // not unique Transfer_Asset(db, init2, init1, Asset(100)); - BOOST_CHECK_EQUAL(model.get_account("init1").balance, Asset(100000)); - BOOST_CHECK_EQUAL(model.get_account("init2").balance, Asset(100000)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init1"), Asset(100000)); + BOOST_CHECK_EQUAL(db.get_liquid_balance("init2"), Asset(100000)); } FC_LOG_AND_RETHROW() } // Simple test of creating/updating a new block producer BOOST_FIXTURE_TEST_CASE(producer_creation, testing_fixture) { try { Make_Database(db) - auto model = db.get_model(); db.produce_blocks(); BOOST_CHECK_EQUAL(db.head_block_num(), 1); @@ -131,8 +123,8 @@ BOOST_FIXTURE_TEST_CASE(producer_creation, testing_fixture) Make_Producer(db, producer, producer_public_key); while (db.head_block_num() < 3) { - auto& producer = model.get_producer("producer"); - BOOST_CHECK_EQUAL(model.get(producer.owner).name, "producer"); + auto& producer = db.get_producer("producer"); + BOOST_CHECK_EQUAL(db.get_producer(producer.owner).owner, "producer"); BOOST_CHECK_EQUAL(producer.signing_key, producer_public_key); BOOST_CHECK_EQUAL(producer.last_aslot, 0); BOOST_CHECK_EQUAL(producer.total_missed, 0); @@ -142,7 +134,7 @@ BOOST_FIXTURE_TEST_CASE(producer_creation, testing_fixture) Make_Key(signing); Update_Producer(db, "producer", signing_public_key); - auto& producer = model.get_producer("producer"); + auto& producer = db.get_producer("producer"); BOOST_CHECK_EQUAL(producer.signing_key, signing_public_key); } FC_LOG_AND_RETHROW() }