Graphene Updates and DApp Support #643

Closed
nathanielhourt wants to merge 84 commits from dapp-support into develop
39 changed files with 772 additions and 300 deletions
Showing only changes of commit f9fcffbb4d - Show all commits

View file

@ -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() {

View file

@ -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);

View file

@ -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,

View file

@ -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) ) }

View file

@ -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; }

View file

@ -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();
}

View file

@ -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 )

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -4,6 +4,7 @@ set( GRAPHENE_DB_FILES
undo_database.cpp
index.cpp
object_database.cpp
safety_check_policy.cpp
${HEADERS}
)

View file

@ -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

View file

@ -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;
};

View 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

View file

@ -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()
{
}

View 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

View file

@ -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
};

View file

@ -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 > >();

View file

@ -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> >();

View file

@ -101,7 +101,10 @@ struct history_key {
};
struct order_history_object : public abstract_object<order_history_object>
{
history_key key;
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
{

View file

@ -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 > >();

View file

@ -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)

View file

@ -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()
}

View file

@ -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());
});
});
}

View file

@ -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;

View file

@ -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;
});
});
}
}

View file

@ -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;

View file

@ -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() }

View file

@ -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 ) ) );

View file

@ -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 )

View file

@ -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);
}

View file

@ -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) ));

View file

@ -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;
});
});
}

View file

@ -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");

View file

@ -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)
{

View file

@ -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 = [&](

View file

@ -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();
{

View file

@ -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);
});
});
{

View file

@ -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));