Merge branches 'history-324', 'bugfix-390', 'wallet-398', 'opt-410', 'script-411', 'bugfix-412', 'bugfix-414', 'bugfix-423' and 'feature-427' into develop

This commit is contained in:
theoreticalbts 2015-10-31 17:11:41 -04:00
24 changed files with 338 additions and 71 deletions

View file

@ -21,6 +21,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/uint128.hpp>
namespace graphene { namespace chain {
@ -38,12 +39,26 @@ share_type cut_fee(share_type a, uint16_t p)
return r.to_uint64();
}
bool account_object::is_authorized_asset(const asset_object& asset_obj) const {
bool account_object::is_authorized_asset(const asset_object& asset_obj, const database& d) const
{
for( const auto id : blacklisting_accounts )
if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) return false;
{
if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() )
return false;
}
if( d.head_block_time() > HARDFORK_415_TIME )
{
if( asset_obj.options.whitelist_authorities.size() == 0 )
return true;
}
for( const auto id : whitelisting_accounts )
if( asset_obj.options.whitelist_authorities.find(id) != asset_obj.options.whitelist_authorities.end() ) return true;
{
if( asset_obj.options.whitelist_authorities.find(id) != asset_obj.options.whitelist_authorities.end() )
return true;
}
return false;
}

View file

@ -126,7 +126,7 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o
void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
{ try {
database& d = db();
const database& d = db();
const asset_object& a = o.asset_to_issue.asset_id(d);
FC_ASSERT( o.issuer == a.issuer );
@ -136,7 +136,7 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
if( a.options.flags & white_list )
{
FC_ASSERT( to_account->is_authorized_asset( a ) );
FC_ASSERT( to_account->is_authorized_asset( a, d ) );
}
asset_dyn_data = &a.dynamic_asset_data_id(d);
@ -158,7 +158,7 @@ void_result asset_issue_evaluator::do_apply( const asset_issue_operation& o )
void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& o )
{ try {
database& d = db();
const database& d = db();
const asset_object& a = o.amount_to_reserve.asset_id(d);
GRAPHENE_ASSERT(
@ -172,7 +172,7 @@ void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation&
if( a.options.flags & white_list )
{
FC_ASSERT( from_account->is_authorized_asset( a ) );
FC_ASSERT( from_account->is_authorized_asset( a, d ) );
}
asset_dyn_data = &a.dynamic_asset_data_id(d);

View file

@ -366,11 +366,13 @@ void database::clear_expired_orders()
void database::update_expired_feeds()
{
auto& asset_idx = get_index_type<asset_index>().indices();
for( const asset_object& a : asset_idx )
auto& asset_idx = get_index_type<asset_index>().indices().get<by_type>();
auto itr = asset_idx.lower_bound( true /** market issued */ );
while( itr != asset_idx.end() )
{
if( !a.is_market_issued() )
continue;
const asset_object& a = *itr;
++itr;
assert( a.is_market_issued() );
const asset_bitasset_data_object& b = a.bitasset_data(*this);
if( b.feed_is_expired(head_block_time()) )

View file

@ -229,7 +229,7 @@ namespace graphene { namespace chain {
* @return true if this account is whitelisted and not blacklisted to transact in the provided asset; false
* otherwise.
*/
bool is_authorized_asset(const asset_object& asset_obj)const;
bool is_authorized_asset(const asset_object& asset_obj, const database& d)const;
account_id_type get_id()const { return id; }
};

View file

@ -213,7 +213,6 @@ namespace graphene { namespace chain {
void update_median_feeds(time_point_sec current_time);
};
struct by_feed_expiration;
typedef multi_index_container<
asset_bitasset_data_object,
@ -227,17 +226,19 @@ namespace graphene { namespace chain {
typedef flat_index<asset_bitasset_data_object> asset_bitasset_data_index;
struct by_symbol;
struct by_type;
typedef multi_index_container<
asset_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >
ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >,
ordered_non_unique< tag<by_type>, const_mem_fun<asset_object, bool, &asset_object::is_market_issued> >
>
> asset_object_multi_index_type;
typedef generic_index<asset_object, asset_object_multi_index_type> asset_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::object),
(current_supply)(confidential_supply)(accumulated_fees)(fee_pool) )
@ -260,4 +261,3 @@ FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
(dynamic_asset_data_id)
(bitasset_data_id)
)

View file

@ -128,6 +128,7 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
(time)
(current_witness)
(next_maintenance_time)
(last_budget_time)
(witness_budget)
(accounts_registered_this_interval)
(recently_missed_count)

View file

@ -22,3 +22,4 @@
#define HARDFORK_357_TIME (fc::time_point_sec( 1444416300 ))
#define HARDFORK_359_TIME (fc::time_point_sec( 1444416300 ))
#define HARDFORK_415_TIME (fc::time_point_sec( 1446652800 ))

View file

@ -117,4 +117,5 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT_TYPENAME( graphene::chain::operation_result )
FC_REFLECT_TYPENAME( graphene::chain::future_extensions )
FC_REFLECT( graphene::chain::void_result, )

View file

@ -310,6 +310,7 @@ FC_REFLECT_TYPENAME( graphene::chain::operation_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type )
FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type )
FC_REFLECT_TYPENAME( graphene::chain::worker_id_type )
FC_REFLECT_TYPENAME( graphene::chain::balance_id_type )
FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type )
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )

View file

@ -29,7 +29,7 @@
namespace graphene { namespace chain {
void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_operation& op)
{ try {
database& d = db();
const database& d = db();
FC_ASSERT( op.expiration >= d.head_block_time() );
@ -42,8 +42,8 @@ void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_o
if( _sell_asset->options.blacklist_markets.size() )
FC_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) == _sell_asset->options.blacklist_markets.end() );
if( _sell_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_sell_asset ) );
if( _receive_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_receive_asset ) );
if( _sell_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_sell_asset, d ) );
if( _receive_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_receive_asset, d ) );
FC_ASSERT( d.get_balance( *_seller, *_sell_asset ) >= op.amount_to_sell, "insufficient balance",
("balance",d.get_balance(*_seller,*_sell_asset))("amount_to_sell",op.amount_to_sell) );

View file

@ -26,7 +26,7 @@ namespace graphene { namespace chain {
void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
{ try {
database& d = db();
const database& d = db();
const account_object& from_account = op.from(d);
const account_object& to_account = op.to(d);
@ -38,14 +38,14 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
if( asset_type.options.flags & white_list )
{
GRAPHENE_ASSERT(
from_account.is_authorized_asset( asset_type ),
from_account.is_authorized_asset( asset_type, d ),
transfer_from_account_not_whitelisted,
"'from' account ${from} is not whitelisted for asset ${asset}",
("from",op.from)
("asset",op.amount.asset_id)
);
GRAPHENE_ASSERT(
to_account.is_authorized_asset( asset_type ),
to_account.is_authorized_asset( asset_type, d ),
transfer_to_account_not_whitelisted,
"'to' account ${to} is not whitelisted for asset ${asset}",
("to",op.to)
@ -54,7 +54,7 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
}
if( fee_asset_type.options.flags & white_list )
FC_ASSERT( from_account.is_authorized_asset( asset_type ) );
FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) );
if( asset_type.is_transfer_restricted() )
{
@ -87,7 +87,7 @@ void_result transfer_evaluator::do_apply( const transfer_operation& o )
void_result override_transfer_evaluator::do_evaluate( const override_transfer_operation& op )
{ try {
database& d = db();
const database& d = db();
const asset_object& asset_type = op.amount.asset_id(d);
GRAPHENE_ASSERT(
@ -104,12 +104,12 @@ void_result override_transfer_evaluator::do_evaluate( const override_transfer_op
if( asset_type.options.flags & white_list )
{
FC_ASSERT( to_account.is_authorized_asset( asset_type ) );
FC_ASSERT( from_account.is_authorized_asset( asset_type ) );
FC_ASSERT( to_account.is_authorized_asset( asset_type, d ) );
FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) );
}
if( fee_asset_type.options.flags & white_list )
FC_ASSERT( from_account.is_authorized_asset( asset_type ) );
FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) );
FC_ASSERT( d.get_balance( from_account, asset_type ).amount >= op.amount.amount,
"", ("total_transfer",op.amount)("balance",d.get_balance(from_account, asset_type).amount) );

View file

@ -94,7 +94,7 @@ fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned(const vesting_poli
delta_coin_seconds *= delta_seconds;
fc::uint128_t coin_seconds_earned_cap = ctx.balance.amount.value;
coin_seconds_earned_cap *= vesting_seconds;
coin_seconds_earned_cap *= std::max(vesting_seconds, 1u);
return std::min(coin_seconds_earned + delta_coin_seconds, coin_seconds_earned_cap);
}
@ -110,7 +110,7 @@ asset cdd_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx
if(ctx.now <= start_claim)
return asset(0, ctx.balance.asset_id);
fc::uint128_t cs_earned = compute_coin_seconds_earned(ctx);
fc::uint128_t withdraw_available = cs_earned / vesting_seconds;
fc::uint128_t withdraw_available = cs_earned / std::max(vesting_seconds, 1u);
assert(withdraw_available <= ctx.balance.amount.value);
return asset(withdraw_available.to_uint64(), ctx.balance.asset_id);
}
@ -123,14 +123,14 @@ void cdd_vesting_policy::on_deposit(const vesting_policy_context& ctx)
void cdd_vesting_policy::on_deposit_vested(const vesting_policy_context& ctx)
{
on_deposit(ctx);
coin_seconds_earned += ctx.amount.amount.value * vesting_seconds;
coin_seconds_earned += ctx.amount.amount.value * std::max(vesting_seconds, 1u);
}
void cdd_vesting_policy::on_withdraw(const vesting_policy_context& ctx)
{
update_coin_seconds_earned(ctx);
fc::uint128_t coin_seconds_needed = ctx.amount.amount.value;
coin_seconds_needed *= vesting_seconds;
coin_seconds_needed *= std::max(vesting_seconds, 1u);
// is_withdraw_allowed should forbid any withdrawal that
// would trigger this assert
assert(coin_seconds_needed <= coin_seconds_earned);

View file

@ -53,7 +53,7 @@ object_id_type withdraw_permission_create_evaluator::do_apply(const operation_ty
void_result withdraw_permission_claim_evaluator::do_evaluate(const withdraw_permission_claim_evaluator::operation_type& op)
{ try {
database& d = db();
const database& d = db();
const withdraw_permission_object& permit = op.withdraw_permission(d);
FC_ASSERT(permit.expiration > d.head_block_time() );
@ -69,8 +69,8 @@ void_result withdraw_permission_claim_evaluator::do_evaluate(const withdraw_perm
{
const account_object& from = op.withdraw_to_account(d);
const account_object& to = permit.authorized_account(d);
FC_ASSERT( to.is_authorized_asset( _asset ) );
FC_ASSERT( from.is_authorized_asset( _asset ) );
FC_ASSERT( to.is_authorized_asset( _asset, d ) );
FC_ASSERT( from.is_authorized_asset( _asset, d ) );
}
return void_result();

View file

@ -23,7 +23,6 @@
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
namespace graphene { namespace chain {
@ -117,7 +116,7 @@ namespace graphene { namespace chain {
struct sparse_index : public generic_index<T, boost::multi_index_container<
T,
indexed_by<
hashed_unique<
ordered_unique<
tag<by_id>,
member<object, object_id_type, &object::id>
>

View file

@ -146,12 +146,18 @@ void witness_plugin::plugin_shutdown()
void witness_plugin::schedule_production_loop()
{
//Schedule for the next second's tick regardless of chain state
// If we would wait less than 200ms, wait for the whole second.
fc::time_point now = graphene::time::now();
fc::time_point_sec next_second( now + fc::microseconds( 1200000 ) );
//wdump( (now.time_since_epoch().count())(next_second) );
// If we would wait less than 50ms, wait for the whole second.
fc::time_point ntp_now = graphene::time::now();
fc::time_point fc_now = fc::time_point::now();
int64_t time_to_next_second = 1000000 - (ntp_now.time_since_epoch().count() % 1000000);
if( time_to_next_second < 50000 ) // we must sleep for at least 50ms
time_to_next_second += 1000000;
fc::time_point next_wakeup( fc_now + fc::microseconds( time_to_next_second ) );
//wdump( (now.time_since_epoch().count())(next_wakeup.time_since_epoch().count()) );
_block_production_task = fc::schedule([this]{block_production_loop();},
next_second, "Witness Block Production");
next_wakeup, "Witness Block Production");
}
block_production_condition::block_production_condition_enum witness_plugin::block_production_loop()

View file

@ -2955,7 +2955,7 @@ signed_transaction wallet_api::reserve_asset(string from,
string symbol,
bool broadcast /* = false */)
{
return my->fund_asset_fee_pool(from, amount, symbol, broadcast);
return my->reserve_asset(from, amount, symbol, broadcast);
}
signed_transaction wallet_api::global_settle_asset(string symbol,

View file

@ -31,6 +31,9 @@ def main():
with open(filename, "r") as f:
patch = json.load(f)
for k, v in patch.get("append", {}).items():
if k not in genesis:
genesis[k] = []
sys.stderr.write("[WARN] item {k} was created\n".format(k=k))
genesis[k].extend(v)
sys.stderr.write("appended {n} items to {k}\n".format(n=len(v), k=k))
for k, v in patch.get("replace", {}).items():

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python3
import argparse
import json
import subprocess
import sys
def dump_json(obj, out, pretty):
if pretty:
json.dump(obj, out, indent=2, sort_keys=True)
else:
json.dump(obj, out, separators=(",", ":"), sort_keys=True)
return
def main():
parser = argparse.ArgumentParser(description="Generate a patch file that adds init accounts")
parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)")
parser.add_argument("-n", "--num", metavar="N", default=11, type=int, help="number of init witnesses")
parser.add_argument("-w", "--witness", metavar="N", default=1, type=int, help="starting witness ID")
parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output")
parser.add_argument("-m", "--mname", metavar="HOSTNAME", default="", help="machine name of target machine")
parser.add_argument("-s", "--secret", metavar="SECRET", default=None, help="private key generation secret")
opts = parser.parse_args()
if opts.secret is None:
sys.stderr.write("missing required parameter --secret\n")
sys.stderr.flush()
sys.exit(1)
out_wits = []
out_keys = []
for i in range(opts.num):
if opts.mname != "":
istr = "wit-block-signing-"+opts.mname+"-"+str(i)
else:
istr = "wit-block-signing-"+str(i)
prod_str = subprocess.check_output(["programs/genesis_util/get_dev_key", opts.secret, istr]).decode("utf-8")
prod = json.loads(prod_str)
out_wits.append('witness-id = "1.6.'+str(opts.witness+i)+'"\n')
out_keys.append("private-key = "+json.dumps([prod[0]["public_key"], prod[0]["private_key"]])+"\n")
out_data = "".join(out_wits + ["\n"] + out_keys)
if opts.output == "-":
sys.stdout.write(out_data)
sys.stdout.flush()
else:
with open(opts.output, "w") as f:
f.write(out_data)
return
if __name__ == "__main__":
main()

View file

@ -46,6 +46,8 @@
using namespace graphene::chain::test;
uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP = 1431700000;
namespace graphene { namespace chain {
using std::cout;

View file

@ -29,7 +29,7 @@
using namespace graphene::db;
#define GRAPHENE_TESTING_GENESIS_TIMESTAMP (1431700000)
extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP;
#define PUSH_TX \
graphene::chain::test::_push_transaction

View file

@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
{
database db;
db.open(data_dir.path(), []{return genesis_state_type();});
BOOST_CHECK_EQUAL( db.head_block_num(), 200 );
BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num() );
b = cutoff_block;
for( uint32_t i = 0; i < 200; ++i )
{
@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
//BOOST_CHECK( cur_witness != prev_witness );
b = db.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing);
}
BOOST_CHECK_EQUAL( db.head_block_num(), 400 );
BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num()+200 );
}
} catch (fc::exception& e) {
edump((e.to_detail_string()));

View file

@ -23,8 +23,16 @@
#include <iostream>
#include <boost/test/included/unit_test.hpp>
extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP;
boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
std::srand(time(NULL));
std::cout << "Random number generator seeded to " << time(NULL) << std::endl;
const char* genesis_timestamp_str = getenv("GRAPHENE_TESTING_GENESIS_TIMESTAMP");
if( genesis_timestamp_str != nullptr )
{
GRAPHENE_TESTING_GENESIS_TIMESTAMP = std::stoul( genesis_timestamp_str );
}
std::cout << "GRAPHENE_TESTING_GENESIS_TIMESTAMP is " << GRAPHENE_TESTING_GENESIS_TIMESTAMP << std::endl;
return nullptr;
}

View file

@ -27,6 +27,7 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/budget_record_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
@ -453,15 +454,16 @@ BOOST_AUTO_TEST_CASE( witness_create )
generate_block();
int produced = 0;
// Make sure we get scheduled exactly once in witnesses.size() blocks
// Make sure we get scheduled at least once in witnesses.size()*2 blocks
// may take this many unless we measure where in the scheduling round we are
// TODO: intense_test that repeats this loop many times
for( size_t i=0; i<witnesses.size(); i++ )
for( size_t i=0, n=witnesses.size()*2; i<n; i++ )
{
signed_block block = generate_block();
if( block.witness == nathan_witness_id )
produced++;
}
BOOST_CHECK_EQUAL( produced, 2 );
BOOST_CHECK_GE( produced, 1 );
} FC_LOG_AND_RETHROW() }
/**
@ -1036,8 +1038,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
fc::temp_directory td( graphene::utilities::temp_directory_path() );
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});
auto starting_time = time_point_sec((time_point::now().sec_since_epoch() / GRAPHENE_DEFAULT_BLOCK_INTERVAL + 1) *
GRAPHENE_DEFAULT_BLOCK_INTERVAL);
fc::time_point_sec starting_time = genesis_state.initial_timestamp + 3000;
auto n_key = generate_private_key("n");
auto x_key = generate_private_key("x");
@ -1207,6 +1208,121 @@ BOOST_AUTO_TEST_CASE(transfer_with_memo) {
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(zero_second_vbo)
{
try
{
ACTOR(alice);
// don't pay witnesses so we have some worker budget to work with
transfer(account_id_type(), alice_id, asset(int64_t(100000) * 1100 * 1000 * 1000));
{
asset_reserve_operation op;
op.payer = alice_id;
op.amount_to_reserve = asset(int64_t(100000) * 1000 * 1000 * 1000);
transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures );
}
enable_fees();
upgrade_to_lifetime_member(alice_id);
generate_block();
auto check_vesting_1b = [&](vesting_balance_id_type vbid)
{
// this function checks that Alice can't draw any right now,
// but one block later, she can withdraw it all.
vesting_balance_withdraw_operation withdraw_op;
withdraw_op.vesting_balance = vbid;
withdraw_op.owner = alice_id;
withdraw_op.amount = asset(1);
signed_transaction withdraw_tx;
withdraw_tx.operations.push_back( withdraw_op );
sign(withdraw_tx, alice_private_key);
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, withdraw_tx ), fc::exception );
generate_block();
withdraw_tx = signed_transaction();
withdraw_op.amount = asset(500);
withdraw_tx.operations.push_back( withdraw_op );
set_expiration( db, withdraw_tx );
sign(withdraw_tx, alice_private_key);
PUSH_TX( db, withdraw_tx );
};
// This block creates a zero-second VBO with a vesting_balance_create_operation.
{
cdd_vesting_policy_initializer pinit;
pinit.vesting_seconds = 0;
vesting_balance_create_operation create_op;
create_op.creator = alice_id;
create_op.owner = alice_id;
create_op.amount = asset(500);
create_op.policy = pinit;
signed_transaction create_tx;
create_tx.operations.push_back( create_op );
set_expiration( db, create_tx );
sign(create_tx, alice_private_key);
processed_transaction ptx = PUSH_TX( db, create_tx );
vesting_balance_id_type vbid = ptx.operation_results[0].get<object_id_type>();
check_vesting_1b( vbid );
}
// This block creates a zero-second VBO with a worker_create_operation.
{
worker_create_operation create_op;
create_op.owner = alice_id;
create_op.work_begin_date = db.head_block_time();
create_op.work_end_date = db.head_block_time() + fc::days(1000);
create_op.daily_pay = share_type( 10000 );
create_op.name = "alice";
create_op.url = "";
create_op.initializer = vesting_balance_worker_initializer(0);
signed_transaction create_tx;
create_tx.operations.push_back(create_op);
set_expiration( db, create_tx );
sign(create_tx, alice_private_key);
processed_transaction ptx = PUSH_TX( db, create_tx );
worker_id_type wid = ptx.operation_results[0].get<object_id_type>();
// vote it in
account_update_operation vote_op;
vote_op.account = alice_id;
vote_op.new_options = alice_id(db).options;
vote_op.new_options->votes.insert(wid(db).vote_for);
signed_transaction vote_tx;
vote_tx.operations.push_back(vote_op);
set_expiration( db, vote_tx );
sign( vote_tx, alice_private_key );
PUSH_TX( db, vote_tx );
// vote it in, wait for one maint. for vote to take effect
vesting_balance_id_type vbid = wid(db).worker.get<vesting_balance_worker_type>().balance;
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
generate_block();
// wait for another maint. for worker to be paid
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(0) );
generate_block();
BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(10000) );
/*
db.get_index_type< simple_index<budget_record_object> >().inspect_all_objects(
[&](const object& o)
{
ilog( "budget: ${brec}", ("brec", static_cast<const budget_record_object&>(o)) );
});
*/
}
} FC_LOG_AND_RETHROW()
}
// TODO: Write linear VBO tests
BOOST_AUTO_TEST_SUITE_END()

View file

@ -23,6 +23,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
@ -134,33 +135,75 @@ BOOST_AUTO_TEST_CASE( override_transfer_test2 )
BOOST_AUTO_TEST_CASE( issue_whitelist_uia )
{
try {
INVOKE(create_advanced_uia);
const asset_object& advanced = get_asset("ADVANCED");
const account_object& nathan = create_account("nathan");
upgrade_to_lifetime_member(nathan);
account_id_type izzy_id = create_account("izzy").id;
const asset_id_type uia_id = create_user_issued_asset(
"ADVANCED", izzy_id(db), white_list ).id;
account_id_type nathan_id = create_account("nathan").id;
account_id_type vikram_id = create_account("vikram").id;
trx.clear();
asset_issue_operation op;
op.issuer = advanced.issuer;
op.asset_to_issue = advanced.amount(1000);
op.issue_to_account = nathan.id; //({asset(), advanced.issuer, advanced.amount(1000), nathan.id});
op.issuer = uia_id(db).issuer;
op.asset_to_issue = asset(1000, uia_id);
op.issue_to_account = nathan_id;
trx.operations.emplace_back(op);
//Fail because nathan is not whitelisted.
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
set_expiration( db, trx );
//Fail because nathan is not whitelisted, but only before hardfork time
if( db.head_block_time() <= HARDFORK_415_TIME )
{
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
generate_blocks( HARDFORK_415_TIME );
generate_block();
set_expiration( db, trx );
}
PUSH_TX( db, trx, ~0 );
BOOST_CHECK(nathan_id(db).is_authorized_asset(uia_id(db), db));
BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 1000);
// Make a whitelist, now it should fail
{
BOOST_TEST_MESSAGE( "Changing the whitelist authority" );
asset_update_operation uop;
uop.issuer = izzy_id;
uop.asset_to_update = uia_id;
uop.new_options = uia_id(db).options;
uop.new_options.whitelist_authorities.insert(izzy_id);
trx.operations.back() = uop;
PUSH_TX( db, trx, ~0 );
BOOST_CHECK( uia_id(db).options.whitelist_authorities.find(izzy_id) != uia_id(db).options.whitelist_authorities.end() );
}
// Fail because there is a whitelist authority and I'm not whitelisted
trx.operations.back() = op;
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );
account_whitelist_operation wop;
wop.authorizing_account = account_id_type();
wop.account_to_list = nathan.id;
wop.authorizing_account = izzy_id;
wop.account_to_list = vikram_id;
wop.new_listing = account_whitelist_operation::white_listed;
trx.operations.back() = wop;
// Fail because whitelist function is restricted to members only
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );
upgrade_to_lifetime_member( izzy_id );
trx.operations.clear();
trx.operations.push_back( wop );
PUSH_TX( db, trx, ~0 );
BOOST_CHECK(nathan.is_authorized_asset(advanced));
// Still fail after an irrelevant account was added
trx.operations.back() = op;
PUSH_TX( db, trx, ~0 );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );
wop.account_to_list = nathan_id;
trx.operations.back() = wop;
PUSH_TX( db, trx, ~0 );
trx.operations.back() = op;
BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 1000);
// Finally succeed when we were whitelisted
PUSH_TX( db, trx, ~0 );
BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 2000);
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1000);
} catch(fc::exception& e) {
edump((e.to_detail_string()));
throw;
@ -174,6 +217,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
const asset_object& advanced = get_asset("ADVANCED");
const account_object& nathan = get_account("nathan");
const account_object& dan = create_account("dan");
account_id_type izzy_id = get_account("izzy").id;
upgrade_to_lifetime_member(dan);
trx.clear();
@ -189,23 +233,36 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
BOOST_TEST_MESSAGE( "Adding dan to whitelist for asset ADVANCED" );
account_whitelist_operation wop;
wop.authorizing_account = account_id_type();
wop.authorizing_account = izzy_id;
wop.account_to_list = dan.id;
wop.new_listing = account_whitelist_operation::white_listed;
trx.operations.back() = wop;
PUSH_TX( db, trx, ~0 );
BOOST_TEST_MESSAGE( "Attempting to trnsfer from nathan to dan after whitelisting dan, should succeed" );
BOOST_TEST_MESSAGE( "Attempting to transfer from nathan to dan after whitelisting dan, should succeed" );
trx.operations.back() = op;
PUSH_TX( db, trx, ~0 );
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 900);
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1900);
BOOST_CHECK_EQUAL(get_balance(dan, advanced), 100);
BOOST_TEST_MESSAGE( "Attempting to blacklist nathan" );
{
BOOST_TEST_MESSAGE( "Changing the blacklist authority" );
asset_update_operation uop;
uop.issuer = izzy_id;
uop.asset_to_update = advanced.id;
uop.new_options = advanced.options;
uop.new_options.blacklist_authorities.insert(izzy_id);
trx.operations.back() = uop;
PUSH_TX( db, trx, ~0 );
BOOST_CHECK( advanced.options.blacklist_authorities.find(izzy_id) != advanced.options.blacklist_authorities.end() );
}
wop.new_listing |= account_whitelist_operation::black_listed;
wop.account_to_list = nathan.id;
trx.operations.back() = wop;
PUSH_TX( db, trx, ~0 );
BOOST_CHECK( !(nathan.is_authorized_asset(advanced, db)) );
BOOST_TEST_MESSAGE( "Attempting to transfer from nathan after blacklisting, should fail" );
op.amount = advanced.amount(50);
@ -230,6 +287,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
{
BOOST_TEST_MESSAGE( "Changing the blacklist authority to dan" );
asset_update_operation op;
op.issuer = izzy_id;
op.asset_to_update = advanced.id;
op.new_options = advanced.options;
op.new_options.blacklist_authorities.clear();
@ -242,7 +300,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
BOOST_TEST_MESSAGE( "Attempting to transfer from dan back to nathan" );
trx.operations.back() = op;
PUSH_TX( db, trx, ~0 );
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 950);
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1950);
BOOST_CHECK_EQUAL(get_balance(dan, advanced), 50);
BOOST_TEST_MESSAGE( "Blacklisting nathan by dan" );
@ -254,11 +312,11 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
trx.operations.back() = op;
//Fail because nathan is blacklisted
BOOST_CHECK(!nathan.is_authorized_asset(advanced));
BOOST_CHECK(!nathan.is_authorized_asset(advanced, db));
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
//Remove nathan from committee's whitelist, add him to dan's. This should not authorize him to hold ADVANCED.
wop.authorizing_account = account_id_type();
wop.authorizing_account = izzy_id;
wop.account_to_list = nathan.id;
wop.new_listing = account_whitelist_operation::no_listing;
trx.operations.back() = wop;
@ -271,7 +329,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
trx.operations.back() = op;
//Fail because nathan is not whitelisted
BOOST_CHECK(!nathan.is_authorized_asset(advanced));
BOOST_CHECK(!nathan.is_authorized_asset(advanced, db));
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
burn.payer = dan.id;