提交 cf631a66 编写于 作者: D Daniel Larimer

Merge branch 'master' of github.com:EOSIO/eos

......@@ -9,3 +9,4 @@ add_subdirectory( chain )
add_subdirectory( egenesis )
add_subdirectory( utilities )
add_subdirectory( appbase )
add_subdirectory( native_contract )
......@@ -13,7 +13,6 @@ add_library( eos_chain
block_log.cpp
BlockchainConfiguration.cpp
chain_model.cpp
${HEADERS}
)
......
#include <eos/chain/chain_model.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/chain/producer_object.hpp>
namespace eos { namespace chain {
const account_object& chain_model::get_account(const AccountName& name) const {
return db.get<account_object, by_name>(name);
}
const producer_object& chain_model::get_producer(const AccountName& name) const {
return db.get<producer_object, by_owner>(get_account(name).id);
}
} } // namespace eos::chain
......@@ -55,15 +55,15 @@ namespace eos { namespace chain {
shared_vector<types::AccountPermissionWeight> accounts;
shared_vector<types::KeyPermissionWeight> keys;
};
class account_object : public chainbase::object<account_object_type, account_object>
{
class account_object : public chainbase::object<account_object_type, account_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<permission_object_type, permission_object>
{
class permission_object : public chainbase::object<permission_object_type, permission_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<tag<by_id>, member<permission_object, permission_object::id_type, &permission_object::id>>,
ordered_unique<tag<by_parent>,
composite_key< permission_object,
ordered_unique<tag<by_parent>,
composite_key<permission_object,
member<permission_object, permission_object::id_type, &permission_object::parent>,
member<permission_object, permission_object::id_type, &permission_object::id>
>
>,
ordered_unique<tag<by_owner>,
composite_key< permission_object,
member<permission_object, account_object::id_type, &permission_object::owner>,
ordered_unique<tag<by_owner>,
composite_key<permission_object,
member<permission_object, AccountName, &permission_object::owner>,
member<permission_object, PermissionName, &permission_object::name>,
member<permission_object, permission_object::id_type, &permission_object::id>
>
>,
ordered_unique<tag<by_name>, member<permission_object, PermissionName, &permission_object::name> >
ordered_unique<tag<by_name>,
composite_key<permission_object,
member<permission_object, PermissionName, &permission_object::name>,
member<permission_object, permission_object::id_type, &permission_object::id>
>
>
>
>;
......@@ -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<eos::chain::permission_object>, (_id))
FC_REFLECT(chainbase::oid<eos::chain::account_object>, (_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))
......@@ -29,7 +29,6 @@
#include <eos/chain/fork_database.hpp>
#include <eos/chain/genesis_state.hpp>
#include <eos/chain/block_log.hpp>
#include <eos/chain/chain_model.hpp>
#include <chainbase/chainbase.hpp>
#include <fc/scoped_exit.hpp>
......@@ -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<Message> 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<AccountName, config::ProducerCount> 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_state_type()> 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<block_id_type> 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_state_type()> 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); }
......
#pragma once
#include <chainbase/chainbase.hpp>
#include <eos/types/types.hpp>
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<typename ObjectType, typename IndexedByType, typename CompatibleKey>
const ObjectType* find(CompatibleKey&& key) const {
return db.find<ObjectType, IndexedByType, CompatibleKey>(std::forward<CompatibleKey>(key));
}
template<typename ObjectType>
const ObjectType* find(chainbase::oid<ObjectType> key = chainbase::oid<ObjectType>()) const { return db.find(key); }
template<typename ObjectType, typename IndexedByType, typename CompatibleKey>
const ObjectType& get(CompatibleKey&& key) const {
return db.get<ObjectType, IndexedByType, CompatibleKey>(std::forward<CompatibleKey>(key));
}
template<typename ObjectType>
const ObjectType& get(const chainbase::oid<ObjectType>& key = chainbase::oid<ObjectType>()) const {
return db.get(key);
}
};
} } // namespace eos::chain
/*
* 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 <eos/chain/immutable_chain_parameters.hpp>
#include <eos/chain/types.hpp>
#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<chain_property_object_type, chain_property_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<tag<by_id>, 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))
......@@ -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 */
......
......@@ -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_account_type> initial_accounts;
vector<initial_producer_type> 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))
......@@ -49,7 +49,7 @@ namespace eos { namespace chain {
id_type id;
BlockchainConfiguration configuration;
std::array<producer_id_type, config::ProducerCount> active_producers;
std::array<AccountName, config::ProducerCount> 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;
/**
......
......@@ -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)
)
......@@ -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), )
......@@ -10,29 +10,26 @@ 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& scope)
:message_validate_context(m),scope(scope),db(db){}
const types::AccountName& recipient;
const types::AccountName& scope;
const chainbase::database& db;
};
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& scope)
:precondition_validate_context(db,m,scope),mutable_db(db){}
types::String get(types::String key)const;
void set(types::String key, types::String value);
......
......@@ -32,7 +32,7 @@ class producer_object : public chainbase::object<producer_object_type, producer_
OBJECT_CTOR(producer_object)
id_type id;
account_id_type owner;
AccountName owner;
uint64_t last_aslot = 0;
public_key_type signing_key;
int64_t total_missed = 0;
......@@ -48,7 +48,7 @@ using producer_multi_index = chainbase::shared_multi_index_container<
producer_object,
indexed_by<
ordered_unique<tag<by_id>, member<producer_object, producer_object::id_type, &producer_object::id>>,
ordered_unique<tag<by_owner>, member<producer_object, account_id_type, &producer_object::owner>>,
ordered_unique<tag<by_owner>, member<producer_object, AccountName, &producer_object::owner>>,
ordered_non_unique<tag<by_key>, member<producer_object, public_key_type, &producer_object::signing_key>>
>
>;
......
......@@ -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<account_object>;
using producer_id_type = chainbase::oid<producer_object>;
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, )
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" )
#include <eos/native_system_contract_plugin/eos_contract.hpp>
#include <eos/native_contract/eos_contract.hpp>
#include <eos/native_contract/balance_object.hpp>
#include <eos/chain/message_handling_contexts.hpp>
#include <eos/chain/account_object.hpp>
......@@ -7,6 +8,34 @@
namespace eos {
using namespace chain;
void CreateAccount_Notify_Eos::validate_preconditions(precondition_validate_context& context) {
auto create = context.msg.as<types::CreateAccount>();
const auto& creatorBalance = context.db.get<BalanceObject, byOwnerName>(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<types::ClaimUnlockedEos>();
const auto& claimant = context.db.get<BalanceObject, byOwnerName>(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<types::CreateAccount>();
context.mutable_db.create<BalanceObject>([&create](BalanceObject& b) {
b.ownerName = create.name;
b.balance = create.deposit.amount;
});
const auto& creatorBalance = context.mutable_db.get<BalanceObject, byOwnerName>(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<types::Transfer>();
try {
......@@ -19,23 +48,25 @@ void Transfer::validate_preconditions(precondition_validate_context& context) {
const auto& db = context.db;
auto transfer = context.msg.as<types::Transfer>();
db.get<account_object,by_name>(transfer.to); ///< make sure this exists
const auto& from = db.get<account_object,by_name>(transfer.from);
EOS_ASSERT(from.balance >= transfer.amount.amount, message_precondition_exception, "Insufficient Funds",
("from.balance",from.balance)("transfer.amount",transfer.amount));
try {
db.get<account_object,by_name>(transfer.to); ///< make sure this exists
const auto& from = db.get<BalanceObject, byOwnerName>(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<types::Transfer>();
const auto& from = db.get<account_object,by_name>(transfer.from);
const auto& to = db.get<account_object,by_name>(transfer.to);
const auto& from = db.get<BalanceObject, byOwnerName>(transfer.from);
const auto& to = db.get<BalanceObject, byOwnerName>(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(from, [&](BalanceObject& a) {
a.balance -= transfer.amount.amount;
});
db.modify(to, [&](account_object& a) {
a.balance += transfer.amount;
db.modify(to, [&](BalanceObject& a) {
a.balance += transfer.amount.amount;
});
}
......@@ -52,9 +83,9 @@ void TransferToLocked::validate_preconditions(precondition_validate_context& con
auto lock = context.msg.as<types::TransferToLocked>();
ShareType balance;
try {
const auto& sender = context.db.get<account_object, by_name>(lock.from);
context.db.get<account_object, by_name>(lock.to);
balance = sender.balance.amount;
const auto& sender = context.db.get<BalanceObject, byOwnerName>(lock.from);
context.db.get<BalanceObject, byOwnerName>(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));
......@@ -62,17 +93,9 @@ void TransferToLocked::validate_preconditions(precondition_validate_context& con
void TransferToLocked::apply(apply_context& context) {
auto lock = context.msg.as<types::TransferToLocked>();
const auto& locker = context.db.get<account_object, by_name>(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<types::ClaimUnlockedEos>();
const auto& claimant = context.db.get<account_object, by_name>(claim.account);
context.mutable_db.modify(claimant, [&claim](account_object& a) {
a.balance.amount += claim.amount;
const auto& locker = context.db.get<BalanceObject, byOwnerName>(lock.from);
context.mutable_db.modify(locker, [&lock](BalanceObject& a) {
a.balance -= lock.amount;
});
}
......
#pragma once
#include <eos/chain/types.hpp>
#include <eos/chain/multi_index_includes.hpp>
#include <eos/types/types.hpp>
#include <chainbase/chainbase.hpp>
namespace eos {
/**
* @brief The BalanceObject class tracks the EOS balance for accounts
*/
class BalanceObject : public chainbase::object<chain::balance_object_type, BalanceObject> {
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<tag<by_id>,
member<BalanceObject, BalanceObject::id_type, &BalanceObject::id>
>,
ordered_unique<tag<byOwnerName>,
member<BalanceObject, types::AccountName, &BalanceObject::ownerName>
>
>
>;
} // namespace eos
CHAINBASE_SET_INDEX_TYPE(eos::BalanceObject, eos::BalanceMultiIndex)
......@@ -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);
......
#pragma once
#include <eos/chain/chain_controller.hpp>
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<chain::Message> 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<types::AccountName, config::ProducerCount> get_chain_start_producers();
};
} } // namespace eos::native_contract
#pragma once
/// @file This file #include's all database objects/indices used by the C++ native contract implementation
#include <eos/native_contract/staked_balance_objects.hpp>
#include <eos/native_contract/balance_object.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);
......
#include <eos/native_contract/native_contract_chain_initializer.hpp>
#include <eos/native_contract/objects.hpp>
#include <eos/native_contract/staked_balance_contract.hpp>
#include <eos/native_contract/eos_contract.hpp>
#include <eos/native_contract/system_contract.hpp>
#include <eos/chain/producer_object.hpp>
namespace eos { namespace native_contract {
using namespace chain;
std::vector<chain::Message> native_contract_chain_initializer::prepare_database(chain_controller& chain,
chainbase::database& db) {
std::vector<chain::Message> messages_to_process;
// Install the native contract's indexes; we can't do anything until our objects are recognized
db.add_index<StakedBalanceMultiIndex>();
db.add_index<BalanceMultiIndex>();
db.add_index<ProducerVotesMultiIndex>();
/// 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<account_object>([this, &name](account_object& a) {
a.name = name;
a.creation_date = genesis.initial_timestamp;
});
db.create<BalanceObject>([&name, liquidBalance](BalanceObject& b) {
b.ownerName = name;
b.balance = liquidBalance;
});
db.create<StakedBalanceObject>([&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<types::elem>(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<types::AccountName> initial_producers;
for (const auto& producer : genesis.initial_producers) {
db.create<producer_object>([&](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<types::AccountName, config::ProducerCount> native_contract_chain_initializer::get_chain_start_producers() {
std::array<types::AccountName, config::ProducerCount> 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
#include <eos/native_system_contract_plugin/staked_balance_contract.hpp>
#include <eos/native_contract/staked_balance_contract.hpp>
#include <eos/native_contract/staked_balance_objects.hpp>
#include <eos/chain/global_property_object.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/chain/exceptions.hpp>
#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<types::CreateAccount>();
context.mutable_db.create<StakedBalanceObject>([&create](StakedBalanceObject& sbo) {
sbo.ownerName = create.name;
});
}
void TransferToLocked_Notify_Staked::apply(apply_context& context) {
auto lock = context.msg.as<types::TransferToLocked>();
const auto& balance = context.db.get<StakedBalanceObject, byOwnerName>(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<types::CreateProducer>();
const auto& db = context.db;
const auto& owner = db.get<account_object,by_name>(create.name);
auto producer = db.find<producer_object, by_owner>(owner.id);
auto producer = db.find<producer_object, by_owner>(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<types::CreateProducer>();
auto& db = context.mutable_db;
const auto& owner = db.get<account_object,by_name>(create.name);
db.create<producer_object>([&create, &owner](producer_object& p) {
p.owner = owner.id;
db.create<producer_object>([&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<types::UpdateProducer>();
const auto& producer = db.get<producer_object, by_owner>(db.get<account_object, by_name>(update.name).id);
const auto& producer = db.get<producer_object, by_owner>(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<types::UpdateProducer>();
const auto& producer = db.get<producer_object, by_owner>(db.get<account_object, by_name>(update.name).id);
const auto& producer = db.get<producer_object, by_owner>(update.name);
db.modify(producer, [&update](producer_object& p) {
p.signing_key = update.newKey;
......
#include "staked_balance_objects.hpp"
#include <eos/native_contract/staked_balance_objects.hpp>
namespace eos {
using namespace chain;
......
#include <eos/native_system_contract_plugin/system_contract.hpp>
#include <eos/native_contract/system_contract.hpp>
#include <eos/chain/message_handling_contexts.hpp>
#include <eos/chain/action_objects.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/chain/type_object.hpp>
#include <eos/chain/exceptions.hpp>
#include <eos/chain/global_property_object.hpp>
namespace eos {
using namespace chain;
void DefineStruct::validate(message_validate_context& context) {
auto msg = context.msg.as<types::DefineStruct>();
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<types::CreateAccount>();
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<account_object,by_name>(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<types::CreateAccount>();
db.modify(db.get<account_object,by_name>(context.msg.sender), [&create](account_object& a) {
a.balance -= create.deposit;
const auto& new_account = db.create<account_object>([&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<account_object>([&create](account_object& a) {
a.name = create.name;
a.balance = create.deposit;
});
const auto& owner_permission = db.create<permission_object>([&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<permission_object>([&create, &owner_permission](permission_object& p) {
......
......@@ -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)
......@@ -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
......
......@@ -2,6 +2,11 @@
#include <eos/chain/fork_database.hpp>
#include <eos/chain/block_log.hpp>
#include <eos/chain/exceptions.hpp>
#include <eos/chain/genesis_state.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/native_contract/native_contract_chain_initializer.hpp>
#include <fc/io/json.hpp>
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<eos::chain::genesis_state_type>();
};
auto& db = app().get_plugin<database_plugin>().db();
auto genesis = fc::json::from_file(my->genesis_file).as<chain::genesis_state_type>();
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");
......
......@@ -24,7 +24,7 @@ public:
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;
};
......
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" )
#pragma once
#include <appbase/application.hpp>
#include <eos/chain_plugin/chain_plugin.hpp>
#include <eos/chain/authority.hpp>
#include <eos/chain/types.hpp>
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<native_system_contract_plugin> {
public:
native_system_contract_plugin();
virtual ~native_system_contract_plugin();
APPBASE_PLUGIN_REQUIRES((chain_plugin))
virtual void set_program_options(options_description&, options_description&) override {}
void plugin_initialize(const variables_map&);
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<class native_system_contract_plugin_impl> my;
};
}
#include <eos/native_system_contract_plugin/native_system_contract_plugin.hpp>
#include <eos/native_system_contract_plugin/staked_balance_contract.hpp>
#include <eos/native_system_contract_plugin/system_contract.hpp>
#include <eos/native_system_contract_plugin/eos_contract.hpp>
#include <eos/chain/action_objects.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/chain/exceptions.hpp>
#include <eos/types/native.hpp>
#include <eos/types/generated.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
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_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
}
}
......@@ -57,7 +57,7 @@ public:
uint32_t _production_skip_flags = eos::chain::chain_controller::skip_nothing;
std::map<chain::public_key_type, fc::ecc::private_key> _private_keys;
std::set<chain::producer_id_type> _producers;
std::set<types::AccountName> _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<vector<string>>()->composing()->multitoken(),
("producer-name,p", bpo::value<vector<string>>()->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<vector<string>>()->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<std::string>& ops = options[name].as<std::vector<std::string>>(); \
std::transform(ops.begin(), ops.end(), std::inserter(container, container.end()), &dejsonify<type>); \
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_plugin>().chain();
const auto& db = app().get_plugin<database_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() )
......
......@@ -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} )
......@@ -30,7 +30,8 @@
#include <eos/utilities/tempdir.hpp>
#include <eos/native_system_contract_plugin/native_system_contract_plugin.hpp>
#include <eos/native_contract/native_contract_chain_initializer.hpp>
#include <eos/native_contract/objects.hpp>
#include <fc/crypto/digest.hpp>
#include <fc/smart_ref_impl.hpp>
......@@ -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<genesis_state_type> 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<BalanceObject, byOwnerName>(account).balance;
}
types::Asset testing_database::get_staked_balance(const types::AccountName& account) {
return get_database().get<StakedBalanceObject, byOwnerName>(account).stakedBalance;
}
types::Asset testing_database::get_unstaking_balance(const types::AccountName& account) {
return get_database().get<StakedBalanceObject, byOwnerName>(account).unstakingBalance;
}
void testing_network::connect_database(testing_database& new_database) {
if (databases.count(&new_database))
return;
......
......@@ -27,6 +27,8 @@
#include <eos/chain/producer_object.hpp>
#include <eos/chain/exceptions.hpp>
#include <eos/native_contract/native_contract_chain_initializer.hpp>
#include <eos/utilities/tempdir.hpp>
#include <fc/io/json.hpp>
......@@ -133,8 +135,8 @@ protected:
*/
class testing_database : public chain_controller {
public:
testing_database(chainbase::database& db, fork_database& fork_db, block_log& blocklog, testing_fixture& fixture,
fc::optional<genesis_state_type> 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 @@ public:
*/
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 @@ protected:
std::map<testing_database*, scoped_connection> databases;
};
/// Some helpful macros to reduce boilerplate when making testcases
/// @{
#include "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<AccountName>{}, "CreateAccount", \
trx.emplaceMessage(#creator, config::SystemContractName, \
vector<types::AccountName>{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)
......@@ -29,8 +29,6 @@
#include <eos/chain/account_object.hpp>
#include <eos/chain/key_value_object.hpp>
#include <eos/native_system_contract_plugin/native_system_contract_plugin.hpp>
#include <eos/utilities/tempdir.hpp>
#include <fc/crypto/digest.hpp>
......@@ -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<account_object, by_name>("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<key_value_object,by_scope_key>(boost::make_tuple(AccountName("init1"), String("hello")));
const auto& world = db_db.get<key_value_object,by_scope_key>(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);
......
......@@ -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);
......
......@@ -5,8 +5,6 @@
#include <eos/chain/account_object.hpp>
#include <eos/chain/key_value_object.hpp>
#include <eos/native_system_contract_plugin/native_system_contract_plugin.hpp>
#include <eos/utilities/tempdir.hpp>
#include <fc/crypto/digest.hpp>
......@@ -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<permission_object, by_owner>(boost::make_tuple(joe_account.id, "owner"));
const auto& joe_owner_authority = db_db.get<permission_object, by_owner>(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<permission_object, by_owner>(boost::make_tuple(joe_account.id, "active"));
const auto& joe_active_authority = db_db.get<permission_object, by_owner>(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<permission_object, by_owner>(boost::make_tuple(joe_account.id, "owner"));
const auto& joe_owner_authority = db_db.get<permission_object, by_owner>(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<permission_object, by_owner>(boost::make_tuple(joe_account.id, "active"));
const auto& joe_active_authority = db_db.get<permission_object, by_owner>(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() }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册