Safety Check: Part 2 -- Implement and Integrate Checks
Implement a safety check mechanism on object_database, based on the safety_check_policy abstract interface. Create two standard implementations of the safety_check_policy interface, one (null_safety_check) which allows all modifications unconditionally, and the other (database_lock_safety_check) which allows modifications only when unlocked. Integrate these safety checks into chain::database and plugins, so that the consensus databases are locked at all times except during core consensus code pathways. Also ensures that databases are re-locked when calling code outside of consensus pathways from consensus pathways. To make this work, it was necessary to move two objects from the consensus object spaces to a new API object space. The operation_history_object and account_transaction_history_object were moved to the API object space, as they are not actually used by consensus and are maintained by a plugin (which can no longer modify the consensus object spaces, due to the safety checks). Finally, add a mechanism to application and chain::database, which allows the chain to start in "unit testing mode" and allows unchecked actions upon the database within delimited scopes. This was necessary because many tests edit the database directly to set up the environment for their respective tests. This mode is activated by database_fixture so tests can utilize it conveniently, but it is architecturally difficult to enable this mode in production, i.e. from a plugin.
This commit is contained in:
parent
e8b432c19f
commit
f9fcffbb4d
39 changed files with 772 additions and 300 deletions
|
|
@ -291,6 +291,10 @@ public:
|
|||
_self(self),
|
||||
_chain_db(std::make_shared<chain::database>()) {
|
||||
}
|
||||
explicit application_impl(application *self, application::TESTING_MODE) :
|
||||
_self(self),
|
||||
_chain_db(std::make_shared<chain::database>(true)) {
|
||||
}
|
||||
|
||||
~application_impl() {
|
||||
}
|
||||
|
|
@ -846,7 +850,11 @@ public:
|
|||
} // namespace detail
|
||||
|
||||
application::application() :
|
||||
my(new detail::application_impl(this)) {
|
||||
my(new detail::application_impl(this)) {
|
||||
}
|
||||
|
||||
application::application(TESTING_MODE) :
|
||||
my(new detail::application_impl(this, TESTING_MODE())) {
|
||||
}
|
||||
|
||||
application::~application() {
|
||||
|
|
|
|||
|
|
@ -430,9 +430,9 @@ fc::variants database_api::get_objects(const vector<object_id_type> &ids) const
|
|||
fc::variants database_api_impl::get_objects(const vector<object_id_type> &ids) const {
|
||||
if (_subscribe_callback) {
|
||||
for (auto id : ids) {
|
||||
if (id.type() == operation_history_object_type && id.space() == protocol_ids)
|
||||
if (id.type() == api_operation_history_object_type && id.space() == api_ids)
|
||||
continue;
|
||||
if (id.type() == impl_account_transaction_history_object_type && id.space() == implementation_ids)
|
||||
if (id.type() == api_account_transaction_history_object_type && id.space() == api_ids)
|
||||
continue;
|
||||
|
||||
this->subscribe_to_item(id);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,10 @@ class abstract_plugin;
|
|||
|
||||
class application {
|
||||
public:
|
||||
struct TESTING_MODE {};
|
||||
|
||||
application();
|
||||
application(TESTING_MODE);
|
||||
~application();
|
||||
|
||||
void set_program_options(boost::program_options::options_description &cli,
|
||||
|
|
|
|||
|
|
@ -381,6 +381,7 @@ processed_transaction database::push_transaction( const signed_transaction& trx,
|
|||
|
||||
processed_transaction database::_push_transaction( const signed_transaction& trx )
|
||||
{
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
// If this is the first transaction pushed after applying a block, start a new undo session.
|
||||
// This allows us to quickly rewind to the clean state of the head block, in case a new block arrives.
|
||||
if( !_pending_tx_session.valid() )
|
||||
|
|
@ -412,6 +413,7 @@ processed_transaction database::validate_transaction( const signed_transaction&
|
|||
|
||||
processed_transaction database::push_proposal(const proposal_object& proposal)
|
||||
{ try {
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
transaction_evaluation_state eval_state(this);
|
||||
eval_state._is_proposed_trx = true;
|
||||
|
||||
|
|
@ -457,6 +459,7 @@ signed_block database::generate_block(
|
|||
uint32_t skip /* = 0 */
|
||||
)
|
||||
{ try {
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
signed_block result;
|
||||
detail::with_skip_flags( *this, skip, [&]()
|
||||
{
|
||||
|
|
@ -588,6 +591,7 @@ signed_block database::_generate_block(
|
|||
*/
|
||||
void database::pop_block()
|
||||
{ try {
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
_pending_tx_session.reset();
|
||||
auto head_id = head_block_id();
|
||||
optional<signed_block> head_block = fetch_block_by_id( head_id );
|
||||
|
|
@ -602,6 +606,7 @@ void database::pop_block()
|
|||
|
||||
void database::clear_pending()
|
||||
{ try {
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() );
|
||||
_pending_tx.clear();
|
||||
_pending_tx_session.reset();
|
||||
|
|
@ -661,6 +666,7 @@ void database::apply_block( const signed_block& next_block, uint32_t skip )
|
|||
|
||||
void database::_apply_block( const signed_block& next_block )
|
||||
{ try {
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
uint32_t next_block_num = next_block.block_num();
|
||||
uint32_t skip = get_node_properties().skip_flags;
|
||||
_applied_ops.clear();
|
||||
|
|
@ -780,6 +786,7 @@ class undo_size_restorer {
|
|||
|
||||
processed_transaction database::_apply_transaction(const signed_transaction& trx)
|
||||
{ try {
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
uint32_t skip = get_node_properties().skip_flags;
|
||||
|
||||
if( true || !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */
|
||||
|
|
@ -864,6 +871,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
|
|||
|
||||
operation_result database::apply_operation(transaction_evaluation_state& eval_state, const operation& op)
|
||||
{ try {
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
int i_which = op.which();
|
||||
uint64_t u_which = uint64_t( i_which );
|
||||
FC_ASSERT( i_which >= 0, "Negative operation tag in operation ${op}", ("op",op) );
|
||||
|
|
@ -874,10 +882,14 @@ operation_result database::apply_operation(transaction_evaluation_state& eval_st
|
|||
auto result = eval->evaluate( eval_state, op, true );
|
||||
set_applied_operation_result( op_id, result );
|
||||
|
||||
// Run third party evaluator chain
|
||||
// Run third party evaluator chain. Check that they don't yield, and lock consensus databases while they run.
|
||||
if (_third_party_operation_evaluators.size() > u_which && _third_party_operation_evaluators[u_which]) {
|
||||
ASSERT_TASK_NOT_PREEMPTED();
|
||||
_check_policy_1->lock();
|
||||
_check_policy_2->lock();
|
||||
_third_party_operation_evaluators[u_which]->evaluate( eval_state, op, true );
|
||||
_check_policy_1->unlock();
|
||||
_check_policy_2->unlock();
|
||||
}
|
||||
return result;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
|
|
|||
|
|
@ -335,6 +335,12 @@ void database::initialize_indexes()
|
|||
reset_indexes();
|
||||
_undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY );
|
||||
|
||||
_check_policy_1 = allocate_object_space<database_lock_safety_check>(protocol_ids);
|
||||
_check_policy_2 = allocate_object_space<database_lock_safety_check>(implementation_ids);
|
||||
// No checks on API objects
|
||||
allocate_object_space<null_safety_check>(api_ids);
|
||||
FC_ASSERT(_check_policy_1 != nullptr && _check_policy_2 != nullptr, "Failed to allocate object spaces");
|
||||
|
||||
//Protocol object indexes
|
||||
add_index< primary_index<asset_index, 13> >(); // 8192 assets per chunk
|
||||
add_index< primary_index<force_settlement_index> >();
|
||||
|
|
@ -421,6 +427,8 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<son_stats_index > >();
|
||||
add_index< primary_index<random_number_index > >();
|
||||
|
||||
_check_policy_1->lock();
|
||||
_check_policy_2->lock();
|
||||
}
|
||||
|
||||
void database::init_genesis(const genesis_state_type& genesis_state)
|
||||
|
|
@ -434,6 +442,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
"initial_active_witnesses is larger than the number of candidate witnesses.");
|
||||
|
||||
_undo_db.disable();
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
struct auth_inhibitor {
|
||||
auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags)
|
||||
{ db.node_properties().skip_flags |= skip_authority_check; }
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
database::database() :
|
||||
_random_number_generator(fc::ripemd160().data())
|
||||
database::database(bool allow_testing_edits) :
|
||||
_random_number_generator(fc::ripemd160().data()),
|
||||
_allow_safety_check_bypass(allow_testing_edits)
|
||||
{
|
||||
if (allow_testing_edits)
|
||||
elog("UNIT TESTING MODE ENABLED -- NOT FOR PRODUCTION USE");
|
||||
initialize_indexes();
|
||||
initialize_evaluators();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -495,11 +495,7 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
|||
transaction_get_impacted_accounts( aobj->proposed_transaction, accounts,
|
||||
ignore_custom_operation_required_auths);
|
||||
break;
|
||||
} case operation_history_object_type:{
|
||||
const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
operation_get_impacted_accounts( aobj->op, accounts,
|
||||
ignore_custom_operation_required_auths);
|
||||
} case reserved0_object_type:{
|
||||
break;
|
||||
} case withdraw_permission_object_type:{
|
||||
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
|
||||
|
|
@ -668,7 +664,7 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
|||
break;
|
||||
} case impl_block_summary_object_type:
|
||||
break;
|
||||
case impl_account_transaction_history_object_type:
|
||||
case impl_reserved1_object_type:
|
||||
break;
|
||||
case impl_chain_property_object_type:
|
||||
break;
|
||||
|
|
@ -725,6 +721,19 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
|||
default:
|
||||
break;
|
||||
}
|
||||
} else if( obj->id.space() == api_ids ) {
|
||||
switch( (api_object_type)obj->id.type() )
|
||||
{
|
||||
case graphene::chain::api_operation_history_object_type: {
|
||||
const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
operation_get_impacted_accounts( aobj->op, accounts,
|
||||
ignore_custom_operation_required_auths);
|
||||
break;
|
||||
}
|
||||
case api_account_transaction_history_object_type:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // end get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts )
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace graphene { namespace chain {
|
|||
public:
|
||||
//////////////////// db_management.cpp ////////////////////
|
||||
|
||||
database();
|
||||
database(bool allow_testing_edits = false);
|
||||
~database();
|
||||
|
||||
enum validation_steps
|
||||
|
|
@ -454,6 +454,12 @@ namespace graphene { namespace chain {
|
|||
void debug_dump();
|
||||
void apply_debug_updates();
|
||||
void debug_update( const fc::variant_object& update );
|
||||
template<typename Action>
|
||||
auto bypass_safety_checks(Action&& action) {
|
||||
FC_ASSERT(_allow_safety_check_bypass, "Safety check bypass disallowed.");
|
||||
scoped_database_unlocker unlocker(*_check_policy_1, *_check_policy_2);
|
||||
return action();
|
||||
}
|
||||
|
||||
//////////////////// db_market.cpp ////////////////////
|
||||
|
||||
|
|
@ -735,6 +741,13 @@ namespace graphene { namespace chain {
|
|||
const chain_property_object* _p_chain_property_obj = nullptr;
|
||||
const witness_schedule_object* _p_witness_schedule_obj = nullptr;
|
||||
///@}
|
||||
|
||||
/// Whether or not to allow safety check bypassing (for unit testing only)
|
||||
bool _allow_safety_check_bypass;
|
||||
/// Safety check policy for object space 1
|
||||
database_lock_safety_check* _check_policy_1 = nullptr;
|
||||
/// Safety check policy for object space 2
|
||||
database_lock_safety_check* _check_policy_2 = nullptr;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ namespace graphene { namespace chain {
|
|||
class operation_history_object : public abstract_object<operation_history_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = operation_history_object_type;
|
||||
static const uint8_t space_id = api_ids;
|
||||
static const uint8_t type_id = api_operation_history_object_type;
|
||||
|
||||
operation_history_object( const operation& o ):op(o){}
|
||||
operation_history_object(){}
|
||||
|
|
@ -93,8 +93,8 @@ class operation_history_object : public abstract_object<operation_history_object
|
|||
class account_transaction_history_object : public abstract_object<account_transaction_history_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_account_transaction_history_object_type;
|
||||
static const uint8_t space_id = api_ids;
|
||||
static const uint8_t type_id = api_account_transaction_history_object_type;
|
||||
account_id_type account; /// the account this operation applies to
|
||||
operation_history_id_type operation_id;
|
||||
uint32_t sequence = 0; /// the operation position within the given account
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ GRAPHENE_DEFINE_IDS(chain, implementation_ids, impl_,
|
|||
(account_statistics)
|
||||
(transaction_history)
|
||||
(block_summary)
|
||||
(account_transaction_history)
|
||||
(reserved1/*account_transaction_history, moved to api_ids*/)
|
||||
(blinded_balance)
|
||||
(chain_property)
|
||||
(witness_schedule)
|
||||
|
|
@ -56,3 +56,6 @@ GRAPHENE_DEFINE_IDS(chain, implementation_ids, impl_,
|
|||
(son_statistics)
|
||||
(son_schedule)
|
||||
(nft_lottery_balance))
|
||||
GRAPHENE_DEFINE_IDS(chain, api_ids, api_,
|
||||
(operation_history)
|
||||
(account_transaction_history))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ set( GRAPHENE_DB_FILES
|
|||
undo_database.cpp
|
||||
index.cpp
|
||||
object_database.cpp
|
||||
safety_check_policy.cpp
|
||||
${HEADERS}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/safety_check_policy.hpp>
|
||||
|
||||
#include <fc/interprocess/file_mapping.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
|
|
@ -161,7 +162,7 @@ namespace graphene { namespace db {
|
|||
class base_primary_index
|
||||
{
|
||||
public:
|
||||
base_primary_index( object_database& db ):_db(db){}
|
||||
base_primary_index( object_database& db ) : _db(db){}
|
||||
|
||||
/** called just before obj is modified */
|
||||
void save_undo( const object& obj );
|
||||
|
|
@ -332,8 +333,8 @@ namespace graphene { namespace db {
|
|||
public:
|
||||
typedef typename DerivedIndex::object_type object_type;
|
||||
|
||||
primary_index( object_database& db )
|
||||
:base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0)
|
||||
primary_index( object_database& db, safety_check_policy& check_policy )
|
||||
: base_primary_index(db),_next_id(object_type::space_id,object_type::type_id,0),_check(check_policy)
|
||||
{
|
||||
if( DirectBits > 0 )
|
||||
_direct_by_id = add_secondary_index< direct_index< object_type, DirectBits > >();
|
||||
|
|
@ -400,37 +401,78 @@ namespace graphene { namespace db {
|
|||
virtual const object& load( const std::vector<char>& data )override
|
||||
{
|
||||
const auto& result = DerivedIndex::insert( fc::raw::unpack<object_type>( data ) );
|
||||
for( const auto& item : _sindex )
|
||||
uint8_t type_id = object_type::type_id;
|
||||
for( const auto& item : _sindex ) {
|
||||
_check.pre_secondary_index_notification(type_id, *item);
|
||||
item->object_loaded( result );
|
||||
_check.post_secondary_index_notification(type_id, *item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
virtual const object& create(const std::function<void(object&)>& constructor )override
|
||||
{
|
||||
uint8_t type_id = object_type::type_id;
|
||||
try {
|
||||
FC_ASSERT(_check.allow_object_creation(_next_id),
|
||||
"Safety Check: Creation of object ${ID} is not allowed", ("ID", _next_id));
|
||||
} catch(...) {
|
||||
// When debugging a safety check failure, throw a breakpoint here to see where it's coming from
|
||||
throw;
|
||||
}
|
||||
const auto& result = DerivedIndex::create( constructor );
|
||||
for( const auto& item : _sindex )
|
||||
for( const auto& item : _sindex ) {
|
||||
_check.pre_secondary_index_notification(type_id, *item);
|
||||
item->object_created( result );
|
||||
_check.post_secondary_index_notification(type_id, *item);
|
||||
}
|
||||
on_add( result );
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void remove( const object& obj ) override
|
||||
{
|
||||
for( const auto& item : _sindex )
|
||||
uint8_t type_id = object_type::type_id;
|
||||
try {
|
||||
FC_ASSERT(_check.allow_object_deletion(_next_id), "Safety Check: Deletion of object ${ID} is not allowed",
|
||||
("ID", obj.id));
|
||||
} catch(...) {
|
||||
// When debugging a safety check failure, throw a breakpoint here to see where it's coming from
|
||||
throw;
|
||||
}
|
||||
for( const auto& item : _sindex ) {
|
||||
_check.pre_secondary_index_notification(type_id, *item);
|
||||
item->object_removed( obj );
|
||||
_check.post_secondary_index_notification(type_id, *item);
|
||||
}
|
||||
on_remove(obj);
|
||||
DerivedIndex::remove(obj);
|
||||
}
|
||||
|
||||
virtual void modify( const object& obj, const std::function<void(object&)>& m )override
|
||||
{
|
||||
uint8_t type_id = object_type::type_id;
|
||||
try {
|
||||
FC_ASSERT(_check.allow_object_modification(_next_id),
|
||||
"Safety Check: Modification of object ${ID} is not allowed", ("ID", obj.id));
|
||||
} catch(...) {
|
||||
// When debugging a safety check failure, throw a breakpoint here to see where it's coming from
|
||||
throw;
|
||||
}
|
||||
|
||||
save_undo( obj );
|
||||
for( const auto& item : _sindex )
|
||||
for( const auto& item : _sindex ) {
|
||||
_check.pre_secondary_index_notification(type_id, *item);
|
||||
item->about_to_modify( obj );
|
||||
_check.post_secondary_index_notification(type_id, *item);
|
||||
}
|
||||
DerivedIndex::modify( obj, m );
|
||||
for( const auto& item : _sindex )
|
||||
for( const auto& item : _sindex ) {
|
||||
_check.pre_secondary_index_notification(type_id, *item);
|
||||
item->object_modified( obj );
|
||||
_check.post_secondary_index_notification(type_id, *item);
|
||||
}
|
||||
on_modify( obj );
|
||||
}
|
||||
|
||||
|
|
@ -460,6 +502,7 @@ namespace graphene { namespace db {
|
|||
private:
|
||||
object_id_type _next_id;
|
||||
const direct_index< object_type, DirectBits >* _direct_by_id = nullptr;
|
||||
safety_check_policy& _check;
|
||||
};
|
||||
|
||||
} } // graphene::db
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/index.hpp>
|
||||
#include <graphene/db/undo_database.hpp>
|
||||
#include <graphene/db/safety_check_policy.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ namespace graphene { namespace db {
|
|||
object_database();
|
||||
~object_database();
|
||||
|
||||
void reset_indexes() { _index.clear(); _index.resize(255); }
|
||||
void reset_indexes();
|
||||
|
||||
void open(const fc::path& data_dir );
|
||||
|
||||
|
|
@ -53,15 +54,24 @@ namespace graphene { namespace db {
|
|||
void wipe(const fc::path& data_dir); // remove from disk
|
||||
void close();
|
||||
|
||||
template<typename T, typename F>
|
||||
const T& create( F&& constructor )
|
||||
{
|
||||
auto& idx = get_mutable_index<T>();
|
||||
return static_cast<const T&>( idx.create( [&](object& o)
|
||||
{
|
||||
assert( dynamic_cast<T*>(&o) );
|
||||
constructor( static_cast<T&>(o) );
|
||||
} ));
|
||||
/**
|
||||
* @brief Allocate an object space, setting a safety check policy for that object space
|
||||
* @param space_id The ID of the object space to allocate
|
||||
* @tparam SafetyCheckPolicy Specific type of the safety check policy for the object space
|
||||
* @tparam ConstructorArgs Variadic of arguments for the constructor of SafetyCheckPolicy
|
||||
* @return A non-owning pointer to the safety_check_policy object for the newly allocated object space
|
||||
*
|
||||
* Allocate a new object space in the database, setting the safety check policy for that object space. The
|
||||
* policy object's lifetime is managed by the database, and a non-owning pointer to the newly created policy
|
||||
* object is returned to the caller. The pointer will remain valid until the database is closed, or the
|
||||
* indexes are reset.
|
||||
*/
|
||||
template<typename SafetyCheckPolicy, typename... ConstructorArgs>
|
||||
SafetyCheckPolicy* allocate_object_space(uint8_t space_id, ConstructorArgs&&... args) {
|
||||
FC_ASSERT(_safety_checks[space_id] == nullptr,
|
||||
"Cannot allocate object space ${ID}: object space already allocated.", ("ID", space_id));
|
||||
_safety_checks[space_id] = std::make_unique<SafetyCheckPolicy>(std::forward<ConstructorArgs>(args)...);
|
||||
return static_cast<SafetyCheckPolicy*>(_safety_checks[space_id].get());
|
||||
}
|
||||
|
||||
///These methods are used to retrieve indexes on the object_database. All public index accessors are const-access only.
|
||||
|
|
@ -88,6 +98,17 @@ namespace graphene { namespace db {
|
|||
/// in order to maintain proper undo history.
|
||||
///@{
|
||||
|
||||
template<typename T, typename F>
|
||||
const T& create( F&& constructor )
|
||||
{
|
||||
auto& idx = get_mutable_index<T>();
|
||||
return static_cast<const T&>( idx.create( [&](object& o)
|
||||
{
|
||||
assert( dynamic_cast<T*>(&o) );
|
||||
constructor( static_cast<T&>(o) );
|
||||
} ));
|
||||
}
|
||||
|
||||
const object& insert( object&& obj ) { return get_mutable_index(obj.id).insert( std::move(obj) ); }
|
||||
void remove( const object& obj ) { get_mutable_index(obj.id).remove( obj ); }
|
||||
template<typename T, typename Lambda>
|
||||
|
|
@ -139,37 +160,70 @@ namespace graphene { namespace db {
|
|||
IndexType* add_index()
|
||||
{
|
||||
typedef typename IndexType::object_type ObjectType;
|
||||
if( _index[ObjectType::space_id].size() <= ObjectType::type_id )
|
||||
_index[ObjectType::space_id].resize( 255 );
|
||||
assert(!_index[ObjectType::space_id][ObjectType::type_id]);
|
||||
unique_ptr<index> indexptr( new IndexType(*this) );
|
||||
_index[ObjectType::space_id][ObjectType::type_id] = std::move(indexptr);
|
||||
return static_cast<IndexType*>(_index[ObjectType::space_id][ObjectType::type_id].get());
|
||||
uint8_t space_id = ObjectType::space_id;
|
||||
uint8_t type_id = ObjectType::type_id;
|
||||
if( _index[space_id].size() <= type_id )
|
||||
_index[space_id].resize( 255 );
|
||||
assert(!_index[space_id][type_id]);
|
||||
|
||||
safety_check_policy* check = _safety_checks[space_id].get();
|
||||
FC_ASSERT(check != nullptr && check->allow_new_index(type_id),
|
||||
"Safety Check: Addition of new index ${S}.${T} not allowed!",
|
||||
("S", space_id)("T", type_id));
|
||||
unique_ptr<index> indexptr( new IndexType(*this, *check) );
|
||||
_index[space_id][type_id] = std::move(indexptr);
|
||||
return static_cast<IndexType*>(_index[space_id][type_id].get());
|
||||
}
|
||||
|
||||
template<typename SecondaryIndexType, typename PrimaryIndexType = typename SecondaryIndexType::watched_index>
|
||||
SecondaryIndexType* add_secondary_index()
|
||||
{
|
||||
return get_mutable_index_type<PrimaryIndexType>().template add_secondary_index<SecondaryIndexType>();
|
||||
uint8_t space_id = PrimaryIndexType::object_type::space_id;
|
||||
uint8_t type_id = PrimaryIndexType::object_type::type_id;
|
||||
auto new_index =
|
||||
get_mutable_index_type<PrimaryIndexType>().template add_secondary_index<SecondaryIndexType>();
|
||||
safety_check_policy* check = _safety_checks[space_id].get();
|
||||
FC_ASSERT(check != nullptr &&
|
||||
check->allow_new_secondary_index(type_id, *new_index),
|
||||
"Safety Check: Addition of new secondary index on ${S}.${T} not allowed!",
|
||||
("S", space_id)("T", type_id));
|
||||
return new_index;
|
||||
}
|
||||
template<typename SecondaryIndexType>
|
||||
SecondaryIndexType* add_secondary_index(const uint8_t space_id, const uint8_t type_id)
|
||||
{
|
||||
auto* base_primary = dynamic_cast<base_primary_index*>(&get_mutable_index(space_id, type_id));
|
||||
FC_ASSERT(base_primary != nullptr,
|
||||
"Cannot add secondary index: index for space ID ${S} and type ID ${T} does not support secondary indexes.",
|
||||
FC_ASSERT(base_primary != nullptr, "Cannot add secondary index: index for space ID ${S} and type ID ${T} "
|
||||
"does not support secondary indexes.",
|
||||
("S", space_id)("T", type_id));
|
||||
return base_primary->template add_secondary_index<SecondaryIndexType>();
|
||||
|
||||
auto new_index = base_primary->template add_secondary_index<SecondaryIndexType>();
|
||||
safety_check_policy* check = _safety_checks[space_id].get();
|
||||
FC_ASSERT(check != nullptr && check->allow_new_secondary_index(type_id, *new_index),
|
||||
"Safety Check: Addition of new secondary index on ${S}.${T} not allowed!",
|
||||
("S", space_id)("T", type_id));
|
||||
return new_index;
|
||||
}
|
||||
|
||||
template<typename PrimaryIndexType>
|
||||
void delete_secondary_index(const secondary_index& secondary) {
|
||||
uint8_t space_id = PrimaryIndexType::object_type::space_id;
|
||||
uint8_t type_id = PrimaryIndexType::object_type::type_id;
|
||||
safety_check_policy* check = _safety_checks[space_id].get();
|
||||
FC_ASSERT(check != nullptr &&
|
||||
check->allow_delete_secondary_index(type_id, secondary),
|
||||
"Safety Check: Deletion of secondary index on ${S}.${T} not allowed!",
|
||||
("S", space_id)("T", type_id));
|
||||
get_mutable_index_type<PrimaryIndexType>().delete_secondary_index(secondary);
|
||||
}
|
||||
void delete_secondary_index(const uint8_t space_id, const uint8_t type_id, const secondary_index& secondary) {
|
||||
safety_check_policy* check = _safety_checks[space_id].get();
|
||||
FC_ASSERT(check != nullptr && check->allow_delete_secondary_index(type_id, secondary),
|
||||
"Safety Check: Deletion of secondary index on ${S}.${T} not allowed!",
|
||||
("S", space_id)("T", type_id));
|
||||
auto* base_primary = dynamic_cast<base_primary_index*>(&get_mutable_index(space_id, type_id));
|
||||
FC_ASSERT(base_primary != nullptr,
|
||||
"Cannot add secondary index: index for space ID ${S} and type ID ${T} does not support secondary indexes.",
|
||||
FC_ASSERT(base_primary != nullptr, "Cannot delete secondary index: index for space ID ${S} and type ID "
|
||||
"${T} does not support secondary indexes.",
|
||||
("S", space_id)("T", type_id));
|
||||
base_primary->delete_secondary_index(secondary);
|
||||
}
|
||||
|
|
@ -210,6 +264,7 @@ namespace graphene { namespace db {
|
|||
void save_undo_remove( const object& obj );
|
||||
|
||||
fc::path _data_dir;
|
||||
vector< std::unique_ptr<safety_check_policy> > _safety_checks;
|
||||
vector< vector< unique_ptr<index> > > _index;
|
||||
};
|
||||
|
||||
|
|
|
|||
143
libraries/db/include/graphene/db/safety_check_policy.hpp
Normal file
143
libraries/db/include/graphene/db/safety_check_policy.hpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* 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 <graphene/protocol/object_id.hpp>
|
||||
|
||||
namespace graphene {
|
||||
namespace db {
|
||||
|
||||
class secondary_index;
|
||||
|
||||
/**
|
||||
* @brief A polymorphic safety check policy for validating modifications to the database
|
||||
*
|
||||
* The safety check policy is used to catch bugs surrounding modifications to databases early in development. In
|
||||
* particular, it can be used to validate that modifications to a given database object space are made by specific
|
||||
* pathways of the code, and modifications made outside of these pathways can be blocked to preserve architectural
|
||||
* invariants of the system. A safety check policy can be installed on an object space when that space is allocated
|
||||
* to check modifications made to that space.
|
||||
*
|
||||
* Pursuant to this goal, the policy is used to validate modifications to the database, including new indexes (both
|
||||
* primary and secondary), new objects, modifications to objects, and deletion of objects. Additionally, as secondary
|
||||
* indexes represent transfer of execution to a pathway distinct from the pathway that led to the change being
|
||||
* notified about, the policy will also be notified before and after executing a secondary index.
|
||||
*
|
||||
* @note Safety checks are not queried when loading objects from persistence when the database is opened, but are
|
||||
* invoked when secondary indexes are notified of such.
|
||||
*/
|
||||
class safety_check_policy {
|
||||
public:
|
||||
virtual ~safety_check_policy();
|
||||
|
||||
virtual bool allow_new_index(uint8_t type_id) = 0;
|
||||
virtual bool allow_new_secondary_index(uint8_t type_id, const secondary_index& new_secondary_index) = 0;
|
||||
virtual bool allow_delete_secondary_index(uint8_t type_id, const secondary_index& secondary_index) = 0;
|
||||
virtual bool allow_object_creation(object_id_type new_object_id) = 0;
|
||||
virtual bool allow_object_modification(object_id_type modified_object_id) = 0;
|
||||
virtual bool allow_object_deletion(object_id_type deleted_object_id) = 0;
|
||||
|
||||
virtual void pre_secondary_index_notification(uint8_t type_id, const secondary_index& notified_index) = 0;
|
||||
virtual void post_secondary_index_notification(uint8_t type_id, const secondary_index& notified_index) = 0;
|
||||
};
|
||||
|
||||
/** @brief A safety check that allows all modifications and does nothing when secondary indexes are invoked */
|
||||
class null_safety_check : public safety_check_policy {
|
||||
public:
|
||||
null_safety_check() = default;
|
||||
null_safety_check(null_safety_check&&) = default;
|
||||
null_safety_check(const null_safety_check&) = delete;
|
||||
virtual ~null_safety_check();
|
||||
null_safety_check& operator=(const null_safety_check&) = delete;
|
||||
null_safety_check& operator=(null_safety_check&&) = default;
|
||||
|
||||
bool allow_new_index(uint8_t) override { return true; }
|
||||
bool allow_new_secondary_index(uint8_t, const secondary_index&) override { return true; }
|
||||
bool allow_delete_secondary_index(uint8_t, const secondary_index&) override { return true; }
|
||||
bool allow_object_creation(object_id_type) override { return true; }
|
||||
bool allow_object_modification(object_id_type) override { return true; }
|
||||
bool allow_object_deletion(object_id_type) override { return true; }
|
||||
void pre_secondary_index_notification(uint8_t, const secondary_index&) override {}
|
||||
void post_secondary_index_notification(uint8_t, const secondary_index&) override {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A safety check that allows the database object space to be locked and unlocked for changes
|
||||
*
|
||||
* This class implements a time-based database safety check using a lock, such that modifications made at times that
|
||||
* the database is unlocked are always allowed, but modifications made at times the database is locked are always
|
||||
* rejected. The database is initially unlocked, and is automatically locked while secondary indexes are being
|
||||
* notified of changes to the database, and unlocked after.
|
||||
*
|
||||
* Secondary indexes are always allowed to be added or deleted, regardless of lock state.
|
||||
*/
|
||||
class database_lock_safety_check : public safety_check_policy {
|
||||
bool locked;
|
||||
|
||||
public:
|
||||
database_lock_safety_check() : locked(false) {}
|
||||
database_lock_safety_check(database_lock_safety_check&&) = default;
|
||||
database_lock_safety_check(const database_lock_safety_check&) = delete;
|
||||
virtual ~database_lock_safety_check();
|
||||
database_lock_safety_check& operator=(const database_lock_safety_check&) = delete;
|
||||
database_lock_safety_check& operator=(database_lock_safety_check&&) = default;
|
||||
|
||||
bool is_locked() const { return locked; }
|
||||
void lock() { locked = true; }
|
||||
void unlock() { locked = false; }
|
||||
|
||||
bool allow_new_index(uint8_t) override { return !locked; }
|
||||
bool allow_new_secondary_index(uint8_t, const secondary_index&) override { return true; }
|
||||
bool allow_delete_secondary_index(uint8_t, const secondary_index&) override { return true; }
|
||||
bool allow_object_creation(object_id_type) override { return !locked; }
|
||||
bool allow_object_modification(object_id_type) override { return !locked; }
|
||||
bool allow_object_deletion(object_id_type) override { return !locked; }
|
||||
void pre_secondary_index_notification(uint8_t, const secondary_index&) override { locked = true; }
|
||||
void post_secondary_index_notification(uint8_t, const secondary_index&) override { locked = false; }
|
||||
};
|
||||
|
||||
class scoped_database_unlocker {
|
||||
std::vector<database_lock_safety_check*> locks;
|
||||
std::vector<bool> original_states;
|
||||
|
||||
public:
|
||||
template<typename... Locks>
|
||||
scoped_database_unlocker(Locks&... locks) : locks{&locks...}, original_states{locks.is_locked()...} {
|
||||
for (const auto& lock : this->locks)
|
||||
lock->unlock();
|
||||
}
|
||||
|
||||
scoped_database_unlocker(scoped_database_unlocker&&) = default;
|
||||
scoped_database_unlocker(const scoped_database_unlocker&) = delete;
|
||||
scoped_database_unlocker& operator=(const scoped_database_unlocker&) = delete;
|
||||
scoped_database_unlocker& operator=(scoped_database_unlocker&&) = default;
|
||||
|
||||
~scoped_database_unlocker() {
|
||||
for (unsigned int i = 0; i < locks.size(); ++i)
|
||||
if (original_states[i] == true) locks[i]->lock();
|
||||
else locks[i]->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
} } // namespace graphene::db
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/db/object_database.hpp>
|
||||
#include <graphene/db/safety_check_policy.hpp>
|
||||
|
||||
#include <fc/io/raw.hpp>
|
||||
#include <fc/container/flat.hpp>
|
||||
|
|
@ -32,11 +33,19 @@ object_database::object_database()
|
|||
:_undo_db(*this)
|
||||
{
|
||||
_index.resize(255);
|
||||
_safety_checks.resize(255);
|
||||
_undo_db.enable();
|
||||
}
|
||||
|
||||
object_database::~object_database(){}
|
||||
|
||||
void object_database::reset_indexes() {
|
||||
_index.clear();
|
||||
_index.resize(255);
|
||||
_safety_checks.clear();
|
||||
_safety_checks.resize(255);
|
||||
}
|
||||
|
||||
void object_database::close()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
35
libraries/db/safety_check_policy.cpp
Normal file
35
libraries/db/safety_check_policy.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <graphene/db/safety_check_policy.hpp>
|
||||
|
||||
namespace graphene { namespace db {
|
||||
|
||||
// Implement virtual destructors in .cpp file to give vtables a place to live
|
||||
safety_check_policy::~safety_check_policy() = default;
|
||||
null_safety_check::~null_safety_check() = default;
|
||||
database_lock_safety_check::~database_lock_safety_check() = default;
|
||||
|
||||
} } // namespace graphene::db
|
||||
|
||||
|
|
@ -53,7 +53,8 @@ namespace graphene { namespace account_history {
|
|||
enum account_history_object_type
|
||||
{
|
||||
key_account_object_type = 0,
|
||||
bucket_object_type = 1 ///< used in market_history_plugin
|
||||
bucket_object_type = 1, ///< used in market_history_plugin
|
||||
order_history_object_type = 2 ///< used in market_history_plugin
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ void affiliate_stats_plugin::plugin_set_program_options(
|
|||
void affiliate_stats_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{
|
||||
database().applied_block.connect( [this]( const signed_block& b){ my->update_affiliate_stats(b); } );
|
||||
database().allocate_object_space<null_safety_check>(AFFILIATE_STATS_SPACE_ID);
|
||||
|
||||
my->_ar_index = database().add_index< primary_index< app_reward_index > >();
|
||||
my->_rr_index = database().add_index< primary_index< referral_reward_index > >();
|
||||
|
|
|
|||
|
|
@ -516,6 +516,8 @@ void bookie_plugin::plugin_initialize(const boost::program_options::variables_ma
|
|||
database().new_objects.connect([this](const vector<object_id_type>& ids, const flat_set<account_id_type>& impacted_accounts) { my->on_objects_new(ids); });
|
||||
database().removed_objects.connect([this](const vector<object_id_type>& ids, const vector<const object*>& objs, const flat_set<account_id_type>& impacted_accounts) { my->on_objects_removed(ids); });
|
||||
|
||||
database().allocate_object_space<null_safety_check>(bookie_objects);
|
||||
|
||||
//auto event_index =
|
||||
database().add_index<primary_index<detail::persistent_event_index> >();
|
||||
database().add_index<primary_index<detail::persistent_betting_market_group_index> >();
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ struct history_key {
|
|||
};
|
||||
struct order_history_object : public abstract_object<order_history_object>
|
||||
{
|
||||
static const uint8_t space_id = ACCOUNT_HISTORY_SPACE_ID;
|
||||
static const uint8_t type_id = 2; // market_history_plugin type, referenced from account_history_plugin.hpp
|
||||
|
||||
history_key key;
|
||||
fc::time_point_sec time;
|
||||
fill_order_operation op;
|
||||
|
|
@ -133,9 +136,9 @@ namespace detail
|
|||
}
|
||||
|
||||
/**
|
||||
* The market history plugin can be configured to track any number of intervals via its configuration. Once per block it
|
||||
* will scan the virtual operations and look for fill_order_operations and then adjust the appropriate bucket objects for
|
||||
* each fill order.
|
||||
* The market history plugin can be configured to track any number of intervals via its configuration. Once per
|
||||
* block it will scan the virtual operations and look for fill_order_operations and then adjust the appropriate
|
||||
* bucket objects for each fill order.
|
||||
*/
|
||||
class market_history_plugin : public graphene::app::plugin
|
||||
{
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ void market_history_plugin::plugin_set_program_options(
|
|||
void market_history_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||
{ try {
|
||||
database().applied_block.connect( [this]( const signed_block& b){ my->update_market_histories(b); } );
|
||||
database().allocate_object_space<null_safety_check>(ACCOUNT_HISTORY_SPACE_ID);
|
||||
database().add_index< primary_index< bucket_index > >();
|
||||
database().add_index< primary_index< history_index > >();
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace raw { \
|
|||
#define GRAPHENE_NAME_TO_OBJECT_TYPE(x, prefix, name) BOOST_PP_CAT(prefix, BOOST_PP_CAT(name, _object_type))
|
||||
#define GRAPHENE_NAME_TO_ID_TYPE(x, y, name) BOOST_PP_CAT(name, _id_type)
|
||||
#define GRAPHENE_DECLARE_ID(x, space_prefix_seq, name) \
|
||||
using BOOST_PP_CAT(name, _id_type) = object_id<BOOST_PP_TUPLE_ELEM(2, 0, space_prefix_seq), \
|
||||
using BOOST_PP_CAT(name, _id_type) = graphene::protocol::object_id<BOOST_PP_TUPLE_ELEM(2, 0, space_prefix_seq), \
|
||||
GRAPHENE_NAME_TO_OBJECT_TYPE(x, BOOST_PP_TUPLE_ELEM(2, 1, space_prefix_seq), name)>;
|
||||
#define GRAPHENE_REFLECT_ID(x, id_namespace, name) FC_REFLECT_TYPENAME(graphene::id_namespace::name)
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ namespace raw { \
|
|||
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GRAPHENE_NAME_TO_OBJECT_TYPE, object_type_prefix, names_seq)) \
|
||||
}; \
|
||||
\
|
||||
BOOST_PP_SEQ_FOR_EACH(GRAPHENE_DECLARE_ID, (object_space, object_type_prefix), names_seq) \
|
||||
BOOST_PP_SEQ_FOR_EACH(GRAPHENE_DECLARE_ID, (graphene::protocol::object_space, object_type_prefix), names_seq) \
|
||||
\
|
||||
} } \
|
||||
\
|
||||
|
|
@ -165,7 +165,8 @@ const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK =
|
|||
enum reserved_spaces {
|
||||
relative_protocol_ids = 0,
|
||||
protocol_ids = 1,
|
||||
implementation_ids = 2
|
||||
implementation_ids = 2,
|
||||
api_ids = 3 ///< Non-consensus objects used for API. Tracked by respective plugins.
|
||||
};
|
||||
|
||||
inline bool is_relative(object_id_type o) { return o.space() == 0; }
|
||||
|
|
@ -243,7 +244,7 @@ GRAPHENE_DEFINE_IDS(protocol, protocol_ids, /*protocol objects are not prefixed*
|
|||
(call_order)
|
||||
(custom)
|
||||
(proposal)
|
||||
(operation_history)
|
||||
(reserved0/*operation_history, moved to api_ids*/)
|
||||
(withdraw_permission)
|
||||
(vesting_balance)
|
||||
(worker)
|
||||
|
|
|
|||
|
|
@ -1634,76 +1634,78 @@ BOOST_AUTO_TEST_CASE(event_group_delete_test)
|
|||
{
|
||||
try
|
||||
{
|
||||
ACTORS( (alice)(bob) )
|
||||
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
|
||||
db.bypass_safety_checks([this] {
|
||||
ACTORS( (alice)(bob) )
|
||||
CREATE_ICE_HOCKEY_BETTING_MARKET(false, 0);
|
||||
|
||||
const int initialAccountAsset = 10000000;
|
||||
const int betAsset = 1000000;
|
||||
const int initialAccountAsset = 10000000;
|
||||
const int betAsset = 1000000;
|
||||
|
||||
transfer(account_id_type(), alice_id, asset(initialAccountAsset));
|
||||
transfer(account_id_type(), bob_id, asset(initialAccountAsset));
|
||||
transfer(account_id_type(), alice_id, asset(initialAccountAsset));
|
||||
transfer(account_id_type(), bob_id, asset(initialAccountAsset));
|
||||
|
||||
const auto& event = create_event({{"en", "event"}}, {{"en", "2016-17"}}, nhl_id);
|
||||
const auto& event = create_event({{"en", "event"}}, {{"en", "2016-17"}}, nhl_id);
|
||||
|
||||
const auto& market_group = create_betting_market_group({{"en", "market group"}}, event.id, betting_market_rules_id, asset_id_type(), false, 0);
|
||||
//to make bets be not removed immediately
|
||||
update_betting_market_group_impl(market_group.id,
|
||||
fc::optional<internationalized_string_type>(),
|
||||
fc::optional<object_id_type>(),
|
||||
betting_market_group_status::in_play,
|
||||
false);
|
||||
const auto& market_group = create_betting_market_group({{"en", "market group"}}, event.id, betting_market_rules_id, asset_id_type(), false, 0);
|
||||
//to make bets be not removed immediately
|
||||
update_betting_market_group_impl(market_group.id,
|
||||
fc::optional<internationalized_string_type>(),
|
||||
fc::optional<object_id_type>(),
|
||||
betting_market_group_status::in_play,
|
||||
false);
|
||||
|
||||
const auto& market = create_betting_market(market_group.id, {{"en", "market"}});
|
||||
const auto& market = create_betting_market(market_group.id, {{"en", "market"}});
|
||||
|
||||
test_events events(*this, nhl_id);
|
||||
test_markets_groups markets_groups(*this, event.id, betting_market_rules_id);
|
||||
test_markets markets(*this, market_group.id);
|
||||
test_events events(*this, nhl_id);
|
||||
test_markets_groups markets_groups(*this, event.id, betting_market_rules_id);
|
||||
test_markets markets(*this, market_group.id);
|
||||
|
||||
const auto& bet_1_id = place_bet(alice_id, market.id, bet_type::back, asset(betAsset, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
const auto& bet_2_id = place_bet(bob_id, market.id, bet_type::lay, asset(betAsset, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
const auto& bet_1_id = place_bet(alice_id, market.id, bet_type::back, asset(betAsset, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
const auto& bet_2_id = place_bet(bob_id, market.id, bet_type::lay, asset(betAsset, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
|
||||
|
||||
delete_event_group(nhl_id);
|
||||
delete_event_group(nhl_id);
|
||||
|
||||
const auto& event_group_by_id = db.get_index_type<event_group_object_index>().indices().get<by_id>();
|
||||
BOOST_CHECK(event_group_by_id.end() == event_group_by_id.find(nhl_id));
|
||||
const auto& event_group_by_id = db.get_index_type<event_group_object_index>().indices().get<by_id>();
|
||||
BOOST_CHECK(event_group_by_id.end() == event_group_by_id.find(nhl_id));
|
||||
|
||||
BOOST_CHECK(event_status::canceled == event.get_status());
|
||||
BOOST_CHECK(event_status::canceled == event.get_status());
|
||||
|
||||
BOOST_CHECK(event_status::canceled == events.event_upcoming->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_in_progress->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_frozen_in_progress->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_frozen_upcoming->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_finished->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_canceled->get_status());
|
||||
BOOST_CHECK(event_status::settled == events.event_settled->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_upcoming->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_in_progress->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_frozen_in_progress->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_frozen_upcoming->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_finished->get_status());
|
||||
BOOST_CHECK(event_status::canceled == events.event_canceled->get_status());
|
||||
BOOST_CHECK(event_status::settled == events.event_settled->get_status());
|
||||
|
||||
BOOST_CHECK(betting_market_group_status::canceled == market_group.get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == market_group.get_status());
|
||||
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_upcoming->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_frozen_upcoming->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_in_play->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_frozen_in_play->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_closed->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_graded->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_canceled->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::settled == markets_groups.market_group_settled->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_upcoming->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_frozen_upcoming->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_in_play->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_frozen_in_play->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_closed->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_graded->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::canceled == markets_groups.market_group_canceled->get_status());
|
||||
BOOST_CHECK(betting_market_group_status::settled == markets_groups.market_group_settled->get_status());
|
||||
|
||||
BOOST_CHECK(betting_market_status::canceled == market.get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == market.get_status());
|
||||
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_unresolved->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_frozen->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_closed->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_graded->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_canceled->get_status());
|
||||
BOOST_CHECK(betting_market_status::settled == markets.market_settled->get_status()); //settled market should not be canceled
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_unresolved->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_frozen->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_closed->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_graded->get_status());
|
||||
BOOST_CHECK(betting_market_status::canceled == markets.market_canceled->get_status());
|
||||
BOOST_CHECK(betting_market_status::settled == markets.market_settled->get_status()); //settled market should not be canceled
|
||||
|
||||
//check canceled bets and reverted balance changes
|
||||
const auto& bet_by_id = db.get_index_type<bet_object_index>().indices().get<by_id>();
|
||||
BOOST_CHECK(bet_by_id.end() == bet_by_id.find(bet_1_id));
|
||||
BOOST_CHECK(bet_by_id.end() == bet_by_id.find(bet_2_id));
|
||||
//check canceled bets and reverted balance changes
|
||||
const auto& bet_by_id = db.get_index_type<bet_object_index>().indices().get<by_id>();
|
||||
BOOST_CHECK(bet_by_id.end() == bet_by_id.find(bet_1_id));
|
||||
BOOST_CHECK(bet_by_id.end() == bet_by_id.find(bet_2_id));
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), initialAccountAsset);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), initialAccountAsset);
|
||||
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), initialAccountAsset);
|
||||
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), initialAccountAsset);
|
||||
});
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ using std::cout;
|
|||
using std::cerr;
|
||||
|
||||
database_fixture::database_fixture()
|
||||
: app(), db( *app.chain_database() )
|
||||
: app(app::application::TESTING_MODE()), db( *app.chain_database() )
|
||||
{
|
||||
try {
|
||||
int argc = boost::unit_test::framework::master_test_suite().argc;
|
||||
|
|
@ -1056,9 +1056,11 @@ void database_fixture::fund_fee_pool( const account_object& from, const asset_ob
|
|||
|
||||
void database_fixture::enable_fees()
|
||||
{
|
||||
db.modify(global_property_id_type()(db), [](global_property_object& gpo)
|
||||
{
|
||||
gpo.parameters.current_fees = std::make_shared<fee_schedule>(fee_schedule::get_default());
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(global_property_id_type()(db), [](global_property_object& gpo)
|
||||
{
|
||||
gpo.parameters.current_fees = std::make_shared<fee_schedule>(fee_schedule::get_default());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,8 +78,10 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture )
|
|||
//Get a sane head block time
|
||||
generate_block( skip_flags );
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
});
|
||||
});
|
||||
|
||||
transaction tx;
|
||||
|
|
|
|||
|
|
@ -299,9 +299,11 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
|||
int64_t audrey_btc = 0;
|
||||
|
||||
{
|
||||
const tournament_object& game = db.create<tournament_object>( []( tournament_object& t ) {
|
||||
t.options.game_options = rock_paper_scissors_game_options();
|
||||
t.options.buy_in = asset( 10 );
|
||||
const tournament_object& game = db.bypass_safety_checks([this] {
|
||||
return db.create<tournament_object>( []( tournament_object& t ) {
|
||||
t.options.game_options = rock_paper_scissors_game_options();
|
||||
t.options.buy_in = asset( 10 );
|
||||
});
|
||||
});
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// Alice has no distribution set
|
||||
|
|
@ -326,7 +328,9 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
|||
ath.audrey_ppy += 10;
|
||||
ath.ann_ppy += 1;
|
||||
|
||||
helper.commit();
|
||||
db.bypass_safety_checks([&helper] {
|
||||
helper.commit();
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL( ath.alice_ppy, get_balance( ath.alice_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.ann_ppy, get_balance( ath.ann_id, asset_id_type() ) );
|
||||
|
|
@ -334,9 +338,11 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
|||
}
|
||||
|
||||
{
|
||||
const tournament_object& game = db.create<tournament_object>( [btc_id]( tournament_object& t ) {
|
||||
t.options.game_options = rock_paper_scissors_game_options();
|
||||
t.options.buy_in = asset( 10, btc_id );
|
||||
const tournament_object& game = db.bypass_safety_checks([this, &btc_id] {
|
||||
return db.create<tournament_object>( [btc_id]( tournament_object& t ) {
|
||||
t.options.game_options = rock_paper_scissors_game_options();
|
||||
t.options.buy_in = asset( 10, btc_id );
|
||||
});
|
||||
});
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// 20% of 60 = 12: 2 to Alice, 3 to Ann, 7 to Audrey
|
||||
|
|
@ -344,16 +350,20 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
|||
alice_btc += 2;
|
||||
ann_btc += 3;
|
||||
audrey_btc += 7;
|
||||
helper.commit();
|
||||
db.bypass_safety_checks([&helper] {
|
||||
helper.commit();
|
||||
});
|
||||
BOOST_CHECK_EQUAL( alice_btc, get_balance( ath.alice_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( ann_btc, get_balance( ath.ann_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( audrey_btc, get_balance( ath.audrey_id, btc_id ) );
|
||||
}
|
||||
|
||||
{
|
||||
const betting_market_group_object& game = db.create<betting_market_group_object>( []( betting_market_group_object& b ) {
|
||||
b.asset_id = asset_id_type();
|
||||
} );
|
||||
const betting_market_group_object& game = db.bypass_safety_checks([this] {
|
||||
return db.create<betting_market_group_object>( []( betting_market_group_object& b ) {
|
||||
b.asset_id = asset_id_type();
|
||||
});
|
||||
});
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// Alice has no distribution set
|
||||
BOOST_CHECK_EQUAL( 0, helper.payout( ath.alice_id, 1000 ).value );
|
||||
|
|
@ -371,14 +381,18 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
|||
ath.alice_ppy += 8;
|
||||
|
||||
// intermediate commit should clear internal accumulator
|
||||
helper.commit();
|
||||
db.bypass_safety_checks([&helper] {
|
||||
helper.commit();
|
||||
});
|
||||
|
||||
// 20% of 59 = 11: 6 to Alice, 5 to Ann
|
||||
BOOST_CHECK_EQUAL( 11, helper.payout( ath.penny_id, 59 ).value );
|
||||
ath.alice_ppy += 6;
|
||||
ath.ann_ppy += 5;
|
||||
|
||||
helper.commit();
|
||||
db.bypass_safety_checks([&helper] {
|
||||
helper.commit();
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL( ath.alice_ppy, get_balance( ath.alice_id, asset_id_type() ) );
|
||||
BOOST_CHECK_EQUAL( ath.ann_ppy, get_balance( ath.ann_id, asset_id_type() ) );
|
||||
|
|
@ -386,15 +400,19 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
|||
}
|
||||
|
||||
{
|
||||
const betting_market_group_object& game = db.create<betting_market_group_object>( [btc_id]( betting_market_group_object& b ) {
|
||||
b.asset_id = btc_id;
|
||||
} );
|
||||
const betting_market_group_object& game = db.bypass_safety_checks([this, &btc_id] {
|
||||
return db.create<betting_market_group_object>( [btc_id]( betting_market_group_object& b ) {
|
||||
b.asset_id = btc_id;
|
||||
});
|
||||
});
|
||||
affiliate_payout_helper helper = affiliate_payout_helper( db, game );
|
||||
// 20% of 60 = 12: 7 to Alice, 5 to Ann
|
||||
BOOST_CHECK_EQUAL( 12, helper.payout( ath.penny_id, 60 ).value );
|
||||
alice_btc += 7;
|
||||
ann_btc += 5;
|
||||
helper.commit();
|
||||
db.bypass_safety_checks([&helper] {
|
||||
helper.commit();
|
||||
});
|
||||
BOOST_CHECK_EQUAL( alice_btc, get_balance( ath.alice_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( ann_btc, get_balance( ath.ann_id, btc_id ) );
|
||||
BOOST_CHECK_EQUAL( audrey_btc, get_balance( ath.audrey_id, btc_id ) );
|
||||
|
|
@ -405,14 +423,18 @@ BOOST_AUTO_TEST_CASE( affiliate_payout_helper_test )
|
|||
auto& index = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index<balances_by_account_index>();
|
||||
auto abo = index.get_account_balance( account_id_type(), asset_id_type() );
|
||||
BOOST_CHECK( abo != nullptr );
|
||||
db.modify( *abo, [&ath]( account_balance_object& bal ) {
|
||||
bal.balance -= ath.alice_ppy + ath.ann_ppy + ath.audrey_ppy;
|
||||
db.bypass_safety_checks([this, &abo, &ath] {
|
||||
db.modify( *abo, [&ath]( account_balance_object& bal ) {
|
||||
bal.balance -= ath.alice_ppy + ath.ann_ppy + ath.audrey_ppy;
|
||||
});
|
||||
});
|
||||
|
||||
abo = index.get_account_balance( irene_id, btc_id );
|
||||
BOOST_CHECK( abo != nullptr );
|
||||
db.modify( *abo, [alice_btc,ann_btc,audrey_btc]( account_balance_object& bal ) {
|
||||
bal.balance -= alice_btc + ann_btc + audrey_btc;
|
||||
db.bypass_safety_checks([this, &abo, &alice_btc, &ann_btc, &audrey_btc] {
|
||||
db.modify( *abo, [alice_btc,ann_btc,audrey_btc]( account_balance_object& bal ) {
|
||||
bal.balance -= alice_btc + ann_btc + audrey_btc;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -435,9 +435,11 @@ BOOST_AUTO_TEST_CASE( committee_authority )
|
|||
generate_block();
|
||||
|
||||
// Signatures are for suckers.
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
// Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.
|
||||
p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
// Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.
|
||||
p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();
|
||||
});
|
||||
});
|
||||
|
||||
BOOST_TEST_MESSAGE( "transfering 100000 CORE to nathan, signing with committee key should fail because this requires it to be part of a proposal" );
|
||||
|
|
@ -927,8 +929,10 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture )
|
|||
//Get a sane head block time
|
||||
generate_block();
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
});
|
||||
});
|
||||
|
||||
transaction tx;
|
||||
|
|
|
|||
|
|
@ -870,8 +870,10 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture )
|
|||
{ try {
|
||||
generate_block();
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
});
|
||||
});
|
||||
|
||||
BOOST_TEST_MESSAGE( "Creating a proposal to change the block_interval to 1 second" );
|
||||
|
|
@ -954,8 +956,10 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture )
|
|||
//Get a sane head block time
|
||||
generate_block( skip_flags );
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
|
||||
});
|
||||
});
|
||||
|
||||
transaction tx;
|
||||
|
|
@ -990,8 +994,10 @@ BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture )
|
|||
|
||||
uint8_t witness_schedule_algorithm = db.get_global_properties().parameters.witness_schedule_algorithm;
|
||||
if (witness_schedule_algorithm != GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SCHEDULED_ALGORITHM;
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.witness_schedule_algorithm = GRAPHENE_WITNESS_SCHEDULED_ALGORITHM;
|
||||
});
|
||||
});
|
||||
|
||||
db.get_near_witness_schedule();
|
||||
|
|
@ -1019,8 +1025,10 @@ BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture )
|
|||
});
|
||||
|
||||
if (db.get_global_properties().parameters.witness_schedule_algorithm != witness_schedule_algorithm)
|
||||
db.modify(db.get_global_properties(), [&witness_schedule_algorithm](global_property_object& p) {
|
||||
p.parameters.witness_schedule_algorithm = witness_schedule_algorithm;
|
||||
db.bypass_safety_checks([this, &witness_schedule_algorithm] {
|
||||
db.modify(db.get_global_properties(), [&witness_schedule_algorithm](global_property_object& p) {
|
||||
p.parameters.witness_schedule_algorithm = witness_schedule_algorithm;
|
||||
});
|
||||
});
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
|
|
|||
|
|
@ -39,22 +39,24 @@ BOOST_FIXTURE_TEST_SUITE( database_tests, database_fixture )
|
|||
BOOST_AUTO_TEST_CASE( undo_test )
|
||||
{
|
||||
try {
|
||||
database db;
|
||||
auto ses = db._undo_db.start_undo_session();
|
||||
const auto& bal_obj1 = db.create<account_balance_object>( [&]( account_balance_object& obj ){
|
||||
/* no balances right now */
|
||||
});
|
||||
auto id1 = bal_obj1.id;
|
||||
// abandon changes
|
||||
ses.undo();
|
||||
// start a new session
|
||||
ses = db._undo_db.start_undo_session();
|
||||
database db(true);
|
||||
db.bypass_safety_checks([&db] {
|
||||
auto ses = db._undo_db.start_undo_session();
|
||||
const auto& bal_obj1 = db.create<account_balance_object>( [&]( account_balance_object& ){
|
||||
/* no balances right now */
|
||||
});
|
||||
auto id1 = bal_obj1.id;
|
||||
// abandon changes
|
||||
ses.undo();
|
||||
// start a new session
|
||||
ses = db._undo_db.start_undo_session();
|
||||
|
||||
const auto& bal_obj2 = db.create<account_balance_object>( [&]( account_balance_object& obj ){
|
||||
/* no balances right now */
|
||||
const auto& bal_obj2 = db.create<account_balance_object>( [&]( account_balance_object& obj ){
|
||||
/* no balances right now */
|
||||
});
|
||||
auto id2 = bal_obj2.id;
|
||||
BOOST_CHECK( id1 == id2 );
|
||||
});
|
||||
auto id2 = bal_obj2.id;
|
||||
BOOST_CHECK( id1 == id2 );
|
||||
} catch ( const fc::exception& e )
|
||||
{
|
||||
edump( (e.to_detail_string()) );
|
||||
|
|
@ -65,10 +67,12 @@ BOOST_AUTO_TEST_CASE( undo_test )
|
|||
BOOST_AUTO_TEST_CASE( merge_test )
|
||||
{
|
||||
try {
|
||||
database db;
|
||||
database db(true);
|
||||
auto ses = db._undo_db.start_undo_session();
|
||||
db.create<account_balance_object>( [&]( account_balance_object& obj ){
|
||||
obj.balance = 42;
|
||||
db.bypass_safety_checks([&db] {
|
||||
db.create<account_balance_object>( [&]( account_balance_object& obj ){
|
||||
obj.balance = 42;
|
||||
});
|
||||
});
|
||||
ses.merge();
|
||||
|
||||
|
|
@ -92,13 +96,16 @@ BOOST_AUTO_TEST_CASE( flat_index_test )
|
|||
FC_ASSERT( bitusd.bitasset_data_id->instance.value == 0 );
|
||||
FC_ASSERT( !(*bitusd.bitasset_data_id)(db).current_feed.settlement_price.is_null() );
|
||||
try {
|
||||
auto ses = db._undo_db.start_undo_session();
|
||||
const auto& obj1 = db.create<asset_bitasset_data_object>( [&]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_fund = 17;
|
||||
// Bypass safety checks for whole block, because ses will trigger database modifications when destroyed
|
||||
db.bypass_safety_checks([this] {
|
||||
auto ses = db._undo_db.start_undo_session();
|
||||
const auto& obj1 = db.create<asset_bitasset_data_object>( [&]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_fund = 17;
|
||||
});
|
||||
FC_ASSERT( obj1.settlement_fund == 17 );
|
||||
throw std::string("Expected");
|
||||
// With flat_index, obj1 will not really be removed from the index
|
||||
});
|
||||
FC_ASSERT( obj1.settlement_fund == 17 );
|
||||
throw std::string("Expected");
|
||||
// With flat_index, obj1 will not really be removed from the index
|
||||
} catch ( const std::string& e )
|
||||
{ // ignore
|
||||
}
|
||||
|
|
@ -112,12 +119,13 @@ BOOST_AUTO_TEST_CASE( flat_index_test )
|
|||
|
||||
BOOST_AUTO_TEST_CASE( direct_index_test )
|
||||
{ try {
|
||||
null_safety_check check;
|
||||
try {
|
||||
const graphene::db::primary_index< account_index, 6 > small_chunkbits( db );
|
||||
const graphene::db::primary_index< account_index, 6 > small_chunkbits( db, check );
|
||||
BOOST_FAIL( "Expected assertion failure!" );
|
||||
} catch( const fc::assert_exception& expected ) {}
|
||||
|
||||
graphene::db::primary_index< account_index, 8 > my_accounts( db );
|
||||
graphene::db::primary_index< account_index, 8 > my_accounts( db, check );
|
||||
const auto& direct = my_accounts.get_secondary_index<graphene::db::direct_index< account_object, 8 >>();
|
||||
BOOST_CHECK_EQUAL( 0, my_accounts.indices().size() );
|
||||
BOOST_CHECK( nullptr == direct.find( account_id_type( 1 ) ) );
|
||||
|
|
|
|||
|
|
@ -581,10 +581,12 @@ REG : net' ltm' ref'
|
|||
BOOST_AUTO_TEST_CASE( account_create_fee_scaling )
|
||||
{ try {
|
||||
auto accounts_per_scale = db.get_global_properties().parameters.accounts_per_fee_scale;
|
||||
db.modify(global_property_id_type()(db), [](global_property_object& gpo)
|
||||
{
|
||||
gpo.parameters.current_fees = std::make_shared<fee_schedule>(fee_schedule::get_default());
|
||||
gpo.parameters.current_fees->get<account_create_operation>().basic_fee = 1;
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(global_property_id_type()(db), [](global_property_object& gpo)
|
||||
{
|
||||
gpo.parameters.current_fees = std::make_shared<fee_schedule>(fee_schedule::get_default());
|
||||
gpo.parameters.current_fees->get<account_create_operation>().basic_fee = 1;
|
||||
});
|
||||
});
|
||||
|
||||
for( int i = db.get_dynamic_global_properties().accounts_registered_this_interval; i < accounts_per_scale; ++i )
|
||||
|
|
|
|||
|
|
@ -104,11 +104,13 @@ struct gpos_fixture: database_fixture
|
|||
|
||||
void update_gpos_global(uint32_t vesting_period, uint32_t vesting_subperiod, fc::time_point_sec period_start)
|
||||
{
|
||||
db.modify(db.get_global_properties(), [vesting_period, vesting_subperiod, period_start](global_property_object& p) {
|
||||
p.parameters.extensions.value.gpos_period = vesting_period;
|
||||
p.parameters.extensions.value.gpos_subperiod = vesting_subperiod;
|
||||
p.parameters.extensions.value.gpos_period_start = period_start.sec_since_epoch();
|
||||
p.parameters.extensions.value.gpos_vesting_lockin_period = vesting_subperiod;
|
||||
db.bypass_safety_checks([this, vesting_period, vesting_subperiod, &period_start] {
|
||||
db.modify(db.get_global_properties(), [vesting_period, vesting_subperiod, period_start](global_property_object& p) {
|
||||
p.parameters.extensions.value.gpos_period = vesting_period;
|
||||
p.parameters.extensions.value.gpos_subperiod = vesting_subperiod;
|
||||
p.parameters.extensions.value.gpos_period_start = period_start.sec_since_epoch();
|
||||
p.parameters.extensions.value.gpos_vesting_lockin_period = vesting_subperiod;
|
||||
});
|
||||
});
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_period(), vesting_period);
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.gpos_subperiod(), vesting_subperiod);
|
||||
|
|
@ -118,8 +120,10 @@ struct gpos_fixture: database_fixture
|
|||
|
||||
void update_maintenance_interval(uint32_t new_interval)
|
||||
{
|
||||
db.modify(db.get_global_properties(), [new_interval](global_property_object& p) {
|
||||
p.parameters.maintenance_interval = new_interval;
|
||||
db.bypass_safety_checks([this, new_interval] {
|
||||
db.modify(db.get_global_properties(), [new_interval](global_property_object& p) {
|
||||
p.parameters.maintenance_interval = new_interval;
|
||||
});
|
||||
});
|
||||
BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maintenance_interval, new_interval);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,9 +204,13 @@ BOOST_AUTO_TEST_CASE( lottery_end_by_stage_test )
|
|||
|
||||
BOOST_CHECK( participants_percents_sum == winners_part );
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
db.bypass_safety_checks([this, &test_asset] {
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
});
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
db.bypass_safety_checks([this, &test_asset] {
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
});
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == 0 );
|
||||
|
||||
uint64_t benefactor_recieved = db.get_balance( account_id_type(), asset_id_type() ).amount.value - benefactor_balance_before_end;
|
||||
|
|
@ -225,8 +229,10 @@ BOOST_AUTO_TEST_CASE( lottery_end_by_stage_with_fractional_test )
|
|||
try {
|
||||
asset_id_type test_asset_id = db.get_index<asset_object>().get_next_id();
|
||||
INVOKE( create_lottery_asset_test );
|
||||
db.modify(test_asset_id(db), [&](asset_object& ao) {
|
||||
ao.lottery_options->is_active = true;
|
||||
db.bypass_safety_checks([this, &test_asset_id] {
|
||||
db.modify(test_asset_id(db), [&](asset_object& ao) {
|
||||
ao.lottery_options->is_active = true;
|
||||
});
|
||||
});
|
||||
auto test_asset = test_asset_id(db);
|
||||
for( int i = 1; i < 17; ++i ) {
|
||||
|
|
@ -261,9 +267,13 @@ BOOST_AUTO_TEST_CASE( lottery_end_by_stage_with_fractional_test )
|
|||
BOOST_CHECK( participants_percents_sum == winners_part );
|
||||
// balance should be bigger than expected because of rouning during distribution
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value > (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
db.bypass_safety_checks([this, &test_asset] {
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
});
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value > jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT );
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
db.bypass_safety_checks([this, &test_asset] {
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
});
|
||||
// but at the end is always equals 0
|
||||
BOOST_CHECK( db.get_balance( test_asset.get_id() ).amount.value == 0 );
|
||||
|
||||
|
|
@ -373,9 +383,13 @@ BOOST_AUTO_TEST_CASE( more_winners_then_participants_test )
|
|||
generate_block();
|
||||
test_asset = test_asset_id(db);
|
||||
auto holders = test_asset.get_holders(db);
|
||||
auto participants = test_asset.distribute_winners_part( db );
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
|
||||
auto participants = db.bypass_safety_checks([this, &test_asset] {
|
||||
auto ret = test_asset.distribute_winners_part( db );
|
||||
test_asset.distribute_benefactors_part( db );
|
||||
test_asset.distribute_sweeps_holders_part( db );
|
||||
return ret;
|
||||
});
|
||||
generate_block();
|
||||
for( auto p: participants ) {
|
||||
idump(( get_operation_history(p.first) ));
|
||||
|
|
|
|||
|
|
@ -47,9 +47,11 @@ namespace
|
|||
|
||||
transaction.operations = operations;
|
||||
|
||||
fixture.db.create<proposal_object>([&](proposal_object& proposal)
|
||||
{
|
||||
proposal.proposed_transaction = transaction;
|
||||
fixture.db.bypass_safety_checks([&fixture, &transaction] {
|
||||
fixture.db.create<proposal_object>([&](proposal_object& proposal)
|
||||
{
|
||||
proposal.proposed_transaction = transaction;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,9 +136,11 @@ BOOST_AUTO_TEST_CASE(list_pending_proposals_postponed) {
|
|||
try {
|
||||
graphene::app::network_node_api network_node_api(app);
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& properties) {
|
||||
//Size in bytes. Empiricaly found to limit block size for two test transactions
|
||||
properties.parameters.maximum_block_size = 650;
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& properties) {
|
||||
//Size in bytes. Empiricaly found to limit block size for two test transactions
|
||||
properties.parameters.maximum_block_size = 650;
|
||||
});
|
||||
});
|
||||
|
||||
auto sam_transaction = push_transaction_for_account_creation("sam");
|
||||
|
|
|
|||
|
|
@ -239,10 +239,14 @@ BOOST_AUTO_TEST_CASE(lottery_end_by_stage_test)
|
|||
test_nft_md_obj = test_nft_md_id(db);
|
||||
BOOST_CHECK(participants_percents_sum == winners_part);
|
||||
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT);
|
||||
test_nft_md_obj.distribute_benefactors_part(db);
|
||||
db.bypass_safety_checks([this, &test_nft_md_obj] {
|
||||
test_nft_md_obj.distribute_benefactors_part(db);
|
||||
});
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT);
|
||||
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||
db.bypass_safety_checks([this, &test_nft_md_obj] {
|
||||
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||
});
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
|
||||
|
||||
|
|
@ -263,8 +267,10 @@ BOOST_AUTO_TEST_CASE(lottery_end_by_stage_with_fractional_test)
|
|||
{
|
||||
nft_metadata_id_type test_nft_md_id = db.get_index<nft_metadata_object>().get_next_id();
|
||||
INVOKE(create_lottery_nft_md_test);
|
||||
db.modify(test_nft_md_id(db), [&](nft_metadata_object &obj) {
|
||||
obj.lottery_data->lottery_options.is_active = true;
|
||||
db.bypass_safety_checks([this, &test_nft_md_id] {
|
||||
db.modify(test_nft_md_id(db), [&](nft_metadata_object &obj) {
|
||||
obj.lottery_data->lottery_options.is_active = true;
|
||||
});
|
||||
});
|
||||
auto test_nft_md_obj = test_nft_md_id(db);
|
||||
|
||||
|
|
@ -294,7 +300,9 @@ BOOST_AUTO_TEST_CASE(lottery_end_by_stage_with_fractional_test)
|
|||
winners_part += win;
|
||||
|
||||
uint16_t participants_percents_sum = 0;
|
||||
auto participants = test_nft_md_obj.distribute_winners_part(db);
|
||||
auto participants = db.bypass_safety_checks([this, &test_nft_md_obj] {
|
||||
return test_nft_md_obj.distribute_winners_part(db);
|
||||
});
|
||||
for (auto p : participants)
|
||||
for (auto e : p.second)
|
||||
participants_percents_sum += e;
|
||||
|
|
@ -303,10 +311,14 @@ BOOST_AUTO_TEST_CASE(lottery_end_by_stage_with_fractional_test)
|
|||
// balance should be bigger than expected because of rouning during distribution
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value > (jackpot * (GRAPHENE_100_PERCENT - winners_part) / (double)GRAPHENE_100_PERCENT) + jackpot * winners_part * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT / (double)GRAPHENE_100_PERCENT);
|
||||
test_nft_md_obj.distribute_benefactors_part(db);
|
||||
db.bypass_safety_checks([this, &test_nft_md_obj] {
|
||||
test_nft_md_obj.distribute_benefactors_part(db);
|
||||
});
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value > jackpot * SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE / (double)GRAPHENE_100_PERCENT * winners_part / (double)GRAPHENE_100_PERCENT);
|
||||
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||
db.bypass_safety_checks([this, &test_nft_md_obj] {
|
||||
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||
});
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
// but at the end is always equals 0
|
||||
BOOST_CHECK(test_nft_md_obj.get_lottery_jackpot(db).amount.value == 0);
|
||||
|
|
@ -429,12 +441,15 @@ BOOST_AUTO_TEST_CASE(more_winners_then_participants_test)
|
|||
generate_block();
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
auto holders = test_nft_md_obj.get_holders(db);
|
||||
auto participants = test_nft_md_obj.distribute_winners_part(db);
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
test_nft_md_obj.distribute_benefactors_part(db);
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
auto participants = db.bypass_safety_checks([this, &test_nft_md_obj, &test_nft_md_id] {
|
||||
auto ret = test_nft_md_obj.distribute_winners_part(db);
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
test_nft_md_obj.distribute_benefactors_part(db);
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
test_nft_md_obj.distribute_sweeps_holders_part(db);
|
||||
test_nft_md_obj = test_nft_md_id(db);
|
||||
return ret;
|
||||
});
|
||||
generate_block();
|
||||
for (auto p : participants)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1307,10 +1307,12 @@ BOOST_AUTO_TEST_CASE( witness_pay_test )
|
|||
BOOST_CHECK_LT( witness_ppb * 2, ref_budget );
|
||||
BOOST_CHECK_GT( witness_ppb * 3, ref_budget );
|
||||
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
_gpo.parameters.witness_pay_per_block = witness_ppb;
|
||||
} );
|
||||
db.bypass_safety_checks([this, &witness_ppb] {
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
_gpo.parameters.witness_pay_per_block = witness_ppb;
|
||||
});
|
||||
});
|
||||
|
||||
BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 0);
|
||||
BOOST_TEST_MESSAGE( "Upgrading account" );
|
||||
|
|
@ -1335,10 +1337,12 @@ BOOST_AUTO_TEST_CASE( witness_pay_test )
|
|||
auto schedule_maint = [&]()
|
||||
{
|
||||
// now we do maintenance
|
||||
db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.next_maintenance_time = db.head_block_time() + 1;
|
||||
} );
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.next_maintenance_time = db.head_block_time() + 1;
|
||||
});
|
||||
});
|
||||
};
|
||||
BOOST_TEST_MESSAGE( "Generating some blocks" );
|
||||
|
||||
|
|
@ -1632,10 +1636,12 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test )
|
|||
{
|
||||
// HACK: This just modifies the DB creation record to be further
|
||||
// in the past
|
||||
db.modify( vbo, [&]( vesting_balance_object& _vbo )
|
||||
{
|
||||
_vbo.policy.get<cdd_vesting_policy>().coin_seconds_earned_last_update -= dt_secs;
|
||||
} );
|
||||
db.bypass_safety_checks([this, &vbo, dt_secs] {
|
||||
db.modify( vbo, [&]( vesting_balance_object& _vbo )
|
||||
{
|
||||
_vbo.policy.get<cdd_vesting_policy>().coin_seconds_earned_last_update -= dt_secs;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
auto create_vbo = [&](
|
||||
|
|
|
|||
|
|
@ -78,17 +78,21 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) {
|
|||
std::string new_withdraw_address = "withdraw_address";
|
||||
|
||||
generate_block();
|
||||
auto& son = db.create<son_object>( [&]( son_object& sobj )
|
||||
{
|
||||
sobj.son_account = bob_id;
|
||||
sobj.statistics = db.create<son_statistics_object>([&](son_statistics_object& s){s.owner = sobj.id;}).id;
|
||||
const auto& son = db.bypass_safety_checks([this, &bob_id] {
|
||||
return db.create<son_object>( [&]( son_object& sobj )
|
||||
{
|
||||
sobj.son_account = bob_id;
|
||||
sobj.statistics = db.create<son_statistics_object>([&](son_statistics_object& s){s.owner = sobj.id;}).id;
|
||||
});
|
||||
});
|
||||
generate_block();
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
son_info sinfo;
|
||||
sinfo.son_id = son.id;
|
||||
_gpo.active_sons.push_back(sinfo);
|
||||
db.bypass_safety_checks([this, &son] {
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
son_info sinfo;
|
||||
sinfo.son_id = son.id;
|
||||
_gpo.active_sons.push_back(sinfo);
|
||||
});
|
||||
});
|
||||
generate_block();
|
||||
{
|
||||
|
|
|
|||
|
|
@ -191,14 +191,16 @@ try {
|
|||
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||
|
||||
// Modify SON's status to active
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
db.bypass_safety_checks([this, &obj, &son_stats_obj] {
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
|
||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time());
|
||||
});
|
||||
});
|
||||
|
||||
auto deposit_vesting = db.get<vesting_balance_object>(vesting_balance_id_type(0));
|
||||
|
|
@ -319,11 +321,13 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
enable_fees();
|
||||
// Make SON Budget small for testing purposes
|
||||
// Make witness budget zero so that amount can be allocated to SON
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
_gpo.parameters.extensions.value.son_pay_max = 200;
|
||||
_gpo.parameters.witness_pay_per_block = 0;
|
||||
} );
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
_gpo.parameters.extensions.value.son_pay_max = 200;
|
||||
_gpo.parameters.witness_pay_per_block = 0;
|
||||
});
|
||||
});
|
||||
|
||||
GET_ACTOR(alice);
|
||||
GET_ACTOR(bob);
|
||||
|
|
@ -336,10 +340,12 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
// Do maintenance from the upcoming block
|
||||
auto schedule_maint = [&]()
|
||||
{
|
||||
db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.next_maintenance_time = db.head_block_time() + 1;
|
||||
} );
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.next_maintenance_time = db.head_block_time() + 1;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Generate enough blocks to make budget
|
||||
|
|
@ -489,34 +495,36 @@ BOOST_AUTO_TEST_CASE( son_pay_test )
|
|||
BOOST_REQUIRE( son_stats_obj1 != sidx.end() );
|
||||
BOOST_REQUIRE( son_stats_obj2 != sidx.end() );
|
||||
// Modify the transaction signed statistics of Alice's SON
|
||||
db.modify( *son_stats_obj1, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.txs_signed[sidechain_type::bitcoin] = 2;
|
||||
_s.txs_signed[sidechain_type::hive] = 4;
|
||||
db.bypass_safety_checks([this, &son_stats_obj1, &son_stats_obj2] {
|
||||
db.modify( *son_stats_obj1, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.txs_signed[sidechain_type::bitcoin] = 2;
|
||||
_s.txs_signed[sidechain_type::hive] = 4;
|
||||
|
||||
_s.total_txs_signed[sidechain_type::bitcoin] = 2;
|
||||
_s.total_txs_signed[sidechain_type::hive] = 4;
|
||||
_s.total_txs_signed[sidechain_type::bitcoin] = 2;
|
||||
_s.total_txs_signed[sidechain_type::hive] = 4;
|
||||
|
||||
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
||||
_s.sidechain_txs_reported[sidechain_type::hive] = 8;
|
||||
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
||||
_s.sidechain_txs_reported[sidechain_type::hive] = 8;
|
||||
|
||||
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
||||
_s.total_sidechain_txs_reported[sidechain_type::hive] = 8;
|
||||
});
|
||||
// Modify the transaction signed statistics of Bob's SON
|
||||
db.modify( *son_stats_obj2, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.txs_signed[sidechain_type::bitcoin] = 3;
|
||||
_s.txs_signed[sidechain_type::hive] = 6;
|
||||
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 4;
|
||||
_s.total_sidechain_txs_reported[sidechain_type::hive] = 8;
|
||||
});
|
||||
// Modify the transaction signed statistics of Bob's SON
|
||||
db.modify( *son_stats_obj2, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.txs_signed[sidechain_type::bitcoin] = 3;
|
||||
_s.txs_signed[sidechain_type::hive] = 6;
|
||||
|
||||
_s.total_txs_signed[sidechain_type::bitcoin] = 3;
|
||||
_s.total_txs_signed[sidechain_type::hive] = 6;
|
||||
_s.total_txs_signed[sidechain_type::bitcoin] = 3;
|
||||
_s.total_txs_signed[sidechain_type::hive] = 6;
|
||||
|
||||
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
||||
_s.sidechain_txs_reported[sidechain_type::hive] = 12;
|
||||
_s.sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
||||
_s.sidechain_txs_reported[sidechain_type::hive] = 12;
|
||||
|
||||
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
||||
_s.total_sidechain_txs_reported[sidechain_type::hive] = 12;
|
||||
_s.total_sidechain_txs_reported[sidechain_type::bitcoin] = 6;
|
||||
_s.total_sidechain_txs_reported[sidechain_type::hive] = 12;
|
||||
});
|
||||
});
|
||||
|
||||
// Note the balances before the maintenance
|
||||
|
|
@ -602,14 +610,16 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
BOOST_REQUIRE( son_stats_obj != sidx.end() );
|
||||
|
||||
// Modify SON's status to active
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::active;
|
||||
});
|
||||
db.bypass_safety_checks([this, &obj, &son_stats_obj] {
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::active;
|
||||
});
|
||||
|
||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time());
|
||||
db.modify( *son_stats_obj, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.last_down_timestamp = fc::time_point_sec(db.head_block_time());
|
||||
});
|
||||
});
|
||||
|
||||
{
|
||||
|
|
@ -647,9 +657,11 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
}
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
db.bypass_safety_checks([this, &obj] {
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
});
|
||||
|
||||
uint64_t downtime = 0;
|
||||
|
|
@ -674,18 +686,20 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) {
|
|||
BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts);
|
||||
}
|
||||
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
db.bypass_safety_checks([this, &obj] {
|
||||
// Modify SON's status to in_maintenance
|
||||
db.modify( *obj, [&]( son_object& _s)
|
||||
{
|
||||
_s.status = son_status::in_maintenance;
|
||||
});
|
||||
|
||||
// SON is selected as one of the active SONs
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
son_info son_inf;
|
||||
son_inf.son_id = son_id_type(0);
|
||||
_gpo.active_sons.push_back(son_inf);
|
||||
// SON is selected as one of the active SONs
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
son_info son_inf;
|
||||
son_inf.son_id = son_id_type(0);
|
||||
_gpo.active_sons.push_back(son_inf);
|
||||
});
|
||||
});
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -530,8 +530,10 @@ BOOST_FIXTURE_TEST_CASE( delay_between_games_must_not_be_less, database_fixture
|
|||
{ std::string reason("Delay between games must not be less");
|
||||
BOOST_TEST_MESSAGE("Starting test '" + reason + "'");
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.min_round_delay = 1;
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.min_round_delay = 1;
|
||||
});
|
||||
});
|
||||
|
||||
ACTORS((nathan));
|
||||
|
|
@ -604,8 +606,10 @@ BOOST_FIXTURE_TEST_CASE( time_to_commit_the_move_must_not_be_less, database_fixt
|
|||
{ std::string reason("Time to commit the next move must not be less");
|
||||
BOOST_TEST_MESSAGE("Starting test '" + reason + "'");
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.min_time_per_commit_move = 1;
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.min_time_per_commit_move = 1;
|
||||
});
|
||||
});
|
||||
|
||||
ACTORS((nathan));
|
||||
|
|
@ -678,8 +682,10 @@ BOOST_FIXTURE_TEST_CASE( time_to_reveal_the_move_must_not_be_less, database_fixt
|
|||
{ std::string reason("Time to reveal the move must not be less");
|
||||
BOOST_TEST_MESSAGE("Starting test '" + reason + "'");
|
||||
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.min_time_per_reveal_move = 1;
|
||||
db.bypass_safety_checks([this] {
|
||||
db.modify(db.get_global_properties(), [](global_property_object& p) {
|
||||
p.parameters.min_time_per_reveal_move = 1;
|
||||
});
|
||||
});
|
||||
|
||||
ACTORS((nathan));
|
||||
|
|
|
|||
Loading…
Reference in a new issue