Unit test for STEALTH fee routing #563

This commit is contained in:
theoreticalbts 2016-02-15 14:58:01 -05:00
parent 5dd56bd824
commit 36a8c3d6f1
4 changed files with 238 additions and 0 deletions

View file

@ -167,3 +167,6 @@
/// Sentinel value used in the scheduler.
#define GRAPHENE_NULL_WITNESS (graphene::chain::witness_id_type(0))
///@}
// hack for unit test
#define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET (asset_id_type(1))

View file

@ -32,6 +32,7 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
@ -195,6 +196,8 @@ void database_fixture::verify_asset_supplies( const database& db )
}
for( const vesting_balance_object& vbo : db.get_index_type< vesting_balance_index >().indices() )
total_balances[ vbo.balance.asset_id ] += vbo.balance.amount;
for( const fba_accumulator_object& fba : db.get_index_type< simple_index< fba_accumulator_object > >() )
total_balances[ asset_id_type() ] += fba.accumulated_fba_fees;
total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget;
@ -1033,6 +1036,24 @@ int64_t database_fixture::get_balance( const account_object& account, const asse
return db.get_balance(account.get_id(), a.get_id()).amount.value;
}
vector< operation_history_object > database_fixture::get_operation_history( account_id_type account_id )const
{
vector< operation_history_object > result;
const auto& stats = account_id(db).statistics(db);
if(stats.most_recent_op == account_transaction_history_id_type())
return result;
const account_transaction_history_object* node = &stats.most_recent_op(db);
while( true )
{
result.push_back( node->operation_id(db) );
if(node->next == account_transaction_history_id_type())
break;
node = db.find(node->next);
}
return result;
}
namespace test {
void set_expiration( const database& db, transaction& tx )

View file

@ -28,6 +28,8 @@
#include <fc/io/json.hpp>
#include <fc/smart_ref_impl.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <iostream>
using namespace graphene::db;
@ -278,6 +280,7 @@ struct database_fixture {
void print_joint_market( const string& syma, const string& symb )const;
int64_t get_balance( account_id_type account, asset_id_type a )const;
int64_t get_balance( const account_object& account, const asset_object& a )const;
vector< operation_history_object > get_operation_history( account_id_type account_id )const;
};
namespace test {

View file

@ -24,7 +24,12 @@
#include <fc/smart_ref_impl.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/fba_object.hpp>
#include <graphene/chain/market_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
@ -721,4 +726,210 @@ BOOST_AUTO_TEST_CASE( fee_refund_test )
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( stealth_fba_test )
{
try
{
ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin)(tom) );
upgrade_to_lifetime_member(philbin_id);
generate_blocks( HARDFORK_538_TIME );
generate_blocks( HARDFORK_555_TIME );
generate_blocks( HARDFORK_563_TIME );
generate_blocks( HARDFORK_572_TIME );
// Philbin (registrar who registers Rex)
// Izzy (initial issuer of stealth asset, will later transfer to Tom)
// Alice, Bob, Chloe, Dan (ABCD)
// Rex (recycler -- buyback account for stealth asset)
// Tom (owner of stealth asset who will be set as top_n authority)
// Izzy creates STEALTH
asset_id_type stealth_id = create_user_issued_asset( "STEALTH", izzy_id(db),
disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee ).id;
/*
// this is disabled because it doesn't work, our modify() is probably being overwritten by undo
//
// Init blockchain with stealth ID's
// On a real chain, this would be done with #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
// causing the designated_asset fields of these objects to be set at genesis, but for
// this test we modify the db directly.
//
auto set_fba_asset = [&]( uint64_t fba_acc_id, asset_id_type asset_id )
{
db.modify( fba_accumulator_id_type(fba_acc_id)(db), [&]( fba_accumulator_object& fba )
{
fba.designated_asset = asset_id;
} );
};
set_fba_asset( fba_accumulator_id_transfer_to_blind , stealth_id );
set_fba_asset( fba_accumulator_id_blind_transfer , stealth_id );
set_fba_asset( fba_accumulator_id_transfer_from_blind, stealth_id );
*/
// Izzy kills some permission bits (this somehow happened to the real STEALTH in production)
{
asset_update_operation update_op;
update_op.issuer = izzy_id;
update_op.asset_to_update = stealth_id;
asset_options new_options;
new_options = stealth_id(db).options;
new_options.issuer_permissions = charge_market_fee;
new_options.flags = disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee;
// after fixing #579 you should be able to delete the following line
new_options.core_exchange_rate = price( asset( 1, stealth_id ), asset( 1, asset_id_type() ) );
update_op.new_options = new_options;
signed_transaction tx;
tx.operations.push_back( update_op );
set_expiration( db, tx );
sign( tx, izzy_private_key );
PUSH_TX( db, tx );
}
// Izzy transfers issuer duty to Tom
{
asset_update_operation update_op;
update_op.issuer = izzy_id;
update_op.asset_to_update = stealth_id;
update_op.new_issuer = tom_id;
// new_options should be optional, but isn't...the following line should be unnecessary #580
update_op.new_options = stealth_id(db).options;
signed_transaction tx;
tx.operations.push_back( update_op );
set_expiration( db, tx );
sign( tx, izzy_private_key );
PUSH_TX( db, tx );
}
// Tom re-enables the permission bits to clear the flags, then clears them again
// Allowed by #572 when current_supply == 0
{
asset_update_operation update_op;
update_op.issuer = tom_id;
update_op.asset_to_update = stealth_id;
asset_options new_options;
new_options = stealth_id(db).options;
new_options.issuer_permissions = new_options.flags | charge_market_fee;
update_op.new_options = new_options;
signed_transaction tx;
// enable perms is one op
tx.operations.push_back( update_op );
new_options.issuer_permissions = charge_market_fee;
new_options.flags = charge_market_fee;
update_op.new_options = new_options;
// reset wrongly set flags and reset permissions can be done in a single op
tx.operations.push_back( update_op );
set_expiration( db, tx );
sign( tx, tom_private_key );
PUSH_TX( db, tx );
}
// Philbin registers Rex who will be the asset's buyback, including sig from the new issuer (Tom)
account_id_type rex_id;
{
buyback_account_options bbo;
bbo.asset_to_buy = stealth_id;
bbo.asset_to_buy_issuer = tom_id;
bbo.markets.emplace( asset_id_type() );
account_create_operation create_op = make_account( "rex" );
create_op.registrar = philbin_id;
create_op.extensions.value.buyback_options = bbo;
create_op.owner = authority::null_authority();
create_op.active = authority::null_authority();
signed_transaction tx;
tx.operations.push_back( create_op );
set_expiration( db, tx );
sign( tx, philbin_private_key );
sign( tx, tom_private_key );
processed_transaction ptx = PUSH_TX( db, tx );
rex_id = ptx.operation_results.back().get< object_id_type >();
}
// Tom issues some asset to Alice and Bob
set_expiration( db, trx ); // #11
issue_uia( alice_id, asset( 1000, stealth_id ) );
issue_uia( bob_id, asset( 1000, stealth_id ) );
// Tom sets his authority to the top_n of the asset
{
top_holders_special_authority top2;
top2.num_top_holders = 2;
top2.asset = stealth_id;
account_update_operation op;
op.account = tom_id;
op.extensions.value.active_special_authority = top2;
op.extensions.value.owner_special_authority = top2;
signed_transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
sign( tx, tom_private_key );
PUSH_TX( db, tx );
}
// Wait until the next maintenance interval for top_n to take effect
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
// Do a blind op to add some fees to the pool.
fund( chloe_id(db), asset( 100000, asset_id_type() ) );
auto create_transfer_to_blind = [&]( account_id_type account, asset amount, const std::string& key ) -> transfer_to_blind_operation
{
fc::ecc::private_key blind_key = fc::ecc::private_key::regenerate( fc::sha256::hash( key+"-privkey" ) );
public_key_type blind_pub = blind_key.get_public_key();
fc::sha256 secret = fc::sha256::hash( key+"-secret" );
fc::sha256 nonce = fc::sha256::hash( key+"-nonce" );
transfer_to_blind_operation op;
blind_output blind_out;
blind_out.owner = authority( 1, blind_pub, 1 );
blind_out.commitment = fc::ecc::blind( secret, amount.amount.value );
blind_out.range_proof = fc::ecc::range_proof_sign( 0, blind_out.commitment, secret, nonce, 0, 0, amount.amount.value );
op.amount = amount;
op.from = account;
op.blinding_factor = fc::ecc::blind_sum( {secret}, 1 );
op.outputs = {blind_out};
return op;
};
{
transfer_to_blind_operation op = create_transfer_to_blind( chloe_id, asset( 5000, asset_id_type() ), "chloe-key" );
op.fee = asset( 1000, asset_id_type() );
signed_transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
sign( tx, chloe_private_key );
PUSH_TX( db, tx );
}
// wait until next maint interval
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
idump( ( get_operation_history( chloe_id ) ) );
idump( ( get_operation_history( rex_id ) ) );
idump( ( get_operation_history( tom_id ) ) );
}
catch( const fc::exception& e )
{
elog( "caught exception ${e}", ("e", e.to_detail_string()) );
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()