Progress #17
- 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:
parent
a05a13b20e
commit
c530867933
23 changed files with 348 additions and 345 deletions
|
|
@ -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) ) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) )
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} }
|
} }
|
||||||
|
|
|
||||||
|
|
@ -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<
|
||||||
|
|
|
||||||
|
|
@ -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 )
|
||||||
|
|
|
||||||
|
|
@ -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(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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) )
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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 ) );
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue