- Add initial support for vesting genesis balances
- Add owner/active keys to genesis accounts
- Cleanup circa balance_object and evaluator
This commit is contained in:
Nathan Hourt 2015-06-30 16:42:41 -04:00
parent a05a13b20e
commit c530867933
23 changed files with 348 additions and 345 deletions

View file

@ -161,14 +161,21 @@ namespace detail {
for( int i = 0; i < 10; ++i ) for( int i = 0; i < 10; ++i )
{ {
auto name = "init"+fc::to_string(i); auto name = "init"+fc::to_string(i);
initial_state.initial_accounts.emplace_back(name, nathan_key.get_public_key(), true); initial_state.initial_accounts.emplace_back(name,
nathan_key.get_public_key(),
nathan_key.get_public_key(),
true);
initial_state.initial_committee.push_back({name}); initial_state.initial_committee.push_back({name});
initial_state.initial_witnesses.push_back({name, nathan_key.get_public_key(), secret}); initial_state.initial_witnesses.push_back({name, nathan_key.get_public_key(), secret});
} }
initial_state.initial_accounts.emplace_back("nathan", address(public_key_type(nathan_key.get_public_key())), 1); initial_state.initial_accounts.emplace_back("nathan", nathan_key.get_public_key());
initial_state.initial_balances.push_back({nathan_key.get_public_key(),
GRAPHENE_SYMBOL,
GRAPHENE_MAX_SHARE_SUPPLY});
if( _options->count("genesis-json") ) if( _options->count("genesis-json") )
initial_state = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>()).as<genesis_state_type>(); initial_state = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
.as<genesis_state_type>();
else else
dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key))); dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
@ -199,20 +206,9 @@ namespace detail {
try try
{ {
if( id.item_type == graphene::net::block_message_type ) if( id.item_type == graphene::net::block_message_type )
{ return _chain_db->is_known_block(id.item_hash);
// for some reason, the contains() function called by is_known_block
// throws when the block is not present (instead of returning false)
try
{
return _chain_db->is_known_block( id.item_hash );
}
catch (const fc::key_not_found_exception&)
{
return false;
}
}
else else
return _chain_db->is_known_transaction( id.item_hash ); // is_known_transaction behaves normally return _chain_db->is_known_transaction(id.item_hash);
} }
FC_CAPTURE_AND_RETHROW( (id) ) FC_CAPTURE_AND_RETHROW( (id) )
} }
@ -255,7 +251,7 @@ namespace detail {
virtual std::vector<item_hash_t> get_item_ids(uint32_t item_type, virtual std::vector<item_hash_t> get_item_ids(uint32_t item_type,
const std::vector<item_hash_t>& blockchain_synopsis, const std::vector<item_hash_t>& blockchain_synopsis,
uint32_t& remaining_item_count, uint32_t& remaining_item_count,
uint32_t limit ) override uint32_t limit) override
{ try { { try {
FC_ASSERT( item_type == graphene::net::block_message_type ); FC_ASSERT( item_type == graphene::net::block_message_type );
vector<block_id_type> result; vector<block_id_type> result;
@ -268,7 +264,7 @@ namespace detail {
auto itr = blockchain_synopsis.rbegin(); auto itr = blockchain_synopsis.rbegin();
while( itr != blockchain_synopsis.rend() ) while( itr != blockchain_synopsis.rend() )
{ {
if( _chain_db->is_known_block( *itr ) || *itr == block_id_type() ) if( _chain_db->is_known_block(*itr) || *itr == block_id_type() )
{ {
last_known_block_id = *itr; last_known_block_id = *itr;
break; break;
@ -285,7 +281,6 @@ namespace detail {
if( block_header::num_from_id(result.back()) < _chain_db->head_block_num() ) if( block_header::num_from_id(result.back()) < _chain_db->head_block_num() )
remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back()); remaining_item_count = _chain_db->head_block_num() - block_header::num_from_id(result.back());
idump((blockchain_synopsis)(limit)(result)(remaining_item_count));
return result; return result;
} FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) } } FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) }

View file

@ -48,13 +48,11 @@ void block_database::open( const fc::path& dbdir )
} }
} FC_CAPTURE_AND_RETHROW( (dbdir) ) } } FC_CAPTURE_AND_RETHROW( (dbdir) ) }
bool block_database::is_open()const bool block_database::is_open()const
{ {
return _blocks.is_open(); return _blocks.is_open();
} }
void block_database::close() void block_database::close()
{ {
_blocks.close(); _blocks.close();
@ -67,7 +65,6 @@ void block_database::flush()
_block_num_to_pos.flush(); _block_num_to_pos.flush();
} }
void block_database::store( const block_id_type& id, const signed_block& b ) void block_database::store( const block_id_type& id, const signed_block& b )
{ {
auto num = block_header::num_from_id(id); auto num = block_header::num_from_id(id);
@ -82,7 +79,6 @@ void block_database::store( const block_id_type& id, const signed_block& b )
_block_num_to_pos.write( (char*)&e, sizeof(e) ); _block_num_to_pos.write( (char*)&e, sizeof(e) );
} }
void block_database::remove( const block_id_type& id ) void block_database::remove( const block_id_type& id )
{ try { { try {
index_entry e; index_entry e;
@ -102,25 +98,20 @@ void block_database::remove( const block_id_type& id )
} }
} FC_CAPTURE_AND_RETHROW( (id) ) } } FC_CAPTURE_AND_RETHROW( (id) ) }
bool block_database::contains( const block_id_type& id )const
bool block_database::contains( const block_id_type& id )const
{ {
index_entry e; index_entry e;
auto index_pos = sizeof(e)*block_header::num_from_id(id); auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end ); _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos ) if ( _block_num_to_pos.tellg() <= index_pos )
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id)); return false;
_block_num_to_pos.seekg( index_pos ); _block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) ); _block_num_to_pos.read( (char*)&e, sizeof(e) );
return e.block_id == id; return e.block_id == id;
} }
block_id_type block_database::fetch_block_id( uint32_t block_num )const
block_id_type block_database::fetch_block_id( uint32_t block_num )const
{ {
index_entry e; index_entry e;
auto index_pos = sizeof(e)*block_num; auto index_pos = sizeof(e)*block_num;
@ -134,7 +125,6 @@ block_id_type block_database::fetch_block_id( uint32_t block_num )const
return e.block_id; return e.block_id;
} }
optional<signed_block> block_database::fetch_optional( const block_id_type& id )const optional<signed_block> block_database::fetch_optional( const block_id_type& id )const
{ {
try try
@ -143,7 +133,7 @@ optional<signed_block> block_database::fetch_optional( const block_id_type& id )
auto index_pos = sizeof(e)*block_header::num_from_id(id); auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end ); _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos ) if ( _block_num_to_pos.tellg() <= index_pos )
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id)); return {};
_block_num_to_pos.seekg( index_pos ); _block_num_to_pos.seekg( index_pos );
_block_num_to_pos.read( (char*)&e, sizeof(e) ); _block_num_to_pos.read( (char*)&e, sizeof(e) );
@ -174,7 +164,7 @@ optional<signed_block> block_database::fetch_by_number( uint32_t block_num )cons
auto index_pos = sizeof(e)*block_num; auto index_pos = sizeof(e)*block_num;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end ); _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
if ( _block_num_to_pos.tellg() <= index_pos ) if ( _block_num_to_pos.tellg() <= index_pos )
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block number ${block_num} not contained in block database", ("block_num", block_num)); return {};
_block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg ); _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg );
_block_num_to_pos.read( (char*)&e, sizeof(e) ); _block_num_to_pos.read( (char*)&e, sizeof(e) );
@ -195,7 +185,6 @@ optional<signed_block> block_database::fetch_by_number( uint32_t block_num )cons
return optional<signed_block>(); return optional<signed_block>();
} }
optional<signed_block> block_database::last()const optional<signed_block> block_database::last()const
{ {
try try
@ -231,5 +220,4 @@ optional<signed_block> block_database::last()const
} }
return optional<signed_block>(); return optional<signed_block>();
} }
} } } }

View file

@ -435,28 +435,6 @@ processed_transaction database::apply_transaction( const signed_transaction& trx
return result; return result;
} }
struct signature_check_visitor
{
typedef void result_type;
flat_map<address,bool>& sigs;
signature_check_visitor( flat_map<address,bool>& s ):sigs(s){}
template<typename T>
result_type operator()( const T& o )const{}
result_type operator()( const balance_claim_operation& o )const
{
for( auto& owner : o.owners )
{
auto itr = sigs.find(owner);
FC_ASSERT( itr != sigs.end() );
itr->second = true;
}
}
};
processed_transaction database::_apply_transaction( const signed_transaction& trx ) processed_transaction database::_apply_transaction( const signed_transaction& trx )
{ try { { try {
uint32_t skip = get_node_properties().skip_flags; uint32_t skip = get_node_properties().skip_flags;
@ -476,8 +454,6 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr
for( const auto& sig : trx.signatures ) for( const auto& sig : trx.signatures )
FC_ASSERT( eval_state._sigs.insert( std::make_pair( address(fc::ecc::public_key( sig, trx.digest() )), false) ).second, "Multiple signatures by same key detected" ) ; FC_ASSERT( eval_state._sigs.insert( std::make_pair( address(fc::ecc::public_key( sig, trx.digest() )), false) ).second, "Multiple signatures by same key detected" ) ;
trx.visit( signature_check_visitor(eval_state._sigs) );
} }
//If we're skipping tapos check, but not dupe check, assume all transactions have maximum expiration time. //If we're skipping tapos check, but not dupe check, assume all transactions have maximum expiration time.
@ -503,9 +479,9 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr
eval_state._sigs.reserve( trx.signatures.size() ); eval_state._sigs.reserve( trx.signatures.size() );
for( const auto& sig : trx.signatures ) for( const auto& sig : trx.signatures )
FC_ASSERT( eval_state._sigs.insert( std::make_pair(address(fc::ecc::public_key( sig, trx.digest(tapos_block_summary.block_id) )),false) ).second, "Multiple signatures by same key detected" ) ; FC_ASSERT(eval_state._sigs.insert(
std::make_pair(address(fc::ecc::public_key(sig, trx.digest(tapos_block_summary.block_id) )),
trx.visit( signature_check_visitor(eval_state._sigs) ); false)).second, "Multiple signatures by same key detected");
} }
//Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration

View file

@ -161,7 +161,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
transaction_evaluation_state genesis_eval_state(this); transaction_evaluation_state genesis_eval_state(this);
// Create initial accounts // Create blockchain accounts
fc::ecc::private_key null_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); fc::ecc::private_key null_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")));
create<key_object>( [&null_private_key](key_object& k) { create<key_object>( [&null_private_key](key_object& k) {
k.key_data = public_key_type(null_private_key.get_public_key()); k.key_data = public_key_type(null_private_key.get_public_key());
@ -280,6 +280,8 @@ void database::init_genesis(const genesis_state_type& genesis_state)
adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{})); adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{}));
} }
// TODO: Create initial vesting balances
// Create initial accounts // Create initial accounts
if( !genesis_state.initial_accounts.empty() ) if( !genesis_state.initial_accounts.empty() )
{ {
@ -288,12 +290,21 @@ void database::init_genesis(const genesis_state_type& genesis_state)
key_id_type key_id = apply_operation(genesis_eval_state, key_id_type key_id = apply_operation(genesis_eval_state,
key_create_operation({asset(), key_create_operation({asset(),
committee_account.id, committee_account.id,
account.addr})).get<object_id_type>(); account.owner_key})).get<object_id_type>();
account_create_operation cop; account_create_operation cop;
cop.name = account.name; cop.name = account.name;
cop.registrar = account_id_type(1); cop.registrar = account_id_type(1);
cop.active = authority(1, key_id, 1); cop.owner = authority(1, key_id, 1);
cop.owner = cop.active; if( account.owner_key != account.active_key )
{
key_id = apply_operation(genesis_eval_state,
key_create_operation({asset(),
committee_account.id,
account.owner_key})).get<object_id_type>();
cop.active = authority(1, key_id, 1);
} else {
cop.active = cop.owner;
}
cop.options.memo_key = key_id; cop.options.memo_key = key_id;
account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>()); account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>());

View file

@ -28,7 +28,8 @@
#include <fc/uint128.hpp> #include <fc/uint128.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
database& generic_evaluator::db()const { return trx_state->db(); } database& generic_evaluator::db()const { return trx_state->db(); }
operation_result generic_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply ) operation_result generic_evaluator::start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply )
{ try { { try {
trx_state = &eval_state; trx_state = &eval_state;

View file

@ -1,47 +1,60 @@
#pragma once #pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/transaction.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/evaluator.hpp> #include <graphene/chain/evaluator.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
/** class balance_claim_evaluator : public evaluator<balance_claim_evaluator>
* @ingroup operations {
*/ public:
class balance_claim_evaluator : public evaluator<balance_claim_evaluator> typedef balance_claim_operation operation_type;
const balance_object* balance = nullptr;
asset amount_withdrawn;
asset do_evaluate(const balance_claim_operation& op)
{ {
public: database& d = db();
typedef balance_claim_operation operation_type; balance = &op.balance_to_claim(d);
void_result do_evaluate( const balance_claim_operation& op ) FC_ASSERT(trx_state->_sigs.count(balance->owner) == 1);
{ trx_state->_sigs[balance->owner] = true;
return void_result(); FC_ASSERT(op.total_claimed.asset_id == balance->asset_type());
}
/** if( balance->vesting_policy.valid() ) {
* @note the fee is always 0 for this particular operation because once the FC_ASSERT(op.total_claimed.amount == 0);
* balance is claimed it frees up memory and it cannot be used to spam the network return amount_withdrawn = balance->vesting_policy->get_allowed_withdraw({balance->balance,
*/ d.head_block_time(),
void_result do_apply( const balance_claim_operation& op ) {}});
{ }
const auto& bal_idx = db().get_index_type<balance_index>();
const auto& by_owner_idx = bal_idx.indices().get<by_owner>();
asset total(0, op.total_claimed.asset_id); FC_ASSERT(op.total_claimed == balance->balance);
for( const auto& owner : op.owners ) return amount_withdrawn = op.total_claimed;
{ }
auto itr = by_owner_idx.find( boost::make_tuple( owner, total.asset_id ) );
if( itr != by_owner_idx.end() )
{
total += itr->balance;
db().remove( *itr );
}
}
FC_ASSERT( total == op.total_claimed, "", ("total",total)("op",op) ); /**
* @note the fee is always 0 for this particular operation because once the
* balance is claimed it frees up memory and it cannot be used to spam the network
*/
asset do_apply(const balance_claim_operation& op)
{
database& d = db();
db().adjust_balance( op.deposit_to_account, total ); if( balance->vesting_policy.valid() && amount_withdrawn < balance->balance )
d.modify(*balance, [&](balance_object& b) {
b.vesting_policy->on_withdraw({b.balance, d.head_block_time(), amount_withdrawn});
b.balance -= amount_withdrawn;
});
else
d.remove(*balance);
return void_result(); d.adjust_balance(op.deposit_to_account, amount_withdrawn);
} return amount_withdrawn;
}; }
};
} } // graphene::chain } } // graphene::chain

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <graphene/chain/vesting_balance_object.hpp>
namespace graphene { namespace chain { namespace graphene { namespace chain {
class balance_object : public abstract_object<balance_object> class balance_object : public abstract_object<balance_object>
@ -10,6 +12,7 @@ namespace graphene { namespace chain {
address owner; address owner;
asset balance; asset balance;
optional<linear_vesting_policy> vesting_policy;
asset_id_type asset_type()const { return balance.asset_id; } asset_id_type asset_type()const { return balance.asset_id; }
}; };
@ -36,4 +39,4 @@ namespace graphene { namespace chain {
using balance_index = generic_index<balance_object, balance_multi_index_type>; using balance_index = generic_index<balance_object, balance_multi_index_type>;
} } } }
FC_REFLECT_DERIVED( graphene::chain::balance_object, (graphene::db::object), (owner)(balance) ) FC_REFLECT_DERIVED( graphene::chain::balance_object, (graphene::db::object), (owner)(balance)(vesting_policy) )

View file

@ -40,20 +40,34 @@ namespace graphene { namespace chain {
using graphene::db::object; using graphene::db::object;
struct genesis_state_type { struct genesis_state_type {
struct genesis_account_type { struct initial_account_type {
genesis_account_type(const string& name = string(), initial_account_type(const string& name = string(),
const address& addr = address(), const public_key_type& owner_key = public_key_type(),
bool is_lifetime_member = false) const public_key_type& active_key = public_key_type(),
: name(name), addr(addr), is_lifetime_member(is_lifetime_member){} bool is_lifetime_member = false)
: name(name),
owner_key(owner_key),
active_key(active_key == public_key_type()? owner_key : active_key),
is_lifetime_member(is_lifetime_member)
{}
string name; string name;
address addr; public_key_type owner_key;
public_key_type active_key;
bool is_lifetime_member; bool is_lifetime_member;
}; };
struct genesis_balance_type { struct initial_balance_type {
address owner; address owner;
string asset_symbol; string asset_symbol;
share_type amount; share_type amount;
}; };
struct initial_vesting_balance_type {
address owner;
string asset_symbol;
share_type amount;
time_point_sec vesting_start_date;
time_point_sec earliest_withdrawal_date;
time_point_sec vesting_end_date;
};
struct initial_witness_type { struct initial_witness_type {
/// Must correspond to one of the allocation targets. /// Must correspond to one of the allocation targets.
string owner_name; string owner_name;
@ -66,8 +80,8 @@ namespace graphene { namespace chain {
}; };
chain_parameters initial_parameters; chain_parameters initial_parameters;
vector<genesis_account_type> initial_accounts; vector<initial_account_type> initial_accounts;
vector<genesis_balance_type> initial_balances; vector<initial_balance_type> initial_balances;
vector<initial_witness_type> initial_witnesses; vector<initial_witness_type> initial_witnesses;
vector<initial_committee_member_type> initial_committee; vector<initial_committee_member_type> initial_committee;
}; };
@ -513,9 +527,12 @@ namespace graphene { namespace chain {
} }
} } } }
FC_REFLECT(graphene::chain::genesis_state_type::genesis_account_type, (name)(addr)(is_lifetime_member)) FC_REFLECT(graphene::chain::genesis_state_type::initial_account_type, (name)(owner_key)(active_key)(is_lifetime_member))
FC_REFLECT(graphene::chain::genesis_state_type::genesis_balance_type, FC_REFLECT(graphene::chain::genesis_state_type::initial_balance_type,
(owner)(asset_symbol)(amount)) (owner)(asset_symbol)(amount))
FC_REFLECT(graphene::chain::genesis_state_type::initial_vesting_balance_type,
(owner)(asset_symbol)(amount)(vesting_start_date)(earliest_withdrawal_date)(vesting_end_date))
FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key)(initial_secret)) FC_REFLECT(graphene::chain::genesis_state_type::initial_witness_type, (owner_name)(block_signing_key)(initial_secret))
FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (owner_name)) FC_REFLECT(graphene::chain::genesis_state_type::initial_committee_member_type, (owner_name))
FC_REFLECT(graphene::chain::genesis_state_type, (initial_parameters)(initial_accounts)(initial_balances)(initial_witnesses)(initial_committee)) FC_REFLECT(graphene::chain::genesis_state_type,
(initial_parameters)(initial_accounts)(initial_balances)(initial_witnesses)(initial_committee))

View file

@ -22,6 +22,7 @@
namespace graphene { namespace chain { namespace graphene { namespace chain {
class database; class database;
class signed_transaction;
class generic_evaluator; class generic_evaluator;
class transaction_evaluation_state; class transaction_evaluation_state;
@ -42,175 +43,175 @@ namespace graphene { namespace chain {
*/ */
class evaluation_observer class evaluation_observer
{ {
public: public:
virtual ~evaluation_observer(){} virtual ~evaluation_observer(){}
virtual void pre_evaluate( virtual void pre_evaluate(const transaction_evaluation_state& eval_state,
const transaction_evaluation_state& eval_state, const operation& op,
const operation& op, bool apply,
bool apply, generic_evaluator* ge)
generic_evaluator* ge ) {} {}
virtual void post_evaluate( virtual void post_evaluate(const transaction_evaluation_state& eval_state,
const transaction_evaluation_state& eval_state, const operation& op,
const operation& op, bool apply,
bool apply, generic_evaluator* ge,
generic_evaluator* ge, const operation_result& result)
const operation_result& result ) {} {}
virtual void evaluation_failed( virtual void evaluation_failed(const transaction_evaluation_state& eval_state,
const transaction_evaluation_state& eval_state, const operation& op,
const operation& op, bool apply,
bool apply, generic_evaluator* ge,
generic_evaluator* ge, const operation_result& result)
const operation_result& result ) {} {}
}; };
class generic_evaluator class generic_evaluator
{ {
public: public:
virtual ~generic_evaluator(){} virtual ~generic_evaluator(){}
virtual int get_type()const = 0; virtual int get_type()const = 0;
virtual operation_result start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply ); virtual operation_result start_evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply);
/** @note derived classes should ASSUME that the default validation that is /**
* indepenent of chain state should be performed by op.validate() and should * @note derived classes should ASSUME that the default validation that is
* not perform these extra checks. * indepenent of chain state should be performed by op.validate() and should
*/ * not perform these extra checks.
virtual operation_result evaluate( const operation& op ) = 0; */
virtual operation_result apply( const operation& op ) = 0; virtual operation_result evaluate(const operation& op) = 0;
virtual operation_result apply(const operation& op) = 0;
database& db()const; database& db()const;
void check_required_authorities(const operation& op); void check_required_authorities(const operation& op);
protected: protected:
/** /**
* @brief Fetch objects relevant to fee payer and set pointer members * @brief Fetch objects relevant to fee payer and set pointer members
* @param account_id Account which is paying the fee * @param account_id Account which is paying the fee
* @param fee The fee being paid. May be in assets other than core. * @param fee The fee being paid. May be in assets other than core.
* *
* This method verifies that the fee is valid and sets the object pointer members and the fee fields. It should * This method verifies that the fee is valid and sets the object pointer members and the fee fields. It should
* be called during do_evaluate. * be called during do_evaluate.
*/ */
void prepare_fee(account_id_type account_id, asset fee); void prepare_fee(account_id_type account_id, asset fee);
/// Pays the fee and returns the number of CORE asset that were paid. /// Pays the fee and returns the number of CORE asset that were paid.
void pay_fee(); void pay_fee();
bool verify_authority( const account_object&, authority::classification ); bool verify_authority(const account_object&, authority::classification);
//bool verify_signature( const key_object& );
object_id_type get_relative_id( object_id_type rel_id )const; object_id_type get_relative_id( object_id_type rel_id )const;
void check_relative_ids(const authority& a)const; void check_relative_ids(const authority& a)const;
authority resolve_relative_ids( const authority& a )const; authority resolve_relative_ids( const authority& a )const;
asset fee_from_account; asset fee_from_account;
share_type core_fee_paid; share_type core_fee_paid;
const account_object* fee_paying_account = nullptr; const account_object* fee_paying_account = nullptr;
const account_statistics_object* fee_paying_account_statistics = nullptr; const account_statistics_object* fee_paying_account_statistics = nullptr;
const asset_object* fee_asset = nullptr; const asset_object* fee_asset = nullptr;
const asset_dynamic_data_object* fee_asset_dyn_data = nullptr; const asset_dynamic_data_object* fee_asset_dyn_data = nullptr;
transaction_evaluation_state* trx_state; transaction_evaluation_state* trx_state;
}; };
class op_evaluator class op_evaluator
{ {
public: public:
virtual ~op_evaluator(){} virtual ~op_evaluator(){}
virtual operation_result evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply ) = 0; virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply) = 0;
vector< evaluation_observer* > eval_observers; vector<evaluation_observer*> eval_observers;
}; };
template<typename T> template<typename T>
class op_evaluator_impl : public op_evaluator class op_evaluator_impl : public op_evaluator
{ {
public: public:
virtual operation_result evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply = true ) override virtual operation_result evaluate(transaction_evaluation_state& eval_state, const operation& op, bool apply = true) override
{
// fc::exception from observers are suppressed.
// fc::exception from evaluation is deferred (re-thrown
// after all observers receive evaluation_failed)
T eval;
optional< fc::exception > evaluation_exception;
size_t observer_count = 0;
operation_result result;
for( const auto& obs : eval_observers )
{ {
// fc::exception from observers are suppressed. try
// fc::exception from evaluation is deferred (re-thrown {
// after all observers receive evaluation_failed) obs->pre_evaluate(eval_state, op, apply, &eval);
}
T eval; catch( const fc::exception& e )
optional< fc::exception > evaluation_exception; {
size_t observer_count = 0; elog( "suppressed exception in observer pre method:\n${e}", ( "e", e.to_detail_string() ) );
operation_result result; }
observer_count++;
for( const auto& obs : eval_observers )
{
try
{
obs->pre_evaluate( eval_state, op, apply, &eval );
}
catch( const fc::exception& e )
{
elog( "suppressed exception in observer pre method:\n${e}", ( "e", e.to_detail_string() ) );
}
observer_count++;
}
try
{
result = eval.start_evaluate( eval_state, op, apply );
}
catch( const fc::exception& e )
{
evaluation_exception = e;
}
while( observer_count > 0 )
{
--observer_count;
const auto& obs = eval_observers[ observer_count ];
try
{
if( !evaluation_exception.valid() )
obs->post_evaluate( eval_state, op, apply, &eval, result );
else
obs->evaluation_failed( eval_state, op, apply, &eval, result );
}
catch( const fc::exception& e )
{
elog( "suppressed exception in observer post method:\n${e}", ( "e", e.to_detail_string() ) );
}
}
if( evaluation_exception.valid() )
throw *evaluation_exception;
return result;
} }
try
{
result = eval.start_evaluate(eval_state, op, apply);
}
catch( const fc::exception& e )
{
evaluation_exception = e;
}
while( observer_count > 0 )
{
--observer_count;
const auto& obs = eval_observers[observer_count];
try
{
if( !evaluation_exception.valid() )
obs->post_evaluate(eval_state, op, apply, &eval, result);
else
obs->evaluation_failed(eval_state, op, apply, &eval, result);
}
catch( const fc::exception& e )
{
elog( "suppressed exception in observer post method:\n${e}", ( "e", e.to_detail_string() ) );
}
}
if( evaluation_exception.valid() )
throw *evaluation_exception;
return result;
}
}; };
template<typename DerivedEvaluator> template<typename DerivedEvaluator>
class evaluator : public generic_evaluator class evaluator : public generic_evaluator
{ {
public: public:
virtual int get_type()const override { return operation::tag<typename DerivedEvaluator::operation_type>::value; } virtual int get_type()const override { return operation::tag<typename DerivedEvaluator::operation_type>::value; }
virtual operation_result evaluate( const operation& o ) final override virtual operation_result evaluate(const operation& o) final override
{ {
auto* eval = static_cast<DerivedEvaluator*>(this); auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>(); const auto& op = o.get<typename DerivedEvaluator::operation_type>();
prepare_fee(op.fee_payer(), op.fee); prepare_fee(op.fee_payer(), op.fee);
FC_ASSERT( core_fee_paid >= op.calculate_fee(db().current_fee_schedule()) ); FC_ASSERT( core_fee_paid >= op.calculate_fee(db().current_fee_schedule()) );
return eval->do_evaluate( op ); return eval->do_evaluate(op);
} }
virtual operation_result apply( const operation& o ) final override virtual operation_result apply(const operation& o) final override
{ {
auto* eval = static_cast<DerivedEvaluator*>(this); auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>(); const auto& op = o.get<typename DerivedEvaluator::operation_type>();
pay_fee(); pay_fee();
auto result = eval->do_apply( op ); auto result = eval->do_apply(op);
db().adjust_balance(op.fee_payer(), -fee_from_account); db().adjust_balance(op.fee_payer(), -fee_from_account);
return result; return result;
} }
}; };
} } } }

View file

@ -58,7 +58,7 @@ namespace graphene { namespace chain {
class fork_database class fork_database
{ {
public: public:
typedef vector<item_ptr> branch_type; typedef vector<item_ptr> branch_type;
fork_database(); fork_database();
void reset(); void reset();
@ -81,8 +81,8 @@ namespace graphene { namespace chain {
pair< branch_type, branch_type > fetch_branch_from( block_id_type first, pair< branch_type, branch_type > fetch_branch_from( block_id_type first,
block_id_type second )const; block_id_type second )const;
struct block_id{}; struct block_id;
struct block_num{}; struct block_num;
typedef multi_index_container< typedef multi_index_container<
item_ptr, item_ptr,
indexed_by< indexed_by<

View file

@ -130,14 +130,19 @@ namespace graphene { namespace chain {
}; };
/** /**
* This operation will claim all initial balance objects owned by any of the addresses and * @brief Claim a balance in a @ref balanc_object
* deposit them into the deposit_to_account. *
* This operation is used to claim the balance in a given @ref balance_object. If the balance object contains a
* vesting balance, @ref total_claimed must be set to zero, and all available vested funds will be claimed. If the
* object contains a non-vesting balance, @ref total_claimed must be the full balance of the object.
*
* This operation returns the total amount claimed.
*/ */
struct balance_claim_operation struct balance_claim_operation
{ {
asset fee; asset fee;
account_id_type deposit_to_account; account_id_type deposit_to_account;
flat_set<address> owners; balance_id_type balance_to_claim;
asset total_claimed; asset total_claimed;
account_id_type fee_payer()const { return deposit_to_account; } account_id_type fee_payer()const { return deposit_to_account; }
@ -145,8 +150,8 @@ namespace graphene { namespace chain {
share_type calculate_fee(const fee_schedule_type& k)const { return 0; } share_type calculate_fee(const fee_schedule_type& k)const { return 0; }
void validate()const; void validate()const;
void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const { void get_balance_delta(balance_accumulator& acc, const operation_result& result)const {
acc.adjust(fee_payer(), total_claimed); acc.adjust(deposit_to_account, result.get<asset>());
acc.adjust(fee_payer(), -fee); acc.adjust(fee_payer(), -fee);
} }
}; };
@ -1646,7 +1651,7 @@ FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(
FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths) ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths) )
FC_REFLECT( graphene::chain::void_result, ) FC_REFLECT( graphene::chain::void_result, )
FC_REFLECT( graphene::chain::balance_claim_operation, (fee)(deposit_to_account)(owners)(total_claimed) ) FC_REFLECT( graphene::chain::balance_claim_operation, (fee)(deposit_to_account)(balance_to_claim)(total_claimed) )
FC_REFLECT_TYPENAME( graphene::chain::operation ) FC_REFLECT_TYPENAME( graphene::chain::operation )
FC_REFLECT_TYPENAME( graphene::chain::operation_result ) FC_REFLECT_TYPENAME( graphene::chain::operation_result )

View file

@ -153,16 +153,8 @@ namespace graphene { namespace chain {
vector<signature_type> signatures; vector<signature_type> signatures;
// flat_map<key_id_type,signature_type> signatures;
/** some operations may depend only upon a signature and not
* require account approval. This allows those extra signatures
* to be added to the transaction.
*/
// flat_map<address,signature_type> extra_signatures;
/// Removes all operations and signatures /// Removes all operations and signatures
void clear() { operations.clear(); signatures.clear(); /*extra_signatures.clear();*/ } void clear() { operations.clear(); signatures.clear(); }
}; };
/** /**

View file

@ -34,12 +34,9 @@ namespace graphene { namespace chain {
using boost::multi_index_container; using boost::multi_index_container;
using namespace boost::multi_index; using namespace boost::multi_index;
/** /**
* The purpose of this object is to enable the detection * The purpose of this object is to enable the detection of duplicate transactions. When a transaction is included
* of duplicate transactions. When a transaction is * in a block a transaction_object is added. At the end of block processing all transaction_objects that have
* included in a block a transaction_object is * expired can be removed from the index.
* added. At the end of block processing all
* transaction_objects that have expired can
* be removed from the index.
*/ */
class transaction_object : public abstract_object<transaction_object> class transaction_object : public abstract_object<transaction_object>
{ {
@ -52,7 +49,6 @@ namespace graphene { namespace chain {
transaction_id_type trx_id; transaction_id_type trx_id;
}; };
struct by_expiration; struct by_expiration;
struct by_id; struct by_id;
struct by_trx_id; struct by_trx_id;
@ -66,7 +62,6 @@ namespace graphene { namespace chain {
> transaction_multi_index_type; > transaction_multi_index_type;
typedef generic_index<transaction_object, transaction_multi_index_type> transaction_index; typedef generic_index<transaction_object, transaction_multi_index_type> transaction_index;
} } } }
FC_REFLECT_DERIVED( graphene::chain::transaction_object, (graphene::db::object), (trx)(expiration) ) FC_REFLECT_DERIVED( graphene::chain::transaction_object, (graphene::db::object), (trx)(expiration) )

View file

@ -177,7 +177,7 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type; typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type;
typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type;
typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type;
typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type; typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type;
typedef object_id< relative_protocol_ids, key_object_type, key_object> relative_key_id_type; typedef object_id< relative_protocol_ids, key_object_type, key_object> relative_key_id_type;
typedef object_id< relative_protocol_ids, account_object_type, account_object> relative_account_id_type; typedef object_id< relative_protocol_ids, account_object_type, account_object> relative_account_id_type;

View file

@ -53,15 +53,17 @@ namespace graphene { namespace chain {
*/ */
struct linear_vesting_policy struct linear_vesting_policy
{ {
uint32_t vesting_seconds = 0; ///< must be greater than zero /// No amount may be withdrawn before this time, regardless of how much has vested.
/** while coindays may accrue over time, none may be claimed before first_claim date */ fc::time_point_sec earliest_withdraw_time;
fc::time_point_sec start_claim; /// This is the time at which funds begin vesting.
/** linear vesting may begin prior to allowing the user to actually claim the funds, this /// Note that withdrawals are still not available until @ref earliest_withdraw_time
* can be used to create a cliff. fc::time_point_sec begin_date;
*/ /// Duration of vesting period, in seconds. Must be greater than zero.
fc::time_point_sec begin_date; uint32_t vesting_seconds = 0;
share_type begin_balance; ///< same asset as balance /// The total amount of asset to vest
share_type total_withdrawn; ///< same asset as balance share_type begin_balance;
/// The total amount of asset which has already been withdrawn
share_type total_withdrawn;
asset get_allowed_withdraw(const vesting_policy_context& ctx)const; asset get_allowed_withdraw(const vesting_policy_context& ctx)const;
bool is_deposit_allowed(const vesting_policy_context& ctx)const; bool is_deposit_allowed(const vesting_policy_context& ctx)const;
@ -73,7 +75,6 @@ namespace graphene { namespace chain {
void on_withdraw(const vesting_policy_context& ctx); void on_withdraw(const vesting_policy_context& ctx);
}; };
/** /**
* @brief defines vesting in terms of coin-days accrued which allows for dynamic deposit/withdraw * @brief defines vesting in terms of coin-days accrued which allows for dynamic deposit/withdraw
* *
@ -117,7 +118,6 @@ namespace graphene { namespace chain {
cdd_vesting_policy cdd_vesting_policy
> vesting_policy; > vesting_policy;
/** /**
* Vesting balance object is a balance that is locked by the blockchain for a period of time. * Vesting balance object is a balance that is locked by the blockchain for a period of time.
*/ */
@ -127,15 +127,17 @@ namespace graphene { namespace chain {
static const uint8_t space_id = protocol_ids; static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = vesting_balance_object_type; static const uint8_t type_id = vesting_balance_object_type;
account_id_type owner; /// Account which owns and may withdraw from this vesting balance
asset balance; account_id_type owner;
vesting_policy policy; /// Total amount remaining in this vesting balance
/// Includes the unvested funds, and the vested funds which have not yet been withdrawn
asset balance;
/// The vesting policy stores details on when funds vest, and controls when they may be withdrawn
vesting_policy policy;
vesting_balance_object() {} vesting_balance_object() {}
/** ///@brief Deposit amount into vesting balance, requiring it to vest before withdrawal
* Used to increase existing vesting balances.
*/
void deposit(const fc::time_point_sec& now, const asset& amount); void deposit(const fc::time_point_sec& now, const asset& amount);
bool is_deposit_allowed(const fc::time_point_sec& now, const asset& amount)const; bool is_deposit_allowed(const fc::time_point_sec& now, const asset& amount)const;
@ -144,12 +146,12 @@ namespace graphene { namespace chain {
bool is_deposit_vested_allowed(const fc::time_point_sec& now, const asset& amount)const; bool is_deposit_vested_allowed(const fc::time_point_sec& now, const asset& amount)const;
/** /**
* Used to remove a vesting balance from the VBO. As well * Used to remove a vesting balance from the VBO. As well as the
* as the balance field, coin_seconds_earned and * balance field, coin_seconds_earned and
* coin_seconds_earned_last_update fields are updated. * coin_seconds_earned_last_update fields are updated.
* *
* The money doesn't "go" anywhere; the caller is responsible * The money doesn't "go" anywhere; the caller is responsible for
* for crediting it to the proper account. * crediting it to the proper account.
*/ */
void withdraw(const fc::time_point_sec& now, const asset& amount); void withdraw(const fc::time_point_sec& now, const asset& amount);
bool is_withdraw_allowed(const fc::time_point_sec& now, const asset& amount)const; bool is_withdraw_allowed(const fc::time_point_sec& now, const asset& amount)const;
@ -158,9 +160,9 @@ namespace graphene { namespace chain {
} } // graphene::chain } } // graphene::chain
FC_REFLECT(graphene::chain::linear_vesting_policy, FC_REFLECT(graphene::chain::linear_vesting_policy,
(vesting_seconds) (earliest_withdraw_time)
(start_claim)
(begin_date) (begin_date)
(vesting_seconds)
(begin_balance) (begin_balance)
(total_withdrawn) (total_withdrawn)
) )

View file

@ -939,8 +939,6 @@ void balance_claim_operation::get_required_auth(flat_set<account_id_type>& acti
void balance_claim_operation::validate()const void balance_claim_operation::validate()const
{ {
FC_ASSERT( owners.size() > 0 );
FC_ASSERT( total_claimed.amount > 0 );
FC_ASSERT( fee == asset() ); FC_ASSERT( fee == asset() );
} }

View file

@ -56,7 +56,7 @@ struct init_policy_visitor
linear_vesting_policy policy; linear_vesting_policy policy;
policy.vesting_seconds = i.vesting_seconds; policy.vesting_seconds = i.vesting_seconds;
policy.begin_date = i.begin_date; policy.begin_date = i.begin_date;
policy.start_claim = i.start_claim; policy.earliest_withdraw_time = i.start_claim;
policy.begin_balance = init_balance; policy.begin_balance = init_balance;
p = policy; p = policy;
} }

View file

@ -25,13 +25,12 @@ inline bool sum_below_max_shares(const asset& a, const asset& b)
assert(GRAPHENE_MAX_SHARE_SUPPLY + GRAPHENE_MAX_SHARE_SUPPLY > GRAPHENE_MAX_SHARE_SUPPLY); assert(GRAPHENE_MAX_SHARE_SUPPLY + GRAPHENE_MAX_SHARE_SUPPLY > GRAPHENE_MAX_SHARE_SUPPLY);
return (a.amount <= GRAPHENE_MAX_SHARE_SUPPLY) return (a.amount <= GRAPHENE_MAX_SHARE_SUPPLY)
&& ( b.amount <= GRAPHENE_MAX_SHARE_SUPPLY) && ( b.amount <= GRAPHENE_MAX_SHARE_SUPPLY)
&& ((a.amount + b.amount) <= GRAPHENE_MAX_SHARE_SUPPLY) && ((a.amount + b.amount) <= GRAPHENE_MAX_SHARE_SUPPLY);
;
} }
asset linear_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx) const asset linear_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx) const
{ {
if(ctx.now <= start_claim) if(ctx.now <= earliest_withdraw_time)
return asset(0, ctx.balance.asset_id); return asset(0, ctx.balance.asset_id);
if(ctx.now <= begin_date) if(ctx.now <= begin_date)
return asset(0, ctx.balance.asset_id); return asset(0, ctx.balance.asset_id);

View file

@ -2148,18 +2148,17 @@ signed_transaction wallet_api::import_balance( string name_or_id, const vector<s
for( auto a : bal_types ) for( auto a : bal_types )
{ {
balance_claim_operation op; balance_claim_operation op;
op.total_claimed = asset( 0, a ); op.deposit_to_account = claimer.id;
for( auto b : balances ) for( const auto& b : balances )
{ {
if( b.balance.asset_id == a ) if( b.balance.asset_id == a )
{ {
op.total_claimed += b.balance; op.total_claimed = b.vesting_policy.valid()? asset(0, b.balance.asset_id) : b.balance;
op.owners.insert( b.owner ); op.balance_to_claim = b.id;
op.deposit_to_account = claimer.id; trx.operations.push_back(op);
required_addrs.insert( b.owner ); required_addrs.insert(b.owner);
} }
} }
trx.operations.push_back(op);
} }
trx.visit( operation_set_fee( my->_remote_db->get_global_properties().parameters.current_fees ) ); trx.visit( operation_set_fee( my->_remote_db->get_global_properties().parameters.current_fees ) );

View file

@ -24,6 +24,7 @@
#include <graphene/chain/call_order_object.hpp> #include <graphene/chain/call_order_object.hpp>
#include <graphene/chain/limit_order_object.hpp> #include <graphene/chain/limit_order_object.hpp>
#include <graphene/chain/account_object.hpp> #include <graphene/chain/account_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/block.hpp> #include <graphene/chain/block.hpp>
#include <iostream> #include <iostream>

View file

@ -66,7 +66,10 @@ database_fixture::database_fixture()
for( int i = 0; i < 10; ++i ) for( int i = 0; i < 10; ++i )
{ {
auto name = "init"+fc::to_string(i); auto name = "init"+fc::to_string(i);
genesis_state.initial_accounts.emplace_back(name, delegate_priv_key.get_public_key(), true); genesis_state.initial_accounts.emplace_back(name,
delegate_priv_key.get_public_key(),
delegate_priv_key.get_public_key(),
true);
genesis_state.initial_committee.push_back({name}); genesis_state.initial_committee.push_back({name});
genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret}); genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret});
} }

View file

@ -45,7 +45,10 @@ genesis_state_type make_genesis() {
for( int i = 0; i < 10; ++i ) for( int i = 0; i < 10; ++i )
{ {
auto name = "init"+fc::to_string(i); auto name = "init"+fc::to_string(i);
genesis_state.initial_accounts.emplace_back(name, delegate_priv_key.get_public_key(), true); genesis_state.initial_accounts.emplace_back(name,
delegate_priv_key.get_public_key(),
delegate_priv_key.get_public_key(),
true);
genesis_state.initial_committee.push_back({name}); genesis_state.initial_committee.push_back({name});
genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret}); genesis_state.initial_witnesses.push_back({name, delegate_priv_key.get_public_key(), secret});
} }

View file

@ -981,31 +981,32 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
fc::temp_directory td; fc::temp_directory td;
genesis_state.initial_balances.push_back({generate_private_key("n").get_public_key(), GRAPHENE_SYMBOL, 1}); genesis_state.initial_balances.push_back({generate_private_key("n").get_public_key(), GRAPHENE_SYMBOL, 1});
genesis_state.initial_balances.push_back({generate_private_key("x").get_public_key(), GRAPHENE_SYMBOL, 1}); genesis_state.initial_balances.push_back({generate_private_key("x").get_public_key(), GRAPHENE_SYMBOL, 1});
genesis_state.initial_accounts.emplace_back("n", generate_private_key("n").get_public_key(), false); // TODO: vesting genesis balances
genesis_state.initial_accounts.emplace_back("n", generate_private_key("n").get_public_key());
db.open(td.path(), genesis_state); db.open(td.path(), genesis_state);
const balance_object& balance = *db.get_index_type<balance_index>().indices().find(balance_id_type()); const balance_object& balance = balance_id_type()(db);
BOOST_CHECK_EQUAL(balance.balance.amount.value, 1); BOOST_CHECK_EQUAL(balance.balance.amount.value, 1);
BOOST_CHECK_EQUAL(db.get_index_type<balance_index>().indices().find(balance_id_type(1))->balance.amount.value, 1); BOOST_CHECK_EQUAL(balance_id_type(1)(db).balance.amount.value, 1);
balance_claim_operation op; balance_claim_operation op;
op.deposit_to_account = db.get_index_type<account_index>().indices().get<by_name>().find("n")->get_id(); op.deposit_to_account = db.get_index_type<account_index>().indices().get<by_name>().find("n")->get_id();
op.total_claimed = asset(1); op.total_claimed = asset(1);
op.owners.insert(genesis_state.initial_balances.back().owner); op.balance_to_claim = balance_id_type(1);
trx.operations = {op}; trx.operations = {op};
trx.sign(generate_private_key("n")); trx.sign(generate_private_key("n"));
//trx.sign(generate_private_key("n")); // Fail because I'm claiming from an address which hasn't signed
// Fail because I'm claiming the wrong address
BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception); BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception);
trx.clear(); trx.clear();
op.owners = {genesis_state.initial_balances.front().owner}; op.balance_to_claim = balance_id_type();
trx.operations = {op}; trx.operations = {op};
trx.sign(generate_private_key("n")); trx.sign(generate_private_key("n"));
//trx.sign(generate_private_key("n"));
db.push_transaction(trx); db.push_transaction(trx);
// Not using fixture's get_balance() here because it uses fixture's db, not my override // Not using fixture's get_balance() here because it uses fixture's db, not my override
BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 1); BOOST_CHECK_EQUAL(db.get_balance(op.deposit_to_account, asset_id_type()).amount.value, 1);
BOOST_CHECK(db.find_object(balance_id_type()) == nullptr);
BOOST_CHECK(db.find_object(balance_id_type(1)) != nullptr);
} FC_LOG_AND_RETHROW() } } FC_LOG_AND_RETHROW() }
// TODO: Write linear VBO tests // TODO: Write linear VBO tests