- 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 )
{
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_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") )
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
dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));
@ -199,20 +206,9 @@ namespace detail {
try
{
if( id.item_type == graphene::net::block_message_type )
{
// 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;
}
}
return _chain_db->is_known_block(id.item_hash);
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) )
}
@ -255,7 +251,7 @@ namespace detail {
virtual std::vector<item_hash_t> get_item_ids(uint32_t item_type,
const std::vector<item_hash_t>& blockchain_synopsis,
uint32_t& remaining_item_count,
uint32_t limit ) override
uint32_t limit) override
{ try {
FC_ASSERT( item_type == graphene::net::block_message_type );
vector<block_id_type> result;
@ -268,7 +264,7 @@ namespace detail {
auto itr = blockchain_synopsis.rbegin();
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;
break;
@ -285,7 +281,6 @@ namespace detail {
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());
idump((blockchain_synopsis)(limit)(result)(remaining_item_count));
return result;
} 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) ) }
bool block_database::is_open()const
{
return _blocks.is_open();
}
void block_database::close()
{
_blocks.close();
@ -67,7 +65,6 @@ void block_database::flush()
_block_num_to_pos.flush();
}
void block_database::store( const block_id_type& id, const signed_block& b )
{
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) );
}
void block_database::remove( const block_id_type& id )
{ try {
index_entry e;
@ -102,25 +98,20 @@ void block_database::remove( const block_id_type& 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;
auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
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.read( (char*)&e, sizeof(e) );
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;
auto index_pos = sizeof(e)*block_num;
@ -134,22 +125,21 @@ block_id_type block_database::fetch_block_id( uint32_t block_num )const
return e.block_id;
}
optional<signed_block> block_database::fetch_optional( const block_id_type& id )const
{
try
try
{
index_entry e;
auto index_pos = sizeof(e)*block_header::num_from_id(id);
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
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.read( (char*)&e, sizeof(e) );
if( e.block_id != id ) return optional<signed_block>();
vector<char> data( e.block_size );
_blocks.seekg( e.block_pos );
_blocks.read( data.data(), e.block_size );
@ -168,13 +158,13 @@ optional<signed_block> block_database::fetch_optional( const block_id_type& id )
optional<signed_block> block_database::fetch_by_number( uint32_t block_num )const
{
try
try
{
index_entry e;
auto index_pos = sizeof(e)*block_num;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
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.read( (char*)&e, sizeof(e) );
@ -195,10 +185,9 @@ optional<signed_block> block_database::fetch_by_number( uint32_t block_num )cons
return optional<signed_block>();
}
optional<signed_block> block_database::last()const
{
try
try
{
index_entry e;
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
@ -231,5 +220,4 @@ optional<signed_block> block_database::last()const
}
return optional<signed_block>();
}
} }

View file

@ -301,10 +301,10 @@ signed_block database::_generate_block(
{
for( const auto& trx : tmp.transactions )
{
try {
push_transaction( trx, skip );
} catch ( const fc::exception& e ) {
wlog( "Transaction is no longer valid: ${trx}", ("trx",trx) );
try {
push_transaction( trx, skip );
} catch ( const fc::exception& e ) {
wlog( "Transaction is no longer valid: ${trx}", ("trx",trx) );
}
}
return _generate_block( when, witness_id, block_signing_private_key );
@ -435,28 +435,6 @@ processed_transaction database::apply_transaction( const signed_transaction& trx
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 )
{ try {
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 )
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.
@ -503,9 +479,9 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr
eval_state._sigs.reserve( trx.signatures.size() );
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" ) ;
trx.visit( signature_check_visitor(eval_state._sigs) );
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");
}
//Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration
@ -513,7 +489,7 @@ processed_transaction database::_apply_transaction( const signed_transaction& tr
trx_expiration = tapos_block_summary.timestamp + chain_parameters.block_interval*trx.relative_expiration;
} else if( trx.relative_expiration == 0 ) {
trx_expiration = fc::time_point_sec() + fc::seconds(trx.ref_block_prefix);
FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration, "",
FC_ASSERT( trx_expiration <= _pending_block.timestamp + chain_parameters.maximum_time_until_expiration, "",
("trx_expiration",trx_expiration)("_pending_block.timestamp",_pending_block.timestamp)("max_til_exp",chain_parameters.maximum_time_until_expiration));
}
FC_ASSERT( _pending_block.timestamp <= trx_expiration, "", ("pending.timestamp",_pending_block.timestamp)("trx_exp",trx_expiration) );

View file

@ -161,7 +161,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
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")));
create<key_object>( [&null_private_key](key_object& k) {
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,{}));
}
// TODO: Create initial vesting balances
// Create initial accounts
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_create_operation({asset(),
committee_account.id,
account.addr})).get<object_id_type>();
account.owner_key})).get<object_id_type>();
account_create_operation cop;
cop.name = account.name;
cop.registrar = account_id_type(1);
cop.active = authority(1, key_id, 1);
cop.owner = cop.active;
cop.owner = authority(1, key_id, 1);
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;
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>
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 )
{ try {
trx_state = &eval_state;

View file

@ -1,47 +1,60 @@
#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>
namespace graphene { namespace chain {
/**
* @ingroup operations
*/
class balance_claim_evaluator : public evaluator<balance_claim_evaluator>
class balance_claim_evaluator : public evaluator<balance_claim_evaluator>
{
public:
typedef balance_claim_operation operation_type;
const balance_object* balance = nullptr;
asset amount_withdrawn;
asset do_evaluate(const balance_claim_operation& op)
{
public:
typedef balance_claim_operation operation_type;
database& d = db();
balance = &op.balance_to_claim(d);
void_result do_evaluate( const balance_claim_operation& op )
{
return void_result();
}
FC_ASSERT(trx_state->_sigs.count(balance->owner) == 1);
trx_state->_sigs[balance->owner] = true;
FC_ASSERT(op.total_claimed.asset_id == balance->asset_type());
/**
* @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
*/
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>();
if( balance->vesting_policy.valid() ) {
FC_ASSERT(op.total_claimed.amount == 0);
return amount_withdrawn = balance->vesting_policy->get_allowed_withdraw({balance->balance,
d.head_block_time(),
{}});
}
asset total(0, op.total_claimed.asset_id);
for( const auto& owner : op.owners )
{
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(op.total_claimed == balance->balance);
return amount_withdrawn = op.total_claimed;
}
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

View file

@ -1,5 +1,7 @@
#pragma once
#include <graphene/chain/vesting_balance_object.hpp>
namespace graphene { namespace chain {
class balance_object : public abstract_object<balance_object>
@ -10,6 +12,7 @@ namespace graphene { namespace chain {
address owner;
asset balance;
optional<linear_vesting_policy> vesting_policy;
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>;
} }
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;
struct genesis_state_type {
struct genesis_account_type {
genesis_account_type(const string& name = string(),
const address& addr = address(),
bool is_lifetime_member = false)
: name(name), addr(addr), is_lifetime_member(is_lifetime_member){}
struct initial_account_type {
initial_account_type(const string& name = string(),
const public_key_type& owner_key = public_key_type(),
const public_key_type& active_key = public_key_type(),
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;
address addr;
public_key_type owner_key;
public_key_type active_key;
bool is_lifetime_member;
};
struct genesis_balance_type {
struct initial_balance_type {
address owner;
string asset_symbol;
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 {
/// Must correspond to one of the allocation targets.
string owner_name;
@ -66,8 +80,8 @@ namespace graphene { namespace chain {
};
chain_parameters initial_parameters;
vector<genesis_account_type> initial_accounts;
vector<genesis_balance_type> initial_balances;
vector<initial_account_type> initial_accounts;
vector<initial_balance_type> initial_balances;
vector<initial_witness_type> initial_witnesses;
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::genesis_balance_type,
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::initial_balance_type,
(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_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 {
class database;
class signed_transaction;
class generic_evaluator;
class transaction_evaluation_state;
@ -42,175 +43,175 @@ namespace graphene { namespace chain {
*/
class evaluation_observer
{
public:
virtual ~evaluation_observer(){}
public:
virtual ~evaluation_observer(){}
virtual void pre_evaluate(
const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge ) {}
virtual void pre_evaluate(const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge)
{}
virtual void post_evaluate(
const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result ) {}
virtual void post_evaluate(const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result)
{}
virtual void evaluation_failed(
const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result ) {}
virtual void evaluation_failed(const transaction_evaluation_state& eval_state,
const operation& op,
bool apply,
generic_evaluator* ge,
const operation_result& result)
{}
};
class generic_evaluator
{
public:
virtual ~generic_evaluator(){}
public:
virtual ~generic_evaluator(){}
virtual int get_type()const = 0;
virtual operation_result start_evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply );
virtual int get_type()const = 0;
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
* not perform these extra checks.
*/
virtual operation_result evaluate( const operation& op ) = 0;
virtual operation_result apply( const operation& op ) = 0;
/**
* @note derived classes should ASSUME that the default validation that is
* 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;
database& db()const;
database& db()const;
void check_required_authorities(const operation& op);
void check_required_authorities(const operation& op);
protected:
/**
* @brief Fetch objects relevant to fee payer and set pointer members
* @param account_id Account which is paying the fee
* @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
* be called during do_evaluate.
*/
void prepare_fee(account_id_type account_id, asset fee);
/// Pays the fee and returns the number of CORE asset that were paid.
void pay_fee();
/**
* @brief Fetch objects relevant to fee payer and set pointer members
* @param account_id Account which is paying the fee
* @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
* be called during do_evaluate.
*/
void prepare_fee(account_id_type account_id, asset fee);
/// Pays the fee and returns the number of CORE asset that were paid.
void pay_fee();
bool verify_authority( const account_object&, authority::classification );
//bool verify_signature( const key_object& );
bool verify_authority(const account_object&, authority::classification);
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;
authority resolve_relative_ids( const authority& a )const;
void check_relative_ids(const authority& a)const;
authority resolve_relative_ids( const authority& a )const;
asset fee_from_account;
share_type core_fee_paid;
const account_object* fee_paying_account = nullptr;
const account_statistics_object* fee_paying_account_statistics = nullptr;
const asset_object* fee_asset = nullptr;
const asset_dynamic_data_object* fee_asset_dyn_data = nullptr;
transaction_evaluation_state* trx_state;
asset fee_from_account;
share_type core_fee_paid;
const account_object* fee_paying_account = nullptr;
const account_statistics_object* fee_paying_account_statistics = nullptr;
const asset_object* fee_asset = nullptr;
const asset_dynamic_data_object* fee_asset_dyn_data = nullptr;
transaction_evaluation_state* trx_state;
};
class op_evaluator
{
public:
virtual ~op_evaluator(){}
virtual operation_result evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply ) = 0;
public:
virtual ~op_evaluator(){}
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>
class op_evaluator_impl : public op_evaluator
{
public:
virtual operation_result evaluate( transaction_evaluation_state& eval_state, const operation& op, bool apply = true ) override
public:
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.
// 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 )
{
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
{
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;
}
};
template<typename DerivedEvaluator>
class evaluator : public generic_evaluator
{
public:
virtual int get_type()const override { return operation::tag<typename DerivedEvaluator::operation_type>::value; }
public:
virtual int get_type()const override { return operation::tag<typename DerivedEvaluator::operation_type>::value; }
virtual operation_result evaluate( const operation& o ) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
virtual operation_result evaluate(const operation& o) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
prepare_fee(op.fee_payer(), op.fee);
FC_ASSERT( core_fee_paid >= op.calculate_fee(db().current_fee_schedule()) );
prepare_fee(op.fee_payer(), op.fee);
FC_ASSERT( core_fee_paid >= op.calculate_fee(db().current_fee_schedule()) );
return eval->do_evaluate( op );
}
virtual operation_result apply( const operation& o ) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
const auto& op = o.get<typename DerivedEvaluator::operation_type>();
return eval->do_evaluate(op);
}
virtual operation_result apply(const operation& o) final override
{
auto* eval = static_cast<DerivedEvaluator*>(this);
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
{
public:
typedef vector<item_ptr> branch_type;
typedef vector<item_ptr> branch_type;
fork_database();
void reset();
@ -81,8 +81,8 @@ namespace graphene { namespace chain {
pair< branch_type, branch_type > fetch_branch_from( block_id_type first,
block_id_type second )const;
struct block_id{};
struct block_num{};
struct block_id;
struct block_num;
typedef multi_index_container<
item_ptr,
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
* deposit them into the deposit_to_account.
* @brief Claim a balance in a @ref balanc_object
*
* 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
{
asset fee;
account_id_type deposit_to_account;
flat_set<address> owners;
balance_id_type balance_to_claim;
asset total_claimed;
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; }
void validate()const;
void get_balance_delta(balance_accumulator& acc, const operation_result& result = asset())const {
acc.adjust(fee_payer(), total_claimed);
void get_balance_delta(balance_accumulator& acc, const operation_result& result)const {
acc.adjust(deposit_to_account, result.get<asset>());
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::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_result )

View file

@ -153,16 +153,8 @@ namespace graphene { namespace chain {
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
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 namespace boost::multi_index;
/**
* The purpose of this object is to enable the detection
* of duplicate transactions. When a transaction is
* included in a block a transaction_object is
* added. At the end of block processing all
* transaction_objects that have expired can
* be removed from the index.
* The purpose of this object is to enable the detection of duplicate transactions. When a transaction is included
* in a block a transaction_object is 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>
{
@ -52,7 +49,6 @@ namespace graphene { namespace chain {
transaction_id_type trx_id;
};
struct by_expiration;
struct by_id;
struct by_trx_id;
@ -66,7 +62,6 @@ namespace graphene { namespace chain {
> transaction_multi_index_type;
typedef generic_index<transaction_object, transaction_multi_index_type> transaction_index;
} }
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, 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, 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, account_object_type, account_object> relative_account_id_type;

View file

@ -44,24 +44,26 @@ namespace graphene { namespace chain {
};
/**
* @brief Linear vesting balance with cliff
* @brief Linear vesting balance with cliff
*
* This vesting balance type is used to mimic traditional stock vesting contracts where
* each day a certain amount vests until it is fully matured.
* each day a certain amount vests until it is fully matured.
*
* @note New funds may not be added to a linear vesting balance.
*/
struct linear_vesting_policy
{
uint32_t vesting_seconds = 0; ///< must be greater than zero
/** while coindays may accrue over time, none may be claimed before first_claim date */
fc::time_point_sec start_claim;
/** linear vesting may begin prior to allowing the user to actually claim the funds, this
* can be used to create a cliff.
*/
fc::time_point_sec begin_date;
share_type begin_balance; ///< same asset as balance
share_type total_withdrawn; ///< same asset as balance
/// No amount may be withdrawn before this time, regardless of how much has vested.
fc::time_point_sec earliest_withdraw_time;
/// This is the time at which funds begin vesting.
/// Note that withdrawals are still not available until @ref earliest_withdraw_time
fc::time_point_sec begin_date;
/// Duration of vesting period, in seconds. Must be greater than zero.
uint32_t vesting_seconds = 0;
/// The total amount of asset to vest
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;
bool is_deposit_allowed(const vesting_policy_context& ctx)const;
@ -73,20 +75,19 @@ namespace graphene { namespace chain {
void on_withdraw(const vesting_policy_context& ctx);
};
/**
* @brief defines vesting in terms of coin-days accrued which allows for dynamic deposit/withdraw
*
* The economic effect of this vesting policy is to require a certain amount of "interest" to accrue
* before the full balance may be withdrawn. Interest accrues as coindays (balance * length held). If
* some of the balance is withdrawn, the remaining balance must be held longer.
* some of the balance is withdrawn, the remaining balance must be held longer.
*/
struct cdd_vesting_policy
{
uint32_t vesting_seconds = 0;
fc::uint128_t coin_seconds_earned;
/** while coindays may accrue over time, none may be claimed before first_claim date */
fc::time_point_sec start_claim;
fc::time_point_sec start_claim;
fc::time_point_sec coin_seconds_earned_last_update;
/**
@ -117,7 +118,6 @@ namespace graphene { namespace chain {
cdd_vesting_policy
> vesting_policy;
/**
* 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 type_id = vesting_balance_object_type;
account_id_type owner;
asset balance;
vesting_policy policy;
/// Account which owns and may withdraw from this vesting balance
account_id_type owner;
/// 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() {}
/**
* Used to increase existing vesting balances.
*/
///@brief Deposit amount into vesting balance, requiring it to vest before withdrawal
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;
@ -144,12 +146,12 @@ namespace graphene { namespace chain {
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
* as the balance field, coin_seconds_earned and
* Used to remove a vesting balance from the VBO. As well as the
* balance field, coin_seconds_earned and
* coin_seconds_earned_last_update fields are updated.
*
* The money doesn't "go" anywhere; the caller is responsible
* for crediting it to the proper account.
* The money doesn't "go" anywhere; the caller is responsible for
* crediting it to the proper account.
*/
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;
@ -158,9 +160,9 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT(graphene::chain::linear_vesting_policy,
(vesting_seconds)
(start_claim)
(earliest_withdraw_time)
(begin_date)
(vesting_seconds)
(begin_balance)
(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
{
FC_ASSERT( owners.size() > 0 );
FC_ASSERT( total_claimed.amount > 0 );
FC_ASSERT( fee == asset() );
}

View file

@ -56,7 +56,7 @@ struct init_policy_visitor
linear_vesting_policy policy;
policy.vesting_seconds = i.vesting_seconds;
policy.begin_date = i.begin_date;
policy.start_claim = i.start_claim;
policy.earliest_withdraw_time = i.start_claim;
policy.begin_balance = init_balance;
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);
return (a.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
{
if(ctx.now <= start_claim)
if(ctx.now <= earliest_withdraw_time)
return asset(0, ctx.balance.asset_id);
if(ctx.now <= begin_date)
return asset(0, ctx.balance.asset_id);

View file

@ -277,7 +277,7 @@ public:
{
// Right now the wallet_api has no way of knowing if the connection to the
// witness has already disconnected (via the witness node exiting first).
// If it has exited, cancel_all_subscriptsions() will throw and there's
// If it has exited, cancel_all_subscriptsions() will throw and there's
// nothing we can do about it.
// dlog("Caught exception ${e} while canceling database subscriptions", ("e", e));
}
@ -892,7 +892,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (symbol)(new_issuer)(new_options)(broadcast) ) }
signed_transaction update_bitasset(string symbol,
asset_object::bitasset_options new_options,
bool broadcast /* = false */)
@ -913,7 +913,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (symbol)(new_options)(broadcast) ) }
signed_transaction update_asset_feed_producers(string symbol,
flat_set<string> new_feed_producers,
bool broadcast /* = false */)
@ -959,7 +959,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (publishing_account)(symbol)(feed)(broadcast) ) }
signed_transaction fund_asset_fee_pool(string from,
string symbol,
string amount,
@ -983,7 +983,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (from)(symbol)(amount)(broadcast) ) }
signed_transaction burn_asset(string from,
string amount,
string symbol,
@ -1005,7 +1005,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (from)(amount)(symbol)(broadcast) ) }
signed_transaction global_settle_asset(string symbol,
price settle_price,
bool broadcast /* = false */)
@ -1065,7 +1065,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) }
signed_transaction create_delegate(string owner_account,
bool broadcast /* = false */)
{ try {
@ -1133,7 +1133,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (voting_account)(delegate)(approve)(broadcast) ) }
signed_transaction vote_for_witness(string voting_account,
string witness,
bool approve,
@ -1167,7 +1167,7 @@ public:
return sign_transaction( tx, broadcast );
} FC_CAPTURE_AND_RETHROW( (voting_account)(witness)(approve)(broadcast) ) }
signed_transaction set_voting_proxy(string account_to_modify,
optional<string> voting_account,
bool broadcast /* = false */)
@ -1186,7 +1186,7 @@ public:
FC_THROW("Account ${account} is already voting for itself", ("account", account_to_modify));
account_object_to_modify.options.voting_account = account_id_type();
}
account_update_operation account_update_op;
account_update_op.account = account_object_to_modify.id;
account_update_op.new_options = account_object_to_modify.options;
@ -1849,7 +1849,7 @@ signed_transaction wallet_api::update_asset(string symbol,
{
return my->update_asset(symbol, new_issuer, new_options, broadcast);
}
signed_transaction wallet_api::update_bitasset(string symbol,
asset_object::bitasset_options new_options,
bool broadcast /* = false */)
@ -1937,7 +1937,7 @@ signed_transaction wallet_api::vote_for_witness(string voting_account,
bool broadcast /* = false */)
{
return my->vote_for_witness(voting_account, witness, approve, broadcast);
}
}
signed_transaction wallet_api::set_voting_proxy(string account_to_modify,
optional<string> voting_account,
@ -2148,18 +2148,17 @@ signed_transaction wallet_api::import_balance( string name_or_id, const vector<s
for( auto a : bal_types )
{
balance_claim_operation op;
op.total_claimed = asset( 0, a );
for( auto b : balances )
op.deposit_to_account = claimer.id;
for( const auto& b : balances )
{
if( b.balance.asset_id == a )
{
op.total_claimed += b.balance;
op.owners.insert( b.owner );
op.deposit_to_account = claimer.id;
required_addrs.insert( b.owner );
op.total_claimed = b.vesting_policy.valid()? asset(0, b.balance.asset_id) : b.balance;
op.balance_to_claim = b.id;
trx.operations.push_back(op);
required_addrs.insert(b.owner);
}
}
trx.operations.push_back(op);
}
trx.visit( operation_set_fee( my->_remote_db->get_global_properties().parameters.current_fees ) );
@ -2170,7 +2169,7 @@ signed_transaction wallet_api::import_balance( string name_or_id, const vector<s
for( auto a : required_addrs )
tx.sign( keys[a] );
if( broadcast )
if( broadcast )
my->_remote_net->broadcast_transaction(tx);
return tx;

View file

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

View file

@ -66,7 +66,10 @@ database_fixture::database_fixture()
for( int i = 0; i < 10; ++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_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 )
{
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_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;
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_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);
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(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;
op.deposit_to_account = db.get_index_type<account_index>().indices().get<by_name>().find("n")->get_id();
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.sign(generate_private_key("n"));
//trx.sign(generate_private_key("n"));
// Fail because I'm claiming the wrong address
// Fail because I'm claiming from an address which hasn't signed
BOOST_CHECK_THROW(db.push_transaction(trx), fc::exception);
trx.clear();
op.owners = {genesis_state.initial_balances.front().owner};
op.balance_to_claim = balance_id_type();
trx.operations = {op};
trx.sign(generate_private_key("n"));
//trx.sign(generate_private_key("n"));
db.push_transaction(trx);
// 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(db.find_object(balance_id_type()) == nullptr);
BOOST_CHECK(db.find_object(balance_id_type(1)) != nullptr);
} FC_LOG_AND_RETHROW() }
// TODO: Write linear VBO tests