Added bitcoin_issue_operation, bitcoin_issue_evaluator with tests

This commit is contained in:
Alexander Suslikov 2019-02-01 15:49:01 +03:00 committed by Anzhy Cherrnyavski
parent ff57653584
commit e1d99702e4
10 changed files with 384 additions and 7 deletions

View file

@ -298,6 +298,10 @@ struct get_impacted_account_visitor
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_issue_operation& op )
{
_impacted.insert( op.payer );
}
};
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )

View file

@ -88,6 +88,7 @@ add_library( graphene_chain
buyback.cpp
bitcoin_address_evaluator.cpp
bitcoin_transaction_evaluator.cpp
sidechain_evaluator.cpp
account_object.cpp
asset_object.cpp

View file

@ -85,6 +85,7 @@
#include <graphene/chain/withdraw_pbtc_evaluator.hpp>
#include <graphene/chain/bitcoin_address_evaluator.hpp>
#include <graphene/chain/bitcoin_transaction_evaluator.hpp>
#include <graphene/chain/sidechain_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -251,6 +252,7 @@ void database::initialize_evaluators()
register_evaluator<bitcoin_address_create_evaluator>();
register_evaluator<bitcoin_transaction_send_evaluator>();
register_evaluator<bitcoin_transaction_sign_evaluator>();
register_evaluator<bitcoin_issue_evaluator>();
}
void database::initialize_indexes()

View file

@ -285,6 +285,10 @@ struct get_impacted_account_visitor
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_issue_operation& op )
{
_impacted.insert( op.payer );
}
};
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )

View file

@ -14,17 +14,17 @@ class bitcoin_transaction_object : public abstract_object<bitcoin_transaction_ob
bitcoin_transaction_id_type get_id()const { return id; }
fc::optional< fc::sha256 > pw_vin;
fc::sha256 pw_vin;
std::vector< info_for_used_vin_id_type > vins;
std::vector< info_for_vout_id_type > vouts;
std::vector< info_for_vout_id_type > vouts;
sidechain::bitcoin_transaction transaction;
fc::sha256 transaction_id;
sidechain::bitcoin_transaction transaction;
fc::sha256 transaction_id;
uint64_t fee_for_size;
uint64_t fee_for_size;
bool confirm = false;
bool confirm = false;
};
struct by_transaction_id;

View file

@ -47,6 +47,7 @@
#include <graphene/chain/protocol/withdraw_pbtc.hpp>
#include <graphene/chain/protocol/bitcoin_address.hpp>
#include <graphene/chain/protocol/bitcoin_transaction.hpp>
#include <graphene/chain/protocol/sidechain.hpp>
namespace graphene { namespace chain {
@ -136,7 +137,8 @@ namespace graphene { namespace chain {
withdraw_pbtc_operation,
bitcoin_address_create_operation,
bitcoin_transaction_send_operation,
bitcoin_transaction_sign_operation
bitcoin_transaction_sign_operation,
bitcoin_issue_operation
> operation;
/// @} // operations group

View file

@ -0,0 +1,28 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
struct bitcoin_issue_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
};
asset fee;
account_id_type payer;
fc::sha256 transaction_id;
account_id_type fee_payer()const { return payer; }
void validate()const {}
share_type calculate_fee( const fee_parameters_type& k )const {
share_type fee_required = k.fee;
return fee_required;
}
};
} } // graphene::chain
FC_REFLECT( graphene::chain::bitcoin_issue_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::bitcoin_issue_operation, (fee)(payer)(transaction_id) )

View file

@ -0,0 +1,26 @@
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
struct bitcoin_issue_evaluator : public evaluator< bitcoin_issue_evaluator >
{
typedef bitcoin_issue_operation operation_type;
void_result do_evaluate( const bitcoin_issue_operation& op );
void_result do_apply( const bitcoin_issue_operation& op );
void add_issue( const bitcoin_transaction_object& btc_obj );
void clear_btc_transaction_information( const bitcoin_transaction_object& btc_obj );
std::vector<uint64_t> get_amounts_to_issue( std::vector<info_for_used_vin_id_type> vins_id );
std::vector<account_id_type> get_accounts_to_issue( std::vector<info_for_used_vin_id_type> vins_id );
};
} } // graphene::chain

View file

@ -0,0 +1,134 @@
#include <graphene/chain/sidechain_evaluator.hpp>
#include <graphene/chain/bitcoin_transaction_object.hpp>
#include <graphene/chain/info_for_used_vin_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
namespace graphene { namespace chain {
void_result bitcoin_issue_evaluator::do_evaluate( const bitcoin_issue_operation& op )
{
database& d = db();
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
const auto& btc_addr_idx = d.get_index_type<bitcoin_address_index>().indices().get<by_address>();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_id>();
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
FC_ASSERT( op.payer == db().get_sidechain_account_id() );
const auto& btc_itr = btc_trx_idx.find( op.transaction_id );
FC_ASSERT( btc_itr != btc_trx_idx.end() );
for( auto& vin_id : btc_itr->vins ){
FC_ASSERT( vins_info_idx.find( vin_id ) != vins_info_idx.end() );
auto addr_itr = btc_addr_idx.find( vins_info_idx.find( vin_id )->address );
FC_ASSERT( addr_itr != btc_addr_idx.end() );
}
for( auto& vout_id : btc_itr->vouts )
FC_ASSERT( vouts_info_idx.find( vout_id ) != vouts_info_idx.end() );
return void_result();
}
void_result bitcoin_issue_evaluator::do_apply( const bitcoin_issue_operation& op )
{
database& d = db();
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
const auto& btc_obj = *btc_trx_idx.find( op.transaction_id );
add_issue( btc_obj );
d.pw_vout_manager.confirm_vout( btc_obj.pw_vin );
clear_btc_transaction_information( btc_obj );
return void_result();
}
void bitcoin_issue_evaluator::add_issue( const bitcoin_transaction_object& btc_obj )
{
database& d = db();
const auto& accounts_to_issue = get_accounts_to_issue( btc_obj.vins );
const auto& amounts_to_issue = get_amounts_to_issue( btc_obj.vins );
bool skip_fee_old = trx_state->skip_fee;
bool skip_fee_schedule_check_old = trx_state->skip_fee_schedule_check;
trx_state->skip_fee = true;
trx_state->skip_fee_schedule_check = true;
for( size_t i = 0; i < accounts_to_issue.size(); i++ ){
asset_issue_operation issue_op;
issue_op.issuer = d.get_sidechain_account_id();
issue_op.asset_to_issue = asset( amounts_to_issue[i], d.get_sidechain_asset_id() );
issue_op.issue_to_account = accounts_to_issue[i];
d.apply_operation( *trx_state, issue_op );
}
trx_state->skip_fee = skip_fee_old;
trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old;
}
void bitcoin_issue_evaluator::clear_btc_transaction_information( const bitcoin_transaction_object& btc_obj )
{
database& d = db();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_id>();
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
for( auto& vin_id : btc_obj.vins ) {
auto vin_itr = vins_info_idx.find( vin_id );
d.remove( *vin_itr );
}
for( auto& vout_id : btc_obj.vouts ) {
auto vout_itr = vouts_info_idx.find( vout_id );
d.remove( *vout_itr );
}
auto trx_approvals = d.bitcoin_confirmations.find<sidechain::by_hash>( btc_obj.transaction_id );
if( trx_approvals.valid() ) {
d.bitcoin_confirmations.remove<sidechain::by_hash>( btc_obj.transaction_id );
}
d.remove( btc_obj );
}
std::vector<uint64_t> bitcoin_issue_evaluator::get_amounts_to_issue( std::vector<info_for_used_vin_id_type> vins_id )
{
database& d = db();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_id>();
std::vector<uint64_t> result;
for( auto& id : vins_id ) {
auto vin_itr = vins_info_idx.find( id );
result.push_back( vin_itr->out.amount );
}
return result;
}
std::vector<account_id_type> bitcoin_issue_evaluator::get_accounts_to_issue( std::vector<info_for_used_vin_id_type> vins_id )
{
database& d = db();
const auto& btc_addr_idx = d.get_index_type<bitcoin_address_index>().indices().get<by_address>();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_id>();
std::vector<account_id_type> result;
for( auto& id : vins_id ) {
auto vin_itr = vins_info_idx.find( id );
auto addr_itr = btc_addr_idx.find( vin_itr->address );
result.push_back( addr_itr->owner );
}
return result;
}
} } // graphene::chain

View file

@ -0,0 +1,176 @@
#include <boost/test/unit_test.hpp>
#include "../common/database_fixture.hpp"
#include <fc/crypto/digest.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
#include <graphene/chain/bitcoin_transaction_object.hpp>
#include <graphene/chain/info_for_used_vin_object.hpp>
#include <graphene/chain/info_for_vout_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
using namespace graphene::chain;
BOOST_FIXTURE_TEST_SUITE( bitcoin_issue_tests, database_fixture )
void create_bitcoin_issue_operation_environment( database& db )
{
std::vector< info_for_used_vin_id_type > vins;
std::vector< info_for_vout_id_type > vouts;
for( auto i = 0; i < 3; i++ ){
db.create<bitcoin_address_object>([&]( bitcoin_address_object& obj ) {
obj.owner = account_id_type( i );
obj.address.address = std::to_string( i );
});
auto vin_id = db.create<info_for_used_vin_object>([&]( info_for_used_vin_object& obj ) {
obj.identifier = fc::sha256( std::to_string( i ) );
obj.out.amount = 100000 + i * 100000;
obj.address = std::to_string( i );
}).get_id();
auto vout_id = db.create<info_for_vout_object>([&]( info_for_vout_object& obj ) {
obj.payer = account_id_type( i );
obj.amount = 100000 + i * 100000;
obj.address = std::to_string( i );
}).get_id();
vins.push_back( vin_id );
vouts.push_back( vout_id );
}
db.create<bitcoin_transaction_object>([&]( bitcoin_transaction_object& obj ) {
obj.pw_vin = db.pw_vout_manager.get_latest_unused_vout()->hash_id;
obj.vins = vins;
obj.vouts = vouts;
obj.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
});
}
BOOST_AUTO_TEST_CASE( check_deleting_all_btc_transaction_information )
{
transaction_evaluation_state context(&db);
const auto& btc_trx_idx = db.get_index_type<bitcoin_transaction_index>().indices().get<by_id>();
const auto& vins_info_idx = db.get_index_type<info_for_used_vin_index>().indices().get<by_id>();
const auto& vouts_info_idx = db.get_index_type<info_for_vout_index>().indices().get<by_id>();
create_bitcoin_issue_operation_environment( db );
db.bitcoin_confirmations.insert( sidechain::bitcoin_transaction_confirmations( fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" ) ) );
FC_ASSERT( btc_trx_idx.size() == 1 );
FC_ASSERT( vins_info_idx.size() == 3 );
FC_ASSERT( vouts_info_idx.size() == 3 );
FC_ASSERT( db.bitcoin_confirmations.size() == 1 );
bitcoin_issue_operation op;
op.payer = db.get_sidechain_account_id();
op.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
db.apply_operation( context, op );
FC_ASSERT( btc_trx_idx.size() == 0 );
FC_ASSERT( vins_info_idx.size() == 0 );
FC_ASSERT( vouts_info_idx.size() == 0 );
FC_ASSERT( db.bitcoin_confirmations.size() == 0 );
}
BOOST_AUTO_TEST_CASE( check_adding_issue_to_accounts )
{
transaction_evaluation_state context(&db);
const auto& btc_trx_idx = db.get_index_type<account_balance_index>().indices().get<by_account_asset>();
create_bitcoin_issue_operation_environment( db );
bitcoin_issue_operation op;
op.payer = db.get_sidechain_account_id();
op.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
db.apply_operation( context, op );
for( auto i = 0; i < 3; i++ ){
auto itr = btc_trx_idx.find( boost::make_tuple( account_id_type( i ), db.get_sidechain_asset_id() ) );
FC_ASSERT( itr != btc_trx_idx.end() );
FC_ASSERT( itr->balance == 100000 + i * 100000 );
}
}
BOOST_AUTO_TEST_CASE( check_bitcoin_issue_operation_throw )
{
transaction_evaluation_state context(&db);
const auto& btc_trx_idx = db.get_index_type<bitcoin_transaction_index>().indices().get<by_id>();
const auto& btc_addr_idx = db.get_index_type<bitcoin_address_index>().indices().get<by_address>();
const auto& vins_info_idx = db.get_index_type<info_for_used_vin_index>().indices().get<by_id>();
const auto& vouts_info_idx = db.get_index_type<info_for_vout_index>().indices().get<by_id>();
create_bitcoin_issue_operation_environment( db );
db.bitcoin_confirmations.insert( sidechain::bitcoin_transaction_confirmations( fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" ) ) );
{
auto session = db._undo_db.start_undo_session();
db.remove( *vouts_info_idx.begin() );
bitcoin_issue_operation op;
op.payer = db.get_sidechain_account_id();
op.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
GRAPHENE_REQUIRE_THROW( db.apply_operation( context, op ) , fc::exception );
session.undo();
}
{
auto session = db._undo_db.start_undo_session();
db.remove( *vins_info_idx.begin() );
bitcoin_issue_operation op;
op.payer = db.get_sidechain_account_id();
op.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
GRAPHENE_REQUIRE_THROW( db.apply_operation( context, op ) , fc::exception );
session.undo();
}
{
auto session = db._undo_db.start_undo_session();
db.remove( *btc_addr_idx.begin() );
bitcoin_issue_operation op;
op.payer = db.get_sidechain_account_id();
op.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
GRAPHENE_REQUIRE_THROW( db.apply_operation( context, op ) , fc::exception );
session.undo();
}
{
auto session = db._undo_db.start_undo_session();
db.remove( *btc_trx_idx.begin() );
bitcoin_issue_operation op;
op.payer = db.get_sidechain_account_id();
op.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
GRAPHENE_REQUIRE_THROW( db.apply_operation( context, op ) , fc::exception );
session.undo();
}
{
auto session = db._undo_db.start_undo_session();
bitcoin_issue_operation op;
op.payer = account_id_type(1);
op.transaction_id = fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" );
GRAPHENE_REQUIRE_THROW( db.apply_operation( context, op ) , fc::exception );
session.undo();
}
}
BOOST_AUTO_TEST_SUITE_END()