Compare commits
48 commits
master
...
feature-si
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b79928a15 | ||
|
|
f616e4c755 | ||
|
|
4a20369013 | ||
|
|
22826fd680 | ||
|
|
b747c7bb3c | ||
|
|
d07d3bb9fc | ||
|
|
329c75668f | ||
|
|
0109bc8c04 | ||
|
|
2142bc4d08 | ||
|
|
113ba26632 | ||
|
|
b26e3bc8a1 | ||
|
|
b27656d2c9 | ||
|
|
a229d0acc4 | ||
|
|
b33c5bf86f | ||
|
|
fa2eff74c2 | ||
|
|
7428c10414 | ||
|
|
774e7b34cb | ||
|
|
a9ca86cab2 | ||
|
|
320cba9f42 | ||
|
|
9a5c9092e1 | ||
|
|
7a78d95241 | ||
|
|
ec3b7e8b80 | ||
|
|
0eda7025c9 | ||
|
|
7c02821b84 | ||
|
|
f6c2e8c92d | ||
|
|
40e13800f2 | ||
|
|
f4afdbe145 | ||
|
|
e1d99702e4 | ||
|
|
ff57653584 | ||
|
|
810051e7c9 | ||
|
|
38ba47bdd5 | ||
|
|
787a0382be | ||
|
|
ffc51ba1ea | ||
|
|
f22d61855b | ||
|
|
df264442da | ||
|
|
6babaf2c6a | ||
|
|
12c5ec3f3f | ||
|
|
0bb271a455 | ||
|
|
7a6fca9180 | ||
|
|
9c7694a5e4 | ||
|
|
fd5a650eeb | ||
|
|
67486275c1 | ||
|
|
0c42d7ea6f | ||
|
|
9137f31960 | ||
|
|
73106a3b7e | ||
|
|
5ebb987b8e | ||
|
|
24c6963ef3 | ||
|
|
d18e36d459 |
97 changed files with 7957 additions and 43 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -25,6 +25,11 @@ tests/chain_bench
|
|||
tests/chain_test
|
||||
tests/intense_test
|
||||
tests/performance_test
|
||||
tests/betting_test
|
||||
tests/tournament_test
|
||||
tests/random_test
|
||||
tests/sidechain_test
|
||||
tests/generate_empty_blocks/generate_empty_blocks
|
||||
|
||||
doxygen
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ IF( WIN32 )
|
|||
set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries
|
||||
ENDIF(WIN32)
|
||||
|
||||
FIND_PACKAGE(Boost 1.57 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
FIND_PACKAGE(Boost 1.60 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||
# For Boost 1.53 on windows, coroutine was not in BOOST_LIBRARYDIR and do not need it to build, but if boost versin >= 1.54, find coroutine otherwise will cause link errors
|
||||
IF(NOT "${Boost_VERSION}" MATCHES "1.53(.*)")
|
||||
SET(BOOST_LIBRARIES_TEMP ${Boost_LIBRARIES})
|
||||
|
|
@ -132,6 +132,9 @@ else( WIN32 ) # Apple AND Linux
|
|||
|
||||
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp" )
|
||||
if ( CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0 )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive" )
|
||||
endif()
|
||||
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
|
||||
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ add_subdirectory( utilities )
|
|||
add_subdirectory( app )
|
||||
add_subdirectory( plugins )
|
||||
add_subdirectory( wallet )
|
||||
add_subdirectory( sidechain )
|
||||
|
|
|
|||
|
|
@ -538,7 +538,9 @@ namespace detail {
|
|||
// you can help the network code out by throwing a block_older_than_undo_history exception.
|
||||
// when the net code sees that, it will stop trying to push blocks from that chain, but
|
||||
// leave that peer connected so that they can get sync blocks from us
|
||||
bool result = _chain_db->push_block(blk_msg.block, (_is_block_producer | _force_validate) ? database::skip_nothing : database::skip_transaction_signatures);
|
||||
auto skip_nothing_mode = sync_mode ? database::skip_nothing | database::skip_btc_tx_sending : database::skip_nothing;
|
||||
auto skip_transaction_signatures_mode = sync_mode ? database::skip_transaction_signatures | database::skip_btc_tx_sending : database::skip_transaction_signatures;
|
||||
bool result = _chain_db->push_block(blk_msg.block, (_is_block_producer | _force_validate) ? skip_nothing_mode : skip_transaction_signatures_mode);
|
||||
|
||||
// the block was accepted, so we now know all of the transactions contained in the block
|
||||
if (!sync_mode)
|
||||
|
|
@ -963,6 +965,11 @@ void application::set_program_options(boost::program_options::options_descriptio
|
|||
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")
|
||||
("dbg-init-key", bpo::value<string>(), "Block signing key to use for init witnesses, overrides genesis file")
|
||||
("api-access", bpo::value<boost::filesystem::path>(), "JSON file specifying API permissions")
|
||||
("bitcoin-node-ip", bpo::value<string>()->implicit_value("127.0.0.1"), "IP address of Bitcoin node")
|
||||
("bitcoin-node-zmq-port", bpo::value<uint32_t>()->implicit_value(28332), "ZMQ port of Bitcoin node")
|
||||
("bitcoin-node-rpc-port", bpo::value<uint32_t>()->implicit_value(18332), "RPC port of Bitcoin node")
|
||||
("bitcoin-node-rpc-user", bpo::value<string>(), "Bitcoin RPC user")
|
||||
("bitcoin-node-rpc-password", bpo::value<string>(), "Bitcoin RPC password")
|
||||
;
|
||||
command_line_options.add(configuration_file_options);
|
||||
command_line_options.add_options()
|
||||
|
|
|
|||
|
|
@ -161,6 +161,9 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state);
|
||||
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
|
||||
|
||||
//Sidechain
|
||||
vector<bitcoin_address_object> get_bitcoin_addresses(const account_id_type& acc_id) const;
|
||||
|
||||
|
||||
//private:
|
||||
template<typename T>
|
||||
|
|
@ -172,7 +175,6 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
|
||||
if( !is_subscribed_to_item(i) )
|
||||
{
|
||||
idump((i));
|
||||
_subscribe_filter.insert( vec.data(), vec.size() );//(vecconst char*)&i, sizeof(i) );
|
||||
}
|
||||
}
|
||||
|
|
@ -2021,6 +2023,32 @@ vector<tournament_id_type> database_api_impl::get_registered_tournaments(account
|
|||
return tournament_ids;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Sidechain //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<bitcoin_address_object> database_api::get_bitcoin_addresses(const account_id_type& acc) const
|
||||
{
|
||||
return my->get_bitcoin_addresses(acc);
|
||||
}
|
||||
|
||||
vector<bitcoin_address_object> database_api_impl::get_bitcoin_addresses(const account_id_type& acc) const
|
||||
{
|
||||
vector<bitcoin_address_object> result;
|
||||
|
||||
const auto& btc_addr_idx = _db.get_index_type<bitcoin_address_index>().indices().get<by_owner>();
|
||||
|
||||
auto itr = btc_addr_idx.lower_bound( acc );
|
||||
while( itr != btc_addr_idx.end() && itr->owner == acc )
|
||||
{
|
||||
result.push_back( *itr );
|
||||
++itr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Private methods //
|
||||
|
|
|
|||
|
|
@ -282,6 +282,30 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
void operator()( const withdraw_pbtc_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_address_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_transaction_send_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_transaction_sign_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_issue_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_transaction_revert_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
|
||||
#include <graphene/market_history/market_history_plugin.hpp>
|
||||
|
||||
|
|
@ -644,6 +645,11 @@ class database_api
|
|||
* @return the list of tournaments that a given account is registered to play in
|
||||
*/
|
||||
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
|
||||
|
||||
/**
|
||||
* @return the list of bitcoin addresses which belong to acc_id
|
||||
*/
|
||||
vector<bitcoin_address_object> get_bitcoin_addresses(const account_id_type& acc_id) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr< database_api_impl > my;
|
||||
|
|
@ -765,4 +771,5 @@ FC_API(graphene::app::database_api,
|
|||
(get_tournaments_by_state)
|
||||
(get_tournaments )
|
||||
(get_registered_tournaments)
|
||||
(get_bitcoin_addresses)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ if( GRAPHENE_DISABLE_UNITY_BUILD )
|
|||
db_market.cpp
|
||||
db_update.cpp
|
||||
db_witness_schedule.cpp
|
||||
db_sidechain.cpp
|
||||
)
|
||||
message( STATUS "Graphene database unity build disabled" )
|
||||
else( GRAPHENE_DISABLE_UNITY_BUILD )
|
||||
|
|
@ -85,6 +86,9 @@ add_library( graphene_chain
|
|||
confidential_evaluator.cpp
|
||||
special_authority.cpp
|
||||
buyback.cpp
|
||||
bitcoin_address_evaluator.cpp
|
||||
bitcoin_transaction_evaluator.cpp
|
||||
sidechain_evaluator.cpp
|
||||
|
||||
account_object.cpp
|
||||
asset_object.cpp
|
||||
|
|
@ -110,6 +114,7 @@ add_library( graphene_chain
|
|||
betting_market_group_object.cpp
|
||||
|
||||
affiliate_payout.cpp
|
||||
withdraw_pbtc_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
|
|
@ -117,7 +122,8 @@ add_library( graphene_chain
|
|||
)
|
||||
|
||||
add_dependencies( graphene_chain build_hardfork_hpp )
|
||||
target_link_libraries( graphene_chain fc graphene_db )
|
||||
|
||||
target_link_libraries( graphene_chain fc graphene_db sidechain )
|
||||
target_include_directories( graphene_chain
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" )
|
||||
|
||||
|
|
|
|||
46
libraries/chain/bitcoin_address_evaluator.cpp
Normal file
46
libraries/chain/bitcoin_address_evaluator.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include <graphene/chain/bitcoin_address_evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result bitcoin_address_create_evaluator::do_evaluate( const bitcoin_address_create_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
FC_ASSERT( !d.is_sidechain_fork_needed() );
|
||||
auto& acc_idx = d.get_index_type<account_index>().indices().get<by_id>();
|
||||
auto acc_itr = acc_idx.find( op.owner );
|
||||
FC_ASSERT( acc_itr != acc_idx.end() );
|
||||
return void_result();
|
||||
}
|
||||
|
||||
object_id_type bitcoin_address_create_evaluator::do_apply( const bitcoin_address_create_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
const auto pw_obj = d.get_latest_PW();
|
||||
auto witnesses_keys = pw_obj.address.witnesses_keys;
|
||||
|
||||
const auto& new_btc_address = d.create<bitcoin_address_object>( [&]( bitcoin_address_object& a ) {
|
||||
witnesses_keys.erase( --witnesses_keys.end() );
|
||||
witnesses_keys.emplace( d.get_sidechain_account_id(), pubkey_from_id( a.id ) );
|
||||
|
||||
a.owner = op.owner;
|
||||
a.address = sidechain::btc_multisig_segwit_address( SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG, witnesses_keys );
|
||||
a.count_invalid_pub_key = 1;
|
||||
});
|
||||
|
||||
return new_btc_address.id;
|
||||
}
|
||||
|
||||
public_key_type bitcoin_address_create_evaluator::pubkey_from_id( object_id_type id )
|
||||
{
|
||||
fc::ecc::public_key_data pubkey_data;
|
||||
|
||||
pubkey_data.at( 0 ) = 0x02; // version
|
||||
const auto hash = fc::sha256::hash( std::to_string( id.instance() ) ) ;
|
||||
std::copy( hash.data(), hash.data() + hash.data_size(), pubkey_data.begin() + 1 );
|
||||
|
||||
return public_key_type( pubkey_data );
|
||||
}
|
||||
} } // namespace graphene::chain
|
||||
274
libraries/chain/bitcoin_transaction_evaluator.cpp
Normal file
274
libraries/chain/bitcoin_transaction_evaluator.cpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#include <graphene/chain/bitcoin_transaction_evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/bitcoin_transaction_object.hpp>
|
||||
#include <graphene/chain/info_for_used_vin_object.hpp>
|
||||
#include <graphene/chain/sidechain_proposal_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
|
||||
#include <sidechain/sign_bitcoin_transaction.hpp>
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <sidechain/sidechain_proposal_checker.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
using namespace sidechain;
|
||||
|
||||
void_result bitcoin_transaction_send_evaluator::do_evaluate( const bitcoin_transaction_send_operation& op )
|
||||
{
|
||||
// FC_ASSERT( db().get_sidechain_account_id() == op.payer );
|
||||
return void_result();
|
||||
}
|
||||
|
||||
object_id_type bitcoin_transaction_send_evaluator::do_apply( const bitcoin_transaction_send_operation& op )
|
||||
{
|
||||
bitcoin_transaction_send_operation& mutable_op = const_cast<bitcoin_transaction_send_operation&>( op );
|
||||
database& d = db();
|
||||
|
||||
finalize_bitcoin_transaction( mutable_op );
|
||||
|
||||
auto new_vins = create_transaction_vins( mutable_op );
|
||||
|
||||
const bitcoin_transaction_object& btc_tx = d.create< bitcoin_transaction_object >( [&]( bitcoin_transaction_object& obj )
|
||||
{
|
||||
obj.pw_vin = mutable_op.pw_vin.identifier;
|
||||
obj.vins = new_vins;
|
||||
obj.vouts = mutable_op.vouts;
|
||||
obj.transaction = mutable_op.transaction;
|
||||
obj.transaction_id = mutable_op.transaction.get_txid();
|
||||
obj.fee_for_size = mutable_op.fee_for_size;
|
||||
});
|
||||
|
||||
sidechain::prev_out new_pw_vout = { "", 0, 0 };
|
||||
sidechain::bytes wit_script = d.get_latest_PW().address.get_witness_script();
|
||||
if( btc_tx.transaction.vout[0].is_p2wsh() && btc_tx.transaction.vout[0].scriptPubKey == wit_script )
|
||||
new_pw_vout = { btc_tx.transaction.get_txid(), 0, btc_tx.transaction.vout[0].value };
|
||||
|
||||
d.pw_vout_manager.create_new_vout( new_pw_vout );
|
||||
send_bitcoin_transaction( btc_tx );
|
||||
|
||||
return btc_tx.id;
|
||||
}
|
||||
|
||||
std::vector<fc::sha256> bitcoin_transaction_send_evaluator::create_transaction_vins( bitcoin_transaction_send_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
std::vector<fc::sha256> new_vins;
|
||||
|
||||
for( auto itr : op.vins ) {
|
||||
auto info_for_used_vin_itr = d.create< info_for_used_vin_object >( [&]( info_for_used_vin_object& obj )
|
||||
{
|
||||
obj.identifier = itr.identifier;
|
||||
obj.out = itr.out;
|
||||
obj.address = itr.address;
|
||||
obj.script = itr.script;
|
||||
});
|
||||
new_vins.push_back( info_for_used_vin_itr.identifier );
|
||||
|
||||
auto obj_itr = d.i_w_info.find_info_for_vin( itr.identifier );
|
||||
if( obj_itr.valid() ) {
|
||||
d.i_w_info.remove_info_for_vin( *obj_itr );
|
||||
}
|
||||
}
|
||||
|
||||
return new_vins;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_send_evaluator::finalize_bitcoin_transaction( bitcoin_transaction_send_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
|
||||
auto vins = op.vins;
|
||||
if( op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
|
||||
vins.insert( vins.begin(), op.pw_vin );
|
||||
}
|
||||
|
||||
std::vector<bytes> redeem_scripts( d.i_w_info.get_redeem_scripts( vins ) );
|
||||
std::vector<uint64_t> amounts( d.i_w_info.get_amounts( vins ) );
|
||||
|
||||
std::vector<std::vector<sidechain::bytes>> new_stacks( sidechain::sort_sigs( op.transaction, redeem_scripts, amounts, d.context_verify ) );
|
||||
|
||||
for( size_t i = 0; i < new_stacks.size(); i++ ) {
|
||||
op.transaction.vin[i].scriptWitness = new_stacks[i];
|
||||
}
|
||||
|
||||
sidechain::sign_witness_transaction_finalize( op.transaction, redeem_scripts );
|
||||
}
|
||||
|
||||
void bitcoin_transaction_send_evaluator::send_bitcoin_transaction( const bitcoin_transaction_object& btc_tx )
|
||||
{
|
||||
database& d = db();
|
||||
uint32_t skip = d.get_node_properties().skip_flags;
|
||||
if( !(skip & graphene::chain::database::skip_btc_tx_sending) && d.send_btc_tx_flag ){
|
||||
idump((btc_tx));
|
||||
d.send_btc_tx( btc_tx.transaction );
|
||||
}
|
||||
}
|
||||
|
||||
void_result bitcoin_transaction_sign_evaluator::do_evaluate( const bitcoin_transaction_sign_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
|
||||
const auto& proposal_idx = d.get_index_type<proposal_index>().indices().get<by_id>();
|
||||
const auto& proposal_itr = proposal_idx.find( op.proposal_id );
|
||||
FC_ASSERT( proposal_idx.end() != proposal_itr, "proposal not found");
|
||||
|
||||
const auto& witness_obj = d.get< witness_object >( d._current_witness_id );
|
||||
FC_ASSERT( witness_obj.witness_account == op.payer, "Incorrect witness." );
|
||||
|
||||
sidechain::bytes public_key( public_key_data_to_bytes( witness_obj.signing_key.key_data ) );
|
||||
auto btc_send_op = proposal_itr->proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();
|
||||
|
||||
auto vins = btc_send_op.vins;
|
||||
if( btc_send_op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
|
||||
vins.insert( vins.begin(), btc_send_op.pw_vin );
|
||||
}
|
||||
|
||||
FC_ASSERT( check_sigs( public_key, op.signatures, vins, btc_send_op.transaction ) );
|
||||
|
||||
sidechain::sidechain_proposal_checker checker( d );
|
||||
FC_ASSERT( checker.check_witness_opportunity_to_approve( witness_obj, *proposal_itr ), "Can't sign this transaction" );
|
||||
|
||||
return void_result();
|
||||
}
|
||||
|
||||
void_result bitcoin_transaction_sign_evaluator::do_apply( const bitcoin_transaction_sign_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
const auto& proposal = op.proposal_id( d );
|
||||
|
||||
d.modify( proposal, [&]( proposal_object& po ) {
|
||||
auto bitcoin_transaction_send_op = po.proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();
|
||||
for( size_t i = 0; i < op.signatures.size(); i++ ) {
|
||||
bitcoin_transaction_send_op.transaction.vin[i].scriptWitness.push_back( op.signatures[i] );
|
||||
}
|
||||
po.proposed_transaction.operations[0] = bitcoin_transaction_send_op;
|
||||
});
|
||||
|
||||
update_proposal( op );
|
||||
|
||||
return void_result();
|
||||
}
|
||||
|
||||
void bitcoin_transaction_sign_evaluator::update_proposal( const bitcoin_transaction_sign_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
proposal_update_operation update_op;
|
||||
|
||||
update_op.fee_paying_account = op.payer;
|
||||
update_op.proposal = op.proposal_id;
|
||||
update_op.active_approvals_to_add = { op.payer };
|
||||
|
||||
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;
|
||||
|
||||
d.apply_operation( *trx_state, update_op );
|
||||
|
||||
trx_state->skip_fee = skip_fee_old;
|
||||
trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old;
|
||||
}
|
||||
|
||||
bool bitcoin_transaction_sign_evaluator::check_sigs( const bytes& key_data, const std::vector<bytes>& sigs,
|
||||
const std::vector<sidechain::info_for_vin>& info_for_vins,
|
||||
const bitcoin_transaction& tx )
|
||||
{
|
||||
FC_ASSERT( sigs.size() == info_for_vins.size() && sigs.size() == tx.vin.size() );
|
||||
const auto& bitcoin_address_idx = db().get_index_type<bitcoin_address_index>().indices().get< by_address >();
|
||||
|
||||
for( size_t i = 0; i < tx.vin.size(); i++ ) {
|
||||
const auto pbtc_address = bitcoin_address_idx.find( info_for_vins[i].address );
|
||||
const bytes& script = pbtc_address->address.get_redeem_script();
|
||||
|
||||
const auto& sighash_str = get_signature_hash( tx, script, static_cast<int64_t>( info_for_vins[i].out.amount ), i, 1, true ).str();
|
||||
const bytes& sighash_hex = parse_hex( sighash_str );
|
||||
|
||||
if( !verify_sig( sigs[i], key_data, sighash_hex, db().context_verify ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t count_sigs = 0;
|
||||
for( auto& s : tx.vin[i].scriptWitness ) {
|
||||
if( verify_sig( s, key_data, sighash_hex, db().context_verify ) ) {
|
||||
count_sigs++;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bytes> pubkeys = get_pubkey_from_redeemScript( script );
|
||||
size_t count_pubkeys = std::count( pubkeys.begin(), pubkeys.end(), key_data );
|
||||
if( count_sigs >= count_pubkeys ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t position = std::find( op_num.begin(), op_num.end(), script[0] ) - op_num.begin();
|
||||
if( !( position >= 0 && position < op_num.size() ) || tx.vin[i].scriptWitness.size() == position + 1 ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void_result bitcoin_transaction_revert_evaluator::do_evaluate( const bitcoin_transaction_revert_operation& op )
|
||||
{ try {
|
||||
|
||||
database& d = db();
|
||||
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
|
||||
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
|
||||
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
|
||||
|
||||
for( auto trx_info: op.transactions_info ) {
|
||||
const auto& btc_itr = btc_trx_idx.find( trx_info.transaction_id );
|
||||
FC_ASSERT( btc_itr != btc_trx_idx.end() );
|
||||
|
||||
for( const auto& vout_id : btc_itr->vouts ) {
|
||||
FC_ASSERT( vouts_info_idx.find( vout_id ) != vouts_info_idx.end() );
|
||||
}
|
||||
|
||||
for( const auto& vout_id : btc_itr->vins ) {
|
||||
FC_ASSERT( vins_info_idx.find( vout_id ) != vins_info_idx.end() );
|
||||
}
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result bitcoin_transaction_revert_evaluator::do_apply( const bitcoin_transaction_revert_operation& op )
|
||||
{ try {
|
||||
|
||||
database& d = db();
|
||||
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
|
||||
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
|
||||
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
|
||||
|
||||
for( auto trx_info: op.transactions_info ) {
|
||||
const auto& btc_trx_obj = *btc_trx_idx.find( trx_info.transaction_id );
|
||||
d.pw_vout_manager.delete_vouts_after( btc_trx_obj.pw_vin );
|
||||
d.pw_vout_manager.mark_as_unused_vout( btc_trx_obj.pw_vin );
|
||||
|
||||
for( const auto& vout_id : btc_trx_obj.vouts ) {
|
||||
const auto& vout_obj = *vouts_info_idx.find( vout_id );
|
||||
d.modify( vout_obj, [&]( info_for_vout_object& obj ){
|
||||
obj.used = false;
|
||||
});
|
||||
}
|
||||
|
||||
for( const auto& vin_id : btc_trx_obj.vins ) {
|
||||
const auto& vin_obj = *vins_info_idx.find( vin_id );
|
||||
|
||||
if( trx_info.valid_vins.count( fc::sha256 ( vin_obj.out.hash_tx ) ) ) {
|
||||
d.i_w_info.insert_info_for_vin( vin_obj.out, vin_obj.address, vin_obj.script, true );
|
||||
}
|
||||
d.remove( vin_obj );
|
||||
}
|
||||
d.bitcoin_confirmations.remove<sidechain::by_hash>( trx_info.transaction_id );
|
||||
d.remove( btc_trx_obj );
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} }
|
||||
|
|
@ -33,4 +33,5 @@
|
|||
#include "db_market.cpp"
|
||||
#include "db_update.cpp"
|
||||
#include "db_witness_schedule.cpp"
|
||||
#include "db_notify.cpp"
|
||||
#include "db_notify.cpp"
|
||||
#include "db_sidechain.cpp"
|
||||
|
|
|
|||
|
|
@ -71,9 +71,13 @@ void database::adjust_balance(account_id_type account, asset delta )
|
|||
} else {
|
||||
if( delta.amount < 0 )
|
||||
FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta)));
|
||||
modify(*itr, [delta](account_balance_object& b) {
|
||||
b.adjust_balance(delta);
|
||||
});
|
||||
if( delta.amount < 0 && itr->get_balance() == -delta ) {
|
||||
remove(*itr);
|
||||
} else {
|
||||
modify(*itr, [delta](account_balance_object& b) {
|
||||
b.adjust_balance(delta);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} FC_CAPTURE_AND_RETHROW( (account)(delta) ) }
|
||||
|
|
|
|||
|
|
@ -176,6 +176,9 @@ void database::check_tansaction_for_duplicated_operations(const signed_transacti
|
|||
bool database::push_block(const signed_block& new_block, uint32_t skip)
|
||||
{
|
||||
// idump((new_block.block_num())(new_block.id())(new_block.timestamp)(new_block.previous));
|
||||
|
||||
send_btc_tx_flag = true;
|
||||
|
||||
bool result;
|
||||
detail::with_skip_flags( *this, skip, [&]()
|
||||
{
|
||||
|
|
@ -333,6 +336,7 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
|
|||
auto session = _undo_db.start_undo_session(true);
|
||||
for( auto& op : proposal.proposed_transaction.operations )
|
||||
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
|
||||
remove_sidechain_proposal_object(proposal);
|
||||
remove(proposal);
|
||||
session.merge();
|
||||
} catch ( const fc::exception& e ) {
|
||||
|
|
@ -393,6 +397,10 @@ signed_block database::_generate_block(
|
|||
auto maximum_block_size = get_global_properties().parameters.maximum_block_size;
|
||||
size_t total_block_size = max_block_header_size;
|
||||
|
||||
if( !is_sidechain_fork_needed() ) {
|
||||
processing_sidechain_proposals( witness_obj, block_signing_private_key );
|
||||
}
|
||||
|
||||
signed_block pending_block;
|
||||
|
||||
//
|
||||
|
|
@ -409,6 +417,27 @@ signed_block database::_generate_block(
|
|||
_pending_tx_session.reset();
|
||||
_pending_tx_session = _undo_db.start_undo_session();
|
||||
|
||||
if( !is_sidechain_fork_needed() ) {
|
||||
auto op = create_send_btc_tx_proposal( witness_obj );
|
||||
if( op.valid() ) {
|
||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *op ) );
|
||||
}
|
||||
|
||||
auto iss_op = create_bitcoin_issue_proposals( witness_obj );
|
||||
if( iss_op.valid() ) {
|
||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *iss_op ) );
|
||||
}
|
||||
|
||||
auto revert_op = create_bitcoin_revert_proposals( witness_obj );
|
||||
if( revert_op.valid() ) {
|
||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *revert_op ) );
|
||||
}
|
||||
}
|
||||
|
||||
send_btc_tx_flag = false;
|
||||
|
||||
_current_witness_id = witness_obj.id;
|
||||
|
||||
uint64_t postponed_tx_count = 0;
|
||||
// pop pending state (reset to head block state)
|
||||
for( const processed_transaction& tx : _pending_tx )
|
||||
|
|
@ -560,6 +589,8 @@ void database::apply_block( const signed_block& next_block, uint32_t skip )
|
|||
skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
|
||||
}
|
||||
|
||||
_current_witness_id = next_block.witness;
|
||||
|
||||
detail::with_skip_flags( *this, skip, [&]()
|
||||
{
|
||||
_apply_block( next_block );
|
||||
|
|
@ -573,6 +604,11 @@ void database::_apply_block( const signed_block& next_block )
|
|||
uint32_t skip = get_node_properties().skip_flags;
|
||||
_applied_ops.clear();
|
||||
|
||||
if( head_block_time() > HARDFORK_SIDECHAIN_TIME && is_sidechain_fork_needed() )
|
||||
{
|
||||
perform_sidechain_fork();
|
||||
}
|
||||
|
||||
FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) );
|
||||
|
||||
const witness_object& signing_witness = validate_block_header(skip, next_block);
|
||||
|
|
|
|||
|
|
@ -94,8 +94,7 @@ node_property_object& database::node_properties()
|
|||
|
||||
uint32_t database::last_non_undoable_block_num() const
|
||||
{
|
||||
return head_block_num() - _undo_db.size();
|
||||
return head_block_num() < _undo_db.size() ? 0 : head_block_num() - _undo_db.size();
|
||||
}
|
||||
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@
|
|||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/info_for_vout_object.hpp>
|
||||
#include <graphene/chain/info_for_used_vin_object.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
#include <graphene/chain/sidechain_proposal_object.hpp>
|
||||
#include <graphene/chain/bitcoin_transaction_object.hpp>
|
||||
|
||||
|
||||
#include <graphene/chain/sport_object.hpp>
|
||||
|
|
@ -76,6 +82,10 @@
|
|||
#include <graphene/chain/event_evaluator.hpp>
|
||||
#include <graphene/chain/betting_market_evaluator.hpp>
|
||||
#include <graphene/chain/tournament_evaluator.hpp>
|
||||
#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>
|
||||
|
||||
|
|
@ -85,6 +95,7 @@
|
|||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
// C++ requires that static class variables declared and initialized
|
||||
|
|
@ -237,6 +248,12 @@ void database::initialize_evaluators()
|
|||
register_evaluator<tournament_join_evaluator>();
|
||||
register_evaluator<game_move_evaluator>();
|
||||
register_evaluator<tournament_leave_evaluator>();
|
||||
register_evaluator<withdraw_pbtc_evaluator>();
|
||||
register_evaluator<bitcoin_address_create_evaluator>();
|
||||
register_evaluator<bitcoin_transaction_send_evaluator>();
|
||||
register_evaluator<bitcoin_transaction_sign_evaluator>();
|
||||
register_evaluator<bitcoin_issue_evaluator>();
|
||||
register_evaluator<bitcoin_transaction_revert_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -301,6 +318,13 @@ void database::initialize_indexes()
|
|||
//add_index< primary_index<distributed_dividend_balance_object_index > >();
|
||||
add_index< primary_index<pending_dividend_payout_balance_for_holder_object_index > >();
|
||||
add_index< primary_index<total_distributed_dividend_balance_object_index > >();
|
||||
|
||||
add_index< primary_index<info_for_vout_index > >();
|
||||
add_index< primary_index<info_for_used_vin_index > >();
|
||||
add_index< primary_index<bitcoin_address_index > >();
|
||||
add_index< primary_index<primary_wallet_vout_index > >();
|
||||
add_index< primary_index<sidechain_proposal_index > >();
|
||||
add_index< primary_index<bitcoin_transaction_index > >();
|
||||
}
|
||||
|
||||
void database::init_genesis(const genesis_state_type& genesis_state)
|
||||
|
|
|
|||
|
|
@ -36,15 +36,22 @@
|
|||
namespace graphene { namespace chain {
|
||||
|
||||
database::database() :
|
||||
_random_number_generator(fc::ripemd160().data())
|
||||
i_w_info(*this), pw_vout_manager(*this), _random_number_generator(fc::ripemd160().data())
|
||||
{
|
||||
initialize_indexes();
|
||||
initialize_evaluators();
|
||||
context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||||
estimated_feerate = 0;
|
||||
}
|
||||
|
||||
database::~database()
|
||||
{
|
||||
clear_pending();
|
||||
secp256k1_context_destroy(context_sign);
|
||||
context_sign = nullptr;
|
||||
secp256k1_context_destroy(context_verify);
|
||||
context_verify = nullptr;
|
||||
}
|
||||
|
||||
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
|
||||
|
|
@ -101,18 +108,21 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
|
|||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
skip_authority_check |
|
||||
skip_btc_tx_sending);
|
||||
else
|
||||
apply_block(*block, skip_witness_signature |
|
||||
skip_transaction_signatures |
|
||||
skip_transaction_dupe_check |
|
||||
skip_tapos_check |
|
||||
skip_witness_schedule_check |
|
||||
skip_authority_check);
|
||||
skip_authority_check |
|
||||
skip_btc_tx_sending);
|
||||
}
|
||||
if (!_slow_replays)
|
||||
_undo_db.enable();
|
||||
auto end = fc::time_point::now();
|
||||
restore_bitcoin_transaction_status();
|
||||
ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) );
|
||||
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -269,6 +269,30 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.affiliate );
|
||||
}
|
||||
void operator()( const affiliate_referral_payout_operation& op ) { }
|
||||
void operator()( const withdraw_pbtc_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_address_create_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_transaction_send_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_transaction_sign_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_issue_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const bitcoin_transaction_revert_operation& op )
|
||||
{
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
432
libraries/chain/db_sidechain.cpp
Normal file
432
libraries/chain/db_sidechain.cpp
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_proposal_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/bitcoin_transaction_object.hpp>
|
||||
|
||||
#include <sidechain/sidechain_condensing_tx.hpp>
|
||||
#include <sidechain/sign_bitcoin_transaction.hpp>
|
||||
#include <sidechain/sidechain_proposal_checker.hpp>
|
||||
|
||||
using namespace sidechain;
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
std::map< account_id_type, public_key_type> database::get_active_witnesses_keys() const
|
||||
{
|
||||
const auto& witnesses_by_id = get_index_type<witness_index>().indices().get<by_id>();
|
||||
std::map< account_id_type, public_key_type > witnesses_keys;
|
||||
auto& active_witnesses = get_global_properties().active_witnesses;
|
||||
for( auto witness_id : active_witnesses ) {
|
||||
const auto& witness_obj = witnesses_by_id.find( witness_id );
|
||||
if( witness_obj != witnesses_by_id.end() ){
|
||||
witnesses_keys.emplace( witness_obj->witness_account, witness_obj->signing_key );
|
||||
}
|
||||
}
|
||||
return witnesses_keys;
|
||||
}
|
||||
|
||||
bool database::is_sidechain_fork_needed() const
|
||||
{
|
||||
const auto& params = get_global_properties().parameters.extensions.value.sidechain_parameters;
|
||||
return !params;
|
||||
}
|
||||
|
||||
void database::perform_sidechain_fork()
|
||||
{
|
||||
const auto& sidechain_account = create<account_object>( [&]( account_object& obj ) {
|
||||
obj.name = "sidechain_account";
|
||||
obj.statistics = create<account_statistics_object>([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id;
|
||||
obj.owner.weight_threshold = 5;
|
||||
obj.active.weight_threshold = 5;
|
||||
obj.membership_expiration_date = time_point_sec::maximum();
|
||||
obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
});
|
||||
|
||||
const asset_object& new_asset = create<asset_object>( [&]( asset_object& obj ) {
|
||||
obj.symbol = SIDECHAIN_SYMBOL;
|
||||
obj.precision = SIDECHAIN_PRECISION_DIGITS;
|
||||
obj.issuer = sidechain_account.get_id();
|
||||
obj.options.max_supply = SIDECHAIN_MAX_SHARE_SUPPLY;
|
||||
obj.options.issuer_permissions = 0;
|
||||
obj.options.flags = 0;
|
||||
obj.dynamic_asset_data_id = create<asset_dynamic_data_object>([&]( asset_dynamic_data_object& a ) { a.current_supply = 0; }).id;
|
||||
});
|
||||
|
||||
modify( get_global_properties(), [&]( global_property_object& gpo ) {
|
||||
sidechain_parameters_extension params_ext;
|
||||
params_ext.managing_account = sidechain_account.get_id();
|
||||
params_ext.asset_id = new_asset.get_id();
|
||||
|
||||
gpo.parameters.extensions.value.sidechain_parameters = params_ext;
|
||||
if( gpo.pending_parameters )
|
||||
gpo.pending_parameters->extensions.value.sidechain_parameters = params_ext;
|
||||
});
|
||||
|
||||
auto global_properties = get_global_properties();
|
||||
const auto& witnesses_idx = get_index_type<witness_index>().indices().get<by_id>();
|
||||
std::vector<account_id_type> witness_accounts;
|
||||
|
||||
for( auto witness_id : global_properties.active_witnesses ) {
|
||||
const auto& witness_obj = witnesses_idx.find( witness_id );
|
||||
if( witness_obj != witnesses_idx.end() )
|
||||
witness_accounts.push_back( witness_obj->witness_account );
|
||||
}
|
||||
|
||||
modify( sidechain_account, [&]( account_object& obj ) {
|
||||
for( auto& a : witness_accounts ) {
|
||||
obj.owner.add_authority( a, 1 );
|
||||
obj.active.add_authority( a, 1 );
|
||||
}
|
||||
});
|
||||
|
||||
create<bitcoin_address_object>( [&]( bitcoin_address_object& pw ) { // Create PW address
|
||||
pw.address = btc_multisig_segwit_address( 5, get_active_witnesses_keys() );
|
||||
pw.owner = sidechain_account.get_id();
|
||||
pw.count_invalid_pub_key = 1;
|
||||
});
|
||||
|
||||
pw_vout_manager.create_new_vout( {"", 0, 0 } );
|
||||
}
|
||||
|
||||
const sidechain_parameters_extension& database::get_sidechain_params() const
|
||||
{
|
||||
const auto& params = get_global_properties().parameters.extensions.value.sidechain_parameters;
|
||||
FC_ASSERT( params.valid() );
|
||||
return *params;
|
||||
}
|
||||
|
||||
const account_id_type& database::get_sidechain_account_id() const
|
||||
{
|
||||
return get_sidechain_params().managing_account;
|
||||
}
|
||||
|
||||
const asset_id_type& database::get_sidechain_asset_id() const
|
||||
{
|
||||
return get_sidechain_params().asset_id;
|
||||
}
|
||||
|
||||
bitcoin_address_object database::get_latest_PW() const
|
||||
{
|
||||
const auto& btc_addr_idx = get_index_type<bitcoin_address_index>().indices().get<by_owner>();
|
||||
auto itr = btc_addr_idx.upper_bound( get_sidechain_account_id() );
|
||||
return *(--itr);
|
||||
}
|
||||
|
||||
int64_t database::get_estimated_fee( size_t tx_vsize, uint64_t estimated_feerate ) {
|
||||
static const uint64_t default_feerate = 1000;
|
||||
static const uint64_t min_relay_fee = 1000;
|
||||
|
||||
const auto feerate = std::max( default_feerate, estimated_feerate );
|
||||
const auto fee = feerate * int64_t( tx_vsize ) / 1000;
|
||||
|
||||
return std::max( min_relay_fee, fee );
|
||||
}
|
||||
|
||||
void database::processing_sidechain_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key )
|
||||
{
|
||||
const auto& sidechain_proposal_idx = get_index_type<sidechain_proposal_index>().indices().get< by_id >();
|
||||
const auto& proposal_idx = get_index_type<proposal_index>().indices().get< by_id >();
|
||||
|
||||
sidechain_proposal_checker checker( *this );
|
||||
|
||||
auto approve_propose = [ & ]( const proposal_id_type& id )
|
||||
{
|
||||
proposal_update_operation puo;
|
||||
puo.fee_paying_account = current_witness.witness_account;
|
||||
puo.proposal = id;
|
||||
puo.active_approvals_to_add = { current_witness.witness_account };
|
||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( private_key, puo ) );
|
||||
};
|
||||
|
||||
for( auto& sidechain_proposal : sidechain_proposal_idx ) {
|
||||
|
||||
const auto& proposal = proposal_idx.find( sidechain_proposal.proposal_id );
|
||||
FC_ASSERT( proposal != proposal_idx.end() );
|
||||
|
||||
if( !checker.check_reuse( proposal->proposed_transaction.operations.back() ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch( sidechain_proposal.proposal_type ) {
|
||||
case sidechain_proposal_type::ISSUE_BTC :{
|
||||
approve_propose( proposal->id );
|
||||
break;
|
||||
}
|
||||
case sidechain_proposal_type::SEND_BTC_TRANSACTION :{
|
||||
bitcoin_transaction_send_operation op = proposal->proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
|
||||
if( checker.check_bitcoin_transaction_send_operation( op ) &&
|
||||
checker.check_witnesses_keys( current_witness, *proposal ) &&
|
||||
checker.check_witness_opportunity_to_approve( current_witness, *proposal ) ) {
|
||||
const auto& sign_operation = create_sign_btc_tx_operation( current_witness, private_key, proposal->id );
|
||||
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( private_key, sign_operation ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case sidechain_proposal_type::REVERT_BTC_TRANSACTION :{
|
||||
bitcoin_transaction_revert_operation op = proposal->proposed_transaction.operations.back().get<bitcoin_transaction_revert_operation>();
|
||||
if( checker.check_bitcoin_transaction_revert_operation( op ) )
|
||||
approve_propose( proposal->id );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
full_btc_transaction database::create_btc_transaction( const std::vector<info_for_vin>& info_vins,
|
||||
const std::vector<info_for_vout>& info_vouts,
|
||||
const info_for_vin& info_pw_vin )
|
||||
{
|
||||
sidechain_condensing_tx ctx( info_vins, info_vouts );
|
||||
|
||||
|
||||
if( info_pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
|
||||
ctx.create_pw_vin( info_pw_vin );
|
||||
}
|
||||
|
||||
const auto& pw_address = get_latest_PW().address;
|
||||
if( info_vouts.size() > 0 ) {
|
||||
ctx.create_vouts_for_witness_fee( pw_address.witnesses_keys );
|
||||
}
|
||||
|
||||
const uint64_t& change = ctx.get_amount_vins() - ctx.get_amount_transfer_to_bitcoin();
|
||||
if( change > 0 ) {
|
||||
ctx.create_pw_vout( change, pw_address.get_witness_script() );
|
||||
}
|
||||
|
||||
const uint64_t& size_fee = get_estimated_fee( sidechain_condensing_tx::get_estimate_tx_size( ctx.get_transaction(), pw_address.witnesses_keys.size() ), estimated_feerate.load() );
|
||||
ctx.subtract_fee( size_fee, get_sidechain_params().percent_payment_to_witnesses );
|
||||
|
||||
return std::make_pair( ctx.get_transaction(), size_fee );
|
||||
}
|
||||
|
||||
bool database::delete_invalid_amount_with_fee( sidechain::input_withdrawal_info& i_w_info, std::vector<info_for_vin> info_vins, const uint64_t& fee, const uint64_t& count_transfer_vout )
|
||||
{
|
||||
uint64_t size_fee = fee / ( info_vins.size() + count_transfer_vout );
|
||||
bool deleted = false;
|
||||
for( auto info: info_vins ) {
|
||||
if( info.out.amount < size_fee ) {
|
||||
wlog("Amount is too small, vin will be ignored (prevout hash tx = ${hash_tx})",("hash_tx", info.out.hash_tx));
|
||||
i_w_info.remove_info_for_vin( info );
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
};
|
||||
|
||||
database::full_btc_tx_and_new_vins database::create_tx_with_valid_vin( const std::vector<info_for_vout>& info_vouts,
|
||||
const info_for_vin& info_pw_vin )
|
||||
{
|
||||
// we have to be sure, that we have enough BTC on vin to pay size_fee
|
||||
full_btc_transaction btc_tx_and_fee;
|
||||
std::vector<info_for_vin> info_vins = i_w_info.get_info_for_vins();
|
||||
bool deleted = true;
|
||||
|
||||
while( info_vins.size() && deleted ) {
|
||||
btc_tx_and_fee = create_btc_transaction( info_vins, info_vouts, info_pw_vin );
|
||||
deleted = delete_invalid_amount_with_fee( i_w_info, info_vins, btc_tx_and_fee.second, info_vouts.size() );
|
||||
info_vins = i_w_info.get_info_for_vins();
|
||||
}
|
||||
|
||||
return std::make_pair( btc_tx_and_fee, info_vins );
|
||||
}
|
||||
|
||||
fc::optional<operation> database::create_send_btc_tx_proposal( const witness_object& current_witness )
|
||||
{
|
||||
const auto& info_vins = i_w_info.get_info_for_vins();
|
||||
const auto& info_vouts = i_w_info.get_info_for_vouts();
|
||||
const auto& info_pw_vin = i_w_info.get_info_for_pw_vin();
|
||||
|
||||
if( info_pw_vin.valid() && ( info_vins.size() || info_vouts.size() ) ) {
|
||||
// tx_and_new_vins.first = sidechain::full_btc_transaction; tx_and_new_vins.second = std::vector<info_for_vin>
|
||||
const auto& tx_and_new_vins = create_tx_with_valid_vin( info_vouts, *info_pw_vin );
|
||||
if( !tx_and_new_vins.second.size() && !info_vouts.size() )
|
||||
return fc::optional<operation>();
|
||||
|
||||
const auto& btc_tx_and_size_fee = tx_and_new_vins.first;
|
||||
|
||||
bitcoin_transaction_send_operation btc_send_op;
|
||||
btc_send_op.payer = get_sidechain_account_id();
|
||||
btc_send_op.pw_vin = *info_pw_vin;
|
||||
btc_send_op.vins = tx_and_new_vins.second;
|
||||
for( auto& out : info_vouts ) {
|
||||
btc_send_op.vouts.push_back( out.get_id() );
|
||||
}
|
||||
btc_send_op.transaction = btc_tx_and_size_fee.first;
|
||||
btc_send_op.fee_for_size = btc_tx_and_size_fee.second;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = current_witness.witness_account;
|
||||
proposal_op.proposed_ops.push_back( op_wrapper( btc_send_op ) );
|
||||
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
|
||||
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
|
||||
return proposal_op;
|
||||
}
|
||||
return fc::optional<operation>();
|
||||
}
|
||||
|
||||
signed_transaction database::create_signed_transaction( const private_key& signing_private_key, const operation& op )
|
||||
{
|
||||
signed_transaction processed_trx;
|
||||
auto dyn_props = get_dynamic_global_properties();
|
||||
processed_trx.set_reference_block( dyn_props.head_block_id );
|
||||
processed_trx.set_expiration( head_block_time() + get_global_properties().parameters.maximum_time_until_expiration );
|
||||
processed_trx.operations.push_back( op );
|
||||
current_fee_schedule().set_fee( processed_trx.operations.back() );
|
||||
|
||||
processed_trx.sign( signing_private_key, get_chain_id() );
|
||||
|
||||
return processed_trx;
|
||||
}
|
||||
|
||||
operation database::create_sign_btc_tx_operation( const witness_object& current_witness, const private_key_type& privkey,
|
||||
const proposal_id_type& proposal_id )
|
||||
{
|
||||
const auto& proposal_idx = get_index_type<proposal_index>().indices().get<by_id>();
|
||||
const auto& proposal_itr = proposal_idx.find( proposal_id );
|
||||
bitcoin_transaction_send_operation op = proposal_itr->proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
|
||||
|
||||
bitcoin_transaction_sign_operation sign_operation;
|
||||
sign_operation.payer = current_witness.witness_account;
|
||||
sign_operation.proposal_id = proposal_id;
|
||||
const auto secret = privkey.get_secret();
|
||||
bytes key(secret.data(), secret.data() + secret.data_size());
|
||||
|
||||
auto vins = op.vins;
|
||||
if( op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
|
||||
vins.insert( vins.begin(), op.pw_vin );
|
||||
}
|
||||
|
||||
std::vector<bytes> redeem_scripts( i_w_info.get_redeem_scripts( vins ) );
|
||||
std::vector<uint64_t> amounts( i_w_info.get_amounts( vins ) );
|
||||
|
||||
sign_operation.signatures = sign_witness_transaction_part( op.transaction, redeem_scripts, amounts, key, context_sign, 1 );
|
||||
|
||||
return sign_operation;
|
||||
}
|
||||
|
||||
void database::remove_sidechain_proposal_object( const proposal_object& proposal )
|
||||
{ try {
|
||||
if( proposal.proposed_transaction.operations.size() == 1 &&
|
||||
( proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_transaction_send_operation>::value ||
|
||||
proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_issue_operation>::value ||
|
||||
proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_transaction_revert_operation>::value ) )
|
||||
{
|
||||
const auto& sidechain_proposal_idx = get_index_type<sidechain_proposal_index>().indices().get<by_proposal>();
|
||||
auto sidechain_proposal_itr = sidechain_proposal_idx.find( proposal.id );
|
||||
if( sidechain_proposal_itr == sidechain_proposal_idx.end() ) {
|
||||
return;
|
||||
}
|
||||
remove( *sidechain_proposal_itr );
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||
|
||||
void database::roll_back_vin_and_vout( const proposal_object& proposal )
|
||||
{
|
||||
if( proposal.proposed_transaction.operations.size() == 1 &&
|
||||
proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_transaction_send_operation>::value )
|
||||
{
|
||||
bitcoin_transaction_send_operation op = proposal.proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
|
||||
|
||||
if( pw_vout_manager.get_vout( op.pw_vin.identifier ).valid() ) {
|
||||
pw_vout_manager.mark_as_unused_vout( op.pw_vin.identifier );
|
||||
}
|
||||
|
||||
for( const auto& vin : op.vins ) {
|
||||
const auto& v = i_w_info.find_info_for_vin( vin.identifier );
|
||||
if( v.valid() ) {
|
||||
i_w_info.mark_as_unused_vin( vin );
|
||||
}
|
||||
}
|
||||
|
||||
for( const auto& vout : op.vouts ) {
|
||||
const auto& v = i_w_info.find_info_for_vout( vout );
|
||||
if( v.valid() ) {
|
||||
i_w_info.mark_as_unused_vout( *v );
|
||||
}
|
||||
}
|
||||
|
||||
remove_sidechain_proposal_object( proposal );
|
||||
}
|
||||
}
|
||||
|
||||
fc::optional<operation> database::create_bitcoin_issue_proposals( const witness_object& current_witness )
|
||||
{
|
||||
std::vector<fc::sha256> trx_ids;
|
||||
bitcoin_confirmations.safe_for<by_confirmed_and_not_used>([&]( btc_tx_confirmations_index::index<by_confirmed_and_not_used>::type::iterator itr_b, btc_tx_confirmations_index::index<by_confirmed_and_not_used>::type::iterator itr_e ){
|
||||
for(auto iter = itr_b; iter != itr_e; iter++) {
|
||||
if( !iter->is_confirmed_and_not_used() ) return;
|
||||
|
||||
const auto& btc_trx_idx = get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
|
||||
const auto& btc_tx = btc_trx_idx.find( iter->transaction_id );
|
||||
if( btc_tx == btc_trx_idx.end() ) continue;
|
||||
trx_ids.push_back( iter->transaction_id );
|
||||
}
|
||||
});
|
||||
|
||||
if( trx_ids.size() ) {
|
||||
bitcoin_issue_operation issue_op;
|
||||
issue_op.payer = get_sidechain_account_id();
|
||||
issue_op.transaction_ids = trx_ids;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = current_witness.witness_account;
|
||||
proposal_op.proposed_ops.push_back( op_wrapper( issue_op ) );
|
||||
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
|
||||
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
|
||||
|
||||
return fc::optional<operation>( proposal_op );
|
||||
}
|
||||
|
||||
return fc::optional<operation>();
|
||||
}
|
||||
|
||||
fc::optional<operation> database::create_bitcoin_revert_proposals( const witness_object& current_witness )
|
||||
{
|
||||
using iter_by_missing = btc_tx_confirmations_index::index<by_missing_and_not_used>::type::iterator;
|
||||
std::vector<revert_trx_info> trx_info;
|
||||
|
||||
bitcoin_confirmations.safe_for<by_missing_and_not_used>([&]( iter_by_missing itr_b, iter_by_missing itr_e ){
|
||||
for(auto iter = itr_b; iter != itr_e; iter++) {
|
||||
if( !iter->is_missing_and_not_used() ) return;
|
||||
|
||||
const auto& btc_trx_idx = get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
|
||||
const auto& btc_tx = btc_trx_idx.find( iter->transaction_id );
|
||||
if( btc_tx == btc_trx_idx.end() ) continue;
|
||||
trx_info.push_back( revert_trx_info( iter->transaction_id, iter->valid_vins ) );
|
||||
}
|
||||
});
|
||||
|
||||
if( trx_info.size() ) {
|
||||
bitcoin_transaction_revert_operation revert_op;
|
||||
revert_op.payer = get_sidechain_account_id();
|
||||
revert_op.transactions_info = trx_info;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = current_witness.witness_account;
|
||||
proposal_op.proposed_ops.push_back( op_wrapper( revert_op ) );
|
||||
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
|
||||
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
|
||||
|
||||
return fc::optional<operation>( proposal_op );
|
||||
}
|
||||
|
||||
return fc::optional<operation>();
|
||||
}
|
||||
|
||||
|
||||
void database::restore_bitcoin_transaction_status()
|
||||
{
|
||||
const auto& btc_tx_idx = get_index_type<bitcoin_transaction_index>().indices().get<by_id>();
|
||||
for( const auto& bto : btc_tx_idx ) {
|
||||
std::set<fc::sha256> valid_vins;
|
||||
for( const auto& v : bto.transaction.vin ) {
|
||||
valid_vins.insert( v.prevout.hash );
|
||||
}
|
||||
bitcoin_confirmations.insert( bitcoin_transaction_confirmations( bto.transaction.get_txid(), valid_vins ) );
|
||||
}
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
@ -253,6 +253,7 @@ void database::clear_expired_proposals()
|
|||
elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}",
|
||||
("proposal", proposal)("error", e.to_detail_string()));
|
||||
}
|
||||
roll_back_vin_and_vout(proposal);
|
||||
remove(proposal);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
libraries/chain/hardfork.d/sidechain.hf
Normal file
4
libraries/chain/hardfork.d/sidechain.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// sidechain fardfork
|
||||
#ifndef HARDFORK_SIDECHAIN_TIME
|
||||
#define HARDFORK_SIDECHAIN_TIME (fc::time_point_sec( 1550000000 ))
|
||||
#endif
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class bitcoin_address_create_evaluator : public evaluator<bitcoin_address_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bitcoin_address_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const bitcoin_address_create_operation& op);
|
||||
|
||||
object_id_type do_apply(const bitcoin_address_create_operation& op);
|
||||
|
||||
public_key_type pubkey_from_id( object_id_type id );
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
||||
#include <sidechain/bitcoin_address.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class bitcoin_address_object : public abstract_object<bitcoin_address_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = bitcoin_address_object_type;
|
||||
|
||||
bitcoin_address_id_type get_id()const { return id; }
|
||||
// multisig m-of-n (m = 5). Address is valid before count of changed witnesses < 5
|
||||
bool valid() { return count_invalid_pub_key < SIDECHAIN_NUMBER_INVALID_KEYS; }
|
||||
|
||||
std::string get_address() const { return address.get_address(); }
|
||||
|
||||
void update_count_invalid_pub_key( const sidechain::accounts_keys& incoming_wit_keys ) {
|
||||
count_invalid_pub_key = incoming_wit_keys.size() - address.count_intersection( incoming_wit_keys );
|
||||
}
|
||||
|
||||
account_id_type owner;
|
||||
sidechain::btc_multisig_segwit_address address;
|
||||
uint8_t count_invalid_pub_key;
|
||||
};
|
||||
|
||||
struct by_address;
|
||||
struct by_owner;
|
||||
|
||||
typedef boost::multi_index_container<
|
||||
bitcoin_address_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_address>, const_mem_fun< bitcoin_address_object, std::string, &bitcoin_address_object::get_address > >,
|
||||
ordered_non_unique< tag<by_owner>, member< bitcoin_address_object, account_id_type, &bitcoin_address_object::owner > >
|
||||
>
|
||||
> bitcoin_address_multi_index_container;
|
||||
typedef generic_index<bitcoin_address_object, bitcoin_address_multi_index_container> bitcoin_address_index;
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::bitcoin_address_object, (graphene::chain::object), (owner)(address)(count_invalid_pub_key) )
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
|
||||
namespace sidechain { class info_for_vin; }
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class bitcoin_transaction_send_evaluator : public evaluator<bitcoin_transaction_send_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bitcoin_transaction_send_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bitcoin_transaction_send_operation& op );
|
||||
|
||||
object_id_type do_apply( const bitcoin_transaction_send_operation& op );
|
||||
|
||||
std::vector<fc::sha256> create_transaction_vins( bitcoin_transaction_send_operation& op );
|
||||
|
||||
void finalize_bitcoin_transaction( bitcoin_transaction_send_operation& op );
|
||||
|
||||
void send_bitcoin_transaction( const bitcoin_transaction_object& btc_tx );
|
||||
};
|
||||
|
||||
class bitcoin_transaction_sign_evaluator : public evaluator<bitcoin_transaction_sign_evaluator>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
typedef bitcoin_transaction_sign_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bitcoin_transaction_sign_operation& op );
|
||||
|
||||
void_result do_apply( const bitcoin_transaction_sign_operation& op );
|
||||
|
||||
void update_proposal( const bitcoin_transaction_sign_operation& op );
|
||||
|
||||
bool check_sigs( const sidechain::bytes& key_data, const std::vector<sidechain::bytes>& sigs,
|
||||
const std::vector<sidechain::info_for_vin>& info_for_vins, const sidechain::bitcoin_transaction& tx );
|
||||
|
||||
};
|
||||
|
||||
class bitcoin_transaction_revert_evaluator : public evaluator<bitcoin_transaction_revert_evaluator>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
typedef bitcoin_transaction_revert_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const bitcoin_transaction_revert_operation& op );
|
||||
|
||||
void_result do_apply( const bitcoin_transaction_revert_operation& op );
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <sidechain/bitcoin_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class bitcoin_transaction_object : public abstract_object<bitcoin_transaction_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = bitcoin_transaction_object_type;
|
||||
|
||||
bitcoin_transaction_id_type get_id()const { return id; }
|
||||
|
||||
fc::sha256 pw_vin;
|
||||
|
||||
std::vector< fc::sha256 > vins;
|
||||
std::vector< info_for_vout_id_type > vouts;
|
||||
|
||||
sidechain::bitcoin_transaction transaction;
|
||||
fc::sha256 transaction_id;
|
||||
|
||||
uint64_t fee_for_size;
|
||||
};
|
||||
|
||||
struct by_transaction_id;
|
||||
|
||||
typedef boost::multi_index_container<
|
||||
bitcoin_transaction_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag< by_transaction_id >, member< bitcoin_transaction_object, fc::sha256, &bitcoin_transaction_object::transaction_id > >
|
||||
>
|
||||
> bitcoin_transaction_multi_index_container;
|
||||
typedef generic_index<bitcoin_transaction_object, bitcoin_transaction_multi_index_container> bitcoin_transaction_index;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::bitcoin_transaction_object, (graphene::chain::object), (pw_vin)(vins)(vouts)(transaction)(transaction_id)(fee_for_size) )
|
||||
|
|
@ -226,3 +226,18 @@
|
|||
#define TOURNAMENT_MAX_WHITELIST_LENGTH 1000
|
||||
#define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month
|
||||
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////// SideChain
|
||||
#define SIDECHAIN_SYMBOL "pBTC"
|
||||
#define SIDECHAIN_PRECISION_DIGITS 8
|
||||
#define SIDECHAIN_MAX_SHARE_SUPPLY int64_t(21000000ll * 100000000ll)
|
||||
#define SIDECHAIN_NUMBER_INVALID_KEYS 5
|
||||
#define SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG 5
|
||||
#define SIDECHAIN_DEFAULT_NUMBER_OF_CONFIRMATIONS 6
|
||||
#define SIDECHAIN_DEFAULT_MAX_UNCONFIRMED_VOUTS 25
|
||||
#define SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VINS 5
|
||||
#define SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VOUTS 5
|
||||
#define SIDECHAIN_DEFAULT_PERCENT_PAYMENT_TO_WITNESSES (GRAPHENE_1_PERCENT/10)
|
||||
#define SIDECHAIN_NULL_VIN_IDENTIFIER "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b4" // fc::sha256::hash( "" + std::to_string( 0 ) ) - ( 8 bytes )
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -40,9 +40,20 @@
|
|||
|
||||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <sidechain/bitcoin_transaction_confirmations.hpp>
|
||||
#include <sidechain/primary_wallet_vout_manager.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <secp256k1.h>
|
||||
|
||||
using namespace fc::ecc;
|
||||
using sidechain::bitcoin_transaction;
|
||||
using sidechain::info_for_vin;
|
||||
using sidechain::info_for_vout;
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using graphene::db::abstract_object;
|
||||
|
|
@ -78,7 +89,8 @@ namespace graphene { namespace chain {
|
|||
skip_assert_evaluation = 1 << 8, ///< used while reindexing
|
||||
skip_undo_history_check = 1 << 9, ///< used while reindexing
|
||||
skip_witness_schedule_check = 1 << 10, ///< used while reindexing
|
||||
skip_validate = 1 << 11 ///< used prior to checkpoint, skips validate() call on transaction
|
||||
skip_validate = 1 << 11, ///< used prior to checkpoint, skips validate() call on transaction
|
||||
skip_btc_tx_sending = 1 << 12
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -285,6 +297,7 @@ namespace graphene { namespace chain {
|
|||
|
||||
|
||||
uint32_t last_non_undoable_block_num() const;
|
||||
|
||||
//////////////////// db_init.cpp ////////////////////
|
||||
|
||||
void initialize_evaluators();
|
||||
|
|
@ -503,6 +516,65 @@ namespace graphene { namespace chain {
|
|||
void perform_account_maintenance(std::tuple<Types...> helpers);
|
||||
///@}
|
||||
///@}
|
||||
//////////////////// db_sidechain.cpp ////////////////////
|
||||
public:
|
||||
|
||||
using full_btc_tx_and_new_vins = std::pair< sidechain::full_btc_transaction, std::vector<info_for_vin> >;
|
||||
|
||||
std::map< account_id_type, public_key_type> get_active_witnesses_keys() const;
|
||||
bool is_sidechain_fork_needed() const;
|
||||
void perform_sidechain_fork();
|
||||
bitcoin_address_object get_latest_PW() const;
|
||||
|
||||
const sidechain::sidechain_parameters_extension& get_sidechain_params() const;
|
||||
const account_id_type& get_sidechain_account_id() const;
|
||||
const asset_id_type& get_sidechain_asset_id() const;
|
||||
int64_t get_estimated_fee( size_t tx_vsize, uint64_t estimated_feerate );
|
||||
|
||||
void processing_sidechain_proposals( const witness_object& current_witness, const private_key& signing_private_key );
|
||||
|
||||
inline bool delete_invalid_amount_with_fee( sidechain::input_withdrawal_info& i_w_info, std::vector<info_for_vin> info_vins, const uint64_t& fee, const uint64_t& count_transfer_vout );
|
||||
|
||||
full_btc_tx_and_new_vins create_tx_with_valid_vin( const std::vector<info_for_vout>& info_vouts,
|
||||
const info_for_vin& info_pw_vin );
|
||||
|
||||
sidechain::full_btc_transaction create_btc_transaction( const std::vector<info_for_vin>& info_vins,
|
||||
const std::vector<info_for_vout>& info_vouts,
|
||||
const info_for_vin& info_pw_vin );
|
||||
fc::optional<operation> create_send_btc_tx_proposal( const witness_object& current_witness );
|
||||
operation create_sign_btc_tx_operation( const witness_object& current_witness, const private_key_type& privkey,
|
||||
const proposal_id_type& proposal_id );
|
||||
signed_transaction create_signed_transaction( const private_key& signing_private_key, const operation& op );
|
||||
|
||||
void remove_sidechain_proposal_object( const proposal_object& proposal );
|
||||
|
||||
void roll_back_vin_and_vout( const proposal_object& proposal );
|
||||
|
||||
fc::optional<operation> create_bitcoin_issue_proposals( const witness_object& current_witness );
|
||||
|
||||
fc::optional<operation> create_bitcoin_revert_proposals( const witness_object& current_witness );
|
||||
|
||||
fc::signal<void( const bitcoin_transaction& )> send_btc_tx;
|
||||
|
||||
sidechain::input_withdrawal_info i_w_info;
|
||||
|
||||
sidechain::thread_safe_index<sidechain::btc_tx_confirmations_index> bitcoin_confirmations;
|
||||
|
||||
sidechain::primary_wallet_vout_manager pw_vout_manager;
|
||||
|
||||
std::atomic<uint64_t> estimated_feerate;
|
||||
|
||||
secp256k1_context_t* context_sign;
|
||||
|
||||
secp256k1_context_t* context_verify;
|
||||
|
||||
bool send_btc_tx_flag = true;
|
||||
|
||||
witness_id_type _current_witness_id;
|
||||
|
||||
private:
|
||||
|
||||
void restore_bitcoin_transaction_status(); // db_sidechain
|
||||
|
||||
vector< processed_transaction > _pending_tx;
|
||||
fork_database _fork_db;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <sidechain/types.hpp>
|
||||
#include <sidechain/bitcoin_address.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class info_for_used_vin_object : public abstract_object<info_for_used_vin_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = info_for_used_vin_object_type;
|
||||
|
||||
info_for_used_vin_id_type get_id()const { return id; }
|
||||
|
||||
fc::sha256 identifier;
|
||||
|
||||
sidechain::prev_out out;
|
||||
std::string address;
|
||||
sidechain::bytes script;
|
||||
};
|
||||
|
||||
struct by_id;
|
||||
struct by_identifier;
|
||||
|
||||
typedef boost::multi_index_container<
|
||||
info_for_used_vin_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique<tag<by_identifier>, member<info_for_used_vin_object, fc::sha256, &info_for_used_vin_object::identifier>>
|
||||
>
|
||||
> info_for_used_vin_multi_index_container;
|
||||
typedef generic_index<info_for_used_vin_object, info_for_used_vin_multi_index_container> info_for_used_vin_index;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::info_for_used_vin_object, (graphene::chain::object), (identifier)(out)(address)(script) )
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <sidechain/types.hpp>
|
||||
#include <sidechain/bitcoin_address.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class info_for_vout_object : public abstract_object<info_for_vout_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = info_for_vout_object_type;
|
||||
|
||||
struct comparer {
|
||||
bool operator()( const info_for_vout_object& lhs, const info_for_vout_object& rhs ) const {
|
||||
if( lhs.used != rhs.used )
|
||||
return lhs.used < rhs.used;
|
||||
return lhs.id < rhs.id;
|
||||
}
|
||||
};
|
||||
|
||||
bool operator==( const info_for_vout_object& ifv ) const {
|
||||
return ( this->payer == ifv.payer ) &&
|
||||
( this->address == ifv.address ) &&
|
||||
( this->amount == ifv.amount );
|
||||
}
|
||||
|
||||
info_for_vout_id_type get_id()const { return id; }
|
||||
|
||||
account_id_type payer;
|
||||
sidechain::bitcoin_address address;
|
||||
uint64_t amount;
|
||||
|
||||
bool used = false;
|
||||
};
|
||||
|
||||
struct by_created;
|
||||
struct by_id_and_not_used;
|
||||
|
||||
typedef boost::multi_index_container<
|
||||
info_for_vout_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
|
||||
ordered_non_unique< tag< by_created >, member< info_for_vout_object, bool, &info_for_vout_object::used > >,
|
||||
ordered_non_unique<tag<by_id_and_not_used>, identity< info_for_vout_object >, info_for_vout_object::comparer >
|
||||
>
|
||||
> info_for_vout_multi_index_container;
|
||||
typedef generic_index<info_for_vout_object, info_for_vout_multi_index_container> info_for_vout_index;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::info_for_vout_object, (graphene::chain::object), (payer)(address)(amount)(used) )
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <sidechain/types.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class primary_wallet_vout_object : public abstract_object<primary_wallet_vout_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = primary_wallet_vout_object_type;
|
||||
|
||||
struct comparer {
|
||||
bool operator()(const primary_wallet_vout_object& lhs, const primary_wallet_vout_object& rhs) const;
|
||||
};
|
||||
|
||||
primary_wallet_vout_id_type get_id() const { return id; }
|
||||
|
||||
sidechain::prev_out vout;
|
||||
fc::sha256 hash_id; // ( sha256(hash + n_vout) - 8 bytes ) + id_obj
|
||||
|
||||
bool confirmed;
|
||||
bool used;
|
||||
|
||||
};
|
||||
|
||||
struct by_hash_id;
|
||||
|
||||
typedef boost::multi_index_container<
|
||||
primary_wallet_vout_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag< by_hash_id >, member< primary_wallet_vout_object, fc::uint256, &primary_wallet_vout_object::hash_id > >
|
||||
>
|
||||
> primary_wallet_vout_multi_index_container;
|
||||
|
||||
typedef generic_index<primary_wallet_vout_object, primary_wallet_vout_multi_index_container> primary_wallet_vout_index;
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::primary_wallet_vout_object, (graphene::chain::object), (vout)(hash_id)(confirmed)(used) )
|
||||
|
||||
|
|
@ -29,6 +29,25 @@
|
|||
#include <graphene/chain/transaction_evaluation_state.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class sidechain_hardfork_visitor
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
database& db;
|
||||
proposal_id_type prop_id;
|
||||
|
||||
sidechain_hardfork_visitor( database& _db, const proposal_id_type& _prop_id ) : db( _db ), prop_id( _prop_id ) {}
|
||||
|
||||
template<typename T>
|
||||
void operator()( const T &v ) const {}
|
||||
|
||||
void operator()( const bitcoin_transaction_send_operation &v );
|
||||
|
||||
void operator()( const bitcoin_issue_operation &v );
|
||||
|
||||
void operator()( const bitcoin_transaction_revert_operation &v );
|
||||
};
|
||||
|
||||
class proposal_create_evaluator : public evaluator<proposal_create_evaluator>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct bitcoin_address_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
account_id_type owner;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
void validate()const {}
|
||||
share_type calculate_fee( const fee_parameters_type& k )const {
|
||||
return k.fee;
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::bitcoin_address_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_address_create_operation, (fee)(payer)(owner) )
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <sidechain/bitcoin_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct bitcoin_transaction_send_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 0;
|
||||
uint32_t price_per_kbyte = 0;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain::info_for_vin pw_vin;
|
||||
std::vector< sidechain::info_for_vin > vins;
|
||||
std::vector< info_for_vout_id_type > vouts;
|
||||
|
||||
sidechain::bitcoin_transaction transaction;
|
||||
uint64_t fee_for_size;
|
||||
|
||||
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;
|
||||
fee_required += calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );
|
||||
return fee_required;
|
||||
}
|
||||
};
|
||||
|
||||
struct bitcoin_transaction_sign_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 0;
|
||||
uint32_t price_per_kbyte = 0;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
proposal_id_type proposal_id;
|
||||
std::vector<sidechain::bytes> signatures;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
void validate()const {}
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
|
||||
};
|
||||
|
||||
struct revert_trx_info
|
||||
{
|
||||
revert_trx_info() = default;
|
||||
revert_trx_info( fc::sha256 trx_id, std::set< fc::sha256 > vins ): transaction_id( trx_id ), valid_vins( vins ) {}
|
||||
|
||||
fc::sha256 transaction_id;
|
||||
std::set< fc::sha256 > valid_vins;
|
||||
|
||||
bool operator ==( const revert_trx_info& trx_info ){
|
||||
return this->transaction_id == trx_info.transaction_id && this->valid_vins == trx_info.valid_vins;
|
||||
}
|
||||
|
||||
bool operator !=( const revert_trx_info& trx_info ){
|
||||
return this->transaction_id != trx_info.transaction_id || this->valid_vins != trx_info.valid_vins;
|
||||
}
|
||||
};
|
||||
|
||||
struct bitcoin_transaction_revert_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 0;
|
||||
uint32_t price_per_kbyte = 0;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
std::vector< revert_trx_info > transactions_info;
|
||||
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
void validate()const {}
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return k.fee; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation::fee_parameters_type, (fee)(price_per_kbyte) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation, (fee)(payer)(pw_vin)(vins)(vouts)(transaction)(fee_for_size) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation::fee_parameters_type, (fee)(price_per_kbyte) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(proposal_id)(signatures) )
|
||||
|
||||
FC_REFLECT( graphene::chain::revert_trx_info, (transaction_id)(valid_vins) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_revert_operation::fee_parameters_type, (fee)(price_per_kbyte) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_revert_operation, (fee)(payer)(transactions_info) )
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
#include <graphene/chain/protocol/ext.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <fc/smart_ref_fwd.hpp>
|
||||
#include <sidechain/sidechain_parameters.hpp>
|
||||
|
||||
namespace graphene { namespace chain { struct fee_schedule; } }
|
||||
|
||||
|
|
@ -37,6 +38,7 @@ namespace graphene { namespace chain {
|
|||
optional< uint16_t > betting_rake_fee_percentage;
|
||||
optional< flat_map<bet_multiplier_type, bet_multiplier_type> > permitted_betting_odds_increments;
|
||||
optional< uint16_t > live_betting_delay_time;
|
||||
optional< sidechain::sidechain_parameters_extension > sidechain_parameters;
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@
|
|||
#include <graphene/chain/protocol/event.hpp>
|
||||
#include <graphene/chain/protocol/betting_market.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#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 {
|
||||
|
||||
|
|
@ -129,7 +133,13 @@ namespace graphene { namespace chain {
|
|||
sport_delete_operation,
|
||||
event_group_delete_operation,
|
||||
affiliate_payout_operation, // VIRTUAL
|
||||
affiliate_referral_payout_operation // VIRTUAL
|
||||
affiliate_referral_payout_operation, // VIRTUAL
|
||||
withdraw_pbtc_operation,
|
||||
bitcoin_address_create_operation,
|
||||
bitcoin_transaction_send_operation,
|
||||
bitcoin_transaction_sign_operation,
|
||||
bitcoin_issue_operation,
|
||||
bitcoin_transaction_revert_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
std::vector<fc::sha256> transaction_ids;
|
||||
|
||||
|
||||
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) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_issue_operation, (fee)(payer)(transaction_ids) )
|
||||
|
|
@ -145,6 +145,12 @@ namespace graphene { namespace chain {
|
|||
betting_market_group_object_type,
|
||||
betting_market_object_type,
|
||||
bet_object_type,
|
||||
info_for_vout_object_type,
|
||||
info_for_used_vin_object_type,
|
||||
bitcoin_address_object_type,
|
||||
primary_wallet_vout_object_type,
|
||||
sidechain_proposal_object_type,
|
||||
bitcoin_transaction_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -202,32 +208,44 @@ namespace graphene { namespace chain {
|
|||
class betting_market_group_object;
|
||||
class betting_market_object;
|
||||
class bet_object;
|
||||
class info_for_vout_object;
|
||||
class info_for_used_vin_object;
|
||||
class bitcoin_address_object;
|
||||
class primary_wallet_vout_object;
|
||||
class sidechain_proposal_object;
|
||||
class bitcoin_transaction_object;
|
||||
|
||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||
typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type;
|
||||
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
|
||||
typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type;
|
||||
typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type;
|
||||
typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type;
|
||||
typedef object_id< protocol_ids, custom_object_type, custom_object> custom_id_type;
|
||||
typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type;
|
||||
typedef object_id< protocol_ids, operation_history_object_type, operation_history_object> operation_history_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, 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, tournament_object_type, tournament_object> tournament_id_type;
|
||||
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
|
||||
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
|
||||
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
|
||||
typedef object_id< protocol_ids, sport_object_type, sport_object> sport_id_type;
|
||||
typedef object_id< protocol_ids, event_group_object_type, event_group_object> event_group_id_type;
|
||||
typedef object_id< protocol_ids, event_object_type, event_object> event_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_rules_object_type, betting_market_rules_object> betting_market_rules_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
|
||||
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
|
||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||
typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type;
|
||||
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
|
||||
typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type;
|
||||
typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type;
|
||||
typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type;
|
||||
typedef object_id< protocol_ids, custom_object_type, custom_object> custom_id_type;
|
||||
typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type;
|
||||
typedef object_id< protocol_ids, operation_history_object_type, operation_history_object> operation_history_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, 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, tournament_object_type, tournament_object> tournament_id_type;
|
||||
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
|
||||
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
|
||||
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
|
||||
typedef object_id< protocol_ids, sport_object_type, sport_object> sport_id_type;
|
||||
typedef object_id< protocol_ids, event_group_object_type, event_group_object> event_group_id_type;
|
||||
typedef object_id< protocol_ids, event_object_type, event_object> event_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_rules_object_type, betting_market_rules_object> betting_market_rules_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
|
||||
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
|
||||
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
|
||||
typedef object_id< protocol_ids, info_for_vout_object_type, info_for_vout_object> info_for_vout_id_type;
|
||||
typedef object_id< protocol_ids, info_for_used_vin_object_type, info_for_used_vin_object> info_for_used_vin_id_type;
|
||||
typedef object_id< protocol_ids, bitcoin_address_object_type, bitcoin_address_object> bitcoin_address_id_type;
|
||||
typedef object_id< protocol_ids, primary_wallet_vout_object_type, primary_wallet_vout_object> primary_wallet_vout_id_type;
|
||||
typedef object_id< protocol_ids, sidechain_proposal_object_type, sidechain_proposal_object> sidechain_proposal_id_type;
|
||||
typedef object_id< protocol_ids, bitcoin_transaction_object_type, bitcoin_transaction_object> bitcoin_transaction_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -402,6 +420,12 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(betting_market_group_object_type)
|
||||
(betting_market_object_type)
|
||||
(bet_object_type)
|
||||
(info_for_vout_object_type)
|
||||
(info_for_used_vin_object_type)
|
||||
(bitcoin_address_object_type)
|
||||
(primary_wallet_vout_object_type)
|
||||
(sidechain_proposal_object_type)
|
||||
(bitcoin_transaction_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -453,6 +477,12 @@ FC_REFLECT_TYPENAME( graphene::chain::betting_market_group_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::betting_market_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::bet_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::tournament_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::info_for_vout_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::info_for_used_vin_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::bitcoin_address_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::primary_wallet_vout_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_proposal_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::bitcoin_transaction_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 )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct withdraw_pbtc_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
uint32_t price_per_kbyte = 10;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
std::string data; // address or script
|
||||
uint64_t amount;
|
||||
|
||||
// object_id_type tx_obj_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;
|
||||
fee_required += calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );
|
||||
return fee_required;
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::withdraw_pbtc_operation::fee_parameters_type, (fee)(price_per_kbyte) )
|
||||
FC_REFLECT( graphene::chain::withdraw_pbtc_operation, (fee)(payer)(data)(amount) )
|
||||
|
|
@ -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<fc::sha256> vins_identifier );
|
||||
|
||||
std::vector<account_id_type> get_accounts_to_issue( std::vector<fc::sha256> vins_identifier );
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <sidechain/types.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class sidechain_proposal_object : public abstract_object<sidechain_proposal_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = sidechain_proposal_object_type;
|
||||
|
||||
sidechain_proposal_id_type get_id()const { return id; }
|
||||
|
||||
proposal_id_type proposal_id;
|
||||
sidechain::sidechain_proposal_type proposal_type;
|
||||
};
|
||||
|
||||
struct by_proposal;
|
||||
struct by_type;
|
||||
typedef boost::multi_index_container<
|
||||
sidechain_proposal_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag< by_proposal >, member< sidechain_proposal_object, proposal_id_type, &sidechain_proposal_object::proposal_id > >,
|
||||
ordered_non_unique< tag< by_type >, member< sidechain_proposal_object, sidechain::sidechain_proposal_type, &sidechain_proposal_object::proposal_type > >
|
||||
>
|
||||
> sidechain_multi_index_container;
|
||||
typedef generic_index<sidechain_proposal_object, sidechain_multi_index_container> sidechain_proposal_index;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sidechain_proposal_object, (graphene::chain::object), (proposal_id)(proposal_type) )
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <sidechain/types.hpp>
|
||||
|
||||
using namespace sidechain;
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class withdraw_pbtc_evaluator : public evaluator<withdraw_pbtc_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef withdraw_pbtc_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const withdraw_pbtc_operation& op );
|
||||
|
||||
void_result do_apply( const withdraw_pbtc_operation& op );
|
||||
|
||||
void reserve_issue( const withdraw_pbtc_operation& op );
|
||||
|
||||
bool check_amount_higher_than_fee( const withdraw_pbtc_operation& op );
|
||||
|
||||
payment_type type;
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/sidechain_proposal_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
|
|
@ -142,6 +143,62 @@ struct proposal_operation_hardfork_visitor
|
|||
}
|
||||
};
|
||||
|
||||
void sidechain_hardfork_visitor::operator()( const bitcoin_transaction_send_operation &v )
|
||||
{
|
||||
db.create<sidechain_proposal_object>([&]( sidechain_proposal_object& sch_prop ) {
|
||||
sch_prop.proposal_type = sidechain::sidechain_proposal_type::SEND_BTC_TRANSACTION;
|
||||
sch_prop.proposal_id = prop_id;
|
||||
});
|
||||
|
||||
for( auto& vin : v.vins ) {
|
||||
auto obj = db.i_w_info.find_info_for_vin( vin.identifier );
|
||||
if( obj.valid() )
|
||||
db.i_w_info.mark_as_used_vin( *obj );
|
||||
}
|
||||
|
||||
for( auto& vout : v.vouts ) {
|
||||
auto obj = db.i_w_info.find_info_for_vout( vout );
|
||||
FC_ASSERT( obj.valid(), "info_for_vout_object don't exist." );
|
||||
db.i_w_info.mark_as_used_vout( *obj );
|
||||
}
|
||||
|
||||
db.pw_vout_manager.mark_as_used_vout( v.pw_vin.identifier );
|
||||
}
|
||||
|
||||
void sidechain_hardfork_visitor::operator()( const bitcoin_issue_operation &v )
|
||||
{
|
||||
db.create<sidechain_proposal_object>([&]( sidechain_proposal_object& sch_prop ) {
|
||||
sch_prop.proposal_type = sidechain::sidechain_proposal_type::ISSUE_BTC;
|
||||
sch_prop.proposal_id = prop_id;
|
||||
});
|
||||
|
||||
for( const auto& trx_id : v.transaction_ids ) {
|
||||
const auto& trx_confirmations = db.bitcoin_confirmations.find<sidechain::by_hash>( trx_id );
|
||||
if( trx_confirmations.valid() ) {
|
||||
db.bitcoin_confirmations.modify<sidechain::by_hash>( trx_id, [&]( sidechain::bitcoin_transaction_confirmations& obj ) {
|
||||
obj.used = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_hardfork_visitor::operator()( const bitcoin_transaction_revert_operation &v )
|
||||
{
|
||||
db.create<sidechain_proposal_object>([&]( sidechain_proposal_object& sch_prop ) {
|
||||
sch_prop.proposal_type = sidechain::sidechain_proposal_type::REVERT_BTC_TRANSACTION;
|
||||
sch_prop.proposal_id = prop_id;
|
||||
});
|
||||
|
||||
for( const auto& trx : v.transactions_info ) {
|
||||
const auto& trx_confirmations = db.bitcoin_confirmations.find<sidechain::by_hash>( trx.transaction_id );
|
||||
if( trx_confirmations.valid() ) {
|
||||
db.bitcoin_confirmations.modify<sidechain::by_hash>( trx.transaction_id, [&]( sidechain::bitcoin_transaction_confirmations& obj ) {
|
||||
obj.used = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o)
|
||||
{ try {
|
||||
const database& d = db();
|
||||
|
|
@ -157,6 +214,14 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
|
|||
FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - d.head_block_time()),
|
||||
"Proposal review period must be less than its overall lifetime." );
|
||||
|
||||
bool pbtc_op = ( o.proposed_ops[0].op.which() == operation::tag<bitcoin_transaction_send_operation>::value ) ||
|
||||
( o.proposed_ops[0].op.which() == operation::tag<bitcoin_issue_operation>::value ) ||
|
||||
( o.proposed_ops[0].op.which() == operation::tag<bitcoin_transaction_revert_operation>::value );
|
||||
|
||||
if( d.is_sidechain_fork_needed() && pbtc_op ) {
|
||||
FC_THROW( "Currently the operation is unavailable." );
|
||||
}
|
||||
|
||||
{
|
||||
// If we're dealing with the committee authority, make sure this transaction has a sufficient review period.
|
||||
flat_set<account_id_type> auths;
|
||||
|
|
@ -220,6 +285,9 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
|
|||
std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin()));
|
||||
});
|
||||
|
||||
sidechain_hardfork_visitor sidechain_vtor( d , proposal.id );
|
||||
o.proposed_ops[0].op.visit( sidechain_vtor );
|
||||
|
||||
return proposal.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
|
|
|||
141
libraries/chain/sidechain_evaluator.cpp
Normal file
141
libraries/chain/sidechain_evaluator.cpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
#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 )
|
||||
{ try {
|
||||
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_identifier>();
|
||||
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() );
|
||||
|
||||
for( const auto& id: op.transaction_ids ) {
|
||||
const auto& btc_itr = btc_trx_idx.find( id );
|
||||
FC_ASSERT( btc_itr != btc_trx_idx.end() );
|
||||
|
||||
for( auto& vin_id : btc_itr->vins ) {
|
||||
const auto& itr = vins_info_idx.find( vin_id );
|
||||
FC_ASSERT( itr != vins_info_idx.end() );
|
||||
auto addr_itr = btc_addr_idx.find( itr->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();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result bitcoin_issue_evaluator::do_apply( const bitcoin_issue_operation& op )
|
||||
{ try {
|
||||
database& d = db();
|
||||
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
|
||||
|
||||
for( const auto& id: op.transaction_ids ) {
|
||||
const auto& btc_obj = *btc_trx_idx.find( id );
|
||||
add_issue( btc_obj );
|
||||
|
||||
d.pw_vout_manager.confirm_vout( btc_obj.pw_vin );
|
||||
clear_btc_transaction_information( btc_obj );
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
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 );
|
||||
|
||||
uint64_t fee_deduction = btc_obj.fee_for_size / ( btc_obj.vins.size() + btc_obj.vouts.size() );
|
||||
|
||||
if( btc_obj.fee_for_size % ( btc_obj.vins.size() + btc_obj.vouts.size() ) != 0 ) {
|
||||
fee_deduction += 1;
|
||||
}
|
||||
|
||||
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] - fee_deduction, 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_identifier>();
|
||||
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<fc::sha256> vins_identifier )
|
||||
{
|
||||
database& d = db();
|
||||
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
|
||||
|
||||
std::vector<uint64_t> result;
|
||||
|
||||
for( auto& identifier : vins_identifier ) {
|
||||
auto vin_itr = vins_info_idx.find( identifier );
|
||||
result.push_back( vin_itr->out.amount );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<account_id_type> bitcoin_issue_evaluator::get_accounts_to_issue( std::vector<fc::sha256> vins_identifier )
|
||||
{
|
||||
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_identifier>();
|
||||
|
||||
std::vector<account_id_type> result;
|
||||
|
||||
for( auto& identifier : vins_identifier ) {
|
||||
auto vin_itr = vins_info_idx.find( identifier );
|
||||
auto addr_itr = btc_addr_idx.find( vin_itr->address );
|
||||
|
||||
result.push_back( addr_itr->owner );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
85
libraries/chain/withdraw_pbtc_evaluator.cpp
Normal file
85
libraries/chain/withdraw_pbtc_evaluator.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#include <graphene/chain/withdraw_pbtc_evaluator.hpp>
|
||||
#include <graphene/chain/info_for_vout_object.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
|
||||
#include <sidechain/sidechain_condensing_tx.hpp>
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <sidechain/bitcoin_address.hpp>
|
||||
#include <sidechain/utils.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result withdraw_pbtc_evaluator::do_evaluate(const withdraw_pbtc_operation& op)
|
||||
{
|
||||
database& d = db();
|
||||
|
||||
FC_ASSERT( !d.is_sidechain_fork_needed() );
|
||||
FC_ASSERT( op.data.size() > 0 );
|
||||
type = bitcoin_address( op.data ).get_type();
|
||||
FC_ASSERT( type != payment_type::NULLDATA , "Invalid address type." );
|
||||
FC_ASSERT( check_amount_higher_than_fee( op ) );
|
||||
asset acc_balance = db().get_balance( op.payer, d.get_sidechain_asset_id() );
|
||||
FC_ASSERT( acc_balance.amount.value >= op.amount );
|
||||
|
||||
return void_result();
|
||||
}
|
||||
|
||||
void_result withdraw_pbtc_evaluator::do_apply(const withdraw_pbtc_operation& op)
|
||||
{
|
||||
db().i_w_info.insert_info_for_vout( op.payer, op.data, op.amount );
|
||||
reserve_issue( op );
|
||||
return void_result();
|
||||
}
|
||||
|
||||
void withdraw_pbtc_evaluator::reserve_issue( const withdraw_pbtc_operation& op )
|
||||
{
|
||||
database& d = db();
|
||||
|
||||
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;
|
||||
|
||||
asset_reserve_operation reserve_op;
|
||||
reserve_op.amount_to_reserve = asset( op.amount, d.get_sidechain_asset_id() );
|
||||
reserve_op.payer = op.payer;
|
||||
|
||||
d.apply_operation( *trx_state, reserve_op );
|
||||
|
||||
trx_state->skip_fee = skip_fee_old;
|
||||
trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old;
|
||||
}
|
||||
|
||||
bool withdraw_pbtc_evaluator::check_amount_higher_than_fee( const withdraw_pbtc_operation& op ) {
|
||||
database& d = db();
|
||||
|
||||
info_for_vout_object obj;
|
||||
obj.payer = op.payer;
|
||||
obj.address = op.data;
|
||||
obj.amount = op.amount;
|
||||
obj.used = false;
|
||||
|
||||
const auto& pw_address = d.get_latest_PW().address;
|
||||
|
||||
sidechain::info_for_vin pw_vin;
|
||||
pw_vin.identifier = fc::sha256( std::string(64, '1') );
|
||||
pw_vin.out.hash_tx = std::string(64, '1');
|
||||
pw_vin.out.n_vout = 0;
|
||||
pw_vin.out.amount = 2*op.amount;
|
||||
pw_vin.address = pw_address.get_address();
|
||||
pw_vin.script = pw_address.get_witness_script();
|
||||
|
||||
const auto& mock_trx = d.create_btc_transaction( {}, { obj }, pw_vin );
|
||||
|
||||
if( op.amount < mock_trx.second )
|
||||
return false;
|
||||
|
||||
uint64_t fee_for_witnesses = ( (op.amount - mock_trx.second) * d.get_sidechain_params().percent_payment_to_witnesses ) / GRAPHENE_100_PERCENT;
|
||||
|
||||
if( op.amount < mock_trx.second + fee_for_witnesses )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -10,7 +10,7 @@ set(SOURCES node.cpp
|
|||
add_library( graphene_net ${SOURCES} ${HEADERS} )
|
||||
|
||||
target_link_libraries( graphene_net
|
||||
PUBLIC fc graphene_db )
|
||||
PUBLIC fc graphene_db sidechain )
|
||||
target_include_directories( graphene_net
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../chain/include"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ add_library( graphene_witness
|
|||
witness.cpp
|
||||
)
|
||||
|
||||
target_link_libraries( graphene_witness graphene_chain graphene_app )
|
||||
target_link_libraries( graphene_witness graphene_chain graphene_app graphene_time sidechain_network )
|
||||
target_include_directories( graphene_witness
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <sidechain/network/sidechain_net_manager.hpp>
|
||||
|
||||
#include <fc/thread/future.hpp>
|
||||
|
||||
|
|
@ -82,6 +83,7 @@ private:
|
|||
bool _consecutive_production_enabled = false;
|
||||
uint32_t _required_witness_participation = 33 * GRAPHENE_1_PERCENT;
|
||||
uint32_t _production_skip_flags = graphene::chain::database::skip_nothing;
|
||||
sidechain::sidechain_net_manager bitcoin_manager;
|
||||
|
||||
std::map<chain::public_key_type, fc::ecc::private_key> _private_keys;
|
||||
std::set<chain::witness_id_type> _witnesses;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,19 @@ void witness_plugin::plugin_initialize(const boost::program_options::variables_m
|
|||
_private_keys[key_id_to_wif_pair.first] = *private_key;
|
||||
}
|
||||
}
|
||||
if( options.count( "bitcoin-node-ip" ) && options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" )
|
||||
&& options.count( "bitcoin-node-rpc-user" ) && options.count( "bitcoin-node-rpc-password" ) )
|
||||
{
|
||||
const auto ip = options.at("bitcoin-node-ip").as<std::string>();
|
||||
const auto zmq_port = options.at("bitcoin-node-zmq-port").as<uint32_t>();
|
||||
const auto rpc_port = options.at("bitcoin-node-rpc-port").as<uint32_t>();
|
||||
const auto rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>();
|
||||
const auto rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>();
|
||||
|
||||
bitcoin_manager.initialize_manager(&database(), ip, zmq_port, rpc_port, rpc_user, rpc_password);
|
||||
} else {
|
||||
wlog("Haven't set up sidechain parameters");
|
||||
}
|
||||
ilog("witness plugin: plugin_initialize() end");
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
|
|
|
|||
9
libraries/sidechain/CMakeLists.txt
Normal file
9
libraries/sidechain/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
file( GLOB SOURCES "*.cpp" )
|
||||
file( GLOB HEADERS "include/*.hpp" )
|
||||
|
||||
add_subdirectory( network )
|
||||
|
||||
add_library( sidechain STATIC ${SOURCES} ${HEADERS} )
|
||||
|
||||
target_link_libraries( sidechain fc graphene_chain )
|
||||
target_include_directories( sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
192
libraries/sidechain/bech32.cpp
Normal file
192
libraries/sidechain/bech32.cpp
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <sidechain/bech32.hpp>
|
||||
|
||||
// #include <bech32.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** The Bech32 character set for encoding. */
|
||||
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** The Bech32 character set for decoding. */
|
||||
const int8_t CHARSET_REV[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
data Cat(data x, const data& y)
|
||||
{
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
return x;
|
||||
}
|
||||
|
||||
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
|
||||
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
|
||||
* bits correspond to earlier values. */
|
||||
uint32_t PolyMod(const data& v)
|
||||
{
|
||||
// The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
|
||||
// implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
|
||||
// 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
|
||||
// [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
|
||||
|
||||
// The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
|
||||
// v(x) mod g(x), where g(x) is the Bech32 generator,
|
||||
// x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
|
||||
// that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
|
||||
// window of 1023 characters. Among the various possible BCH codes, one was selected to in
|
||||
// fact guarantee detection of up to 4 errors within a window of 89 characters.
|
||||
|
||||
// Note that the coefficients are elements of GF(32), here represented as decimal numbers
|
||||
// between {}. In this finite field, addition is just XOR of the corresponding numbers. For
|
||||
// example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
|
||||
// treating the bits of values themselves as coefficients of a polynomial over a smaller field,
|
||||
// GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
|
||||
// (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
|
||||
// = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
|
||||
|
||||
// During the course of the loop below, `c` contains the bitpacked coefficients of the
|
||||
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
|
||||
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
|
||||
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
|
||||
// for `c`.
|
||||
uint32_t c = 1;
|
||||
for (auto v_i : v) {
|
||||
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
|
||||
// value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
|
||||
// correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
|
||||
// process. Simplifying:
|
||||
// c'(x) = (f(x) * x + v_i) mod g(x)
|
||||
// ((f(x) mod g(x)) * x + v_i) mod g(x)
|
||||
// (c(x) * x + v_i) mod g(x)
|
||||
// If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
|
||||
// c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
|
||||
// = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
|
||||
// = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
|
||||
// If we call (x^6 mod g(x)) = k(x), this can be written as
|
||||
// c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
|
||||
|
||||
// First, determine the value of c0:
|
||||
uint8_t c0 = c >> 25;
|
||||
|
||||
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
|
||||
c = ((c & 0x1ffffff) << 5) ^ v_i;
|
||||
|
||||
// Finally, for each set bit n in c0, conditionally add {2^n}k(x):
|
||||
if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
|
||||
if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
|
||||
if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
|
||||
if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
|
||||
if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Convert to lower case. */
|
||||
inline unsigned char LowerCase(unsigned char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
data ExpandHRP(const std::string& hrp)
|
||||
{
|
||||
data ret;
|
||||
ret.reserve(hrp.size() + 90);
|
||||
ret.resize(hrp.size() * 2 + 1);
|
||||
for (size_t i = 0; i < hrp.size(); ++i) {
|
||||
unsigned char c = hrp[i];
|
||||
ret[i] = c >> 5;
|
||||
ret[i + hrp.size() + 1] = c & 0x1f;
|
||||
}
|
||||
ret[hrp.size()] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Verify a checksum. */
|
||||
bool VerifyChecksum(const std::string& hrp, const data& values)
|
||||
{
|
||||
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
|
||||
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
|
||||
// list of values would result in a new valid list. For that reason, Bech32 requires the
|
||||
// resulting checksum to be 1 instead.
|
||||
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
data CreateChecksum(const std::string& hrp, const data& values)
|
||||
{
|
||||
data enc = Cat(ExpandHRP(hrp), values);
|
||||
enc.resize(enc.size() + 6); // Append 6 zeroes
|
||||
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
|
||||
data ret(6);
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
// Convert the 5-bit groups in mod to checksum values.
|
||||
ret[i] = (mod >> (5 * (5 - i))) & 31;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace sidechain { namespace bech32 {
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string Encode(const std::string& hrp, const data& values) {
|
||||
data checksum = CreateChecksum(hrp, values);
|
||||
data combined = Cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (auto c : combined) {
|
||||
ret += CHARSET[c];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Bech32 string. */
|
||||
std::pair<std::string, data> Decode(const std::string& str) {
|
||||
bool lower = false, upper = false;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
unsigned char c = str[i];
|
||||
if (c < 33 || c > 126) return {};
|
||||
if (c >= 'a' && c <= 'z') lower = true;
|
||||
if (c >= 'A' && c <= 'Z') upper = true;
|
||||
}
|
||||
if (lower && upper) return {};
|
||||
size_t pos = str.rfind('1');
|
||||
if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
|
||||
return {};
|
||||
}
|
||||
data values(str.size() - 1 - pos);
|
||||
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
|
||||
unsigned char c = str[i + pos + 1];
|
||||
int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c];
|
||||
if (rev == -1) {
|
||||
return {};
|
||||
}
|
||||
values[i] = rev;
|
||||
}
|
||||
std::string hrp;
|
||||
for (size_t i = 0; i < pos; ++i) {
|
||||
hrp += LowerCase(str[i]);
|
||||
}
|
||||
if (!VerifyChecksum(hrp, values)) {
|
||||
return {};
|
||||
}
|
||||
return {hrp, data(values.begin(), values.end() - 6)};
|
||||
}
|
||||
|
||||
} } // namespace sidechain::bech32
|
||||
258
libraries/sidechain/bitcoin_address.cpp
Normal file
258
libraries/sidechain/bitcoin_address.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
#include <sidechain/bitcoin_address.hpp>
|
||||
#include <sstream>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <sidechain/segwit_addr.hpp>
|
||||
#include <sidechain/bitcoin_script.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
bool bitcoin_address::operator==( const bitcoin_address& btc_addr ) const {
|
||||
return ( this->address == btc_addr.address ) &&
|
||||
( this->type == btc_addr.type ) &&
|
||||
( this->raw_address == btc_addr.raw_address );
|
||||
}
|
||||
|
||||
bytes bitcoin_address::get_script() const
|
||||
{
|
||||
switch ( type ) {
|
||||
case payment_type::NULLDATA:
|
||||
return script_builder() << op::RETURN << raw_address;
|
||||
case payment_type::P2PK:
|
||||
return script_builder() << raw_address << op::CHECKSIG;
|
||||
case payment_type::P2PKH:
|
||||
return script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG;
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
return script_builder() << op::HASH160 << raw_address << op::EQUAL;
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH:
|
||||
return script_builder() << op::_0 << raw_address;
|
||||
default:
|
||||
return raw_address;
|
||||
}
|
||||
}
|
||||
|
||||
payment_type bitcoin_address::determine_type()
|
||||
{
|
||||
if( is_p2pk() ) {
|
||||
return payment_type::P2PK;
|
||||
} else if( is_p2wpkh() ) {
|
||||
return payment_type::P2WPKH;
|
||||
} else if( is_p2wsh() ) {
|
||||
return payment_type::P2WSH;
|
||||
} else if( is_p2pkh() ) {
|
||||
return payment_type::P2PKH;
|
||||
} else if( is_p2sh() ) {
|
||||
return payment_type::P2SH;
|
||||
} else {
|
||||
return payment_type::NULLDATA;
|
||||
}
|
||||
}
|
||||
|
||||
bytes bitcoin_address::determine_raw_address()
|
||||
{
|
||||
bytes result;
|
||||
switch( type ) {
|
||||
case payment_type::P2PK : {
|
||||
result = parse_hex( address );
|
||||
break;
|
||||
}
|
||||
case payment_type::P2WPKH :
|
||||
case payment_type::P2WSH : {
|
||||
std::string prefix( address.compare(0,4,"bcrt") == 0 ? std::string( address.begin(), address.begin() + 4 ) :
|
||||
std::string( address.begin(), address.begin() + 2 ) );
|
||||
const auto& decode_bech32 = segwit_addr::decode( prefix, address );
|
||||
result = bytes( decode_bech32.second.begin(), decode_bech32.second.end() );
|
||||
break;
|
||||
}
|
||||
case payment_type::P2SH_WPKH :
|
||||
case payment_type::P2SH_WSH :
|
||||
case payment_type::P2PKH :
|
||||
case payment_type::P2SH : {
|
||||
bytes hex_addr = fc::from_base58( address );
|
||||
result = bytes( hex_addr.begin() + 1, hex_addr.begin() + 21 );
|
||||
break;
|
||||
}
|
||||
case payment_type::NULLDATA : return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bitcoin_address::check_segwit_address( const size_segwit_address& size ) const {
|
||||
if( !address.compare(0,4,"bcrt") || !address.compare(0,2,"bc") || !address.compare(0,2,"tb") ) {
|
||||
std::string prefix( !address.compare(0,4,"bcrt") ? std::string(address.begin(), address.begin() + 4) :
|
||||
std::string(address.begin(), address.begin() + 2) );
|
||||
|
||||
const auto& decode_bech32 = segwit_addr::decode( prefix, address );
|
||||
|
||||
if( decode_bech32.first == -1 || decode_bech32.second.size() != size ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2pk() const
|
||||
{
|
||||
try {
|
||||
bool prefix = !address.compare(0,2,"02") || !address.compare(0,2,"03");
|
||||
if( address.size() == 66 && prefix ) {
|
||||
parse_hex( address );
|
||||
return true;
|
||||
}
|
||||
} catch( fc::exception e ) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2wpkh() const
|
||||
{
|
||||
return check_segwit_address( size_segwit_address::P2WPKH );
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2wsh() const
|
||||
{
|
||||
return check_segwit_address( size_segwit_address::P2WSH );
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2pkh() const
|
||||
{
|
||||
try {
|
||||
bytes hex_addr = fc::from_base58( address );
|
||||
if( hex_addr.size() == 25 && ( static_cast<unsigned char>( hex_addr[0] ) == 0x00 ||
|
||||
static_cast<unsigned char>( hex_addr[0] ) == 0x6f ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch( fc::exception e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2sh() const
|
||||
{
|
||||
try {
|
||||
bytes hex_addr = fc::from_base58( address );
|
||||
if( hex_addr.size() == 25 && ( static_cast<unsigned char>( hex_addr[0] ) == 0x05 ||
|
||||
static_cast<unsigned char>( hex_addr[0] ) == 0xc4 ) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch( fc::exception e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
btc_multisig_address::btc_multisig_address( const size_t n_required, const accounts_keys& keys ) :
|
||||
keys_required ( n_required ), witnesses_keys( keys )
|
||||
{
|
||||
create_redeem_script();
|
||||
create_address();
|
||||
type = payment_type::P2SH;
|
||||
}
|
||||
|
||||
size_t btc_multisig_address::count_intersection( const accounts_keys& keys ) const
|
||||
{
|
||||
FC_ASSERT( keys.size() > 0 );
|
||||
|
||||
int intersections_count = 0;
|
||||
for( auto& key : keys ) {
|
||||
auto witness_key = witnesses_keys.find( key.first );
|
||||
if( witness_key == witnesses_keys.end() ) continue;
|
||||
if( key.second == witness_key->second )
|
||||
intersections_count++;
|
||||
}
|
||||
return intersections_count;
|
||||
}
|
||||
|
||||
void btc_multisig_address::create_redeem_script()
|
||||
{
|
||||
FC_ASSERT( keys_required > 0 );
|
||||
FC_ASSERT( keys_required < witnesses_keys.size() );
|
||||
redeem_script.clear();
|
||||
redeem_script.push_back( op_num[keys_required - 1] );
|
||||
for( const auto& key : witnesses_keys ) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << key.second.key_data.size();
|
||||
auto key_size_hex = sidechain::parse_hex( ss.str() );
|
||||
redeem_script.insert( redeem_script.end(), key_size_hex.begin(), key_size_hex.end() );
|
||||
redeem_script.insert( redeem_script.end(), key.second.key_data.begin(), key.second.key_data.end() );
|
||||
}
|
||||
redeem_script.push_back( op_num[witnesses_keys.size() - 1] );
|
||||
redeem_script.push_back( OP_CHECKMULTISIG );
|
||||
}
|
||||
|
||||
void btc_multisig_address::create_address()
|
||||
{
|
||||
FC_ASSERT( redeem_script.size() > 0 );
|
||||
raw_address.clear();
|
||||
fc::sha256 hash256 = fc::sha256::hash( redeem_script.data(), redeem_script.size() );
|
||||
fc::ripemd160 hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
|
||||
bytes temp_addr_hash( sidechain::parse_hex( hash160.str() ) );
|
||||
|
||||
raw_address.push_back( OP_HASH160 );
|
||||
std::stringstream ss;
|
||||
ss << std::hex << temp_addr_hash.size();
|
||||
auto address_size_hex = sidechain::parse_hex( ss.str() );
|
||||
raw_address.insert( raw_address.end(), address_size_hex.begin(), address_size_hex.end() );
|
||||
raw_address.insert( raw_address.end(), temp_addr_hash.begin(), temp_addr_hash.end() );
|
||||
raw_address.push_back( OP_EQUAL );
|
||||
}
|
||||
|
||||
btc_multisig_segwit_address::btc_multisig_segwit_address( const size_t n_required, const accounts_keys& keys ) :
|
||||
btc_multisig_address( n_required, keys )
|
||||
{
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2SH;
|
||||
}
|
||||
|
||||
bool btc_multisig_segwit_address::operator==( const btc_multisig_segwit_address& addr ) const
|
||||
{
|
||||
if( address != addr.address || redeem_script != addr.redeem_script ||
|
||||
witnesses_keys != addr.witnesses_keys || witness_script != addr.witness_script ||
|
||||
raw_address != addr.raw_address )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<public_key_type> btc_multisig_segwit_address::get_keys() {
|
||||
std::vector<public_key_type> keys;
|
||||
for( const auto& k : witnesses_keys ) {
|
||||
keys.push_back( k.second );
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
void btc_multisig_segwit_address::create_witness_script()
|
||||
{
|
||||
const auto redeem_sha256 = fc::sha256::hash( redeem_script.data(), redeem_script.size() );
|
||||
witness_script.push_back( OP_0 );
|
||||
witness_script.push_back( 0x20 ); // PUSH_32
|
||||
witness_script.insert( witness_script.end(), redeem_sha256.data(), redeem_sha256.data() + redeem_sha256.data_size() );
|
||||
}
|
||||
|
||||
void btc_multisig_segwit_address::create_segwit_address()
|
||||
{
|
||||
fc::sha256 hash256 = fc::sha256::hash( witness_script.data(), witness_script.size() );
|
||||
fc::ripemd160 hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
|
||||
|
||||
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size() );
|
||||
address = fc::to_base58( get_address_bytes( raw_address ) );
|
||||
}
|
||||
|
||||
bytes btc_multisig_segwit_address::get_address_bytes( const bytes& script_hash )
|
||||
{
|
||||
bytes address_bytes( 1, TESTNET_SCRIPT ); // 1 byte version
|
||||
address_bytes.insert( address_bytes.end(), script_hash.begin(), script_hash.end() );
|
||||
fc::sha256 hash256 = fc::sha256::hash( fc::sha256::hash( address_bytes.data(), address_bytes.size() ) );
|
||||
address_bytes.insert( address_bytes.end(), hash256.data(), hash256.data() + 4 ); // 4 byte checksum
|
||||
|
||||
return address_bytes;
|
||||
}
|
||||
|
||||
}
|
||||
60
libraries/sidechain/bitcoin_script.cpp
Normal file
60
libraries/sidechain/bitcoin_script.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#include <sidechain/bitcoin_script.hpp>
|
||||
#include <sidechain/serialize.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
script_builder& script_builder::operator<<( op opcode )
|
||||
{
|
||||
const auto op_byte = static_cast<uint8_t>( opcode );
|
||||
if ( op_byte < 0 || op_byte > 0xff )
|
||||
throw std::runtime_error( "script_builder::operator<<(OP): invalid opcode" );
|
||||
script.push_back( op_byte );
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder& script_builder::operator<<( uint8_t number )
|
||||
{
|
||||
FC_ASSERT( 0 <= number && number <= 16 );
|
||||
|
||||
if ( number == 0 )
|
||||
script.push_back( static_cast<uint8_t>( op::_0 ) );
|
||||
else
|
||||
script.push_back( static_cast<uint8_t>( op::_1 ) + number - 1 );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder& script_builder::operator<<( size_t size )
|
||||
{
|
||||
sidechain::write_compact_size( script, size );
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder& script_builder::operator<<( const bytes& sc ) {
|
||||
sidechain::write_compact_size( script, sc.size() );
|
||||
script.insert( script.end(), sc.begin(), sc.end() );
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder& script_builder::operator<<( const fc::sha256& hash )
|
||||
{
|
||||
sidechain::write_compact_size( script, hash.data_size() );
|
||||
script.insert( script.end(), hash.data(), hash.data() + hash.data_size() );
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder& script_builder::operator<<( const fc::ripemd160& hash )
|
||||
{
|
||||
sidechain::write_compact_size( script, hash.data_size() );
|
||||
script.insert( script.end(), hash.data(), hash.data() + hash.data_size() );
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder& script_builder::operator<<( const fc::ecc::public_key_data& pubkey_data )
|
||||
{
|
||||
sidechain::write_compact_size( script, pubkey_data.size() );
|
||||
script.insert( script.end(), pubkey_data.begin(), pubkey_data.begin() + pubkey_data.size() );
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
279
libraries/sidechain/bitcoin_transaction.cpp
Normal file
279
libraries/sidechain/bitcoin_transaction.cpp
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
#include <sidechain/bitcoin_transaction.hpp>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <sidechain/bitcoin_script.hpp>
|
||||
#include <sidechain/serialize.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
bool out_point::operator==( const out_point& op ) const
|
||||
{
|
||||
if( this->hash == op.hash &&
|
||||
this->n == op.n )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_in::operator==( const tx_in& ti ) const
|
||||
{
|
||||
if( this->prevout == ti.prevout &&
|
||||
this->scriptSig == ti.scriptSig &&
|
||||
this->nSequence == ti.nSequence )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::operator==( const tx_out& to ) const
|
||||
{
|
||||
if( this->value == to.value &&
|
||||
this->scriptPubKey == to.scriptPubKey )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2wsh() const
|
||||
{
|
||||
if( scriptPubKey.size() == 34 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x20) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2wpkh() const
|
||||
{
|
||||
if( scriptPubKey.size() == 22 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x14) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2pkh() const
|
||||
{
|
||||
if( scriptPubKey.size() == 25 && scriptPubKey[0] == static_cast<char>(0x76) && scriptPubKey[1] == static_cast<char>(0xa9) &&
|
||||
scriptPubKey[2] == static_cast<char>(0x14) && scriptPubKey[23] == static_cast<char>(0x88) && scriptPubKey[24] == static_cast<char>(0xac) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2sh() const
|
||||
{
|
||||
if( scriptPubKey.size() == 23 && scriptPubKey[0] == static_cast<char>(0xa9) &&
|
||||
scriptPubKey[1] == static_cast<char>(0x14) && scriptPubKey[22] == static_cast<char>(0x87) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2pk() const
|
||||
{
|
||||
if( scriptPubKey.size() == 35 && scriptPubKey[0] == static_cast<char>(0x21) && scriptPubKey[34] == static_cast<char>(0xac) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes tx_out::get_data_or_script() const
|
||||
{
|
||||
if( is_p2pkh() ) {
|
||||
return bytes( scriptPubKey.begin() + 3, scriptPubKey.begin() + 23 );
|
||||
} else if( is_p2sh() || is_p2wpkh() ) {
|
||||
return bytes( scriptPubKey.begin() + 2, scriptPubKey.begin() + 22 );
|
||||
} else if( is_p2wsh() ) {
|
||||
return bytes( scriptPubKey.begin() + 2, scriptPubKey.begin() + 34 );
|
||||
} else if( is_p2pk() ) {
|
||||
return bytes( scriptPubKey.begin() + 1, scriptPubKey.begin() + 34 );
|
||||
}
|
||||
return scriptPubKey;
|
||||
}
|
||||
|
||||
bool bitcoin_transaction::operator!=( const bitcoin_transaction& bt ) const
|
||||
{
|
||||
if( this->nVersion != bt.nVersion ||
|
||||
this->vin != bt.vin ||
|
||||
this->vout != bt.vout ||
|
||||
this->nLockTime != bt.nLockTime )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fc::sha256 bitcoin_transaction::get_hash() const
|
||||
{
|
||||
const auto bytes = pack( *this, true) ;
|
||||
const auto hash = fc::sha256::hash( fc::sha256::hash(bytes.data(), bytes.size()) );
|
||||
std::reverse( hash.data(), hash.data() + hash.data_size() );
|
||||
return hash;
|
||||
}
|
||||
|
||||
fc::sha256 bitcoin_transaction::get_txid() const
|
||||
{
|
||||
const auto bytes = pack( *this, false );
|
||||
const auto hash = fc::sha256::hash( fc::sha256::hash(bytes.data(), bytes.size()) );
|
||||
std::reverse( hash.data(), hash.data() + hash.data_size() );
|
||||
return hash;
|
||||
}
|
||||
|
||||
size_t bitcoin_transaction::get_vsize() const
|
||||
{
|
||||
static const auto witness_scale_factor = 4;
|
||||
|
||||
fc::datastream<size_t> no_wit_ds;
|
||||
pack( no_wit_ds, *this, false );
|
||||
|
||||
fc::datastream<size_t> wit_ds;
|
||||
pack( wit_ds, *this, true );
|
||||
|
||||
const size_t weight = no_wit_ds.tellp() * ( witness_scale_factor - 1 ) + wit_ds.tellp();
|
||||
const size_t vsize = ( weight + witness_scale_factor - 1 ) / witness_scale_factor;
|
||||
|
||||
return vsize;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::set_version( int32_t version )
|
||||
{
|
||||
tx.nVersion = version;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::set_locktime(uint32_t lock_time)
|
||||
{
|
||||
tx.nLockTime = lock_time;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in( payment_type type, const fc::sha256& txid, uint32_t n_out, const bytes& script_code, bool front, uint32_t sequence )
|
||||
{
|
||||
out_point prevout;
|
||||
prevout.hash = txid;
|
||||
prevout.n = n_out;
|
||||
|
||||
tx_in txin;
|
||||
txin.prevout = prevout;
|
||||
txin.nSequence = sequence;
|
||||
|
||||
add_in( type, txin, script_code, front );
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in( payment_type type, tx_in txin, const bytes& script_code, bool front )
|
||||
{
|
||||
switch ( type ) {
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
FC_ASSERT( script_code != bytes() );
|
||||
txin.scriptSig = script_code;
|
||||
break;
|
||||
default:{
|
||||
if( txin.prevout.hash == fc::sha256("0000000000000000000000000000000000000000000000000000000000000000") ) { //coinbase
|
||||
FC_ASSERT( script_code != bytes() );
|
||||
txin.scriptSig = script_code;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( front ) {
|
||||
tx.vin.insert( tx.vin.begin(), txin );
|
||||
} else {
|
||||
tx.vin.push_back( txin );
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out( payment_type type, int64_t amount, const std::string& base58_address, bool front )
|
||||
{
|
||||
// TODO: add checks
|
||||
const auto address_bytes = fc::from_base58(base58_address);
|
||||
add_out( type, amount, bytes( address_bytes.begin() + 1, address_bytes.begin() + 1 + 20 ), front );
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out( payment_type type, int64_t amount, const fc::ecc::public_key_data& pubkey, bool front )
|
||||
{
|
||||
FC_ASSERT( is_payment_to_pubkey( type ) );
|
||||
|
||||
if ( type == payment_type::P2PK ) {
|
||||
const auto pubkey_bytes = bytes( pubkey.begin(), pubkey.begin() + pubkey.size() );
|
||||
add_out( type, amount, pubkey_bytes, front );
|
||||
} else {
|
||||
const auto hash256 = fc::sha256::hash( pubkey.begin(), pubkey.size() );
|
||||
const auto hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
|
||||
add_out( type, amount, bytes( hash160.data(), hash160.data() + hash160.data_size() ), front );
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out( payment_type type, int64_t amount, const bytes& script_code, bool front )
|
||||
{
|
||||
tx_out out;
|
||||
out.value = amount;
|
||||
out.scriptPubKey = get_script_pubkey( type, script_code );
|
||||
|
||||
if( front ) {
|
||||
tx.vout.insert( tx.vout.begin(), out );
|
||||
} else {
|
||||
tx.vout.push_back( out );
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out_all_type( const uint64_t& amount, const bitcoin_address& address, bool front )
|
||||
{
|
||||
switch( address.get_type() ) {
|
||||
case payment_type::P2PK: {
|
||||
bytes raw_address( address.get_raw_address() );
|
||||
fc::ecc::public_key_data public_key;
|
||||
std::copy( raw_address.begin(), raw_address.end(), public_key.begin() );
|
||||
add_out( address.get_type(), amount, public_key, front );
|
||||
break;
|
||||
}
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH: {
|
||||
add_out( address.get_type(), amount, address.get_address(), front );
|
||||
break;
|
||||
}
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH: {
|
||||
add_out( address.get_type(), amount, address.get_raw_address(), front );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool bitcoin_transaction_builder::is_payment_to_pubkey( payment_type type )
|
||||
{
|
||||
switch ( type ) {
|
||||
case payment_type::P2PK:
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bytes bitcoin_transaction_builder::get_script_pubkey( payment_type type, const bytes& script_code )
|
||||
{
|
||||
switch ( type ) {
|
||||
case payment_type::NULLDATA:
|
||||
return script_builder() << op::RETURN << script_code;
|
||||
case payment_type::P2PK:
|
||||
return script_builder() << script_code << op::CHECKSIG;
|
||||
case payment_type::P2PKH:
|
||||
return script_builder() << op::DUP << op::HASH160 << script_code << op::EQUALVERIFY << op::CHECKSIG;
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
return script_builder() << op::HASH160 << script_code << op::EQUAL;
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH:
|
||||
return script_builder() << op::_0 << script_code;
|
||||
default:
|
||||
return script_code;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
24
libraries/sidechain/include/sidechain/bech32.hpp
Normal file
24
libraries/sidechain/include/sidechain/bech32.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Bech32 is a string encoding format used in newer address types.
|
||||
// The output consists of a human-readable part (alphanumeric), a
|
||||
// separator character (1), and a base32 data section, the last
|
||||
// 6 characters of which are a checksum.
|
||||
//
|
||||
// For more information, see BIP 173.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sidechain { namespace bech32 {
|
||||
|
||||
/** Encode a Bech32 string. Returns the empty string in case of failure. */
|
||||
std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
|
||||
|
||||
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
|
||||
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
|
||||
|
||||
} } // namespace sidechain::bech32
|
||||
133
libraries/sidechain/include/sidechain/bitcoin_address.hpp
Normal file
133
libraries/sidechain/include/sidechain/bitcoin_address.hpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#pragma once
|
||||
|
||||
#include <sidechain/types.hpp>
|
||||
#include <sidechain/utils.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
const bytes op_num = {0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f}; // OP_1 - OP_15
|
||||
|
||||
class bitcoin_address
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
bitcoin_address() = default;
|
||||
|
||||
bitcoin_address( const std::string& addr ) : address( addr ), type( determine_type() ),
|
||||
raw_address( determine_raw_address() ) {}
|
||||
|
||||
bool operator==( const bitcoin_address& btc_addr ) const;
|
||||
|
||||
payment_type get_type() const { return type; }
|
||||
|
||||
std::string get_address() const { return address; }
|
||||
|
||||
bytes get_raw_address() const { return raw_address; }
|
||||
|
||||
bytes get_script() const;
|
||||
|
||||
private:
|
||||
|
||||
enum size_segwit_address { P2WSH = 32, P2WPKH = 20 };
|
||||
|
||||
payment_type determine_type();
|
||||
|
||||
bytes determine_raw_address();
|
||||
|
||||
bool check_segwit_address( const size_segwit_address& size ) const;
|
||||
|
||||
bool is_p2pk() const;
|
||||
|
||||
bool is_p2wpkh() const;
|
||||
|
||||
bool is_p2wsh() const;
|
||||
|
||||
bool is_p2pkh() const;
|
||||
|
||||
bool is_p2sh() const;
|
||||
|
||||
public:
|
||||
|
||||
std::string address;
|
||||
|
||||
payment_type type;
|
||||
|
||||
bytes raw_address;
|
||||
|
||||
};
|
||||
|
||||
class btc_multisig_address : public bitcoin_address
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
btc_multisig_address() = default;
|
||||
|
||||
btc_multisig_address( const size_t n_required, const accounts_keys& keys );
|
||||
|
||||
size_t count_intersection( const accounts_keys& keys ) const;
|
||||
|
||||
bytes get_redeem_script() const { return redeem_script; }
|
||||
|
||||
private:
|
||||
|
||||
void create_redeem_script();
|
||||
|
||||
void create_address();
|
||||
|
||||
public:
|
||||
|
||||
enum address_types { MAINNET_SCRIPT = 5, TESTNET_SCRIPT = 196 };
|
||||
|
||||
enum { OP_0 = 0x00, OP_EQUAL = 0x87, OP_HASH160 = 0xa9, OP_CHECKMULTISIG = 0xae };
|
||||
|
||||
bytes redeem_script;
|
||||
|
||||
size_t keys_required = 0;
|
||||
|
||||
accounts_keys witnesses_keys;
|
||||
|
||||
};
|
||||
|
||||
// multisig segwit address (P2WSH)
|
||||
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
|
||||
class btc_multisig_segwit_address : public btc_multisig_address
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
btc_multisig_segwit_address() = default;
|
||||
|
||||
btc_multisig_segwit_address( const size_t n_required, const accounts_keys& keys );
|
||||
|
||||
bool operator==( const btc_multisig_segwit_address& addr ) const;
|
||||
|
||||
bytes get_witness_script() const { return witness_script; }
|
||||
|
||||
std::vector<public_key_type> get_keys();
|
||||
|
||||
private:
|
||||
|
||||
void create_witness_script();
|
||||
|
||||
void create_segwit_address();
|
||||
|
||||
bytes get_address_bytes( const bytes& script_hash );
|
||||
|
||||
public:
|
||||
|
||||
bytes witness_script;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
FC_REFLECT( sidechain::bitcoin_address, (address)(type)(raw_address) );
|
||||
|
||||
FC_REFLECT_DERIVED( sidechain::btc_multisig_address, (sidechain::bitcoin_address),
|
||||
(redeem_script)(keys_required)(witnesses_keys) );
|
||||
|
||||
FC_REFLECT_DERIVED( sidechain::btc_multisig_segwit_address, (sidechain::btc_multisig_address), (witness_script) );
|
||||
69
libraries/sidechain/include/sidechain/bitcoin_script.hpp
Normal file
69
libraries/sidechain/include/sidechain/bitcoin_script.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
#include <sidechain/types.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
enum class op {
|
||||
// push value
|
||||
_0 = 0x00,
|
||||
_1 = 0x51,
|
||||
_2 = 0x52,
|
||||
_3 = 0x53,
|
||||
_4 = 0x54,
|
||||
_5 = 0x55,
|
||||
_6 = 0x56,
|
||||
_7 = 0x57,
|
||||
_8 = 0x58,
|
||||
_9 = 0x59,
|
||||
_10 = 0x5a,
|
||||
_11 = 0x5b,
|
||||
_12 = 0x5c,
|
||||
_13 = 0x5d,
|
||||
_14 = 0x5e,
|
||||
_15 = 0x5f,
|
||||
_16 = 0x60,
|
||||
|
||||
// control
|
||||
RETURN = 0x6a,
|
||||
|
||||
// stack ops
|
||||
DUP = 0x76,
|
||||
|
||||
// bit logic
|
||||
EQUAL = 0x87,
|
||||
EQUALVERIFY = 0x88,
|
||||
|
||||
// crypto
|
||||
HASH160 = 0xa9,
|
||||
CHECKSIG = 0xac,
|
||||
CHECKMULTISIG = 0xae,
|
||||
};
|
||||
|
||||
class script_builder {
|
||||
|
||||
public:
|
||||
|
||||
script_builder& operator<<( op opcode );
|
||||
|
||||
script_builder& operator<<( uint8_t number );
|
||||
|
||||
script_builder& operator<<( size_t size );
|
||||
|
||||
script_builder& operator<<( const bytes& sc );
|
||||
|
||||
script_builder& operator<<( const fc::sha256& hash );
|
||||
|
||||
script_builder& operator<<( const fc::ripemd160& hash );
|
||||
|
||||
script_builder& operator<<( const fc::ecc::public_key_data& pubkey_data );
|
||||
|
||||
operator bytes() const { return std::move( script ); }
|
||||
|
||||
private:
|
||||
|
||||
bytes script;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
100
libraries/sidechain/include/sidechain/bitcoin_transaction.hpp
Normal file
100
libraries/sidechain/include/sidechain/bitcoin_transaction.hpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
#include <sidechain/bitcoin_address.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
struct out_point {
|
||||
fc::sha256 hash;
|
||||
uint32_t n = 0;
|
||||
out_point() = default;
|
||||
out_point( fc::sha256 _hash, uint32_t _n ) : hash( _hash ), n( _n ) {}
|
||||
bool operator==( const out_point& op ) const;
|
||||
};
|
||||
|
||||
struct tx_in {
|
||||
|
||||
bool operator==( const tx_in& ti ) const;
|
||||
|
||||
out_point prevout;
|
||||
bytes scriptSig;
|
||||
uint32_t nSequence = 0xffffffff;
|
||||
std::vector<bytes> scriptWitness;
|
||||
};
|
||||
|
||||
struct tx_out {
|
||||
int64_t value = 0;
|
||||
bytes scriptPubKey;
|
||||
|
||||
bool operator==( const tx_out& to ) const;
|
||||
|
||||
bool is_p2wsh() const;
|
||||
|
||||
bool is_p2wpkh() const;
|
||||
|
||||
bool is_p2pkh() const;
|
||||
|
||||
bool is_p2sh() const;
|
||||
|
||||
bool is_p2pk() const;
|
||||
|
||||
bytes get_data_or_script() const;
|
||||
};
|
||||
|
||||
struct bitcoin_transaction {
|
||||
|
||||
bool operator!=( const bitcoin_transaction& bt ) const;
|
||||
|
||||
int32_t nVersion = 1;
|
||||
std::vector<tx_in> vin;
|
||||
std::vector<tx_out> vout;
|
||||
uint32_t nLockTime = 0;
|
||||
|
||||
fc::sha256 get_hash() const;
|
||||
fc::sha256 get_txid() const;
|
||||
size_t get_vsize() const;
|
||||
};
|
||||
|
||||
class bitcoin_transaction_builder
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
bitcoin_transaction_builder() = default;
|
||||
|
||||
bitcoin_transaction_builder( const bitcoin_transaction _tx ) : tx( _tx ) {}
|
||||
|
||||
void set_version( int32_t version );
|
||||
|
||||
void set_locktime( uint32_t lock_time );
|
||||
|
||||
void add_in( payment_type type, const fc::sha256& txid, uint32_t n_out,
|
||||
const bytes& script_code, bool front = false, uint32_t sequence = 0xffffffff );
|
||||
|
||||
void add_in( payment_type type, tx_in txin, const bytes& script_code, bool front = false );
|
||||
|
||||
void add_out( payment_type type, int64_t amount, const std::string& base58_address, bool front = false );
|
||||
|
||||
void add_out( payment_type type, int64_t amount, const fc::ecc::public_key_data& pubkey, bool front = false );
|
||||
|
||||
void add_out( payment_type type, int64_t amount, const bytes& script_code, bool front = false );
|
||||
|
||||
void add_out_all_type( const uint64_t& amount, const bitcoin_address& address, bool front = false );
|
||||
|
||||
bitcoin_transaction get_transaction() const { return tx; }
|
||||
|
||||
private:
|
||||
|
||||
inline bool is_payment_to_pubkey( payment_type type );
|
||||
|
||||
bytes get_script_pubkey( payment_type type, const bytes& script_code );
|
||||
|
||||
bitcoin_transaction tx;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
FC_REFLECT( sidechain::out_point, (hash)(n) )
|
||||
FC_REFLECT( sidechain::tx_in, (prevout)(scriptSig)(nSequence)(scriptWitness) )
|
||||
FC_REFLECT( sidechain::tx_out, (value)(scriptPubKey) )
|
||||
FC_REFLECT( sidechain::bitcoin_transaction, (nVersion)(vin)(vout)(nLockTime) )
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
|
||||
#include <sidechain/thread_safe_index.hpp>
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
using boost::multi_index_container;
|
||||
using namespace boost::multi_index;
|
||||
|
||||
namespace graphene { namespace chain { class database; } }
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
struct bitcoin_transaction_confirmations
|
||||
{
|
||||
bitcoin_transaction_confirmations() = default;
|
||||
|
||||
bitcoin_transaction_confirmations( const fc::sha256& trx_id, const std::set<fc::sha256>& vins ) :
|
||||
id( count_id_tx_conf++ ), transaction_id( trx_id ), valid_vins( vins ) {}
|
||||
|
||||
struct comparer {
|
||||
bool operator()( const bitcoin_transaction_confirmations& lhs, const bitcoin_transaction_confirmations& rhs ) const {
|
||||
if( lhs.is_confirmed_and_not_used() != rhs.is_confirmed_and_not_used() )
|
||||
return lhs.is_confirmed_and_not_used() < rhs.is_confirmed_and_not_used();
|
||||
return lhs.id < rhs.id;
|
||||
}
|
||||
};
|
||||
|
||||
static uint64_t count_id_tx_conf;
|
||||
uint64_t id;
|
||||
|
||||
bool is_confirmed_and_not_used() const { return !used && confirmed; }
|
||||
bool is_missing_and_not_used() const { return !used && missing; }
|
||||
|
||||
fc::sha256 transaction_id;
|
||||
std::set<fc::sha256> valid_vins;
|
||||
|
||||
uint64_t count_block = 0;
|
||||
bool confirmed = false;
|
||||
bool missing = false;
|
||||
bool used = false;
|
||||
};
|
||||
|
||||
struct by_hash;
|
||||
struct by_confirmed_and_not_used;
|
||||
struct by_missing_and_not_used;
|
||||
|
||||
using btc_tx_confirmations_index = boost::multi_index_container<bitcoin_transaction_confirmations,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_hash>, member<bitcoin_transaction_confirmations, fc::sha256, &bitcoin_transaction_confirmations::transaction_id>>,
|
||||
ordered_non_unique<tag<by_confirmed_and_not_used>, identity< bitcoin_transaction_confirmations >, bitcoin_transaction_confirmations::comparer >,
|
||||
ordered_non_unique<tag<by_missing_and_not_used>, identity< bitcoin_transaction_confirmations >, bitcoin_transaction_confirmations::comparer >
|
||||
>
|
||||
>;
|
||||
|
||||
}
|
||||
114
libraries/sidechain/include/sidechain/input_withdrawal_info.hpp
Normal file
114
libraries/sidechain/include/sidechain/input_withdrawal_info.hpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
|
||||
#include <sidechain/types.hpp>
|
||||
#include <sidechain/thread_safe_index.hpp>
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
|
||||
#include <graphene/chain/info_for_vout_object.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
using boost::multi_index_container;
|
||||
using namespace boost::multi_index;
|
||||
|
||||
namespace graphene { namespace chain { class database; } }
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
struct info_for_vin
|
||||
{
|
||||
info_for_vin() = default;
|
||||
|
||||
info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes(), bool _resend = false );
|
||||
|
||||
bool operator!=( const info_for_vin& obj ) const;
|
||||
|
||||
struct comparer {
|
||||
bool operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const;
|
||||
};
|
||||
|
||||
static uint64_t count_id_info_for_vin;
|
||||
uint64_t id;
|
||||
|
||||
fc::sha256 identifier;
|
||||
|
||||
prev_out out;
|
||||
std::string address;
|
||||
bytes script;
|
||||
|
||||
bool used = false;
|
||||
bool resend = false;
|
||||
};
|
||||
|
||||
struct by_id;
|
||||
struct by_identifier;
|
||||
struct by_id_resend_not_used;
|
||||
|
||||
using info_for_vin_index = boost::multi_index_container<info_for_vin,
|
||||
indexed_by<
|
||||
ordered_unique<tag<by_id>, member<info_for_vin, uint64_t, &info_for_vin::id>>,
|
||||
ordered_unique<tag<by_identifier>, member<info_for_vin, fc::sha256, &info_for_vin::identifier>>,
|
||||
ordered_non_unique<tag<by_id_resend_not_used>, identity< info_for_vin >, info_for_vin::comparer >
|
||||
>
|
||||
>;
|
||||
|
||||
class input_withdrawal_info
|
||||
{
|
||||
|
||||
public:
|
||||
input_withdrawal_info( graphene::chain::database& _db ) : db( _db ) {}
|
||||
|
||||
|
||||
std::vector<sidechain::bytes> get_redeem_scripts( const std::vector<info_for_vin>& info_vins );
|
||||
|
||||
std::vector<uint64_t> get_amounts( const std::vector<info_for_vin>& info_vins );
|
||||
|
||||
|
||||
fc::optional<info_for_vin> get_info_for_pw_vin();
|
||||
|
||||
|
||||
void insert_info_for_vin( const prev_out& out, const std::string& address, bytes script = bytes(), bool resend = false );
|
||||
|
||||
void modify_info_for_vin( const info_for_vin& obj, const std::function<void( info_for_vin& e )>& func );
|
||||
|
||||
void mark_as_used_vin( const info_for_vin& obj );
|
||||
|
||||
void mark_as_unused_vin( const info_for_vin& obj );
|
||||
|
||||
void remove_info_for_vin( const info_for_vin& obj );
|
||||
|
||||
fc::optional<info_for_vin> find_info_for_vin( fc::sha256 identifier );
|
||||
|
||||
size_t size_info_for_vins() { return info_for_vins.size(); }
|
||||
|
||||
std::vector<info_for_vin> get_info_for_vins();
|
||||
|
||||
|
||||
void insert_info_for_vout( const graphene::chain::account_id_type& payer, const std::string& data, const uint64_t& amount );
|
||||
|
||||
void mark_as_used_vout( const graphene::chain::info_for_vout_object& obj );
|
||||
|
||||
void mark_as_unused_vout( const graphene::chain::info_for_vout_object& obj );
|
||||
|
||||
void remove_info_for_vout( const info_for_vout& obj );
|
||||
|
||||
fc::optional<graphene::chain::info_for_vout_object> find_info_for_vout( graphene::chain::info_for_vout_id_type id );
|
||||
|
||||
size_t size_info_for_vouts();
|
||||
|
||||
std::vector<info_for_vout> get_info_for_vouts();
|
||||
|
||||
private:
|
||||
|
||||
graphene::chain::database& db;
|
||||
|
||||
thread_safe_index<info_for_vin_index> info_for_vins;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
FC_REFLECT( sidechain::info_for_vin, (identifier)(out)(address)(script)(used)(resend) )
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <sidechain/types.hpp>
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
|
||||
namespace graphene { namespace chain { class database; } }
|
||||
|
||||
using graphene::chain::primary_wallet_vout_object;
|
||||
using graphene::chain::primary_wallet_vout_id_type;
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class primary_wallet_vout_manager
|
||||
{
|
||||
|
||||
public:
|
||||
primary_wallet_vout_manager( graphene::chain::database& _db ) : db( _db ) {}
|
||||
|
||||
bool is_max_vouts() const;
|
||||
|
||||
fc::optional< primary_wallet_vout_object > get_latest_unused_vout() const;
|
||||
|
||||
fc::optional< primary_wallet_vout_object > get_vout( fc::sha256 hash_id ) const;
|
||||
|
||||
void create_new_vout( const sidechain::prev_out& out );
|
||||
|
||||
void delete_vouts_after( fc::sha256 hash_id );
|
||||
|
||||
void confirm_vout( fc::sha256 hash_id );
|
||||
|
||||
void mark_as_used_vout( fc::sha256 hash_id );
|
||||
|
||||
void mark_as_unused_vout( fc::sha256 hash_id );
|
||||
|
||||
private:
|
||||
|
||||
fc::sha256 create_hash_id( const std::string& hash_tx, const uint32_t& n_vout, const uint64_t& id ) const;
|
||||
|
||||
fc::optional< primary_wallet_vout_id_type > get_vout_id( fc::sha256 hash_id ) const;
|
||||
|
||||
graphene::chain::database& db;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
34
libraries/sidechain/include/sidechain/segwit_addr.hpp
Normal file
34
libraries/sidechain/include/sidechain/segwit_addr.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* Copyright (c) 2017 Pieter Wuille
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace sidechain { namespace segwit_addr {
|
||||
|
||||
/** Decode a SegWit address. Returns (witver, witprog). witver = -1 means failure. */
|
||||
std::pair<int, std::vector<uint8_t> > decode(const std::string& hrp, const std::string& addr);
|
||||
|
||||
/** Encode a SegWit address. Empty string means failure. */
|
||||
std::string encode(const std::string& hrp, int witver, const std::vector<uint8_t>& witprog);
|
||||
|
||||
} }
|
||||
360
libraries/sidechain/include/sidechain/serialize.hpp
Normal file
360
libraries/sidechain/include/sidechain/serialize.hpp
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
#pragma once
|
||||
|
||||
#include <sidechain/types.hpp>
|
||||
#include <sidechain/bitcoin_transaction.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
inline void write_compact_size( bytes& vec, size_t size )
|
||||
{
|
||||
bytes sb;
|
||||
sb.reserve( 2 );
|
||||
if ( size < 253 ) {
|
||||
sb.insert( sb.end(), static_cast<uint8_t>( size ) );
|
||||
} else if ( size <= std::numeric_limits<unsigned short>::max() ) {
|
||||
uint16_t tmp = htole16( static_cast<uint16_t>( size ) );
|
||||
sb.insert( sb.end(), static_cast<uint8_t>( 253 ) );
|
||||
sb.insert( sb.end(), reinterpret_cast<const char*>( tmp ), reinterpret_cast<const char*>( tmp ) + sizeof( tmp ) );
|
||||
} else if ( size <= std::numeric_limits<unsigned int>::max() ) {
|
||||
uint32_t tmp = htole32( static_cast<uint32_t>( size ) );
|
||||
sb.insert( sb.end(), static_cast<uint8_t>( 254 ) );
|
||||
sb.insert( sb.end(), reinterpret_cast<const char*>( tmp ), reinterpret_cast<const char*>( tmp ) + sizeof( tmp ) );
|
||||
} else {
|
||||
uint64_t tmp = htole64( static_cast<uint64_t>( size ) );
|
||||
sb.insert( sb.end(), static_cast<uint8_t>( 255 ) );
|
||||
sb.insert( sb.end(), reinterpret_cast<const char*>( tmp ), reinterpret_cast<const char*>( tmp ) + sizeof( tmp ) );
|
||||
}
|
||||
vec.insert( vec.end(), sb.begin(), sb.end() );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack_compact_size(Stream& s, size_t size)
|
||||
{
|
||||
if ( size < 253 ) {
|
||||
fc::raw::pack( s, static_cast<uint8_t>( size ) );
|
||||
} else if ( size <= std::numeric_limits<unsigned short>::max() ) {
|
||||
fc::raw::pack( s, static_cast<uint8_t>( 253 ) );
|
||||
fc::raw::pack( s, htole16( static_cast<uint16_t>( size ) ) );
|
||||
} else if ( size <= std::numeric_limits<unsigned int>::max() ) {
|
||||
fc::raw::pack( s, static_cast<uint8_t>( 254 ) );
|
||||
fc::raw::pack( s, htole32(static_cast<uint32_t>( size ) ) );
|
||||
} else {
|
||||
fc::raw::pack( s, static_cast<uint8_t>( 255 ) );
|
||||
fc::raw::pack( s, htole64( static_cast<uint64_t>( size ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline uint64_t unpack_compact_size( Stream& s )
|
||||
{
|
||||
uint8_t size;
|
||||
uint64_t size_ret;
|
||||
|
||||
fc::raw::unpack( s, size );
|
||||
|
||||
if (size < 253) {
|
||||
size_ret = size;
|
||||
} else if ( size == 253 ) {
|
||||
uint16_t tmp;
|
||||
fc::raw::unpack( s, tmp );
|
||||
size_ret = le16toh( tmp );
|
||||
if ( size_ret < 253 )
|
||||
FC_THROW_EXCEPTION( fc::parse_error_exception, "non-canonical unpack_compact_size()" );
|
||||
} else if ( size == 254 ) {
|
||||
uint32_t tmp;
|
||||
fc::raw::unpack( s, tmp );
|
||||
size_ret = le32toh( tmp );
|
||||
if ( size_ret < 0x10000u )
|
||||
FC_THROW_EXCEPTION( fc::parse_error_exception, "non-canonical unpack_compact_size()" );
|
||||
} else {
|
||||
uint32_t tmp;
|
||||
fc::raw::unpack( s, tmp );
|
||||
size_ret = le64toh( tmp );
|
||||
if ( size_ret < 0x100000000ULL )
|
||||
FC_THROW_EXCEPTION( fc::parse_error_exception, "non-canonical unpack_compact_size()" );
|
||||
}
|
||||
|
||||
if ( size_ret > 0x08000000 )
|
||||
FC_THROW_EXCEPTION( fc::parse_error_exception, "unpack_compact_size(): size too large" );
|
||||
|
||||
return size_ret;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack( Stream& s, const std::vector<char>& v )
|
||||
{
|
||||
pack_compact_size( s, v.size() );
|
||||
if ( !v.empty() )
|
||||
s.write( v.data(), v.size() );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void unpack( Stream& s, std::vector<char>& v )
|
||||
{
|
||||
const auto size = unpack_compact_size( s );
|
||||
v.resize( size );
|
||||
if ( size )
|
||||
s.read( v.data(), size );
|
||||
}
|
||||
|
||||
template<typename Stream, typename T>
|
||||
inline void pack( Stream& s, const T& val )
|
||||
{
|
||||
fc::raw::pack( s, val );
|
||||
}
|
||||
|
||||
template<typename Stream, typename T>
|
||||
inline void unpack( Stream& s, T& val )
|
||||
{
|
||||
fc::raw::unpack( s, val );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack( Stream& s, const out_point& op )
|
||||
{
|
||||
fc::sha256 reversed( op.hash );
|
||||
std::reverse( reversed.data(), reversed.data() + reversed.data_size() );
|
||||
s.write( reversed.data(), reversed.data_size() );
|
||||
pack( s, op.n );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void unpack( Stream& s, out_point& op )
|
||||
{
|
||||
uint64_t hash_size = op.hash.data_size();
|
||||
std::unique_ptr<char[]> hash_data( new char[hash_size] );
|
||||
s.read( hash_data.get(), hash_size );
|
||||
std::reverse( hash_data.get(), hash_data.get() + hash_size );
|
||||
|
||||
op.hash = fc::sha256( hash_data.get(), hash_size );
|
||||
unpack( s, op.n );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack( Stream& s, const tx_in& in )
|
||||
{
|
||||
pack( s, in.prevout );
|
||||
pack( s, in.scriptSig );
|
||||
pack( s, in.nSequence );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void unpack( Stream& s, tx_in& in )
|
||||
{
|
||||
unpack( s, in.prevout );
|
||||
unpack( s, in.scriptSig );
|
||||
unpack( s, in.nSequence );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack( Stream& s, const tx_out& out )
|
||||
{
|
||||
pack( s, out.value );
|
||||
pack( s, out.scriptPubKey );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void unpack( Stream& s, tx_out& out )
|
||||
{
|
||||
unpack( s, out.value );
|
||||
unpack( s, out.scriptPubKey );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack( Stream& s, const bitcoin_transaction& tx, bool with_witness = true )
|
||||
{
|
||||
uint8_t flags = 0;
|
||||
|
||||
if ( with_witness ) {
|
||||
for ( const auto& in : tx.vin ) {
|
||||
if ( !in.scriptWitness.empty() ) {
|
||||
flags |= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pack( s, tx.nVersion );
|
||||
|
||||
if ( flags ) {
|
||||
pack_compact_size( s, 0 );
|
||||
pack( s, flags );
|
||||
}
|
||||
|
||||
pack_compact_size( s, tx.vin.size() );
|
||||
for ( const auto& in : tx.vin )
|
||||
pack( s, in );
|
||||
|
||||
pack_compact_size( s, tx.vout.size() );
|
||||
for ( const auto& out : tx.vout )
|
||||
pack( s, out );
|
||||
|
||||
if ( flags & 1 ) {
|
||||
for ( const auto in : tx.vin ) {
|
||||
pack_compact_size( s, in.scriptWitness.size() );
|
||||
for ( const auto& sc : in.scriptWitness )
|
||||
pack( s, sc );
|
||||
}
|
||||
}
|
||||
|
||||
pack( s, tx.nLockTime );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void unpack( Stream& s, bitcoin_transaction& tx )
|
||||
{
|
||||
uint8_t flags = 0;
|
||||
|
||||
unpack( s, tx.nVersion );
|
||||
|
||||
auto vin_size = unpack_compact_size( s );
|
||||
if ( vin_size == 0 ) {
|
||||
unpack( s, flags );
|
||||
vin_size = unpack_compact_size( s );
|
||||
}
|
||||
|
||||
tx.vin.reserve( vin_size );
|
||||
for ( size_t i = 0; i < vin_size; i++ ) {
|
||||
tx_in in;
|
||||
unpack( s, in );
|
||||
tx.vin.push_back( in );
|
||||
}
|
||||
|
||||
const auto vout_size = unpack_compact_size( s );
|
||||
tx.vout.reserve( vout_size );
|
||||
for ( size_t i = 0; i < vout_size; i++ ) {
|
||||
tx_out out;
|
||||
unpack( s, out );
|
||||
tx.vout.push_back( out );
|
||||
}
|
||||
|
||||
if ( flags & 1 ) {
|
||||
for ( auto& in : tx.vin ) {
|
||||
uint64_t stack_size = unpack_compact_size( s );
|
||||
in.scriptWitness.reserve( stack_size );
|
||||
for ( uint64_t i = 0; i < stack_size; i++ ) {
|
||||
std::vector<char> script;
|
||||
unpack( s, script );
|
||||
in.scriptWitness.push_back( script );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unpack( s, tx.nLockTime );
|
||||
}
|
||||
|
||||
inline std::vector<char> pack( const bitcoin_transaction& v, bool with_witness = true )
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
pack( ps, v, with_witness );
|
||||
std::vector<char> vec( ps.tellp() );
|
||||
|
||||
if( !vec.empty() ) {
|
||||
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||
pack( ds, v, with_witness );
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline bitcoin_transaction unpack( const std::vector<char>& s )
|
||||
{ try {
|
||||
bitcoin_transaction tmp;
|
||||
if( !s.empty() ) {
|
||||
fc::datastream<const char*> ds( s.data(), size_t( s.size() ) );
|
||||
unpack(ds, tmp);
|
||||
}
|
||||
return tmp;
|
||||
} FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type","transaction" ) ) }
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack_tx_signature( Stream& s, const std::vector<char>& scriptPubKey, const bitcoin_transaction& tx, unsigned int in_index, int hash_type )
|
||||
{
|
||||
pack( s, tx.nVersion );
|
||||
|
||||
pack_compact_size( s, tx.vin.size() );
|
||||
for ( size_t i = 0; i < tx.vin.size(); i++ ) {
|
||||
const auto& in = tx.vin[i];
|
||||
pack( s, in.prevout );
|
||||
if ( i == in_index )
|
||||
pack( s, scriptPubKey );
|
||||
else
|
||||
pack_compact_size( s, 0 ); // Blank signature
|
||||
pack( s, in.nSequence );
|
||||
}
|
||||
|
||||
pack_compact_size( s, tx.vout.size() );
|
||||
for ( const auto& out : tx.vout )
|
||||
pack( s, out );
|
||||
|
||||
pack( s, tx.nLockTime );
|
||||
pack( s, hash_type );
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
inline void pack_tx_witness_signature( Stream& s, const std::vector<char>& scriptCode, const bitcoin_transaction& tx, unsigned int in_index, int64_t amount, int hash_type )
|
||||
{
|
||||
|
||||
fc::sha256 hash_prevouts;
|
||||
fc::sha256 hash_sequence;
|
||||
fc::sha256 hash_output;
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for ( const auto in : tx.vin )
|
||||
pack( ps, in.prevout );
|
||||
|
||||
std::vector<char> vec( ps.tellp() );
|
||||
if ( vec.size() ) {
|
||||
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||
for ( const auto in : tx.vin )
|
||||
pack( ds, in.prevout );
|
||||
}
|
||||
|
||||
hash_prevouts = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||
}
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for ( const auto in : tx.vin )
|
||||
pack( ps, in.nSequence );
|
||||
|
||||
std::vector<char> vec( ps.tellp() );
|
||||
if ( vec.size() ) {
|
||||
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||
for ( const auto in : tx.vin )
|
||||
pack( ds, in.nSequence );
|
||||
}
|
||||
|
||||
hash_sequence = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||
};
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for ( const auto out : tx.vout )
|
||||
pack( ps, out );
|
||||
|
||||
std::vector<char> vec( ps.tellp() );
|
||||
if ( vec.size() ) {
|
||||
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||
for ( const auto out : tx.vout )
|
||||
pack( ds, out );
|
||||
}
|
||||
|
||||
hash_output = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||
}
|
||||
|
||||
pack( s, tx.nVersion );
|
||||
pack( s, hash_prevouts );
|
||||
pack( s, hash_sequence );
|
||||
|
||||
pack( s, tx.vin[in_index].prevout );
|
||||
pack( s, scriptCode );
|
||||
pack( s, amount );
|
||||
pack( s, tx.vin[in_index].nSequence );
|
||||
|
||||
pack( s, hash_output );
|
||||
pack( s, tx.nLockTime );
|
||||
pack( s, hash_type );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <sidechain/bitcoin_transaction.hpp>
|
||||
|
||||
using namespace sidechain;
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class sidechain_condensing_tx
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
sidechain_condensing_tx( const std::vector<info_for_vin>& vin_info, const std::vector<info_for_vout>& vout_info );
|
||||
|
||||
void create_pw_vin( const info_for_vin& vin_info, bool front = true );
|
||||
|
||||
void create_pw_vout( const uint64_t amount, const bytes& wit_script_out, bool front = true );
|
||||
|
||||
void create_vouts_for_witness_fee( const accounts_keys& witness_active_keys, bool front = true );
|
||||
|
||||
static uint64_t get_estimate_tx_size( bitcoin_transaction tx, size_t number_witness );
|
||||
|
||||
void subtract_fee( const uint64_t& fee, const uint16_t& witnesses_percentage );
|
||||
|
||||
bitcoin_transaction get_transaction() const { return tb.get_transaction(); }
|
||||
|
||||
uint64_t get_amount_vins() { return amount_vins; }
|
||||
|
||||
uint64_t get_amount_transfer_to_bitcoin() { return amount_transfer_to_bitcoin; }
|
||||
|
||||
private:
|
||||
|
||||
void create_vins_for_condensing_tx( const std::vector<info_for_vin>& vin_info );
|
||||
|
||||
void create_vouts_for_condensing_tx( const std::vector<info_for_vout>& vout_info );
|
||||
|
||||
uint32_t count_transfer_vin = 0;
|
||||
|
||||
uint32_t count_transfer_vout = 0;
|
||||
|
||||
uint32_t count_witness_vout = 0;
|
||||
|
||||
uint64_t amount_vins = 0;
|
||||
|
||||
uint64_t amount_transfer_to_bitcoin = 0;
|
||||
|
||||
bitcoin_transaction_builder tb;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
struct sidechain_parameters_extension {
|
||||
uint8_t maximum_condensing_tx_vins = SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VINS;
|
||||
uint8_t maximum_condensing_tx_vouts = SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VOUTS;
|
||||
uint8_t maximum_unconfirmed_vouts = SIDECHAIN_DEFAULT_MAX_UNCONFIRMED_VOUTS;
|
||||
uint16_t percent_payment_to_witnesses = SIDECHAIN_DEFAULT_PERCENT_PAYMENT_TO_WITNESSES;
|
||||
uint8_t multisig_sigs_num = SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG;
|
||||
uint8_t confirmations_num = SIDECHAIN_DEFAULT_NUMBER_OF_CONFIRMATIONS;
|
||||
|
||||
graphene::chain::account_id_type managing_account;
|
||||
graphene::chain::asset_id_type asset_id;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
FC_REFLECT( sidechain::sidechain_parameters_extension,
|
||||
(maximum_condensing_tx_vins)
|
||||
(maximum_condensing_tx_vouts)
|
||||
(maximum_unconfirmed_vouts)
|
||||
(percent_payment_to_witnesses)
|
||||
(multisig_sigs_num)
|
||||
(confirmations_num)
|
||||
(managing_account)
|
||||
(asset_id)
|
||||
)
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
#include <sidechain/types.hpp>
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <graphene/chain/protocol/bitcoin_transaction.hpp>
|
||||
#include <graphene/chain/protocol/operations.hpp>
|
||||
|
||||
namespace graphene { namespace chain { class database; } };
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class sidechain_proposal_checker
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
sidechain_proposal_checker( graphene::chain::database& _db ) : db(_db) {}
|
||||
|
||||
bool check_bitcoin_transaction_send_operation( const bitcoin_transaction_send_operation& op );
|
||||
|
||||
bool check_reuse( const graphene::chain::operation& op );
|
||||
|
||||
bool check_witness_opportunity_to_approve( const witness_object& current_witness, const proposal_object& proposal );
|
||||
|
||||
bool check_witnesses_keys( const witness_object& current_witness, const proposal_object& proposal );
|
||||
|
||||
bool check_bitcoin_transaction_revert_operation( const bitcoin_transaction_revert_operation& op );
|
||||
|
||||
private:
|
||||
|
||||
bool check_info_for_pw_vin( const info_for_vin& info_for_vin );
|
||||
|
||||
bool check_info_for_vins( const std::vector<info_for_vin>& info_for_vins );
|
||||
|
||||
bool check_info_for_vouts( const std::vector<info_for_vout_id_type>& info_for_vout_ids );
|
||||
|
||||
bool check_transaction( const bitcoin_transaction_send_operation& btc_trx_op );
|
||||
|
||||
bool check_reuse_pw_vin( const fc::sha256& pw_vin );
|
||||
|
||||
bool check_reuse_user_vins( const std::vector<fc::sha256>& user_vin_identifiers );
|
||||
|
||||
bool check_reuse_vouts( const std::vector<info_for_vout_id_type>& user_vout_ids );
|
||||
|
||||
std::set<fc::sha256> pw_vin_ident;
|
||||
std::set<fc::sha256> user_vin_ident;
|
||||
std::set<info_for_vout_id_type> vout_ids;
|
||||
|
||||
graphene::chain::database& db;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <sidechain/types.hpp>
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class bitcoin_transaction;
|
||||
|
||||
fc::sha256 get_signature_hash( const bitcoin_transaction& tx, const bytes& scriptPubKey, int64_t amount,
|
||||
size_t in_index, int hash_type, bool is_witness );
|
||||
|
||||
std::vector<char> privkey_sign( const bytes& privkey, const fc::sha256 &hash, const secp256k1_context_t* context_sign = nullptr );
|
||||
|
||||
std::vector<bytes> sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
|
||||
const std::vector<uint64_t>& amounts, const bytes& privkey,
|
||||
const secp256k1_context_t* context_sign = nullptr, int hash_type = 1 );
|
||||
|
||||
void sign_witness_transaction_finalize( bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts );
|
||||
|
||||
bool verify_sig( const bytes& sig, const bytes& pubkey, const bytes& msg, const secp256k1_context_t* context );
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
|
||||
const std::vector<uint64_t>& amounts, const secp256k1_context_t* context );
|
||||
|
||||
}
|
||||
82
libraries/sidechain/include/sidechain/thread_safe_index.hpp
Normal file
82
libraries/sidechain/include/sidechain/thread_safe_index.hpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <fc/optional.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
template<class T1>
|
||||
class thread_safe_index
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
using iterator = typename T1::iterator;
|
||||
|
||||
std::pair<iterator,bool> insert( const typename T1::value_type& value ) {
|
||||
std::lock_guard<std::recursive_mutex> locker( lock );
|
||||
return data.insert( value );
|
||||
}
|
||||
|
||||
template<class T2, typename Key>
|
||||
bool modify( const Key _key, const std::function<void( typename T1::value_type& e)>& func ) {
|
||||
std::lock_guard<std::recursive_mutex> locker( lock );
|
||||
const auto option = find_iterator<T2>( _key );
|
||||
if( !option )
|
||||
return false;
|
||||
data.modify( data.iterator_to( **option ), [&func]( typename T1::value_type& obj ) { func( obj ); } );
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T2, typename Key>
|
||||
bool remove( const Key _key ) {
|
||||
std::lock_guard<std::recursive_mutex> locker( lock );
|
||||
const auto option = find_iterator<T2>( _key );
|
||||
if( !option )
|
||||
return false;
|
||||
data.erase( data.iterator_to( **option ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t size() {
|
||||
std::lock_guard<std::recursive_mutex> locker( lock );
|
||||
return data.size();
|
||||
}
|
||||
|
||||
template<class T2, typename Key>
|
||||
fc::optional<typename T1::value_type> find( const Key _key ) {
|
||||
std::lock_guard<std::recursive_mutex> locker( lock );
|
||||
auto& index = data.template get<T2>();
|
||||
auto it = index.find( _key );
|
||||
if( it != index.end() ) {
|
||||
return fc::optional<typename T1::value_type>(*it);
|
||||
}
|
||||
return fc::optional<typename T1::value_type>();
|
||||
}
|
||||
|
||||
template<class T2>
|
||||
void safe_for(std::function<void(typename T1::template index<T2>::type::iterator itr1,
|
||||
typename T1::template index<T2>::type::iterator itr2)> func) {
|
||||
std::lock_guard<std::recursive_mutex> locker( lock );
|
||||
auto& index = data.template get<T2>();
|
||||
func(index.begin(), index.end());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<class T2, typename Key>
|
||||
fc::optional<typename T1::template index<T2>::type::iterator> find_iterator( const Key _key ) {
|
||||
auto& index = data.template get<T2>();
|
||||
auto it = index.find( _key );
|
||||
if( it == index.end() )
|
||||
return fc::optional<typename T1::template index<T2>::type::iterator>();
|
||||
return it;
|
||||
}
|
||||
|
||||
std::recursive_mutex lock;
|
||||
|
||||
T1 data;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
60
libraries/sidechain/include/sidechain/types.hpp
Normal file
60
libraries/sidechain/include/sidechain/types.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class bitcoin_transaction;
|
||||
|
||||
using bytes = std::vector<char>;
|
||||
using accounts_keys = std::map< graphene::chain::account_id_type, graphene::chain::public_key_type >;
|
||||
using info_for_vout = graphene::chain::info_for_vout_object;
|
||||
using full_btc_transaction = std::pair<bitcoin_transaction, uint64_t>;
|
||||
|
||||
enum class payment_type
|
||||
{
|
||||
NULLDATA,
|
||||
P2PK,
|
||||
P2PKH,
|
||||
P2SH,
|
||||
P2WPKH,
|
||||
P2WSH,
|
||||
P2SH_WPKH,
|
||||
P2SH_WSH
|
||||
};
|
||||
|
||||
enum class sidechain_proposal_type
|
||||
{
|
||||
ISSUE_BTC,
|
||||
SEND_BTC_TRANSACTION,
|
||||
REVERT_BTC_TRANSACTION
|
||||
};
|
||||
|
||||
struct prev_out
|
||||
{
|
||||
bool operator!=( const prev_out& obj ) const
|
||||
{
|
||||
if( this->hash_tx != obj.hash_tx ||
|
||||
this->n_vout != obj.n_vout ||
|
||||
this->amount != obj.amount )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash_tx;
|
||||
uint32_t n_vout;
|
||||
uint64_t amount;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
FC_REFLECT_ENUM( sidechain::payment_type, (NULLDATA)(P2PK)(P2PKH)(P2SH)(P2WPKH)(P2WSH)(P2SH_WPKH)(P2SH_WSH) );
|
||||
FC_REFLECT_ENUM( sidechain::sidechain_proposal_type, (ISSUE_BTC)(SEND_BTC_TRANSACTION)(REVERT_BTC_TRANSACTION) );
|
||||
FC_REFLECT( sidechain::prev_out, (hash_tx)(n_vout)(amount) );
|
||||
15
libraries/sidechain/include/sidechain/utils.hpp
Normal file
15
libraries/sidechain/include/sidechain/utils.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <sidechain/types.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
bytes parse_hex( const std::string& str );
|
||||
|
||||
std::vector<bytes> get_pubkey_from_redeemScript( bytes script );
|
||||
|
||||
bytes public_key_data_to_bytes( const fc::ecc::public_key_data& key );
|
||||
|
||||
}
|
||||
207
libraries/sidechain/input_withdrawal_info.cpp
Normal file
207
libraries/sidechain/input_withdrawal_info.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
#include <sidechain/bitcoin_transaction_confirmations.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
uint64_t info_for_vin::count_id_info_for_vin = 0;
|
||||
uint64_t bitcoin_transaction_confirmations::count_id_tx_conf = 0;
|
||||
|
||||
info_for_vin::info_for_vin( const prev_out& _out, const std::string& _address, bytes _script, bool _resend ) :
|
||||
id( count_id_info_for_vin++ ), out( _out ), address( _address ), script( _script ), resend( _resend )
|
||||
{
|
||||
identifier = fc::sha256::hash( out.hash_tx + std::to_string( out.n_vout ) );
|
||||
}
|
||||
|
||||
bool info_for_vin::operator!=( const info_for_vin& obj ) const
|
||||
{
|
||||
if( this->identifier != obj.identifier ||
|
||||
this->out.hash_tx != obj.out.hash_tx ||
|
||||
this->out.n_vout != obj.out.n_vout ||
|
||||
this->out.amount != obj.out.amount ||
|
||||
this->address != obj.address ||
|
||||
this->script != obj.script )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool info_for_vin::comparer::operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const
|
||||
{
|
||||
if( lhs.used != rhs.used ) {
|
||||
return lhs.used < rhs.used;
|
||||
} else if ( lhs.resend != rhs.resend ) {
|
||||
return lhs.resend > rhs.resend;
|
||||
}
|
||||
return lhs.id < rhs.id;
|
||||
}
|
||||
|
||||
std::vector<bytes> input_withdrawal_info::get_redeem_scripts( const std::vector<info_for_vin>& info_vins )
|
||||
{
|
||||
std::vector<bytes> redeem_scripts;
|
||||
const auto& bitcoin_address_idx = db.get_index_type<bitcoin_address_index>().indices().get< by_address >();
|
||||
for( const auto& v : info_vins ) {
|
||||
const auto& pbtc_address = bitcoin_address_idx.find( v.address );
|
||||
redeem_scripts.push_back( pbtc_address->address.get_redeem_script() );
|
||||
}
|
||||
return redeem_scripts;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> input_withdrawal_info::get_amounts( const std::vector<info_for_vin>& info_vins )
|
||||
{
|
||||
std::vector<uint64_t> amounts;
|
||||
for( const auto& v : info_vins ) {
|
||||
amounts.push_back( v.out.amount );
|
||||
}
|
||||
return amounts;
|
||||
}
|
||||
|
||||
fc::optional<info_for_vin> input_withdrawal_info::get_info_for_pw_vin()
|
||||
{
|
||||
fc::optional< primary_wallet_vout_object > vout = db.pw_vout_manager.get_latest_unused_vout();
|
||||
if( !vout.valid() || db.pw_vout_manager.is_max_vouts() ) {
|
||||
return fc::optional<info_for_vin>();
|
||||
}
|
||||
|
||||
const auto& pw_address = db.get_latest_PW().address;
|
||||
|
||||
info_for_vin vin;
|
||||
vin.identifier = vout->hash_id;
|
||||
vin.out = vout->vout;
|
||||
vin.address = pw_address.get_address();
|
||||
vin.script = pw_address.get_witness_script();
|
||||
|
||||
return vin;
|
||||
}
|
||||
|
||||
void input_withdrawal_info::insert_info_for_vin( const prev_out& out, const std::string& address, bytes script, bool resend )
|
||||
{
|
||||
info_for_vins.insert( info_for_vin( out, address, script, resend ) );
|
||||
}
|
||||
|
||||
void input_withdrawal_info::modify_info_for_vin( const info_for_vin& obj, const std::function<void( info_for_vin& e )>& func )
|
||||
{
|
||||
info_for_vins.modify<by_identifier>( obj.identifier, func );
|
||||
}
|
||||
|
||||
void input_withdrawal_info::mark_as_used_vin( const info_for_vin& obj )
|
||||
{
|
||||
info_for_vins.modify<by_identifier>( obj.identifier, [&]( info_for_vin& o ) {
|
||||
o.used = true;
|
||||
});
|
||||
}
|
||||
|
||||
void input_withdrawal_info::mark_as_unused_vin( const info_for_vin& obj )
|
||||
{
|
||||
info_for_vins.modify<by_identifier>( obj.identifier, [&]( info_for_vin& o ) {
|
||||
o.used = false;
|
||||
});
|
||||
}
|
||||
|
||||
void input_withdrawal_info::remove_info_for_vin( const info_for_vin& obj )
|
||||
{
|
||||
info_for_vins.remove<by_identifier>( obj.identifier );
|
||||
}
|
||||
|
||||
fc::optional<info_for_vin> input_withdrawal_info::find_info_for_vin( fc::sha256 identifier )
|
||||
{
|
||||
return info_for_vins.find<by_identifier>( identifier );
|
||||
}
|
||||
|
||||
std::vector<info_for_vin> input_withdrawal_info::get_info_for_vins()
|
||||
{
|
||||
std::vector<info_for_vin> result;
|
||||
const auto max_vins = db.get_sidechain_params().maximum_condensing_tx_vins;
|
||||
|
||||
info_for_vins.safe_for<by_id_resend_not_used>( [&]( info_for_vin_index::index<by_id_resend_not_used>::type::iterator itr_b,
|
||||
info_for_vin_index::index<by_id_resend_not_used>::type::iterator itr_e )
|
||||
{
|
||||
for( size_t i = 0; itr_b != itr_e && i < max_vins && !itr_b->used; i++ ) {
|
||||
info_for_vin vin;
|
||||
vin.identifier = itr_b->identifier;
|
||||
vin.out.hash_tx = itr_b->out.hash_tx;
|
||||
vin.out.n_vout = itr_b->out.n_vout;
|
||||
vin.out.amount = itr_b->out.amount;
|
||||
vin.address = itr_b->address;
|
||||
vin.script = itr_b->script;
|
||||
|
||||
result.push_back( vin );
|
||||
++itr_b;
|
||||
}
|
||||
} );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void input_withdrawal_info::insert_info_for_vout( const graphene::chain::account_id_type& payer, const std::string& data, const uint64_t& amount )
|
||||
{
|
||||
db.create<graphene::chain::info_for_vout_object>([&](graphene::chain::info_for_vout_object& obj) {
|
||||
obj.payer = payer;
|
||||
obj.address = bitcoin_address( data );
|
||||
obj.amount = amount;
|
||||
});
|
||||
}
|
||||
|
||||
void input_withdrawal_info::mark_as_used_vout( const graphene::chain::info_for_vout_object& obj )
|
||||
{
|
||||
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto itr = info_for_vout_idx.find( obj.id );
|
||||
|
||||
db.modify<graphene::chain::info_for_vout_object>( *itr, [&]( graphene::chain::info_for_vout_object& o ) {
|
||||
o.used = true;
|
||||
});
|
||||
}
|
||||
|
||||
void input_withdrawal_info::mark_as_unused_vout( const graphene::chain::info_for_vout_object& obj )
|
||||
{
|
||||
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto itr = info_for_vout_idx.find( obj.id );
|
||||
|
||||
db.modify<graphene::chain::info_for_vout_object>( *itr, [&]( graphene::chain::info_for_vout_object& o ) {
|
||||
o.used = false;
|
||||
});
|
||||
}
|
||||
|
||||
void input_withdrawal_info::remove_info_for_vout( const info_for_vout& obj )
|
||||
{
|
||||
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto itr = info_for_vout_idx.find( obj.id );
|
||||
db.remove( *itr );
|
||||
}
|
||||
|
||||
fc::optional<graphene::chain::info_for_vout_object> input_withdrawal_info::find_info_for_vout( graphene::chain::info_for_vout_id_type id )
|
||||
{
|
||||
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto itr = info_for_vout_idx.find( id );
|
||||
if( itr != info_for_vout_idx.end() )
|
||||
return fc::optional<graphene::chain::info_for_vout_object>( *itr );
|
||||
return fc::optional<graphene::chain::info_for_vout_object>();
|
||||
}
|
||||
|
||||
size_t input_withdrawal_info::size_info_for_vouts()
|
||||
{
|
||||
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
return info_for_vout_idx.size();
|
||||
}
|
||||
|
||||
std::vector<info_for_vout> input_withdrawal_info::get_info_for_vouts()
|
||||
{
|
||||
std::vector<info_for_vout> result;
|
||||
|
||||
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id_and_not_used >();
|
||||
const auto max_vouts = db.get_sidechain_params().maximum_condensing_tx_vouts;
|
||||
|
||||
auto itr = info_for_vout_idx.begin();
|
||||
for(size_t i = 0; i < max_vouts && itr != info_for_vout_idx.end() && !itr->used; i++) {
|
||||
|
||||
result.push_back( *itr );
|
||||
++itr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
7
libraries/sidechain/network/CMakeLists.txt
Normal file
7
libraries/sidechain/network/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
file( GLOB HEADERS "include/sidechain/network/*.hpp" )
|
||||
file( GLOB SOURCES "*.cpp" )
|
||||
|
||||
add_library( sidechain_network STATIC ${SOURCES} ${HEADERS} )
|
||||
|
||||
target_link_libraries( sidechain_network PUBLIC graphene_chain fc zmq )
|
||||
target_include_directories( sidechain_network PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
140
libraries/sidechain/network/bitcoin_rpc_client.cpp
Normal file
140
libraries/sidechain/network/bitcoin_rpc_client.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
#include <sidechain/network/bitcoin_rpc_client.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <fc/network/ip.hpp>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
bitcoin_rpc_client::bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password ):
|
||||
ip( _ip ), rpc_port( _rpc ), user( _user ), password( _password )
|
||||
{
|
||||
authorization.key = "Authorization";
|
||||
authorization.val = "Basic " + fc::base64_encode( user + ":" + password );
|
||||
}
|
||||
|
||||
std::string bitcoin_rpc_client::receive_full_block( const std::string& block_hash )
|
||||
{
|
||||
fc::http::connection conn;
|
||||
conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) );
|
||||
|
||||
const auto url = "http://" + ip + ":" + std::to_string( rpc_port ) + "/rest/block/" + block_hash + ".json";
|
||||
|
||||
const auto reply = conn.request( "GET", url );
|
||||
if ( reply.status != 200 )
|
||||
return "";
|
||||
|
||||
ilog( "Receive Bitcoin block: ${hash}", ( "hash", block_hash ) );
|
||||
return std::string( reply.body.begin(), reply.body.end() );
|
||||
}
|
||||
|
||||
int32_t bitcoin_rpc_client::receive_confirmations_tx( const std::string& tx_hash )
|
||||
{
|
||||
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getrawtransaction\", \"params\": [") +
|
||||
std::string("\"") + tx_hash + std::string("\"") + ", " + "true" + std::string("] }");
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
if ( reply.status != 200 )
|
||||
return 0;
|
||||
|
||||
const auto result = std::string( reply.body.begin(), reply.body.end() );
|
||||
|
||||
std::stringstream ss( result );
|
||||
boost::property_tree::ptree tx;
|
||||
boost::property_tree::read_json( ss, tx );
|
||||
|
||||
if( tx.count( "result" ) ) {
|
||||
if( tx.get_child( "result" ).count( "confirmations" ) ) {
|
||||
return tx.get_child( "result" ).get_child( "confirmations" ).get_value<int64_t>();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
bool bitcoin_rpc_client::receive_mempool_entry_tx( const std::string& tx_hash )
|
||||
{
|
||||
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getmempoolentry\", \"params\": [") +
|
||||
std::string("\"") + tx_hash + std::string("\"") + std::string("] }");
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
if ( reply.status != 200 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t bitcoin_rpc_client::receive_estimated_fee()
|
||||
{
|
||||
static const auto confirmation_target_blocks = 6;
|
||||
|
||||
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimated_feerate\", \"method\": \"estimatesmartfee\", \"params\": [") +
|
||||
std::to_string(confirmation_target_blocks) + std::string("] }");
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
if( reply.status != 200 )
|
||||
return 0;
|
||||
|
||||
std::stringstream ss( std::string( reply.body.begin(), reply.body.end() ) );
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json( ss, json );
|
||||
|
||||
if( json.count( "result" ) )
|
||||
if ( json.get_child( "result" ).count( "feerate" ) ) {
|
||||
auto feerate_str = json.get_child( "result" ).get_child( "feerate" ).get_value<std::string>();
|
||||
feerate_str.erase( std::remove( feerate_str.begin(), feerate_str.end(), '.' ), feerate_str.end() );
|
||||
return std::stoll( feerate_str );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex )
|
||||
{
|
||||
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"send_tx\", \"method\": \"sendrawtransaction\", \"params\": [") +
|
||||
std::string("\"") + tx_hex + std::string("\"") + std::string("] }");
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
if( reply.body.empty() )
|
||||
return;
|
||||
|
||||
std::string reply_str( reply.body.begin(), reply.body.end() );
|
||||
|
||||
std::stringstream ss(reply_str);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json( ss, json );
|
||||
|
||||
if( reply.status == 200 ) {
|
||||
idump(( tx_hex ));
|
||||
return;
|
||||
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
|
||||
const auto error_code = json.get_child( "error" ).get_child( "code" ).get_value<int>();
|
||||
if( error_code == -27 ) // transaction already in block chain
|
||||
return;
|
||||
|
||||
wlog( "BTC tx is not sent! Reply: ${msg}", ("msg", reply_str) );
|
||||
}
|
||||
}
|
||||
|
||||
bool bitcoin_rpc_client::connection_is_not_defined() const
|
||||
{
|
||||
return ip.empty() || rpc_port == 0 || user.empty() || password.empty();
|
||||
}
|
||||
|
||||
fc::http::reply bitcoin_rpc_client::send_post_request( std::string body )
|
||||
{
|
||||
fc::http::connection conn;
|
||||
conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) );
|
||||
|
||||
const auto url = "http://" + ip + ":" + std::to_string( rpc_port );
|
||||
|
||||
return conn.request( "POST", url, body, fc::http::headers{authorization} );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fc/network/http/connection.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class bitcoin_rpc_client
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ;
|
||||
|
||||
std::string receive_full_block( const std::string& block_hash );
|
||||
|
||||
int32_t receive_confirmations_tx( const std::string& tx_hash );
|
||||
|
||||
bool receive_mempool_entry_tx( const std::string& tx_hash );
|
||||
|
||||
uint64_t receive_estimated_fee();
|
||||
|
||||
void send_btc_tx( const std::string& tx_hex );
|
||||
|
||||
bool connection_is_not_defined() const;
|
||||
|
||||
private:
|
||||
|
||||
fc::http::reply send_post_request( std::string body );
|
||||
|
||||
std::string ip;
|
||||
uint32_t rpc_port;
|
||||
std::string user;
|
||||
std::string password;
|
||||
|
||||
fc::http::header authorization;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include <sidechain/network/zmq_listener.hpp>
|
||||
#include <sidechain/network/bitcoin_rpc_client.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class sidechain_net_manager
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
sidechain_net_manager() {};
|
||||
|
||||
sidechain_net_manager( graphene::chain::database* _db, std::string _ip,
|
||||
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password );
|
||||
|
||||
~sidechain_net_manager() { db = nullptr; }
|
||||
|
||||
void initialize_manager( graphene::chain::database* _db, std::string _ip,
|
||||
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password );
|
||||
|
||||
void update_tx_infos( const std::string& block_hash );
|
||||
|
||||
void update_tx_approvals();
|
||||
|
||||
void update_estimated_fee();
|
||||
|
||||
void send_btc_tx( const sidechain::bitcoin_transaction& trx );
|
||||
|
||||
bool connection_is_not_defined() const;
|
||||
|
||||
private:
|
||||
|
||||
void handle_block( const std::string& block_hash );
|
||||
|
||||
std::vector<info_for_vin> extract_info_from_block( const std::string& _block );
|
||||
|
||||
void update_transaction_status( std::vector<fc::sha256> trx_for_check );
|
||||
|
||||
std::set<fc::sha256> get_valid_vins( const std::string tx_hash );
|
||||
|
||||
inline uint64_t parse_amount(std::string raw);
|
||||
|
||||
std::unique_ptr<zmq_listener> listener;
|
||||
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
|
||||
graphene::chain::database* db;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
#include <zmq.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class zmq_listener
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
zmq_listener( std::string _ip, uint32_t _zmq );
|
||||
|
||||
bool connection_is_not_defined() const { return zmq_port == 0; }
|
||||
|
||||
fc::signal<void( const std::string& )> block_received;
|
||||
|
||||
private:
|
||||
|
||||
void handle_zmq();
|
||||
std::vector<zmq::message_t> receive_multipart();
|
||||
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t socket;
|
||||
};
|
||||
|
||||
}
|
||||
202
libraries/sidechain/network/sidechain_net_manager.cpp
Normal file
202
libraries/sidechain/network/sidechain_net_manager.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
#include <sidechain/network/sidechain_net_manager.hpp>
|
||||
#include <sidechain/serialize.hpp>
|
||||
#include <sidechain/sidechain_parameters.hpp>
|
||||
|
||||
#include <fc/network/http/connection.hpp>
|
||||
#include <fc/network/ip.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
sidechain_net_manager::sidechain_net_manager( graphene::chain::database* _db, std::string _ip,
|
||||
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password )
|
||||
{
|
||||
initialize_manager(_db, _ip, _zmq, _rpc, _user, _password );
|
||||
}
|
||||
|
||||
void sidechain_net_manager::initialize_manager( graphene::chain::database* _db, std::string _ip,
|
||||
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password )
|
||||
{
|
||||
fc::http::connection conn;
|
||||
try {
|
||||
conn.connect_to( fc::ip::endpoint( fc::ip::address( _ip ), _rpc ) );
|
||||
} catch ( fc::exception e ) {
|
||||
elog( "No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", _ip) ("port", _rpc) );
|
||||
FC_ASSERT( false );
|
||||
}
|
||||
|
||||
listener = std::unique_ptr<zmq_listener>( new zmq_listener( _ip, _zmq ) );
|
||||
bitcoin_client = std::unique_ptr<bitcoin_rpc_client>( new bitcoin_rpc_client( _ip, _rpc, _user, _password ) );
|
||||
db = _db;
|
||||
|
||||
listener->block_received.connect([this]( const std::string& block_hash ) {
|
||||
std::thread( &sidechain_net_manager::handle_block, this, block_hash ).detach();
|
||||
} );
|
||||
|
||||
db->send_btc_tx.connect([this]( const sidechain::bitcoin_transaction& trx ) {
|
||||
std::thread( &sidechain_net_manager::send_btc_tx, this, trx ).detach();
|
||||
} );
|
||||
}
|
||||
|
||||
void sidechain_net_manager::update_tx_infos( const std::string& block_hash )
|
||||
{
|
||||
std::string block = bitcoin_client->receive_full_block( block_hash );
|
||||
if( block != "" ) {
|
||||
const auto& vins = extract_info_from_block( block );
|
||||
const auto& addr_idx = db->get_index_type<bitcoin_address_index>().indices().get<by_address>();
|
||||
for( const auto& v : vins ) {
|
||||
const auto& addr_itr = addr_idx.find( v.address );
|
||||
FC_ASSERT( addr_itr != addr_idx.end() );
|
||||
db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address, addr_itr->address.get_witness_script() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::update_tx_approvals()
|
||||
{
|
||||
std::vector<fc::sha256> trx_for_check;
|
||||
const auto& confirmations_num = db->get_sidechain_params().confirmations_num;
|
||||
|
||||
db->bitcoin_confirmations.safe_for<by_hash>([&]( btc_tx_confirmations_index::iterator itr_b, btc_tx_confirmations_index::iterator itr_e ){
|
||||
for(auto iter = itr_b; iter != itr_e; iter++) {
|
||||
db->bitcoin_confirmations.modify<by_hash>( iter->transaction_id, [&]( bitcoin_transaction_confirmations& obj ) {
|
||||
obj.count_block++;
|
||||
});
|
||||
|
||||
if( iter->count_block == confirmations_num ) {
|
||||
trx_for_check.push_back( iter->transaction_id );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
update_transaction_status( trx_for_check );
|
||||
|
||||
}
|
||||
|
||||
void sidechain_net_manager::update_estimated_fee()
|
||||
{
|
||||
db->estimated_feerate = bitcoin_client->receive_estimated_fee();
|
||||
}
|
||||
|
||||
void sidechain_net_manager::send_btc_tx( const sidechain::bitcoin_transaction& trx )
|
||||
{
|
||||
std::set<fc::sha256> valid_vins;
|
||||
for( const auto& v : trx.vin ) {
|
||||
valid_vins.insert( v.prevout.hash );
|
||||
}
|
||||
db->bitcoin_confirmations.insert( bitcoin_transaction_confirmations( trx.get_txid(), valid_vins ) );
|
||||
|
||||
FC_ASSERT( !bitcoin_client->connection_is_not_defined() );
|
||||
const auto tx_hex = fc::to_hex( pack( trx ) );
|
||||
|
||||
bitcoin_client->send_btc_tx( tx_hex );
|
||||
}
|
||||
|
||||
bool sidechain_net_manager::connection_is_not_defined() const
|
||||
{
|
||||
return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined();
|
||||
}
|
||||
|
||||
void sidechain_net_manager::handle_block( const std::string& block_hash )
|
||||
{
|
||||
update_tx_approvals();
|
||||
update_estimated_fee();
|
||||
update_tx_infos( block_hash );
|
||||
}
|
||||
|
||||
std::vector<info_for_vin> sidechain_net_manager::extract_info_from_block( const std::string& _block )
|
||||
{
|
||||
std::stringstream ss( _block );
|
||||
boost::property_tree::ptree block;
|
||||
boost::property_tree::read_json( ss, block );
|
||||
|
||||
std::vector<info_for_vin> result;
|
||||
|
||||
const auto& addr_idx = db->get_index_type<bitcoin_address_index>().indices().get<by_address>();
|
||||
|
||||
for (const auto& tx_child : block.get_child("tx")) {
|
||||
const auto& tx = tx_child.second;
|
||||
|
||||
for ( const auto& o : tx.get_child("vout") ) {
|
||||
const auto script = o.second.get_child("scriptPubKey");
|
||||
|
||||
if( !script.count("addresses") ) continue;
|
||||
|
||||
for (const auto& addr : script.get_child("addresses")) { // in which cases there can be more addresses?
|
||||
const auto address_base58 = addr.second.get_value<std::string>();
|
||||
|
||||
if( !addr_idx.count( address_base58 ) ) continue;
|
||||
|
||||
info_for_vin vin;
|
||||
vin.out.hash_tx = tx.get_child("txid").get_value<std::string>();
|
||||
vin.out.amount = parse_amount( o.second.get_child( "value" ).get_value<std::string>() );
|
||||
vin.out.n_vout = o.second.get_child( "n" ).get_value<uint32_t>();
|
||||
vin.address = address_base58;
|
||||
result.push_back( vin );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::set<fc::sha256> sidechain_net_manager::get_valid_vins( const std::string tx_hash )
|
||||
{
|
||||
const auto& confirmations_obj = db->bitcoin_confirmations.find<sidechain::by_hash>( fc::sha256( tx_hash ) );
|
||||
FC_ASSERT( confirmations_obj.valid() );
|
||||
|
||||
std::set<fc::sha256> valid_vins;
|
||||
for( const auto& v : confirmations_obj->valid_vins ) {
|
||||
auto confirmations = bitcoin_client->receive_confirmations_tx( v.str() );
|
||||
if( confirmations == 0 ) {
|
||||
continue;
|
||||
}
|
||||
valid_vins.insert( v );
|
||||
}
|
||||
return valid_vins;
|
||||
}
|
||||
|
||||
void sidechain_net_manager::update_transaction_status( std::vector<fc::sha256> trx_for_check )
|
||||
{
|
||||
const auto& confirmations_num = db->get_sidechain_params().confirmations_num;
|
||||
|
||||
for( const auto& trx : trx_for_check ) {
|
||||
auto confirmations = bitcoin_client->receive_confirmations_tx( trx.str() );
|
||||
db->bitcoin_confirmations.modify<by_hash>( trx, [&]( bitcoin_transaction_confirmations& obj ) {
|
||||
obj.count_block = confirmations;
|
||||
});
|
||||
|
||||
if( confirmations >= confirmations_num ) {
|
||||
db->bitcoin_confirmations.modify<by_hash>( trx, [&]( bitcoin_transaction_confirmations& obj ) {
|
||||
obj.confirmed = true;
|
||||
});
|
||||
|
||||
} else if( confirmations == 0 ) {
|
||||
auto is_in_mempool = bitcoin_client->receive_mempool_entry_tx( trx.str() );
|
||||
|
||||
std::set<fc::sha256> valid_vins;
|
||||
if( !is_in_mempool ) {
|
||||
valid_vins = get_valid_vins( trx.str() );
|
||||
}
|
||||
|
||||
db->bitcoin_confirmations.modify<by_hash>( trx, [&]( bitcoin_transaction_confirmations& obj ) {
|
||||
obj.missing = !is_in_mempool;
|
||||
obj.valid_vins = valid_vins;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes dot from amount output: "50.00000000"
|
||||
inline uint64_t sidechain_net_manager::parse_amount(std::string raw) {
|
||||
raw.erase(std::remove(raw.begin(), raw.end(), '.'), raw.end());
|
||||
return std::stoll(raw);
|
||||
}
|
||||
|
||||
}
|
||||
45
libraries/sidechain/network/zmq_listener.cpp
Normal file
45
libraries/sidechain/network/zmq_listener.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include <sidechain/network/zmq_listener.hpp>
|
||||
#include <boost/algorithm/hex.hpp>
|
||||
#include <thread>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
zmq_listener::zmq_listener( std::string _ip, uint32_t _zmq ): ip( _ip ), zmq_port( _zmq ), ctx( 1 ), socket( ctx, ZMQ_SUB )
|
||||
{
|
||||
std::thread( &zmq_listener::handle_zmq, this ).detach();
|
||||
}
|
||||
|
||||
std::vector<zmq::message_t> zmq_listener::receive_multipart()
|
||||
{
|
||||
std::vector<zmq::message_t> msgs;
|
||||
|
||||
int32_t more;
|
||||
size_t more_size = sizeof( more );
|
||||
while ( true ) {
|
||||
zmq::message_t msg;
|
||||
socket.recv( &msg, 0 );
|
||||
socket.getsockopt( ZMQ_RCVMORE, &more, &more_size );
|
||||
|
||||
if ( !more )
|
||||
break;
|
||||
msgs.push_back( std::move(msg) );
|
||||
}
|
||||
|
||||
return msgs;
|
||||
}
|
||||
|
||||
void zmq_listener::handle_zmq()
|
||||
{
|
||||
socket.setsockopt( ZMQ_SUBSCRIBE, "hashblock", 0 );
|
||||
socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) );
|
||||
|
||||
while ( true ) {
|
||||
auto msg = receive_multipart();
|
||||
const auto header = std::string( static_cast<char*>( msg[0].data() ), msg[0].size() );
|
||||
const auto hash = boost::algorithm::hex( std::string( static_cast<char*>( msg[1].data() ), msg[1].size() ) );
|
||||
|
||||
block_received( hash );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
136
libraries/sidechain/primary_wallet_vout_manager.cpp
Normal file
136
libraries/sidechain/primary_wallet_vout_manager.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
#include <sidechain/primary_wallet_vout_manager.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
#include <graphene/chain/config.hpp>
|
||||
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
bool primary_wallet_vout_manager::is_max_vouts() const
|
||||
{
|
||||
const auto& PW_vout_idx = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
return !( ( PW_vout_idx.size() - 1 ) <= db.get_sidechain_params().maximum_unconfirmed_vouts );
|
||||
}
|
||||
|
||||
fc::optional< primary_wallet_vout_object > primary_wallet_vout_manager::get_latest_unused_vout() const
|
||||
{
|
||||
const auto& PW_vout_idx = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto itr = PW_vout_idx.end();
|
||||
FC_ASSERT( itr != PW_vout_idx.begin() );
|
||||
|
||||
itr--;
|
||||
if( itr->used )
|
||||
return fc::optional< primary_wallet_vout_object >();
|
||||
return fc::optional< primary_wallet_vout_object > (*itr);
|
||||
}
|
||||
|
||||
fc::optional< primary_wallet_vout_object > primary_wallet_vout_manager::get_vout( fc::sha256 hash_id ) const
|
||||
{
|
||||
const auto& PW_vout_by_hash_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_hash_id >();
|
||||
const auto& itr_hash_id = PW_vout_by_hash_id.find( hash_id );
|
||||
if( itr_hash_id == PW_vout_by_hash_id.end() )
|
||||
return fc::optional< primary_wallet_vout_object >();
|
||||
return fc::optional< primary_wallet_vout_object >( *itr_hash_id );
|
||||
}
|
||||
|
||||
void primary_wallet_vout_manager::create_new_vout( const sidechain::prev_out& out )
|
||||
{
|
||||
const auto& next_pw_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().get_next_id().instance();
|
||||
db.create<primary_wallet_vout_object>([&]( primary_wallet_vout_object& obj ) {
|
||||
obj.vout = out;
|
||||
obj.hash_id = create_hash_id( out.hash_tx, out.n_vout, next_pw_id );
|
||||
obj.confirmed = false;
|
||||
obj.used = false;
|
||||
});
|
||||
}
|
||||
|
||||
void primary_wallet_vout_manager::delete_vouts_after( fc::sha256 hash_id )
|
||||
{
|
||||
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto vout_id = get_vout_id( hash_id );
|
||||
if( !vout_id.valid() )
|
||||
return;
|
||||
|
||||
auto itr = PW_vout_by_id.find( *vout_id );
|
||||
itr++;
|
||||
|
||||
while( itr != PW_vout_by_id.end() )
|
||||
{
|
||||
auto temp = itr;
|
||||
itr++;
|
||||
db.remove( *temp );
|
||||
}
|
||||
}
|
||||
|
||||
void primary_wallet_vout_manager::confirm_vout( fc::sha256 hash_id )
|
||||
{
|
||||
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto vout_id = get_vout_id( hash_id );
|
||||
FC_ASSERT( vout_id.valid() );
|
||||
|
||||
auto itr = PW_vout_by_id.find( *vout_id );
|
||||
|
||||
db.modify(*itr, [&]( primary_wallet_vout_object& PW_vout ) {
|
||||
PW_vout.confirmed = true;
|
||||
});
|
||||
|
||||
if( itr != PW_vout_by_id.begin() ){
|
||||
itr--;
|
||||
FC_ASSERT( itr->confirmed == true );
|
||||
db.remove(*itr);
|
||||
}
|
||||
}
|
||||
|
||||
void primary_wallet_vout_manager::mark_as_used_vout( fc::sha256 hash_id )
|
||||
{
|
||||
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto vout_id = get_vout_id( hash_id );
|
||||
FC_ASSERT( vout_id.valid() );
|
||||
|
||||
auto itr = PW_vout_by_id.find( *vout_id );
|
||||
|
||||
db.modify(*itr, [&]( primary_wallet_vout_object& PW_vout ) {
|
||||
PW_vout.used = true;
|
||||
});
|
||||
|
||||
if( itr != PW_vout_by_id.begin() ){
|
||||
itr--;
|
||||
FC_ASSERT( itr->used == true );
|
||||
}
|
||||
}
|
||||
|
||||
void primary_wallet_vout_manager::mark_as_unused_vout( fc::sha256 hash_id )
|
||||
{
|
||||
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
auto vout_id = get_vout_id( hash_id );
|
||||
FC_ASSERT( vout_id.valid() );
|
||||
|
||||
auto itr = PW_vout_by_id.find( *vout_id );
|
||||
|
||||
db.modify(*itr, [&]( primary_wallet_vout_object& PW_vout ) {
|
||||
PW_vout.used = false;
|
||||
});
|
||||
}
|
||||
|
||||
fc::optional< graphene::chain::primary_wallet_vout_id_type > primary_wallet_vout_manager::get_vout_id( fc::sha256 hash_id ) const
|
||||
{
|
||||
const auto& PW_vout_by_hash_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_hash_id >();
|
||||
const auto& itr_hash_id = PW_vout_by_hash_id.find( hash_id );
|
||||
if( itr_hash_id == PW_vout_by_hash_id.end() )
|
||||
return fc::optional< graphene::chain::primary_wallet_vout_id_type >();
|
||||
return fc::optional< graphene::chain::primary_wallet_vout_id_type >( itr_hash_id->get_id() );
|
||||
}
|
||||
|
||||
fc::sha256 primary_wallet_vout_manager::create_hash_id( const std::string& hash_tx, const uint32_t& n_vout, const uint64_t& id ) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << id;
|
||||
std::string id_hex = std::string( 16 - ss.str().size(), '0' ) + ss.str();
|
||||
|
||||
std::string hash_str = fc::sha256::hash( hash_tx + std::to_string( n_vout ) ).str();
|
||||
std::string final_hash_id = std::string( hash_str.begin(), hash_str.begin() + 48 ) + id_hex;
|
||||
|
||||
return fc::sha256( final_hash_id );
|
||||
}
|
||||
|
||||
}
|
||||
81
libraries/sidechain/segwit_addr.cpp
Normal file
81
libraries/sidechain/segwit_addr.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* Copyright (c) 2017 Pieter Wuille
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sidechain/segwit_addr.hpp>
|
||||
#include <sidechain/bech32.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** Convert from one power-of-2 number base to another. */
|
||||
template<int frombits, int tobits, bool pad>
|
||||
bool convertbits(data& out, const data& in) {
|
||||
int acc = 0;
|
||||
int bits = 0;
|
||||
const int maxv = (1 << tobits) - 1;
|
||||
const int max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||
for (size_t i = 0; i < in.size(); ++i) {
|
||||
int value = in[i];
|
||||
acc = ((acc << frombits) | value) & max_acc;
|
||||
bits += frombits;
|
||||
while (bits >= tobits) {
|
||||
bits -= tobits;
|
||||
out.push_back((acc >> bits) & maxv);
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits) out.push_back((acc << (tobits - bits)) & maxv);
|
||||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sidechain { namespace segwit_addr {
|
||||
|
||||
/** Decode a SegWit address. */
|
||||
std::pair<int, data> decode(const std::string& hrp, const std::string& addr) {
|
||||
std::pair<std::string, data> dec = sidechain::bech32::Decode(addr);
|
||||
if (dec.first != hrp || dec.second.size() < 1) return std::make_pair(-1, data());
|
||||
data conv;
|
||||
if (!convertbits<5, 8, false>(conv, data(dec.second.begin() + 1, dec.second.end())) ||
|
||||
conv.size() < 2 || conv.size() > 40 || dec.second[0] > 16 || (dec.second[0] == 0 &&
|
||||
conv.size() != 20 && conv.size() != 32)) {
|
||||
return std::make_pair(-1, data());
|
||||
}
|
||||
return std::make_pair(dec.second[0], conv);
|
||||
}
|
||||
|
||||
/** Encode a SegWit address. */
|
||||
std::string encode(const std::string& hrp, int witver, const data& witprog) {
|
||||
data enc;
|
||||
enc.push_back(witver);
|
||||
convertbits<8, 5, true>(enc, witprog);
|
||||
std::string ret = sidechain::bech32::Encode(hrp, enc);
|
||||
if (decode(hrp, ret).first == -1) return "";
|
||||
return ret;
|
||||
}
|
||||
|
||||
} }
|
||||
107
libraries/sidechain/sidechain_condensing_tx.cpp
Normal file
107
libraries/sidechain/sidechain_condensing_tx.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#include <sidechain/sidechain_condensing_tx.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
sidechain_condensing_tx::sidechain_condensing_tx( const std::vector<info_for_vin>& vin_info, const std::vector<info_for_vout>& vout_info )
|
||||
{
|
||||
create_vins_for_condensing_tx( vin_info );
|
||||
create_vouts_for_condensing_tx( vout_info );
|
||||
}
|
||||
|
||||
void sidechain_condensing_tx::create_pw_vin( const info_for_vin& vin_info, bool front )
|
||||
{
|
||||
bytes witness_script_temp = {0x22};
|
||||
witness_script_temp.insert( witness_script_temp.end(), vin_info.script.begin(), vin_info.script.end() );
|
||||
tb.add_in( payment_type::P2WSH, fc::sha256( vin_info.out.hash_tx ), vin_info.out.n_vout, witness_script_temp, front );
|
||||
|
||||
amount_vins += vin_info.out.amount;
|
||||
}
|
||||
|
||||
void sidechain_condensing_tx::create_pw_vout( const uint64_t amount, const bytes& wit_script_out, bool front )
|
||||
{
|
||||
tb.add_out( payment_type::P2WSH, amount, bytes(wit_script_out.begin() + 2, wit_script_out.end()), front );
|
||||
}
|
||||
|
||||
void sidechain_condensing_tx::create_vins_for_condensing_tx( const std::vector<info_for_vin>& vin_info )
|
||||
{
|
||||
for( const auto& info : vin_info ) {
|
||||
bytes witness_script_temp = {0x22};
|
||||
witness_script_temp.insert( witness_script_temp.end(), info.script.begin(), info.script.end() );
|
||||
tb.add_in( payment_type::P2SH_WSH, fc::sha256( info.out.hash_tx ), info.out.n_vout, witness_script_temp );
|
||||
amount_vins += info.out.amount;
|
||||
count_transfer_vin++;
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_condensing_tx::create_vouts_for_condensing_tx( const std::vector<info_for_vout>& vout_info )
|
||||
{
|
||||
for( const auto& info : vout_info ) {
|
||||
tb.add_out_all_type( info.amount, info.address );
|
||||
amount_transfer_to_bitcoin += info.amount;
|
||||
count_transfer_vout++;
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_condensing_tx::create_vouts_for_witness_fee( const accounts_keys& witness_active_keys, bool front )
|
||||
{
|
||||
for( auto& key : witness_active_keys ) {
|
||||
tb.add_out( payment_type::P2PK, 0, key.second, front );
|
||||
count_witness_vout++;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t sidechain_condensing_tx::get_estimate_tx_size( bitcoin_transaction tx, size_t number_witness )
|
||||
{
|
||||
bytes temp_sig(72, 0x00);
|
||||
bytes temp_key(34, 0x00);
|
||||
bytes temp_script(3, 0x00);
|
||||
for(size_t i = 0; i < number_witness; i++) {
|
||||
temp_script.insert(temp_script.begin() + 1, temp_key.begin(), temp_key.end());
|
||||
}
|
||||
|
||||
std::vector<bytes> temp_scriptWitness = { {},{temp_sig},{temp_sig},{temp_sig},{temp_sig},{temp_sig},{temp_script} };
|
||||
for( auto& vin : tx.vin ) {
|
||||
vin.scriptWitness = temp_scriptWitness;
|
||||
}
|
||||
|
||||
return tx.get_vsize();
|
||||
}
|
||||
|
||||
void sidechain_condensing_tx::subtract_fee( const uint64_t& fee, const uint16_t& witnesses_percentage )
|
||||
{
|
||||
bitcoin_transaction tx = get_transaction();
|
||||
|
||||
uint64_t fee_size = fee / ( count_transfer_vin + count_transfer_vout );
|
||||
if( fee % ( count_transfer_vin + count_transfer_vout ) != 0 ) {
|
||||
fee_size += 1;
|
||||
}
|
||||
|
||||
bool is_pw_vin = tx.vout.size() > ( count_witness_vout + count_transfer_vout );
|
||||
if( is_pw_vin ) {
|
||||
tx.vout[0].value = tx.vout[0].value - fee_size * count_transfer_vin;
|
||||
}
|
||||
|
||||
uint64_t fee_witnesses = 0;
|
||||
size_t offset = is_pw_vin ? 1 + count_witness_vout : count_witness_vout;
|
||||
for( ; offset < tx.vout.size(); offset++ ) {
|
||||
uint64_t amount_without_fee_size = tx.vout[offset].value - fee_size;
|
||||
uint64_t amount_fee_witness = ( amount_without_fee_size * witnesses_percentage ) / GRAPHENE_100_PERCENT;
|
||||
tx.vout[offset].value = amount_without_fee_size;
|
||||
tx.vout[offset].value -= amount_fee_witness;
|
||||
fee_witnesses += amount_fee_witness;
|
||||
}
|
||||
|
||||
if( count_witness_vout > 0 ) {
|
||||
uint64_t fee_witness = fee_witnesses / count_witness_vout;
|
||||
size_t limit = is_pw_vin ? 1 + count_witness_vout : count_witness_vout;
|
||||
size_t offset = is_pw_vin ? 1 : 0;
|
||||
FC_ASSERT( fee_witness > 0 );
|
||||
for( ; offset < limit; offset++ ) {
|
||||
tx.vout[offset].value += fee_witness;
|
||||
}
|
||||
}
|
||||
|
||||
tb = std::move( bitcoin_transaction_builder( tx ) );
|
||||
}
|
||||
|
||||
}
|
||||
214
libraries/sidechain/sidechain_proposal_checker.cpp
Normal file
214
libraries/sidechain/sidechain_proposal_checker.cpp
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#include <sidechain/sidechain_proposal_checker.hpp>
|
||||
#include <sidechain/sidechain_condensing_tx.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/bitcoin_transaction_object.hpp>
|
||||
#include <graphene/chain/info_for_used_vin_object.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
bool sidechain_proposal_checker::check_bitcoin_transaction_send_operation( const bitcoin_transaction_send_operation& op )
|
||||
{
|
||||
bool info_for_pw_vin = check_info_for_pw_vin( op.pw_vin );
|
||||
bool info_for_vins = check_info_for_vins( op.vins );
|
||||
bool info_for_vouts = check_info_for_vouts( op.vouts );
|
||||
bool transaction = check_transaction( op );
|
||||
|
||||
return info_for_pw_vin && info_for_vins && info_for_vouts && transaction;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_reuse_pw_vin( const fc::sha256& pw_vin )
|
||||
{
|
||||
const auto& pw_vin_status = pw_vin_ident.insert( pw_vin );
|
||||
if( !pw_vin_status.second ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_reuse_user_vins( const std::vector<fc::sha256>& user_vin_identifiers )
|
||||
{
|
||||
for( const auto& vin : user_vin_identifiers ) {
|
||||
const auto& user_vin_status = user_vin_ident.insert( vin );
|
||||
if( !user_vin_status.second ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_reuse_vouts( const std::vector<info_for_vout_id_type>& user_vout_ids )
|
||||
{
|
||||
for( const auto& vout : user_vout_ids ) {
|
||||
const auto& user_vout_status = vout_ids.insert( vout );
|
||||
if( !user_vout_status.second ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_reuse( const operation& op )
|
||||
{
|
||||
fc::sha256 pw_vin_identifier;
|
||||
std::vector<fc::sha256> user_vin_identifiers;
|
||||
std::vector<info_for_vout_id_type> user_vout_ids;
|
||||
|
||||
auto get_bto_tx_info = [ & ]( fc::sha256 trx_id ) {
|
||||
const auto& bto_itr_idx = db.get_index_type<bitcoin_transaction_index>().indices().get<graphene::chain::by_transaction_id>();
|
||||
const auto& bto_itr = bto_itr_idx.find( trx_id );
|
||||
if( bto_itr == bto_itr_idx.end() ) {
|
||||
return false;
|
||||
}
|
||||
pw_vin_identifier = bto_itr->pw_vin;
|
||||
user_vout_ids = bto_itr->vouts;
|
||||
user_vin_identifiers = bto_itr->vins;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if( op.which() == operation::tag<bitcoin_transaction_send_operation>::value ) {
|
||||
bitcoin_transaction_send_operation btc_tx_send_op = op.get<bitcoin_transaction_send_operation>();
|
||||
pw_vin_identifier = btc_tx_send_op.pw_vin.identifier;
|
||||
user_vout_ids = btc_tx_send_op.vouts;
|
||||
for( const auto& vin : btc_tx_send_op.vins ) {
|
||||
user_vin_identifiers.push_back( vin.identifier );
|
||||
}
|
||||
} else if ( op.which() == operation::tag<bitcoin_issue_operation>::value ) {
|
||||
bitcoin_issue_operation btc_issue_op = op.get<bitcoin_issue_operation>();
|
||||
for( const auto& id : btc_issue_op.transaction_ids ) {
|
||||
if ( !get_bto_tx_info( id ) )
|
||||
return false;
|
||||
}
|
||||
} else if ( op.which() == operation::tag<bitcoin_transaction_revert_operation>::value ) {
|
||||
bitcoin_transaction_revert_operation btc_tx_revert_op = op.get<bitcoin_transaction_revert_operation>();
|
||||
for( auto trx_info: btc_tx_revert_op.transactions_info ) {
|
||||
if ( !get_bto_tx_info( trx_info.transaction_id ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return check_reuse_pw_vin( pw_vin_identifier ) &&
|
||||
check_reuse_user_vins( user_vin_identifiers ) &&
|
||||
check_reuse_vouts( user_vout_ids );
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_info_for_pw_vin( const info_for_vin& info_for_vin )
|
||||
{
|
||||
const auto& prevout = db.pw_vout_manager.get_vout( info_for_vin.identifier );
|
||||
const auto& pw_address = db.get_latest_PW().address;
|
||||
if( !prevout.valid() || info_for_vin.out != prevout->vout ||
|
||||
info_for_vin.address != pw_address.get_address() || info_for_vin.script != pw_address.get_witness_script() ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_info_for_vins( const std::vector<info_for_vin>& info_for_vins )
|
||||
{
|
||||
for( const auto& vin : info_for_vins ) {
|
||||
const auto& v = db.i_w_info.find_info_for_vin( vin.identifier );
|
||||
if( !v.valid() || *v != vin ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_info_for_vouts( const std::vector<info_for_vout_id_type>& info_for_vout_ids )
|
||||
{
|
||||
const auto& info_for_vout_idx = db.get_index_type<info_for_vout_index>().indices().get<graphene::chain::by_id>();
|
||||
for( const auto& id : info_for_vout_ids ) {
|
||||
const auto& itr = info_for_vout_idx.find( id );
|
||||
if( itr == info_for_vout_idx.end() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_transaction( const bitcoin_transaction_send_operation& btc_trx_op )
|
||||
{
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
const auto& info_for_vout_idx = db.get_index_type<info_for_vout_index>().indices().get<graphene::chain::by_id>();
|
||||
for( const auto& vout_id : btc_trx_op.vouts ) {
|
||||
const auto& vout_itr = info_for_vout_idx.find( vout_id );
|
||||
if( vout_itr == info_for_vout_idx.end() ) {
|
||||
return false;
|
||||
}
|
||||
info_vouts.push_back( *vout_itr );
|
||||
}
|
||||
|
||||
const auto& temp_full_tx = db.create_btc_transaction( btc_trx_op.vins, info_vouts, btc_trx_op.pw_vin );
|
||||
|
||||
if( temp_full_tx.first != btc_trx_op.transaction || temp_full_tx.second != btc_trx_op.fee_for_size ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_witness_opportunity_to_approve( const witness_object& current_witness, const proposal_object& proposal )
|
||||
{
|
||||
auto is_active_witness = [ & ]() {
|
||||
return db.get_global_properties().active_witnesses.find( current_witness.id ) != db.get_global_properties().active_witnesses.end();
|
||||
};
|
||||
|
||||
// Checks can witness approve this proposal or not
|
||||
auto does_the_witness_have_authority = [ & ]() {
|
||||
const auto& accounts_index = db.get_index_type<account_index>().indices().get<graphene::chain::by_id>();
|
||||
auto account_pBTC_issuer = accounts_index.find( db.get_sidechain_account_id() );
|
||||
|
||||
return account_pBTC_issuer->owner.account_auths.count( current_witness.witness_account ) &&
|
||||
!proposal.available_active_approvals.count( current_witness.witness_account );
|
||||
};
|
||||
|
||||
return is_active_witness() && does_the_witness_have_authority();
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_witnesses_keys( const witness_object& current_witness, const proposal_object& proposal )
|
||||
{
|
||||
bitcoin_transaction_send_operation op = proposal.proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
|
||||
|
||||
auto vins = op.vins;
|
||||
if( op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
|
||||
vins.insert( vins.begin(), op.pw_vin );
|
||||
}
|
||||
|
||||
const auto& bitcoin_address_idx = db.get_index_type<bitcoin_address_index>().indices().get< by_address >();
|
||||
|
||||
for ( const auto& info_vins : vins ) {
|
||||
const auto& address_itr = bitcoin_address_idx.find( info_vins.address );
|
||||
if ( address_itr != bitcoin_address_idx.end() ) {
|
||||
const auto& witness_key = current_witness.signing_key;
|
||||
const auto& witnesses_keys = address_itr->address.witnesses_keys;
|
||||
const auto& witnesses_keys_itr = witnesses_keys.find( current_witness.witness_account );
|
||||
if ( witnesses_keys_itr == witnesses_keys.end() || witnesses_keys_itr->second != witness_key ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sidechain_proposal_checker::check_bitcoin_transaction_revert_operation( const bitcoin_transaction_revert_operation& op )
|
||||
{
|
||||
const auto& btc_trx_idx = db.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
|
||||
|
||||
for( auto trx_info: op.transactions_info )
|
||||
{
|
||||
auto value = db.bitcoin_confirmations.find<sidechain::by_hash>( trx_info.transaction_id );
|
||||
if( !value.valid() ||
|
||||
btc_trx_idx.find( value->transaction_id ) == btc_trx_idx.end() ||
|
||||
trx_info != revert_trx_info( value->transaction_id, value->valid_vins ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return !op.transactions_info.empty();
|
||||
}
|
||||
|
||||
}
|
||||
125
libraries/sidechain/sign_bitcoin_transaction.cpp
Normal file
125
libraries/sidechain/sign_bitcoin_transaction.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#include <sidechain/sign_bitcoin_transaction.hpp>
|
||||
#include <sidechain/serialize.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
fc::sha256 get_signature_hash( const bitcoin_transaction& tx, const bytes& scriptCode, int64_t amount,
|
||||
size_t in_index, int hash_type, bool is_witness )
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
if ( is_witness )
|
||||
pack_tx_witness_signature( ps, scriptCode, tx, in_index, amount, hash_type );
|
||||
else
|
||||
pack_tx_signature( ps, scriptCode, tx, in_index, hash_type );
|
||||
|
||||
std::vector<char> vec( ps.tellp() );
|
||||
if ( !vec.empty() ) {
|
||||
fc::datastream<char*> ds( vec.data(), vec.size() );
|
||||
if ( is_witness )
|
||||
pack_tx_witness_signature( ds, scriptCode, tx, in_index, amount, hash_type );
|
||||
else
|
||||
pack_tx_signature( ds, scriptCode, tx, in_index, hash_type );
|
||||
}
|
||||
|
||||
return fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||
}
|
||||
|
||||
std::vector<char> privkey_sign( const bytes& privkey, const fc::sha256 &hash, const secp256k1_context_t* context_sign )
|
||||
{
|
||||
bytes sig;
|
||||
sig.resize( 72 );
|
||||
int sig_len = sig.size();
|
||||
|
||||
FC_ASSERT( secp256k1_ecdsa_sign(
|
||||
context_sign,
|
||||
reinterpret_cast<unsigned char*>( hash.data() ),
|
||||
reinterpret_cast<unsigned char*>( sig.data() ),
|
||||
&sig_len,
|
||||
reinterpret_cast<const unsigned char*>( privkey.data() ),
|
||||
secp256k1_nonce_function_rfc6979,
|
||||
nullptr ) ); // TODO: replace assert with exception
|
||||
|
||||
sig.resize( sig_len );
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
std::vector<bytes> sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
|
||||
const std::vector<uint64_t>& amounts, const bytes& privkey,
|
||||
const secp256k1_context_t* context_sign, int hash_type )
|
||||
{
|
||||
FC_ASSERT( tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size() );
|
||||
FC_ASSERT( !privkey.empty() );
|
||||
|
||||
std::vector< bytes > signatures;
|
||||
for( size_t i = 0; i < tx.vin.size(); i++ ) {
|
||||
const auto sighash = get_signature_hash( tx, redeem_scripts[i], static_cast<int64_t>( amounts[i] ), i, hash_type, true );
|
||||
auto sig = privkey_sign( privkey, sighash, context_sign );
|
||||
sig.push_back( static_cast<uint8_t>( hash_type ) );
|
||||
|
||||
signatures.push_back( sig );
|
||||
}
|
||||
return signatures;
|
||||
}
|
||||
|
||||
void sign_witness_transaction_finalize( bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts )
|
||||
{
|
||||
FC_ASSERT( tx.vin.size() == redeem_scripts.size() );
|
||||
|
||||
for( size_t i = 0; i < tx.vin.size(); i++ ) {
|
||||
tx.vin[i].scriptWitness.insert( tx.vin[i].scriptWitness.begin(), bytes() ); // Bitcoin workaround CHECKMULTISIG bug
|
||||
tx.vin[i].scriptWitness.push_back( redeem_scripts[i] );
|
||||
}
|
||||
}
|
||||
|
||||
bool verify_sig( const bytes& sig, const bytes& pubkey, const bytes& msg, const secp256k1_context_t* context )
|
||||
{
|
||||
std::vector<unsigned char> sig_temp( sig.begin(), sig.end() );
|
||||
std::vector<unsigned char> pubkey_temp( pubkey.begin(), pubkey.end() );
|
||||
std::vector<unsigned char> msg_temp( msg.begin(), msg.end() );
|
||||
|
||||
int result = secp256k1_ecdsa_verify( context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size() );
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
|
||||
const std::vector<uint64_t>& amounts, const secp256k1_context_t* context )
|
||||
{
|
||||
FC_ASSERT( redeem_scripts.size() == amounts.size() );
|
||||
|
||||
using data = std::pair<size_t, bytes>;
|
||||
struct comp {
|
||||
bool operator() (const data& lhs, const data& rhs) const { return lhs.first < rhs.first; }
|
||||
};
|
||||
|
||||
std::vector<std::vector<bytes>> new_stacks;
|
||||
|
||||
for( size_t i = 0; i < redeem_scripts.size(); i++ ) {
|
||||
const std::vector<bytes>& keys = get_pubkey_from_redeemScript( redeem_scripts[i] );
|
||||
const auto& sighash = get_signature_hash( tx, redeem_scripts[i], static_cast<int64_t>( amounts[i] ), i, 1, true ).str();
|
||||
bytes sighash_temp( parse_hex( sighash ) );
|
||||
|
||||
std::vector<bytes> stack( tx.vin[i].scriptWitness );
|
||||
std::vector<bool> marker( tx.vin[i].scriptWitness.size(), false );
|
||||
std::set<data, comp> sigs;
|
||||
|
||||
for( size_t j = 0; j < keys.size(); j++ ) {
|
||||
for( size_t l = 0; l < stack.size(); l++ ) {
|
||||
if( !verify_sig( stack[l], keys[j], sighash_temp, context ) || marker[l] )
|
||||
continue;
|
||||
sigs.insert(std::make_pair(j, stack[l]));
|
||||
marker[l] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bytes> temp_sig;
|
||||
for( auto s : sigs ) {
|
||||
temp_sig.push_back( s.second );
|
||||
}
|
||||
new_stacks.push_back( temp_sig );
|
||||
}
|
||||
return new_stacks;
|
||||
}
|
||||
|
||||
}
|
||||
35
libraries/sidechain/utils.cpp
Normal file
35
libraries/sidechain/utils.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include <sidechain/utils.hpp>
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
bytes parse_hex( const std::string& str )
|
||||
{
|
||||
bytes vec( str.size() / 2 );
|
||||
fc::from_hex( str, vec.data(), vec.size() );
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<bytes> get_pubkey_from_redeemScript( bytes script )
|
||||
{
|
||||
FC_ASSERT( script.size() >= 37 );
|
||||
|
||||
script.erase( script.begin() );
|
||||
script.erase( script.end() - 2, script.end() );
|
||||
|
||||
std::vector<bytes> result;
|
||||
uint64_t count = script.size() / 34;
|
||||
for( size_t i = 0; i < count; i++ ) {
|
||||
result.push_back( bytes( script.begin() + (34 * i) + 1, script.begin() + (34 * (i + 1)) ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes public_key_data_to_bytes( const fc::ecc::public_key_data& key )
|
||||
{
|
||||
bytes result;
|
||||
result.resize( key.size() );
|
||||
std::copy( key.begin(), key.end(), result.begin() );
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -767,6 +767,8 @@ class wallet_api
|
|||
string memo,
|
||||
bool broadcast = false);
|
||||
|
||||
signed_transaction withdraw_pBTC(account_id_type payer, string to, uint64_t amount, bool broadcast = false);
|
||||
|
||||
/**
|
||||
* This method works just like transfer, except it always broadcasts and
|
||||
* returns the transaction ID along with the signed transaction.
|
||||
|
|
@ -1795,6 +1797,20 @@ class wallet_api
|
|||
rock_paper_scissors_gesture gesture,
|
||||
bool broadcast);
|
||||
|
||||
|
||||
/** Create bitcoin multisig address for sidechain
|
||||
* @param payer the id of fee payer
|
||||
* @param owner the id of address owner
|
||||
* @return the signed version of the transaction
|
||||
*/
|
||||
signed_transaction create_bitcoin_address(string payer, string owner, bool broadcast);
|
||||
|
||||
/** Get all account's bitcoin addresses
|
||||
* @param account_name_or_id the name or id of the account to provide information of bitcoin addresses
|
||||
* @return list of bitcoin addresses
|
||||
*/
|
||||
vector<bitcoin_address_object> get_bitcoin_addresses(string account_name_or_id) const;
|
||||
|
||||
void dbg_make_uia(string creator, string symbol);
|
||||
void dbg_make_mia(string creator, string symbol);
|
||||
void dbg_push_blocks( std::string src_filename, uint32_t count );
|
||||
|
|
@ -2031,6 +2047,8 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(tournament_join)
|
||||
(tournament_leave)
|
||||
(rps_throw)
|
||||
(create_bitcoin_address)
|
||||
(get_bitcoin_addresses)
|
||||
(get_upcoming_tournaments)
|
||||
(get_tournaments)
|
||||
(get_tournaments_by_state)
|
||||
|
|
@ -2041,4 +2059,5 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(get_binned_order_book)
|
||||
(get_matched_bets_for_bettor)
|
||||
(get_all_matched_bets_for_bettor)
|
||||
(withdraw_pBTC)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -273,9 +273,10 @@ public:
|
|||
private:
|
||||
void claim_registered_account(const account_object& account)
|
||||
{
|
||||
std::vector<std::string> import_keys;
|
||||
auto it = _wallet.pending_account_registrations.find( account.name );
|
||||
FC_ASSERT( it != _wallet.pending_account_registrations.end() );
|
||||
for (const std::string& wif_key : it->second)
|
||||
for (const std::string& wif_key : it->second) {
|
||||
if( !import_key( account.name, wif_key ) )
|
||||
{
|
||||
// somebody else beat our pending registration, there is
|
||||
|
|
@ -288,8 +289,23 @@ private:
|
|||
// possibility of migrating to a fork where the
|
||||
// name is available, the user can always
|
||||
// manually re-register)
|
||||
} else {
|
||||
import_keys.push_back( wif_key );
|
||||
}
|
||||
}
|
||||
_wallet.pending_account_registrations.erase( it );
|
||||
|
||||
for( const auto& k : import_keys ) {
|
||||
fc::optional<fc::ecc::private_key> optional_private_key = wif_to_key( k );
|
||||
if (!optional_private_key)
|
||||
FC_THROW("Invalid private key");
|
||||
string shorthash = detail::address_to_shorthash(optional_private_key->get_public_key());
|
||||
copy_wallet_file( "before-import-key-" + shorthash );
|
||||
|
||||
save_wallet_file();
|
||||
copy_wallet_file( "after-import-key-" + shorthash );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// after a witness registration succeeds, this saves the private key in the wallet permanently
|
||||
|
|
@ -2392,6 +2408,23 @@ public:
|
|||
return sign_transaction(tx, broadcast);
|
||||
} FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) }
|
||||
|
||||
signed_transaction withdraw_pBTC(account_id_type payer, string to, uint64_t amount, bool broadcast)
|
||||
{ try {
|
||||
FC_ASSERT( !is_locked() );
|
||||
|
||||
withdraw_pbtc_operation withdraw_pbtc_op;
|
||||
withdraw_pbtc_op.payer = payer;
|
||||
withdraw_pbtc_op.data = to;
|
||||
withdraw_pbtc_op.amount = amount;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back(withdraw_pbtc_op);
|
||||
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
|
||||
tx.validate();
|
||||
|
||||
return sign_transaction(tx, broadcast);
|
||||
} FC_CAPTURE_AND_RETHROW( (payer)(to)(amount)(broadcast) ) }
|
||||
|
||||
signed_transaction issue_asset(string to_account, string amount, string symbol,
|
||||
string memo, bool broadcast = false)
|
||||
{
|
||||
|
|
@ -3870,6 +3903,12 @@ signed_transaction wallet_api::transfer(string from, string to, string amount,
|
|||
{
|
||||
return my->transfer(from, to, amount, asset_symbol, memo, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::withdraw_pBTC(account_id_type payer, string to, uint64_t amount, bool broadcast)
|
||||
{
|
||||
return my->withdraw_pBTC(payer, to, amount, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_asset(string issuer,
|
||||
string symbol,
|
||||
uint8_t precision,
|
||||
|
|
@ -5756,6 +5795,28 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id,
|
|||
return my->sign_transaction( tx, broadcast );
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::create_bitcoin_address( string payer, string owner, bool broadcast )
|
||||
{
|
||||
FC_ASSERT( !is_locked() );
|
||||
|
||||
bitcoin_address_create_operation op;
|
||||
op.payer = get_account_id(payer);
|
||||
op.owner = get_account_id(owner);
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations = { op };
|
||||
my->set_operation_fees( tx, my->_remote_db->get_global_properties().parameters.current_fees );
|
||||
tx.validate();
|
||||
|
||||
return my->sign_transaction( tx, broadcast );
|
||||
}
|
||||
|
||||
vector<bitcoin_address_object> wallet_api::get_bitcoin_addresses(string account_name_or_id) const
|
||||
{
|
||||
return my->_remote_db->get_bitcoin_addresses( get_account_id(account_name_or_id) );
|
||||
}
|
||||
|
||||
|
||||
// default ctor necessary for FC_REFLECT
|
||||
signed_block_with_info::signed_block_with_info()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@
|
|||
#include <graphene/chain/tournament_object.hpp>
|
||||
#include <graphene/chain/match_object.hpp>
|
||||
#include <graphene/chain/game_object.hpp>
|
||||
#include <graphene/chain/info_for_vout_object.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
#include <graphene/chain/sidechain_proposal_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <iostream>
|
||||
|
|
|
|||
|
|
@ -41,4 +41,8 @@ file(GLOB RANDOM_SOURCES "random/*.cpp")
|
|||
add_executable( random_test ${RANDOM_SOURCES} ${COMMON_SOURCES} )
|
||||
target_link_libraries( random_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
|
||||
|
||||
file(GLOB SIDECHAIN_TESTS "sidechain_tests/*.cpp")
|
||||
add_executable( sidechain_test ${SIDECHAIN_TESTS} ${COMMON_SOURCES} )
|
||||
target_link_libraries( sidechain_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
|
||||
|
||||
add_subdirectory( generate_empty_blocks )
|
||||
|
|
|
|||
|
|
@ -380,3 +380,34 @@ processed_transaction _push_transaction( database& db, const signed_transaction&
|
|||
}
|
||||
|
||||
} }
|
||||
|
||||
namespace sidechain {
|
||||
|
||||
class test_environment
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
test_environment( database& db )
|
||||
{
|
||||
|
||||
for( size_t i = 1; i < 6; i++ ) {
|
||||
prev_out out{ std::string( 64, std::to_string( i )[0] ), static_cast<uint32_t>( i ), static_cast<uint64_t>( i * 10000 ) };
|
||||
db.i_w_info.insert_info_for_vin( out, std::to_string( i ), { 0x00, 0x01, 0x02 } );
|
||||
db.i_w_info.insert_info_for_vout( account_id_type( i ), "2Mt57VSFqBe7UpDad9QaYHev21E1VscAZMU", i * 5000 );
|
||||
}
|
||||
|
||||
vins = db.i_w_info.get_info_for_vins();
|
||||
pw_vin = *db.i_w_info.get_info_for_pw_vin();
|
||||
vouts = db.i_w_info.get_info_for_vouts();
|
||||
full_tx = db.create_btc_transaction( vins, vouts, pw_vin );
|
||||
}
|
||||
|
||||
std::vector<info_for_vin> vins;
|
||||
info_for_vin pw_vin;
|
||||
std::vector<info_for_vout> vouts;
|
||||
full_btc_transaction full_tx;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
134
tests/sidechain_tests/bitcoin_address_tests.cpp
Normal file
134
tests/sidechain_tests/bitcoin_address_tests.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <sidechain/bitcoin_address.hpp>
|
||||
|
||||
using namespace sidechain;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( bitcoin_address_tests )
|
||||
|
||||
fc::ecc::public_key_data create_public_key_data( const std::vector<char>& public_key )
|
||||
{
|
||||
FC_ASSERT( public_key.size() == 33 );
|
||||
fc::ecc::public_key_data key;
|
||||
for(size_t i = 0; i < 33; i++) {
|
||||
key.at(i) = public_key[i];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( addresses_type_test )
|
||||
{
|
||||
// public_key
|
||||
std::string compressed( "03df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22" );
|
||||
BOOST_CHECK( bitcoin_address( compressed ).get_type() == payment_type::P2PK );
|
||||
|
||||
std::string uncompressed( "04fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904" );
|
||||
BOOST_CHECK( bitcoin_address( uncompressed ).get_type() == payment_type::NULLDATA );
|
||||
|
||||
|
||||
// segwit_address
|
||||
std::string p2wpkh_mainnet( "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" );
|
||||
BOOST_CHECK( bitcoin_address( p2wpkh_mainnet ).get_type() == payment_type::P2WPKH );
|
||||
|
||||
std::string p2wpkh_testnet( "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" );
|
||||
BOOST_CHECK( bitcoin_address( p2wpkh_testnet ).get_type() == payment_type::P2WPKH );
|
||||
|
||||
std::string p2wsh( "bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9" );
|
||||
BOOST_CHECK( bitcoin_address( p2wsh ).get_type() == payment_type::P2WSH );
|
||||
|
||||
|
||||
// base58
|
||||
std::string p2pkh_mainnet( "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem" );
|
||||
BOOST_CHECK( bitcoin_address( p2pkh_mainnet ).get_type() == payment_type::P2PKH );
|
||||
|
||||
std::string p2pkh_testnet( "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn" );
|
||||
BOOST_CHECK( bitcoin_address( p2pkh_testnet ).get_type() == payment_type::P2PKH );
|
||||
|
||||
std::string p2sh_mainnet( "3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX" );
|
||||
BOOST_CHECK( bitcoin_address( p2sh_mainnet ).get_type() == payment_type::P2SH );
|
||||
|
||||
std::string p2sh_testnet( "2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc" );
|
||||
BOOST_CHECK( bitcoin_address( p2sh_testnet ).get_type() == payment_type::P2SH );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( addresses_raw_test )
|
||||
{
|
||||
// public_key
|
||||
std::string compressed( "03df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22" );
|
||||
bytes standard_compressed( parse_hex( compressed ) );
|
||||
BOOST_CHECK( bitcoin_address( compressed ).get_raw_address() == standard_compressed );
|
||||
|
||||
std::string uncompressed( "04fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904" );
|
||||
BOOST_CHECK( bitcoin_address( uncompressed ).get_raw_address() == bytes() );
|
||||
|
||||
|
||||
// segwit_address
|
||||
std::string p2wpkh_mainnet( "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" );
|
||||
bytes standard_p2wpkh_mainnet( parse_hex( "751e76e8199196d454941c45d1b3a323f1433bd6" ) );
|
||||
BOOST_CHECK( bitcoin_address( p2wpkh_mainnet ).get_raw_address() == standard_p2wpkh_mainnet );
|
||||
|
||||
std::string p2wpkh_testnet( "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" );
|
||||
bytes standard_p2wpkh_testnet( parse_hex( "751e76e8199196d454941c45d1b3a323f1433bd6" ) );
|
||||
BOOST_CHECK( bitcoin_address( p2wpkh_testnet ).get_raw_address() == standard_p2wpkh_testnet );
|
||||
|
||||
std::string p2wsh( "bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9" );
|
||||
bytes standard_p2wsh( parse_hex( "c7a1f1a4d6b4c1802a59631966a18359de779e8a6a65973735a3ccdfdabc407d" ) );
|
||||
BOOST_CHECK( bitcoin_address( p2wsh ).get_raw_address() == standard_p2wsh );
|
||||
|
||||
|
||||
// base58
|
||||
std::string p2pkh_mainnet( "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem" );
|
||||
bytes standard_p2pkh_mainnet( parse_hex( "47376c6f537d62177a2c41c4ca9b45829ab99083" ) );
|
||||
BOOST_CHECK( bitcoin_address( p2pkh_mainnet ).get_raw_address() == standard_p2pkh_mainnet );
|
||||
|
||||
std::string p2pkh_testnet( "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn" );
|
||||
bytes standard_p2pkh_testnet( parse_hex( "243f1394f44554f4ce3fd68649c19adc483ce924" ) );
|
||||
BOOST_CHECK( bitcoin_address( p2pkh_testnet ).get_raw_address() == standard_p2pkh_testnet );
|
||||
|
||||
std::string p2sh_mainnet( "3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX" );
|
||||
bytes standard_p2sh_mainnet( parse_hex( "8f55563b9a19f321c211e9b9f38cdf686ea07845" ) );
|
||||
BOOST_CHECK( bitcoin_address( p2sh_mainnet ).get_raw_address() == standard_p2sh_mainnet );
|
||||
|
||||
std::string p2sh_testnet( "2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc" );
|
||||
bytes standard_p2sh_testnet( parse_hex( "4e9f39ca4688ff102128ea4ccda34105324305b0" ) );
|
||||
BOOST_CHECK( bitcoin_address( p2sh_testnet ).get_raw_address() == standard_p2sh_testnet );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_multisig_address_test )
|
||||
{
|
||||
|
||||
std::vector<char> public_key1 = parse_hex( "03db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010" );
|
||||
std::vector<char> public_key2 = parse_hex( "0320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b4417983" );
|
||||
std::vector<char> public_key3 = parse_hex( "033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f" );
|
||||
|
||||
std::vector<char> address = parse_hex( "a91460cb986f0926e7c4ca1984ca9f56767da2af031e87" );
|
||||
std::vector<char> redeem_script = parse_hex( "522103db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010210320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b441798321033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f53ae" );
|
||||
|
||||
fc::ecc::public_key_data key1 = create_public_key_data( public_key1 );
|
||||
fc::ecc::public_key_data key2 = create_public_key_data( public_key2 );
|
||||
fc::ecc::public_key_data key3 = create_public_key_data( public_key3 );
|
||||
|
||||
sidechain::btc_multisig_address cma(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_id_type(3), public_key_type(key3) } });
|
||||
|
||||
BOOST_CHECK( address == cma.raw_address );
|
||||
BOOST_CHECK( redeem_script == cma.redeem_script );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_segwit_address_test )
|
||||
{
|
||||
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
|
||||
std::vector<char> public_key1 = parse_hex( "03b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb" );
|
||||
std::vector<char> public_key2 = parse_hex( "03dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba6" );
|
||||
std::vector<char> public_key3 = parse_hex( "033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be1" );
|
||||
|
||||
std::vector<char> witness_script = parse_hex("0020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1");
|
||||
|
||||
fc::ecc::public_key_data key1 = create_public_key_data( public_key1 );
|
||||
fc::ecc::public_key_data key2 = create_public_key_data( public_key2 );
|
||||
fc::ecc::public_key_data key3 = create_public_key_data( public_key3 );
|
||||
|
||||
sidechain::btc_multisig_segwit_address address(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_id_type(3), public_key_type(key3) } });
|
||||
BOOST_CHECK( address.get_witness_script() == witness_script );
|
||||
BOOST_CHECK( address.get_address() == "2NGU4ogScHEHEpReUzi9RB2ha58KAFnkFyk" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
396
tests/sidechain_tests/bitcoin_sign_tests.cpp
Normal file
396
tests/sidechain_tests/bitcoin_sign_tests.cpp
Normal file
File diff suppressed because one or more lines are too long
169
tests/sidechain_tests/bitcoin_transaction_tests.cpp
Normal file
169
tests/sidechain_tests/bitcoin_transaction_tests.cpp
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <sidechain/utils.hpp>
|
||||
#include <sidechain/bitcoin_transaction.hpp>
|
||||
#include <sidechain/serialize.hpp>
|
||||
|
||||
using namespace sidechain;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( bitcoin_transaction_tests )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( serialize_bitcoin_transaction_test )
|
||||
{
|
||||
out_point prevout;
|
||||
prevout.hash = fc::sha256( "89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a" );
|
||||
prevout.n = 0;
|
||||
|
||||
tx_in in;
|
||||
in.prevout = prevout;
|
||||
in.scriptSig = parse_hex( "473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101" );
|
||||
in.nSequence = 4294967294;
|
||||
|
||||
tx_out out1;
|
||||
out1.value = 3500000000;
|
||||
out1.scriptPubKey = parse_hex( "76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac" );
|
||||
|
||||
tx_out out2;
|
||||
out2.value = 1499996160;
|
||||
out2.scriptPubKey = parse_hex( "76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac" );
|
||||
|
||||
bitcoin_transaction tx;
|
||||
tx.nVersion = 2;
|
||||
tx.vin = { in };
|
||||
tx.vout = { out1, out2 };
|
||||
tx.nLockTime = 101;
|
||||
|
||||
const auto serialized = pack( tx );
|
||||
|
||||
const auto expected = parse_hex( "02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000" );
|
||||
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS( serialized.cbegin(), serialized.cend(), expected.cbegin(), expected.cend() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( deserialize_bitcoin_transaction_test )
|
||||
{
|
||||
bitcoin_transaction tx = unpack(parse_hex( "02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000" ) );
|
||||
|
||||
BOOST_CHECK_EQUAL( tx.nVersion, 2 );
|
||||
BOOST_CHECK_EQUAL( tx.nLockTime, 101 );
|
||||
|
||||
BOOST_REQUIRE_EQUAL( tx.vin.size(), 1 );
|
||||
BOOST_CHECK_EQUAL( tx.vin[0].prevout.hash.str(), "89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a" );
|
||||
BOOST_CHECK_EQUAL( tx.vin[0].prevout.n, 0 );
|
||||
|
||||
BOOST_CHECK( fc::to_hex( tx.vin[0].scriptSig) == "473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101" );
|
||||
BOOST_CHECK_EQUAL( tx.vin[0].nSequence, 4294967294 );
|
||||
|
||||
BOOST_REQUIRE_EQUAL( tx.vout.size(), 2 );
|
||||
BOOST_CHECK_EQUAL( tx.vout[0].value, 3500000000 );
|
||||
BOOST_CHECK( fc::to_hex( tx.vout[0].scriptPubKey ) == "76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac" );
|
||||
BOOST_CHECK_EQUAL(tx.vout[1].value, 1499996160);
|
||||
BOOST_CHECK( fc::to_hex( tx.vout[1].scriptPubKey ) == "76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac" );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( btc_tx_methods_test ) {
|
||||
const auto tx = unpack( parse_hex( "0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f856040000002322002001d5d92effa6ffba3efa379f9830d0f75618b13393827152d26e4309000e88b1ffffffff0188b3f505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02473044022038421164c6468c63dc7bf724aa9d48d8e5abe3935564d38182addf733ad4cd81022076362326b22dd7bfaf211d5b17220723659e4fe3359740ced5762d0e497b7dcc012321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac00000000" ) );
|
||||
|
||||
BOOST_CHECK( tx.get_hash().str() == "a5947589e2762107ff650958ba0e3a3cf341f53281d15593530bf9762c4edab1" );
|
||||
BOOST_CHECK( tx.get_txid().str() == "954f43dbb30ad8024981c07d1f5eb6c9fd461e2cf1760dd1283f052af746fc88" );
|
||||
BOOST_CHECK( tx.get_vsize() == 148 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bitcoin_transaction_builder_test )
|
||||
{
|
||||
// All tests are only to verefy the compilation of transactions, the transactions are not real(not valid)
|
||||
{ // P2PKH to P2PKH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version( 2 );
|
||||
tb.add_in( payment_type::P2PKH, fc::sha256( "5d42b45d5a3ddcf2421b208885871121551acf6ea5cc1c1b4e666537ab6fcbef" ), 0, bytes() );
|
||||
tb.add_out( payment_type::P2PKH, 4999990000, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14" );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex(pack( tx ) ) == "0200000001efcb6fab3765664e1b1ccca56ecf1a552111878588201b42f2dc3d5a5db4425d0000000000ffffffff01f0ca052a010000001976a9143307bf6f98832e53a48b144d65c6a95700a93ffb88ac00000000");
|
||||
}
|
||||
{ // coinbase to P2PK
|
||||
const auto pubkey = fc::raw::unpack<fc::ecc::public_key_data>( parse_hex( "02028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00" ) );
|
||||
out_point prevout;
|
||||
prevout.hash = fc::sha256( "0000000000000000000000000000000000000000000000000000000000000000" );
|
||||
prevout.n = 0xffffffff;
|
||||
|
||||
tx_in txin;
|
||||
txin.prevout = prevout;
|
||||
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version( 2 );
|
||||
tb.add_in( payment_type::P2SH, txin, parse_hex( "022a020101" ) );
|
||||
tb.add_out( payment_type::P2PK, 625000000, pubkey);
|
||||
tb.add_out( payment_type::NULLDATA, 0, parse_hex( "21030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac" ) );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff05022a020101ffffffff0240be402500000000232102028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00ac0000000000000000256a2321030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac00000000" );
|
||||
}
|
||||
{ // P2SH to P2SH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version( 2 );
|
||||
tb.add_in( payment_type::P2SH, fc::sha256( "40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8" ), 0, bytes() );
|
||||
tb.add_out( payment_type::P2SH, 0xffffffff, "3P14159f73E4gFr7JterCCQh9QjiTjiZrG" );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "0200000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee400000000000ffffffff01ffffffff0000000017a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a8700000000" );
|
||||
}
|
||||
{ // P2PK to NULLDATA
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version( 2 );
|
||||
tb.add_in( payment_type::P2PK, fc::sha256( "fa897a4a2b8bc507db6cf4425e81ca7ebde89a369e07d608ac7f7c311cb13b4f" ), 0, bytes() );
|
||||
tb.add_out( payment_type::NULLDATA, 0, parse_hex( "ffffffff" ) );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000014f3bb11c317c7fac08d6079e369ae8bd7eca815e42f46cdb07c58b2b4a7a89fa0000000000ffffffff010000000000000000066a04ffffffff00000000" );
|
||||
}
|
||||
{ // P2PK+P2PKH to P2PKH,P2PKH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in( payment_type::P2PK, fc::sha256( "13d29149e08b6ca63263f3dddd303b32f5aab646ebc6b7db84756d80a227f6d9" ), 0, bytes() );
|
||||
tb.add_in( payment_type::P2PKH, fc::sha256( "a8e7f661925cdd2c0e37fc93c03540c113aa6bcea02b35de09377127f76d0da3" ), 0, bytes() );
|
||||
tb.add_out( payment_type::P2PKH, 4999990000, "mzg9RZ1p29uNXu4uTWoMdMERdVXZpunJhW" );
|
||||
tb.add_out( payment_type::P2PKH, 4999990000, "n2SPW6abRxUnnTSSHp73VGahbPW4WT9GaK" );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "0200000002d9f627a2806d7584dbb7c6eb46b6aaf5323b30ddddf36332a66c8be04991d2130000000000ffffffffa30d6df727713709de352ba0ce6baa13c14035c093fc370e2cdd5c9261f6e7a80000000000ffffffff02f0ca052a010000001976a914d2276c7ed7af07f697175cc2cbcbbf32da81caba88acf0ca052a010000001976a914e57d9a9af070998bedce991c4d8e39f9c51eb93a88ac00000000" );
|
||||
}
|
||||
{ // P2WPKH to P2WPKH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in( payment_type::P2WPKH, fc::sha256( "56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115" ), 1, bytes() );
|
||||
tb.add_out( payment_type::P2WPKH, 99988480, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14" );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560100000000ffffffff0100b4f505000000001600143307bf6f98832e53a48b144d65c6a95700a93ffb00000000" );
|
||||
}
|
||||
{ // P2WSH to P2WSH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in( payment_type::P2WSH, fc::sha256( "56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 2, bytes() );
|
||||
tb.add_out( payment_type::P2WSH, 99988360, "p2xtZoXeX5X8BP8JfFhQK2nD3emtjch7UeFm" );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560200000000ffffffff0188b3f505000000001600140000010966776006953d5567439e5e39f86a0d2700000000" );
|
||||
}
|
||||
{ // P2SH(WPKH) to P2SH(WPKH)
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version( 2 );
|
||||
tb.add_in( payment_type::P2SH_WPKH, fc::sha256( "56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115" ), 3, parse_hex( "ab68025513c3dbd2f7b92a94e0581f5d50f654e7" ) );
|
||||
tb.add_out( payment_type::P2SH_WPKH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd" );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560300000014ab68025513c3dbd2f7b92a94e0581f5d50f654e7ffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000" );
|
||||
}
|
||||
{ // P2SH(WSH) to P2SH(WSH)
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
const auto redeem_script = parse_hex( "21038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac");
|
||||
tb.add_in( payment_type::P2SH_WSH, fc::sha256( "fca01bd539623013f6f945dc6173c395394621ffaa53a9eb6da6e9a2e7c9400e" ), 0, redeem_script );
|
||||
tb.add_out( payment_type::P2SH_WSH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd" );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000010e40c9e7a2e9a66deba953aaff21463995c37361dc45f9f613306239d51ba0fc000000002321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acacffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000" );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
311
tests/sidechain_tests/db_sidechain_tests.cpp
Normal file
311
tests/sidechain_tests/db_sidechain_tests.cpp
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <fc/crypto/digest.hpp>
|
||||
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/sidechain_proposal_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace sidechain;
|
||||
|
||||
template< class SidechainOp >
|
||||
proposal_object create_op_and_prop( database& db, bool check_size = true )
|
||||
{
|
||||
const auto& sidechain_proposal_idx = db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>();
|
||||
|
||||
BOOST_CHECK( !check_size || sidechain_proposal_idx.size() == 0 );
|
||||
|
||||
const flat_set<witness_id_type>& active_witnesses = db.get_global_properties().active_witnesses;
|
||||
const witness_id_type& witness_id = *active_witnesses.begin();
|
||||
const witness_object witness = witness_id(db);
|
||||
const account_object& witness_account = witness.witness_account(db);
|
||||
|
||||
const auto& propose = db.create<proposal_object>( [&]( proposal_object& obj ){
|
||||
obj.expiration_time = db.head_block_time() + fc::days(1);
|
||||
obj.review_period_time = db.head_block_time() + fc::days(1);
|
||||
obj.proposed_transaction.operations.push_back( SidechainOp() );
|
||||
} );
|
||||
|
||||
const auto& spropose = db.create<sidechain_proposal_object>( [&]( sidechain_proposal_object& obj ){
|
||||
obj.proposal_id = propose.id;
|
||||
} );
|
||||
|
||||
return propose;
|
||||
}
|
||||
|
||||
template< class SidechainOp >
|
||||
void check_fork_prop( database& db )
|
||||
{
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& gpo ) {
|
||||
gpo.parameters.extensions.value.sidechain_parameters.reset();
|
||||
if( gpo.pending_parameters )
|
||||
gpo.pending_parameters->extensions.value.sidechain_parameters.reset();
|
||||
});
|
||||
|
||||
BOOST_CHECK( db.is_sidechain_fork_needed() );
|
||||
|
||||
transaction_evaluation_state context(&db);
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = account_id_type();
|
||||
proposal_op.proposed_ops.emplace_back( SidechainOp() );
|
||||
proposal_op.expiration_time = db.head_block_time() + fc::days(1);
|
||||
|
||||
BOOST_CHECK_THROW( db.apply_operation( context, proposal_op ), fc::exception );
|
||||
}
|
||||
|
||||
/////////////////////////////////// functions for roll_back tests
|
||||
|
||||
inline proposal_object create_proposals( database& db, bitcoin_transaction_send_operation op )
|
||||
{
|
||||
const auto& propose_send = db.create<proposal_object>( [&]( proposal_object& obj ){
|
||||
obj.expiration_time = db.head_block_time() + fc::days(1);
|
||||
obj.review_period_time = db.head_block_time() + fc::days(1);
|
||||
obj.proposed_transaction.operations.push_back( op );
|
||||
} );
|
||||
db.create<sidechain_proposal_object>( [&]( sidechain_proposal_object& obj ){
|
||||
obj.proposal_id = propose_send.id;
|
||||
obj.proposal_type = sidechain_proposal_type::SEND_BTC_TRANSACTION;
|
||||
} );
|
||||
|
||||
return propose_send;
|
||||
}
|
||||
|
||||
inline bitcoin_transaction_send_operation create_btc_tx_send_op( const test_environment& env, uint32_t offset = 0 )
|
||||
{
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.pw_vin = env.pw_vin;
|
||||
op.vins = std::vector<info_for_vin>( env.vins.begin(), env.vins.end() - offset );
|
||||
for( const auto& vout : std::vector<info_for_vout>( env.vouts.begin(), env.vouts.end() - offset ) ) {
|
||||
op.vouts.push_back( vout.get_id() );
|
||||
}
|
||||
op.transaction = env.full_tx.first;
|
||||
op.fee_for_size = env.full_tx.second;
|
||||
return op;
|
||||
}
|
||||
|
||||
inline void mark_vin( database& db, const test_environment& env )
|
||||
{
|
||||
for( auto& iter: env.vins ) {
|
||||
db.i_w_info.mark_as_used_vin( iter );
|
||||
}
|
||||
}
|
||||
|
||||
inline void mark_vout( database& db, const test_environment& env )
|
||||
{
|
||||
for( auto& iter: env.vouts ) {
|
||||
db.i_w_info.mark_as_used_vout( iter );
|
||||
}
|
||||
}
|
||||
|
||||
inline void mark_vin_vout_pw( database& db, const test_environment& env )
|
||||
{
|
||||
mark_vin( db, env );
|
||||
mark_vout( db, env );
|
||||
db.pw_vout_manager.mark_as_used_vout( env.pw_vin.identifier );
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( db_sidechain_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( remove_sidechain_proposals_test )
|
||||
{
|
||||
const auto& sidechain_proposal_idx = db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>();
|
||||
|
||||
BOOST_CHECK_EQUAL( sidechain_proposal_idx.size(), 0 );
|
||||
|
||||
const auto& propose_send = create_op_and_prop<bitcoin_transaction_send_operation>( db, false );
|
||||
const auto& propose_issue = create_op_and_prop<bitcoin_issue_operation>( db, false );
|
||||
const auto& propose_revert = create_op_and_prop<bitcoin_transaction_revert_operation>( db, false );
|
||||
|
||||
create_op_and_prop<bitcoin_transaction_send_operation>( db, false );
|
||||
create_op_and_prop<bitcoin_issue_operation>( db, false );
|
||||
create_op_and_prop<bitcoin_transaction_revert_operation>( db, false );
|
||||
|
||||
BOOST_CHECK_EQUAL( sidechain_proposal_idx.size(), 6 );
|
||||
|
||||
db.remove_sidechain_proposal_object( propose_send );
|
||||
db.remove_sidechain_proposal_object( propose_issue );
|
||||
db.remove_sidechain_proposal_object( propose_revert );
|
||||
|
||||
BOOST_CHECK_EQUAL( sidechain_proposal_idx.size(), 3 );
|
||||
|
||||
const auto& propose_transfer = db.create<proposal_object>( [&]( proposal_object& obj ){
|
||||
obj.expiration_time = db.head_block_time() + fc::days(1);
|
||||
obj.review_period_time = db.head_block_time() + fc::days(1);
|
||||
obj.proposed_transaction.operations.push_back( transfer_operation() );
|
||||
} );
|
||||
|
||||
db.remove_sidechain_proposal_object( propose_transfer );
|
||||
|
||||
BOOST_CHECK_EQUAL( sidechain_proposal_idx.size(), 3 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( sidechain_fork_btc_issue_op_test )
|
||||
{
|
||||
check_fork_prop<bitcoin_issue_operation> ( db );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( sidechain_fork_btc_send_op_test )
|
||||
{
|
||||
check_fork_prop<bitcoin_transaction_send_operation> ( db );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( sidechain_fork_btc_revert_op_test )
|
||||
{
|
||||
check_fork_prop<bitcoin_transaction_revert_operation> ( db );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( roll_back_vin_test )
|
||||
{
|
||||
test_environment env( db );
|
||||
|
||||
mark_vin( db, env );
|
||||
|
||||
bitcoin_transaction_send_operation op = create_btc_tx_send_op( env );
|
||||
const auto& propose_send = create_proposals( db, op );
|
||||
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 1 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 0 );
|
||||
BOOST_CHECK( !db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
|
||||
db.roll_back_vin_and_vout( propose_send );
|
||||
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 5 );
|
||||
|
||||
BOOST_CHECK( !db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 0 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( roll_back_vout_test )
|
||||
{
|
||||
test_environment env( db );
|
||||
|
||||
mark_vout( db, env );
|
||||
|
||||
bitcoin_transaction_send_operation op = create_btc_tx_send_op( env );
|
||||
const auto& propose_send = create_proposals( db, op );
|
||||
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 1 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vouts().size(), 0 );
|
||||
BOOST_CHECK( !db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
|
||||
db.roll_back_vin_and_vout( propose_send );
|
||||
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vouts().size(), 5 );
|
||||
|
||||
BOOST_CHECK( !db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 0 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( roll_back_vin_and_vout_test )
|
||||
{
|
||||
test_environment env( db );
|
||||
|
||||
mark_vin_vout_pw( db, env );
|
||||
bitcoin_transaction_send_operation op = create_btc_tx_send_op( env );
|
||||
const auto& propose_send = create_proposals( db, op );
|
||||
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 1 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 0 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vouts().size(), 0 );
|
||||
BOOST_CHECK( db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
db.roll_back_vin_and_vout( propose_send );
|
||||
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 5 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vouts().size(), 5 );
|
||||
|
||||
BOOST_CHECK( !db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 0 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( roll_back_part_test )
|
||||
{
|
||||
test_environment env( db );
|
||||
|
||||
mark_vin_vout_pw( db, env );
|
||||
bitcoin_transaction_send_operation op = create_btc_tx_send_op( env, 2 );
|
||||
|
||||
const auto& propose_send = create_proposals( db, op );
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 1 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 0 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vouts().size(), 0 );
|
||||
BOOST_CHECK( db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
db.roll_back_vin_and_vout( propose_send );
|
||||
|
||||
BOOST_REQUIRE_EQUAL( db.i_w_info.get_info_for_vins().size(), 3 );
|
||||
BOOST_REQUIRE_EQUAL( db.i_w_info.get_info_for_vouts().size(), 3 );
|
||||
|
||||
BOOST_CHECK( !db.pw_vout_manager.get_vout( op.pw_vin.identifier )->used );
|
||||
BOOST_CHECK_EQUAL( db.get_index_type<sidechain_proposal_index>().indices().get<by_proposal>().size(), 0 );
|
||||
|
||||
auto info_vins = db.i_w_info.get_info_for_vins();
|
||||
auto info_vouts = db.i_w_info.get_info_for_vouts();
|
||||
|
||||
BOOST_REQUIRE_EQUAL( op.vins.size(), info_vins.size() );
|
||||
BOOST_REQUIRE_EQUAL( op.vouts.size(), info_vouts.size() );
|
||||
|
||||
// no match for `operator==`; but `operator!=` exists
|
||||
for( uint32_t indx = 0; indx < op.vins.size(); indx++ ) {
|
||||
BOOST_CHECK( !( op.vins[indx] != info_vins[indx] ) );
|
||||
}
|
||||
|
||||
for( uint32_t indx = 0; indx < info_vouts.size(); indx++ ) {
|
||||
BOOST_CHECK( env.vouts[indx] == info_vouts[indx] );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( not_enough_amount_for_fee )
|
||||
{
|
||||
test_environment env( db );
|
||||
|
||||
auto vins = env.vins;
|
||||
for( auto& vin: vins ) {
|
||||
db.i_w_info.modify_info_for_vin( vin, [&]( info_for_vin& obj ) {
|
||||
obj.out.amount = 1;
|
||||
obj.identifier = fc::sha256::hash( obj.out.hash_tx + std::to_string( obj.out.n_vout ) );
|
||||
} );
|
||||
vin.out.amount = 1;
|
||||
}
|
||||
|
||||
auto tx_and_new_infos = db.create_tx_with_valid_vin( env.vouts, env.pw_vin );
|
||||
|
||||
BOOST_CHECK_EQUAL( tx_and_new_infos.second.size(), 0 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 0 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( not_enough_amount_for_fee_tree_of_five )
|
||||
{
|
||||
test_environment env( db );
|
||||
|
||||
auto vins = env.vins;
|
||||
|
||||
for( uint32_t i = 0; i < 3; ++i ) {
|
||||
db.i_w_info.modify_info_for_vin( vins[i], [&]( info_for_vin& obj ) {
|
||||
obj.out.amount = 100 + i;
|
||||
obj.identifier = fc::sha256::hash( obj.out.hash_tx + std::to_string( obj.out.n_vout ) );
|
||||
} );
|
||||
vins[i].out.amount = 100 + i;
|
||||
}
|
||||
|
||||
auto tx_and_new_infos = db.create_tx_with_valid_vin( env.vouts, env.pw_vin );
|
||||
|
||||
BOOST_CHECK_EQUAL( tx_and_new_infos.second.size(), 2 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 2 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( enough_amount_for_fee )
|
||||
{
|
||||
test_environment env( db );
|
||||
|
||||
auto tx_and_new_infos = db.create_tx_with_valid_vin( env.vouts, env.pw_vin );
|
||||
|
||||
BOOST_CHECK_EQUAL( tx_and_new_infos.second.size(), 5 );
|
||||
BOOST_CHECK_EQUAL( db.i_w_info.get_info_for_vins().size(), 5 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
274
tests/sidechain_tests/input_withdrawal_info_tests.cpp
Normal file
274
tests/sidechain_tests/input_withdrawal_info_tests.cpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <sidechain/input_withdrawal_info.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
#include <sidechain/types.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace sidechain;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( input_withdrawal_info_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vin_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
prev_out out = { "1", 1, 13 };
|
||||
infos.insert_info_for_vin( out, "addr1", { 0x01, 0x02, 0x03 } );
|
||||
BOOST_CHECK( infos.size_info_for_vins() == 1 );
|
||||
|
||||
info_for_vin::count_id_info_for_vin = 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vin_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 1; i <= 10; i++ ) {
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
|
||||
}
|
||||
BOOST_CHECK( infos.size_info_for_vins() == 10 );
|
||||
|
||||
info_for_vin::count_id_info_for_vin = 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_id_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier ).valid() );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->id == i );
|
||||
}
|
||||
|
||||
info_for_vin::count_id_info_for_vin = 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_check_data_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier ).valid() );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->id == i );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->out.hash_tx == std::to_string( i ) );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->out.n_vout == i );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->out.amount == i );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->address == "addr" + std::to_string( i ) );
|
||||
std::vector<char> script = { 0x01, 0x02, 0x03 };
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->script == script );
|
||||
}
|
||||
|
||||
info_for_vin::count_id_info_for_vin = 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
if( i % 2 == 0 ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
auto iter = infos.find_info_for_vin( identifier );
|
||||
BOOST_CHECK( iter.valid() );
|
||||
infos.modify_info_for_vin( *iter, [&]( info_for_vin& obj ) {
|
||||
obj.out.hash_tx = std::to_string( i + 1 );
|
||||
obj.out.n_vout = i + 1;
|
||||
obj.out.amount = i + 1;
|
||||
obj.address = "addr" + std::to_string( i ) + std::to_string( i );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
if( i % 2 == 0 ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier ).valid() );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->id == i );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->out.hash_tx == std::to_string( i + 1 ) );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->out.n_vout == i + 1 );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->out.amount == i + 1 );
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->address == "addr" + std::to_string( i ) + std::to_string( i ) );
|
||||
std::vector<char> script = { 0x01, 0x02, 0x03 };
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier )->script == script );
|
||||
}
|
||||
}
|
||||
|
||||
info_for_vin::count_id_info_for_vin = 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
if( i % 2 == 0 ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
auto iter = infos.find_info_for_vin( identifier );
|
||||
BOOST_CHECK( iter.valid() );
|
||||
infos.remove_info_for_vin( *iter );
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
if( i % 2 == 0 ) {
|
||||
BOOST_CHECK( !infos.find_info_for_vin( identifier ).valid() );
|
||||
} else {
|
||||
BOOST_CHECK( infos.find_info_for_vin( identifier ).valid() );
|
||||
}
|
||||
}
|
||||
|
||||
info_for_vin::count_id_info_for_vin = 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
|
||||
const auto pw_obj = db.get_latest_PW();
|
||||
auto witnesses_keys = pw_obj.address.witnesses_keys;
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
const auto& address = db.create<bitcoin_address_object>( [&]( bitcoin_address_object& a ) {
|
||||
const fc::ecc::private_key petra_private_key = generate_private_key( std::to_string( i ) );
|
||||
witnesses_keys.begin()->second = public_key_type( petra_private_key.get_public_key() );
|
||||
a.address = sidechain::btc_multisig_segwit_address( 5, witnesses_keys);
|
||||
});
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
infos.insert_info_for_vin( out, address.get_address(), { 0x01, 0x02, 0x03 } );
|
||||
}
|
||||
|
||||
const auto& vins = infos.get_info_for_vins();
|
||||
BOOST_CHECK( vins.size() == 5 ); // 5 amount vins to bitcoin transaction
|
||||
|
||||
for( size_t i = 0; i < 7; i++ ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
auto iter = infos.find_info_for_vin( identifier );
|
||||
infos.mark_as_used_vin( *iter );
|
||||
}
|
||||
|
||||
const auto& vins2 = infos.get_info_for_vins();
|
||||
BOOST_CHECK( vins2.size() == 3 );
|
||||
|
||||
info_for_vin::count_id_info_for_vin = 0;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vout_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
infos.insert_info_for_vout( account_id_type(), "1", 1 );
|
||||
BOOST_CHECK( infos.size_info_for_vouts() == 1 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vout_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 1; i <= 10; i++ ) {
|
||||
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
|
||||
}
|
||||
BOOST_CHECK( infos.size_info_for_vouts() == 10 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
if( i % 2 == 0 ) {
|
||||
auto iter = infos.find_info_for_vout( graphene::chain::info_for_vout_id_type(i) );
|
||||
BOOST_CHECK( iter.valid() );
|
||||
infos.remove_info_for_vout( *iter );
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
if( i % 2 == 0 ) {
|
||||
BOOST_CHECK( !infos.find_info_for_vout( graphene::chain::info_for_vout_id_type(i) ).valid() );
|
||||
} else {
|
||||
BOOST_CHECK( infos.find_info_for_vout( graphene::chain::info_for_vout_id_type(i) ).valid() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vouts_test )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
|
||||
}
|
||||
|
||||
const auto& vouts = infos.get_info_for_vouts();
|
||||
BOOST_CHECK( vouts.size() == 5 ); // 5 amount vouts to bitcoin transaction
|
||||
|
||||
for( size_t i = 0; i < 7; i++ ) {
|
||||
auto iter = infos.find_info_for_vout( graphene::chain::info_for_vout_id_type(i) );
|
||||
infos.mark_as_used_vout( *iter );
|
||||
}
|
||||
|
||||
const auto& vouts2 = infos.get_info_for_vouts();
|
||||
BOOST_CHECK( vouts2.size() == 3 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( info_for_vin_comparer_tests )
|
||||
{
|
||||
input_withdrawal_info infos( db );
|
||||
|
||||
auto vins_checker = [&]( std::vector<uint32_t> vins_id ) {
|
||||
auto vins = infos.get_info_for_vins();
|
||||
auto i = 0;
|
||||
for( uint32_t j : vins_id ) {
|
||||
BOOST_CHECK( vins[i].out.hash_tx == std::to_string( j ) );
|
||||
BOOST_CHECK( vins[i].out.n_vout == j );
|
||||
BOOST_CHECK( vins[i].out.amount == j );
|
||||
BOOST_CHECK( vins[i].address == "addr" + std::to_string( j ) );
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
bool resend = ( i % 2 == 0 ) ? true : false;
|
||||
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 }, resend );
|
||||
}
|
||||
vins_checker( { 0, 2, 4, 6, 8 } );
|
||||
|
||||
for( size_t i = 0; i < 5; i++ ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
auto iter = infos.find_info_for_vin( identifier );
|
||||
infos.mark_as_used_vin( *iter );
|
||||
}
|
||||
vins_checker( { 6, 8, 5, 7, 9 } );
|
||||
|
||||
for( auto i : { 6, 8 } ) {
|
||||
auto identifier = fc::sha256::hash( std::to_string( i ) + std::to_string( i ) );
|
||||
auto iter = infos.find_info_for_vin( identifier );
|
||||
infos.mark_as_used_vin( *iter );
|
||||
}
|
||||
vins_checker( { 5, 7, 9 } );
|
||||
|
||||
for( size_t i = 10; i < 14; i++ ) {
|
||||
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
|
||||
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 }, true );
|
||||
}
|
||||
vins_checker( { 10, 11, 12, 13, 5 } );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
42
tests/sidechain_tests/main.cpp
Normal file
42
tests/sidechain_tests/main.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <cstdlib>
|
||||
#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 );
|
||||
} else {
|
||||
GRAPHENE_TESTING_GENESIS_TIMESTAMP = 1550000001;
|
||||
}
|
||||
std::cout << "GRAPHENE_TESTING_GENESIS_TIMESTAMP is " << GRAPHENE_TESTING_GENESIS_TIMESTAMP << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
158
tests/sidechain_tests/primary_wallet_vout_manager_tests.cpp
Normal file
158
tests/sidechain_tests/primary_wallet_vout_manager_tests.cpp
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <sidechain/primary_wallet_vout_manager.hpp>
|
||||
#include <graphene/chain/primary_wallet_vout_object.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
#include <sidechain/types.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
using namespace sidechain;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( primary_wallet_vout_manager_tests, database_fixture )
|
||||
|
||||
void create_primary_wallet_vouts( primary_wallet_vout_manager& pw_vout_manager, const graphene::chain::database& db, uint32_t num )
|
||||
{
|
||||
uint64_t start_pos = 0;
|
||||
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
|
||||
if( idx.begin() != idx.end() ){
|
||||
auto last_itr = --idx.end();
|
||||
start_pos = last_itr->id.instance() + 1;
|
||||
}
|
||||
|
||||
for( uint64_t i = start_pos; i < start_pos + num; i++ ) {
|
||||
prev_out out = { std::to_string(i), i, i };
|
||||
pw_vout_manager.create_new_vout( out );
|
||||
}
|
||||
}
|
||||
|
||||
fc::sha256 create_hash_id( const std::string& hash_tx, const uint32_t& n_vout, const uint64_t& id )
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << id;
|
||||
std::string id_hex = std::string( 16 - ss.str().size(), '0' ) + ss.str();
|
||||
|
||||
std::string hash_str = fc::sha256::hash( hash_tx + std::to_string( n_vout ) ).str();
|
||||
std::string final_hash_id = std::string( hash_str.begin(), hash_str.begin() + 48 ) + id_hex;
|
||||
|
||||
return fc::sha256( final_hash_id );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_max_pw_vout_objects )
|
||||
{
|
||||
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
|
||||
primary_wallet_vout_manager pw_vout_manager( db );
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
|
||||
BOOST_CHECK( idx.size() == 2 );
|
||||
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 25 );
|
||||
BOOST_CHECK( idx.size() == 27 );
|
||||
BOOST_CHECK( pw_vout_manager.is_max_vouts() == true );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_pw_vout_objects_chain )
|
||||
{
|
||||
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
primary_wallet_vout_manager pw_vout_manager( db );
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 24 );
|
||||
|
||||
for( auto itr: idx ) {
|
||||
BOOST_CHECK( itr.hash_id == create_hash_id( itr.vout.hash_tx, itr.vout.n_vout, itr.id.instance() ) );
|
||||
}
|
||||
|
||||
BOOST_CHECK( pw_vout_manager.get_latest_unused_vout().valid() );
|
||||
|
||||
auto pw_vout = *pw_vout_manager.get_latest_unused_vout();
|
||||
|
||||
BOOST_CHECK_EQUAL( pw_vout.vout.hash_tx, "24" );
|
||||
BOOST_CHECK_EQUAL( pw_vout.vout.n_vout, 24 );
|
||||
BOOST_CHECK( pw_vout.hash_id == create_hash_id( "24", 24, pw_vout.id.instance() ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( delete_pw_vout_objects )
|
||||
{
|
||||
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
primary_wallet_vout_manager pw_vout_manager( db );
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 24 );
|
||||
|
||||
BOOST_CHECK_EQUAL( idx.size(), 25 );
|
||||
|
||||
auto pw_vout = *pw_vout_manager.get_latest_unused_vout();
|
||||
pw_vout_manager.delete_vouts_after( create_hash_id( pw_vout.vout.hash_tx, pw_vout.vout.n_vout, pw_vout.id.instance() ) );
|
||||
BOOST_CHECK_EQUAL( idx.size(), 25 );
|
||||
|
||||
pw_vout_manager.delete_vouts_after( create_hash_id( "13", 13, 13 ) );
|
||||
|
||||
BOOST_CHECK_EQUAL( idx.size(), 14 );
|
||||
|
||||
for( auto itr: idx ) {
|
||||
BOOST_CHECK( itr.hash_id == create_hash_id( itr.vout.hash_tx, itr.vout.n_vout, itr.id.instance() ) );
|
||||
}
|
||||
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 8 );
|
||||
pw_vout_manager.delete_vouts_after( create_hash_id( "20", 20, 20 ) );
|
||||
BOOST_CHECK_EQUAL( idx.size(), 22 );
|
||||
|
||||
for( auto itr: idx ) {
|
||||
BOOST_CHECK( itr.hash_id == create_hash_id( itr.vout.hash_tx, itr.vout.n_vout, itr.id.instance() ) );
|
||||
}
|
||||
|
||||
auto itr_primary_wallet = idx.begin();
|
||||
pw_vout_manager.delete_vouts_after( create_hash_id( itr_primary_wallet->vout.hash_tx, itr_primary_wallet->vout.n_vout, itr_primary_wallet->id.instance() ) );
|
||||
BOOST_CHECK_EQUAL( idx.size(), 1 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( confirm_pw_vout_objects )
|
||||
{
|
||||
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
primary_wallet_vout_manager pw_vout_manager( db );
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
|
||||
|
||||
auto itr = idx.begin();
|
||||
pw_vout_manager.confirm_vout( create_hash_id( itr->vout.hash_tx, itr->vout.n_vout, itr->id.instance() ) );
|
||||
|
||||
BOOST_CHECK( itr->confirmed == true );
|
||||
|
||||
itr++;
|
||||
pw_vout_manager.confirm_vout( create_hash_id( itr->vout.hash_tx, itr->vout.n_vout, itr->id.instance() ) );
|
||||
|
||||
BOOST_CHECK( itr->confirmed == true );
|
||||
BOOST_CHECK_EQUAL( idx.size(), 1 );
|
||||
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
|
||||
itr++;
|
||||
pw_vout_manager.confirm_vout( create_hash_id( itr->vout.hash_tx, itr->vout.n_vout, itr->id.instance() ) );
|
||||
|
||||
BOOST_CHECK( itr->confirmed == true );
|
||||
BOOST_CHECK_EQUAL( idx.size(), 1 );
|
||||
|
||||
GRAPHENE_REQUIRE_THROW( pw_vout_manager.confirm_vout( fc::sha256::hash( "4" + std::to_string( 4 ))), fc::exception );
|
||||
GRAPHENE_REQUIRE_THROW( pw_vout_manager.confirm_vout( fc::sha256::hash( "123" + std::to_string( 123 ))), fc::exception );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( use_pw_vout_objects )
|
||||
{
|
||||
const auto& idx = db.get_index_type<primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
|
||||
primary_wallet_vout_manager pw_vout_manager( db );
|
||||
|
||||
auto itr = idx.begin();
|
||||
pw_vout_manager.mark_as_used_vout( create_hash_id( itr->vout.hash_tx, itr->vout.n_vout, itr->id.instance() ) );
|
||||
|
||||
BOOST_CHECK( !pw_vout_manager.get_latest_unused_vout().valid() );
|
||||
BOOST_CHECK( itr->used == true );
|
||||
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 1 );
|
||||
itr++;
|
||||
pw_vout_manager.mark_as_used_vout( create_hash_id( itr->vout.hash_tx, itr->vout.n_vout, itr->id.instance() ) );
|
||||
|
||||
BOOST_CHECK( !pw_vout_manager.get_latest_unused_vout().valid() );
|
||||
BOOST_CHECK( itr->used == true );
|
||||
|
||||
create_primary_wallet_vouts( pw_vout_manager, db, 2 );
|
||||
itr++;
|
||||
itr++;
|
||||
|
||||
GRAPHENE_REQUIRE_THROW( pw_vout_manager.mark_as_used_vout( create_hash_id( itr->vout.hash_tx, itr->vout.n_vout, itr->id.instance() ) ), fc::exception );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
254
tests/sidechain_tests/sidechain_condensing_tx_tests.cpp
Normal file
254
tests/sidechain_tests/sidechain_condensing_tx_tests.cpp
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <sidechain/sidechain_condensing_tx.hpp>
|
||||
#include <sidechain/bitcoin_script.hpp>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <fc/crypto/digest.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( sidechain_condensing_tx_tests )
|
||||
|
||||
uint64_t size_fee = 100;
|
||||
uint64_t pw_vout_amount = 113;
|
||||
double witness_percentage = SIDECHAIN_DEFAULT_PERCENT_PAYMENT_TO_WITNESSES;
|
||||
|
||||
void create_info_vins_and_info_vouts( std::vector<info_for_vin>& info_vins, std::vector<info_for_vout>& info_vouts )
|
||||
{
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
info_for_vin vin;
|
||||
vin.out.hash_tx = "1111111111111111111111111111111111111111111111111111111111111111";
|
||||
vin.out.n_vout = static_cast<uint32_t>( i );
|
||||
vin.out.amount = static_cast<uint64_t>( i + 10000 );
|
||||
vin.address = std::to_string( i );
|
||||
vin.script = { 0x0d };
|
||||
info_vins.push_back( vin );
|
||||
|
||||
info_for_vout vout;
|
||||
vout.payer = account_id_type( i );
|
||||
vout.address = sidechain::bitcoin_address( "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn" );
|
||||
vout.amount = static_cast<uint64_t>( i + 10000 );
|
||||
info_vouts.push_back( vout );
|
||||
}
|
||||
}
|
||||
|
||||
info_for_vin create_pw_vin()
|
||||
{
|
||||
info_for_vin vin_info;
|
||||
vin_info.out.hash_tx = "2222222222222222222222222222222222222222222222222222222222222222";
|
||||
vin_info.out.n_vout = static_cast<uint32_t>( 13 );
|
||||
vin_info.out.amount = static_cast<uint64_t>( 1113 );
|
||||
vin_info.address = std::to_string( 13 );
|
||||
vin_info.script = { 0x0d };
|
||||
|
||||
return vin_info;
|
||||
}
|
||||
|
||||
accounts_keys create_accounts_keys()
|
||||
{
|
||||
fc::ecc::private_key priv_key = fc::ecc::private_key::regenerate( fc::digest( "key" ) );
|
||||
return { { account_id_type( 0 ), public_key_type( priv_key.get_public_key() ) },
|
||||
{ account_id_type( 1 ), public_key_type( priv_key.get_public_key() ) },
|
||||
{ account_id_type( 2 ), public_key_type( priv_key.get_public_key() ) } };
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_sidechain_condensing_tx_test )
|
||||
{
|
||||
std::vector<info_for_vin> info_vins;
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
|
||||
create_info_vins_and_info_vouts( info_vins, info_vouts );
|
||||
|
||||
sidechain_condensing_tx ct( info_vins, info_vouts );
|
||||
const auto& tx = ct.get_transaction();
|
||||
|
||||
BOOST_CHECK( tx.vin.size() == 10 );
|
||||
BOOST_CHECK( tx.vout.size() == 10 );
|
||||
for( size_t i = 0; i < 10; i++ ) {
|
||||
BOOST_CHECK( tx.vin[i].prevout.hash == fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" ) );
|
||||
BOOST_CHECK( tx.vin[i].prevout.n == static_cast<uint32_t>( i ) );
|
||||
bytes scriptSig = { 0x22, 0x0d };
|
||||
BOOST_CHECK( tx.vin[i].scriptSig == scriptSig );
|
||||
BOOST_CHECK( tx.vin[i].scriptWitness == std::vector<bytes>() );
|
||||
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( i + 10000 ) );
|
||||
const auto address_bytes = fc::from_base58( "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn" );
|
||||
bytes raw_address( address_bytes.begin() + 1, address_bytes.begin() + 21 );
|
||||
bytes scriptPubKey = script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG;
|
||||
BOOST_CHECK( tx.vout[i].scriptPubKey == scriptPubKey );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( creation_additional_in_out_sidechain_condensing_tx )
|
||||
{
|
||||
std::vector<info_for_vin> info_vins;
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
|
||||
create_info_vins_and_info_vouts( info_vins, info_vouts );
|
||||
|
||||
accounts_keys keys = create_accounts_keys();
|
||||
|
||||
sidechain_condensing_tx ct( info_vins, info_vouts );
|
||||
ct.create_pw_vin( create_pw_vin() );
|
||||
ct.create_vouts_for_witness_fee( keys );
|
||||
ct.create_pw_vout( 1113, bytes{ 0x0d, 0x0d, 0x0d } );
|
||||
const auto& tx = ct.get_transaction();
|
||||
|
||||
BOOST_CHECK( tx.vin.size() == 11 );
|
||||
BOOST_CHECK( tx.vout.size() == 14 );
|
||||
|
||||
BOOST_CHECK( tx.vin[0].prevout.hash == fc::sha256( "2222222222222222222222222222222222222222222222222222222222222222" ) );
|
||||
BOOST_CHECK( tx.vin[0].prevout.n == static_cast<uint32_t>( 13 ) );
|
||||
bytes scriptSig = { 0x22, 0x0d };
|
||||
BOOST_CHECK( tx.vin[0].scriptSig == bytes() );
|
||||
|
||||
BOOST_CHECK( tx.vout[0].value == static_cast<int64_t>( 1113 ) );
|
||||
bytes scriptPubKey{ 0x00, 0x01, 0x0d };
|
||||
BOOST_CHECK( tx.vout[0].scriptPubKey == scriptPubKey );
|
||||
for( size_t i = 1; i <= keys.size(); i++ ) {
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( 0 ) );
|
||||
bytes scriptPubKey{ 0x21 };
|
||||
const auto key_data = keys[account_id_type( i - 1 )].key_data;
|
||||
std::copy( key_data.begin(), key_data.end(), std::back_inserter( scriptPubKey ) );
|
||||
scriptPubKey.push_back( 0xac );
|
||||
BOOST_CHECK( tx.vout[i].scriptPubKey == scriptPubKey );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( subtract_fee_tests )
|
||||
{
|
||||
std::vector<info_for_vin> info_vins;
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
|
||||
accounts_keys keys = create_accounts_keys();
|
||||
create_info_vins_and_info_vouts( info_vins, info_vouts );
|
||||
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
|
||||
|
||||
sidechain_condensing_tx ct( info_vins, info_vouts );
|
||||
ct.create_pw_vin( create_pw_vin() );
|
||||
ct.create_vouts_for_witness_fee( keys );
|
||||
ct.create_pw_vout( pw_vout_amount, bytes{ 0x0d, 0x0d, 0x0d } );
|
||||
ct.subtract_fee( size_fee, witness_percentage );
|
||||
const auto& tx = ct.get_transaction();
|
||||
|
||||
std::vector<uint64_t> witnesses_fee;
|
||||
for( size_t i = 0; i < info_vouts.size(); i++ ) {
|
||||
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage / GRAPHENE_100_PERCENT );
|
||||
}
|
||||
|
||||
uint64_t witnesses_fee_sum = std::accumulate( witnesses_fee.begin(), witnesses_fee.end(), 0 );
|
||||
uint64_t witness_fee = witnesses_fee_sum / keys.size();
|
||||
BOOST_CHECK( tx.vout[0].value == static_cast<int64_t>( pw_vout_amount - size_fee_user * info_vins.size() ) );
|
||||
for( size_t i = 1; i <= keys.size(); i++ ) {
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( witness_fee ) );
|
||||
}
|
||||
|
||||
size_t offset = 1 + keys.size();
|
||||
for( size_t i = offset; i < tx.vout.size(); i++ ) {
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( subtract_fee_not_pw_vout_and_witness_vouts_tests )
|
||||
{
|
||||
std::vector<info_for_vin> info_vins;
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
|
||||
create_info_vins_and_info_vouts( info_vins, info_vouts );
|
||||
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
|
||||
|
||||
sidechain_condensing_tx ct( info_vins, info_vouts );
|
||||
ct.create_pw_vin( create_pw_vin() );
|
||||
ct.subtract_fee( size_fee, witness_percentage );
|
||||
const auto& tx = ct.get_transaction();
|
||||
|
||||
std::vector<uint64_t> witnesses_fee;
|
||||
for( size_t i = 0; i < info_vouts.size(); i++ ) {
|
||||
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage / GRAPHENE_100_PERCENT );
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < tx.vout.size(); i++ ) {
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i].amount - size_fee_user ) - witnesses_fee[i] ) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( subtract_fee_not_witness_vouts_tests )
|
||||
{
|
||||
std::vector<info_for_vin> info_vins;
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
|
||||
create_info_vins_and_info_vouts( info_vins, info_vouts );
|
||||
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
|
||||
|
||||
sidechain_condensing_tx ct( info_vins, info_vouts );
|
||||
ct.create_pw_vin( create_pw_vin() );
|
||||
ct.create_pw_vout( pw_vout_amount, bytes{ 0x0d, 0x0d, 0x0d } );
|
||||
ct.subtract_fee( size_fee, witness_percentage );
|
||||
const auto& tx = ct.get_transaction();
|
||||
|
||||
std::vector<uint64_t> witnesses_fee;
|
||||
for( size_t i = 0; i < info_vouts.size(); i++ ) {
|
||||
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage / GRAPHENE_100_PERCENT );
|
||||
}
|
||||
|
||||
size_t offset = 1;
|
||||
for( size_t i = offset; i < tx.vout.size(); i++ ) {
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( subtract_fee_not_pw_vout_tests )
|
||||
{
|
||||
std::vector<info_for_vin> info_vins;
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
|
||||
create_info_vins_and_info_vouts( info_vins, info_vouts );
|
||||
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
|
||||
accounts_keys keys = create_accounts_keys();
|
||||
|
||||
sidechain_condensing_tx ct( info_vins, info_vouts );
|
||||
ct.create_pw_vin( create_pw_vin() );
|
||||
ct.create_vouts_for_witness_fee( keys );
|
||||
ct.subtract_fee( size_fee, witness_percentage );
|
||||
const auto& tx = ct.get_transaction();
|
||||
|
||||
std::vector<uint64_t> witnesses_fee;
|
||||
for( size_t i = 0; i < info_vouts.size(); i++ ) {
|
||||
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage / GRAPHENE_100_PERCENT );
|
||||
}
|
||||
|
||||
size_t offset = keys.size();
|
||||
for( size_t i = offset; i < tx.vout.size(); i++ ) {
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( subtract_fee_not_vins_tests )
|
||||
{
|
||||
std::vector<info_for_vin> info_vins;
|
||||
std::vector<info_for_vout> info_vouts;
|
||||
|
||||
create_info_vins_and_info_vouts( info_vins, info_vouts );
|
||||
info_vins.clear();
|
||||
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
|
||||
accounts_keys keys = create_accounts_keys();
|
||||
|
||||
sidechain_condensing_tx ct( info_vins, info_vouts );
|
||||
ct.create_pw_vin( create_pw_vin() );
|
||||
ct.create_vouts_for_witness_fee( keys );
|
||||
ct.create_pw_vout( pw_vout_amount, bytes{ 0x0d, 0x0d, 0x0d } );
|
||||
ct.subtract_fee( size_fee, witness_percentage );
|
||||
const auto& tx = ct.get_transaction();
|
||||
|
||||
std::vector<uint64_t> witnesses_fee;
|
||||
for( size_t i = 0; i < info_vouts.size(); i++ ) {
|
||||
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage / GRAPHENE_100_PERCENT );
|
||||
}
|
||||
|
||||
size_t offset = 1 + keys.size();
|
||||
for( size_t i = offset; i < tx.vout.size(); i++ ) {
|
||||
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
223
tests/sidechain_tests/sidechain_operation_tests.cpp
Normal file
223
tests/sidechain_tests/sidechain_operation_tests.cpp
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
#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( sidechain_operation_tests, database_fixture )
|
||||
|
||||
void create_bitcoin_issue_operation_environment( database& db )
|
||||
{
|
||||
std::vector< fc::sha256 > 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::string( 64, std::to_string( i )[0] ) );
|
||||
obj.out.amount = 100000 + i * 100000;
|
||||
obj.out.hash_tx = std::string( 64, std::to_string( i )[0] );
|
||||
obj.out.n_vout = 0;
|
||||
obj.address = std::to_string( i );
|
||||
});
|
||||
|
||||
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.identifier );
|
||||
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" );
|
||||
obj.fee_for_size = 0;
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_bitcoin_address_test )
|
||||
{
|
||||
transaction_evaluation_state context(&db);
|
||||
|
||||
bitcoin_address_create_operation op;
|
||||
op.payer = account_id_type();
|
||||
op.owner = account_id_type();
|
||||
|
||||
const auto& idx = db.get_index_type<bitcoin_address_index>().indices().get< by_id >();
|
||||
|
||||
BOOST_CHECK( idx.size() == 1 );
|
||||
|
||||
db.apply_operation( context, op );
|
||||
|
||||
auto btc_address = idx.begin();
|
||||
BOOST_CHECK( btc_address->count_invalid_pub_key == 1 );
|
||||
}
|
||||
|
||||
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( std::string( 64,'1' ) ), std::set<fc::sha256>() ) );
|
||||
|
||||
BOOST_CHECK( btc_trx_idx.size() == 1 );
|
||||
BOOST_CHECK( vins_info_idx.size() == 3 );
|
||||
BOOST_CHECK( vouts_info_idx.size() == 3 );
|
||||
BOOST_CHECK( db.bitcoin_confirmations.size() == 1 );
|
||||
|
||||
bitcoin_issue_operation op;
|
||||
op.payer = db.get_sidechain_account_id();
|
||||
op.transaction_ids = { fc::sha256( std::string( 64,'1' ) ) };
|
||||
db.apply_operation( context, op );
|
||||
|
||||
BOOST_CHECK( btc_trx_idx.size() == 0 );
|
||||
BOOST_CHECK( vins_info_idx.size() == 0 );
|
||||
BOOST_CHECK( vouts_info_idx.size() == 0 );
|
||||
BOOST_CHECK( 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_ids = { fc::sha256( std::string( 64,'1' ) ) };
|
||||
|
||||
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() ) );
|
||||
BOOST_CHECK( itr != btc_trx_idx.end() );
|
||||
|
||||
BOOST_CHECK( 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( std::string( 64,'1' ) ), std::set<fc::sha256>() ) );
|
||||
|
||||
{
|
||||
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_ids = { fc::sha256( std::string( 64,'1' ) ) };
|
||||
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_ids = { fc::sha256( std::string( 64,'1' ) ) };
|
||||
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_ids = { fc::sha256( std::string( 64,'1' ) ) };
|
||||
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_ids = { fc::sha256( std::string( 64,'1' ) ) };
|
||||
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_ids = { fc::sha256( std::string( 64,'1' ) ) };
|
||||
GRAPHENE_REQUIRE_THROW( db.apply_operation( context, op ) , fc::exception );
|
||||
|
||||
session.undo();
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bitcoin_transaction_revert_operation_test )
|
||||
{
|
||||
transaction_evaluation_state context(&db);
|
||||
const auto& btc_tx_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_identifier >();
|
||||
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( std::string( 64, '1' ) ), std::set<fc::sha256>() ) );
|
||||
|
||||
auto vin = *vins_info_idx.find( fc::sha256( std::string( 64, '1' ) ) );
|
||||
|
||||
bitcoin_transaction_revert_operation revert_op;
|
||||
revert_trx_info info;
|
||||
info.transaction_id = fc::sha256( std::string( 64, '1' ) );
|
||||
info.valid_vins.insert( fc::sha256( std::string( 64, '1' ) ) );
|
||||
revert_op.transactions_info.push_back( info );
|
||||
|
||||
db.apply_operation( context, revert_op );
|
||||
|
||||
BOOST_CHECK_EQUAL( vins_info_idx.size(), 0 );
|
||||
BOOST_CHECK_EQUAL( btc_tx_idx.size(), 0 );
|
||||
|
||||
auto vin_info = db.i_w_info.find_info_for_vin( fc::sha256::hash( vin.out.hash_tx + std::to_string( vin.out.n_vout ) ) );
|
||||
BOOST_CHECK( vin_info.valid() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
386
tests/sidechain_tests/sidechain_proposal_checker_tests.cpp
Normal file
386
tests/sidechain_tests/sidechain_proposal_checker_tests.cpp
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
#include <sidechain/sidechain_proposal_checker.hpp>
|
||||
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/proposal_evaluator.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/bitcoin_address_object.hpp>
|
||||
#include <graphene/chain/bitcoin_transaction_object.hpp>
|
||||
|
||||
using namespace sidechain;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( sidechain_proposal_checker_tests, database_fixture )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_reuse_normal_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
bitcoin_transaction_send_operation op;
|
||||
|
||||
op.pw_vin = info_for_vin( { std::string( 64, '1' ), 0, 0 }, "", bytes() );
|
||||
for( size_t i = 0; i < 5; i++ ) {
|
||||
op.vins.push_back( info_for_vin( { std::string( 64, std::to_string( i )[0] ), 0, 0 }, "", bytes() ) );
|
||||
op.vouts.push_back( info_for_vout_id_type( i ) );
|
||||
}
|
||||
|
||||
BOOST_CHECK( checker.check_reuse( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_reuse_not_normal_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
bitcoin_transaction_send_operation op;
|
||||
|
||||
op.pw_vin = info_for_vin( { std::string( 64, '1' ), 0, 0 }, "", bytes() );
|
||||
for( size_t i = 0; i < 5; i++ ) {
|
||||
op.vins.push_back( info_for_vin( { std::string( 64, std::to_string( i )[0] ), 0, 0 }, "", bytes() ) );
|
||||
op.vouts.push_back( info_for_vout_id_type( i ) );
|
||||
}
|
||||
|
||||
BOOST_CHECK( checker.check_reuse( op ) );
|
||||
BOOST_CHECK( !checker.check_reuse( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_normal_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
test_environment env( db );
|
||||
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.pw_vin = env.pw_vin;
|
||||
op.vins = env.vins;
|
||||
for( const auto& vout : env.vouts ) {
|
||||
op.vouts.push_back( vout.get_id() );
|
||||
}
|
||||
op.transaction = env.full_tx.first;
|
||||
op.fee_for_size = env.full_tx.second;
|
||||
|
||||
BOOST_CHECK( checker.check_bitcoin_transaction_send_operation( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_incorrect_info_for_vin_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
test_environment env( db );
|
||||
|
||||
env.vins[3].out.amount = 13;
|
||||
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.pw_vin = env.pw_vin;
|
||||
op.vins = env.vins;
|
||||
for( const auto& vout : env.vouts ) {
|
||||
op.vouts.push_back( vout.get_id() );
|
||||
}
|
||||
op.transaction = env.full_tx.first;
|
||||
op.fee_for_size = env.full_tx.second;
|
||||
|
||||
BOOST_CHECK( !checker.check_bitcoin_transaction_send_operation( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_incorrect_info_for_pw_vin_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
test_environment env( db );
|
||||
|
||||
env.pw_vin.out.amount = 13;
|
||||
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.pw_vin = env.pw_vin;
|
||||
op.vins = env.vins;
|
||||
for( const auto& vout : env.vouts ) {
|
||||
op.vouts.push_back( vout.get_id() );
|
||||
}
|
||||
op.transaction = env.full_tx.first;
|
||||
op.fee_for_size = env.full_tx.second;
|
||||
|
||||
BOOST_CHECK( !checker.check_bitcoin_transaction_send_operation( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_incorrect_tx_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
test_environment env( db );
|
||||
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.pw_vin = env.pw_vin;
|
||||
op.vins = env.vins;
|
||||
for( const auto& vout : env.vouts ) {
|
||||
op.vouts.push_back( vout.get_id() );
|
||||
}
|
||||
|
||||
env.full_tx.first.vout[0].value = 13;
|
||||
|
||||
op.transaction = env.full_tx.first;
|
||||
op.fee_for_size = env.full_tx.second;
|
||||
|
||||
BOOST_CHECK( !checker.check_bitcoin_transaction_send_operation( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( twice_approve_btc_tx_send_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
const flat_set<witness_id_type>& active_witnesses = db.get_global_properties().active_witnesses;
|
||||
const auto& proposal_idx = db.get_index_type<graphene::chain::proposal_index>().indices().get< graphene::chain::by_id >();
|
||||
transaction_evaluation_state context(&db);
|
||||
|
||||
auto propose = db.create<proposal_object>( [&]( proposal_object& obj ){
|
||||
obj.expiration_time = db.head_block_time() + fc::days(1);
|
||||
obj.review_period_time = db.head_block_time() + fc::days(1);
|
||||
} );
|
||||
|
||||
const witness_id_type& witness_id = *active_witnesses.begin();
|
||||
const witness_object witness = witness_id(db);
|
||||
const account_object& witness_account = witness.witness_account(db);
|
||||
|
||||
proposal_update_operation op;
|
||||
{
|
||||
op.proposal = propose.id;
|
||||
op.fee_paying_account = witness_account.id;
|
||||
op.active_approvals_to_add.insert( witness_account.id );
|
||||
}
|
||||
|
||||
BOOST_CHECK( proposal_idx.size() == 1 );
|
||||
BOOST_CHECK_EQUAL( proposal_idx.begin()->available_active_approvals.size(), 0 );
|
||||
BOOST_CHECK( checker.check_witness_opportunity_to_approve( witness, *proposal_idx.begin() ) );
|
||||
|
||||
db.apply_operation( context, op );
|
||||
|
||||
BOOST_CHECK( proposal_idx.size() == 1 );
|
||||
BOOST_CHECK_EQUAL( proposal_idx.begin()->available_active_approvals.size(), 1 );
|
||||
BOOST_CHECK( !checker.check_witness_opportunity_to_approve( witness, *proposal_idx.begin() ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( not_active_wit_try_to_approve_btc_send_test )
|
||||
{
|
||||
ACTOR(nathan);
|
||||
upgrade_to_lifetime_member( nathan_id );
|
||||
trx.clear();
|
||||
witness_id_type nathan_witness_id = create_witness( nathan_id, nathan_private_key ).id;
|
||||
|
||||
const auto& witnesses = db.get_global_properties().active_witnesses;
|
||||
BOOST_CHECK( std::find(witnesses.begin(), witnesses.end(), nathan_witness_id) == witnesses.end() );
|
||||
|
||||
const auto& witness_idx = db.get_index_type<graphene::chain::witness_index>().indices().get<graphene::chain::by_id>();
|
||||
auto itr = witness_idx.find( nathan_witness_id );
|
||||
BOOST_CHECK( itr != witness_idx.end() );
|
||||
|
||||
sidechain_proposal_checker checker( db );
|
||||
const auto& proposal_idx = db.get_index_type<graphene::chain::proposal_index>().indices().get< graphene::chain::by_id >();
|
||||
auto propose = db.create<proposal_object>( [&]( proposal_object& obj ){
|
||||
obj.expiration_time = db.head_block_time() + fc::days(1);
|
||||
obj.review_period_time = db.head_block_time() + fc::days(1);
|
||||
} );
|
||||
|
||||
BOOST_CHECK( !checker.check_witness_opportunity_to_approve( *itr, *proposal_idx.begin() ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( not_account_auths_wit_try_to_approve_btc_send_test )
|
||||
{
|
||||
ACTOR(nathan);
|
||||
upgrade_to_lifetime_member(nathan_id);
|
||||
trx.clear();
|
||||
witness_id_type nathan_witness_id = create_witness(nathan_id, nathan_private_key).id;
|
||||
// Give nathan some voting stake
|
||||
transfer(committee_account, nathan_id, asset(10000000));
|
||||
generate_block();
|
||||
test::set_expiration( db, trx );
|
||||
|
||||
{
|
||||
account_update_operation op;
|
||||
op.account = nathan_id;
|
||||
op.new_options = nathan_id(db).options;
|
||||
op.new_options->votes.insert(nathan_witness_id(db).vote_id);
|
||||
op.new_options->num_witness = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(),
|
||||
[](vote_id_type id) { return id.type() == vote_id_type::witness; });
|
||||
op.new_options->num_committee = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(),
|
||||
[](vote_id_type id) { return id.type() == vote_id_type::committee; });
|
||||
trx.operations.push_back(op);
|
||||
sign( trx, nathan_private_key );
|
||||
PUSH_TX( db, trx );
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
const auto& witnesses = db.get_global_properties().active_witnesses;
|
||||
auto itr_ = std::find(witnesses.begin(), witnesses.end(), nathan_witness_id);
|
||||
BOOST_CHECK(itr_ != witnesses.end());
|
||||
|
||||
const auto& witness_idx = db.get_index_type<graphene::chain::witness_index>().indices().get<graphene::chain::by_id>();
|
||||
auto itr = witness_idx.find( nathan_witness_id );
|
||||
BOOST_CHECK( itr != witness_idx.end() );
|
||||
|
||||
sidechain_proposal_checker checker( db );
|
||||
const auto& proposal_idx = db.get_index_type<graphene::chain::proposal_index>().indices().get< graphene::chain::by_id >();
|
||||
auto propose = db.create<proposal_object>( [&]( proposal_object& obj ){
|
||||
obj.expiration_time = db.head_block_time() + fc::days(1);
|
||||
obj.review_period_time = db.head_block_time() + fc::days(1);
|
||||
} );
|
||||
|
||||
BOOST_CHECK( !checker.check_witness_opportunity_to_approve( *itr, *proposal_idx.begin() ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE ( check_witness_keys_test_normal )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
|
||||
auto witnesses_keys = db.get_latest_PW().address.witnesses_keys;
|
||||
const auto& bitcoin_address = db.create<bitcoin_address_object>( [&]( bitcoin_address_object& obj ) {
|
||||
witnesses_keys.erase( ++witnesses_keys.begin() );
|
||||
obj.address = sidechain::btc_multisig_segwit_address( SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG, witnesses_keys );
|
||||
obj.owner = account_id_type( 13 );
|
||||
} );
|
||||
|
||||
info_for_vin vin;
|
||||
vin.address = bitcoin_address.address.address;
|
||||
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.vins = { vin };
|
||||
|
||||
proposal_object proposal;
|
||||
proposal.proposed_transaction.operations.push_back( op );
|
||||
|
||||
const witness_id_type& witness_id = *db.get_global_properties().active_witnesses.begin();
|
||||
const witness_object witness = witness_id( db );
|
||||
|
||||
BOOST_CHECK( checker.check_witnesses_keys( witness, proposal ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE ( check_witnesses_keys_incorrect_witness_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
|
||||
auto witnesses_keys = db.get_latest_PW().address.witnesses_keys;
|
||||
const auto& bitcoin_address = db.create<bitcoin_address_object>( [&]( bitcoin_address_object& obj ) {
|
||||
witnesses_keys.erase( ++witnesses_keys.begin() );
|
||||
obj.address = sidechain::btc_multisig_segwit_address( SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG, witnesses_keys );
|
||||
obj.owner = account_id_type( 13 );
|
||||
} );
|
||||
|
||||
info_for_vin vin;
|
||||
vin.address = bitcoin_address.address.address;
|
||||
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.vins = { vin };
|
||||
|
||||
proposal_object proposal;
|
||||
proposal.proposed_transaction.operations.push_back( op );
|
||||
|
||||
const witness_object witness;
|
||||
|
||||
BOOST_CHECK( !checker.check_witnesses_keys( witness, proposal ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE ( check_witnesses_keys_nonexistent_witness_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
|
||||
auto witnesses_keys = db.get_latest_PW().address.witnesses_keys;
|
||||
const auto& bitcoin_address = db.create<bitcoin_address_object>( [&]( bitcoin_address_object& obj ) {
|
||||
witnesses_keys.erase( ++witnesses_keys.begin() );
|
||||
obj.address = sidechain::btc_multisig_segwit_address( SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG, witnesses_keys );
|
||||
obj.owner = account_id_type( 13 );
|
||||
} );
|
||||
|
||||
info_for_vin vin;
|
||||
vin.address = bitcoin_address.address.address;
|
||||
|
||||
bitcoin_transaction_send_operation op;
|
||||
op.vins = { vin };
|
||||
|
||||
proposal_object proposal;
|
||||
proposal.proposed_transaction.operations.push_back( op );
|
||||
|
||||
const witness_id_type& witness_id = *++db.get_global_properties().active_witnesses.begin();
|
||||
const witness_object witness = witness_id( db );
|
||||
|
||||
BOOST_CHECK( !checker.check_witnesses_keys( witness, proposal ) );
|
||||
}
|
||||
|
||||
void create_missing_bto( graphene::chain::database& db, uint32_t amount )
|
||||
{
|
||||
BOOST_REQUIRE( amount < 9 );
|
||||
while( amount-- > 0 ) {
|
||||
std::set<fc::sha256> vins { fc::sha256( std::string( 64,'1' + amount ) ), fc::sha256( std::string( 64,'2' + amount ) ) };
|
||||
sidechain::bitcoin_transaction_confirmations btc_trx_conf ( fc::sha256( std::string( 64,'1' + amount ) ), vins );
|
||||
btc_trx_conf.missing = true;
|
||||
db.bitcoin_confirmations.insert( btc_trx_conf );
|
||||
|
||||
db.create<graphene::chain::bitcoin_transaction_object>( [&]( graphene::chain::bitcoin_transaction_object& obj ){
|
||||
obj.transaction_id = fc::sha256( std::string( 64,'1' + amount ) );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector< revert_trx_info > get_transactions_info( graphene::chain::database& db, uint32_t amount )
|
||||
{
|
||||
using iter_by_missing = btc_tx_confirmations_index::index<by_missing_and_not_used>::type::iterator;
|
||||
std::vector< revert_trx_info > transactions_info;
|
||||
const auto& btc_trx_idx = db.get_index_type<graphene::chain::bitcoin_transaction_index>().indices().get<graphene::chain::by_transaction_id>();
|
||||
BOOST_CHECK_EQUAL( btc_trx_idx.size() , amount );
|
||||
|
||||
db.bitcoin_confirmations.safe_for<by_missing_and_not_used>([&]( iter_by_missing itr_b, iter_by_missing itr_e ){
|
||||
for(auto iter = itr_b; iter != itr_e; iter++) {
|
||||
if( !iter->is_missing_and_not_used() ) return;
|
||||
const auto& btc_tx = btc_trx_idx.find( iter->transaction_id );
|
||||
if( btc_tx == btc_trx_idx.end() ) continue;
|
||||
transactions_info.push_back( revert_trx_info( iter->transaction_id, iter->valid_vins ) );
|
||||
}
|
||||
});
|
||||
|
||||
return transactions_info;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bitcoin_transaction_revert_operation_checker_test )
|
||||
{
|
||||
using namespace graphene::chain;
|
||||
const uint32_t amount = 5;
|
||||
|
||||
create_missing_bto( db, amount );
|
||||
sidechain_proposal_checker checker( db );
|
||||
|
||||
bitcoin_transaction_revert_operation op;
|
||||
|
||||
std::vector< revert_trx_info > transactions_info = get_transactions_info( db, amount );
|
||||
|
||||
op.transactions_info = transactions_info;
|
||||
|
||||
BOOST_CHECK_EQUAL( transactions_info.size(), amount );
|
||||
BOOST_CHECK( checker.check_bitcoin_transaction_revert_operation( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bitcoin_transaction_revert_operation_checker_failed_test )
|
||||
{
|
||||
using namespace graphene::chain;
|
||||
const uint32_t amount = 5;
|
||||
|
||||
create_missing_bto( db, amount );
|
||||
|
||||
std::set<fc::sha256> vins { fc::sha256( std::string( 64,'1' + 6 ) ), fc::sha256( std::string( 64,'1' + 8 ) ) };
|
||||
fc::sha256 trx_id( std::string( 64,'1' + 8 ) );
|
||||
sidechain::bitcoin_transaction_confirmations btc_trx_conf ( trx_id, vins );
|
||||
db.bitcoin_confirmations.insert( btc_trx_conf );
|
||||
db.create<graphene::chain::bitcoin_transaction_object>( [&]( graphene::chain::bitcoin_transaction_object& obj ){
|
||||
obj.transaction_id = fc::sha256( std::string( 64,'1' + 7 ) );
|
||||
} );
|
||||
|
||||
sidechain_proposal_checker checker( db );
|
||||
|
||||
bitcoin_transaction_revert_operation op;
|
||||
std::vector< revert_trx_info > transactions_info = get_transactions_info( db, amount + 1 );
|
||||
|
||||
op.transactions_info = transactions_info;
|
||||
op.transactions_info.push_back( revert_trx_info( trx_id, vins) );
|
||||
|
||||
BOOST_CHECK_EQUAL( op.transactions_info.size(), amount + 1 );
|
||||
BOOST_CHECK( !checker.check_bitcoin_transaction_revert_operation( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( no_btc_trx_to_revert_test )
|
||||
{
|
||||
sidechain_proposal_checker checker( db );
|
||||
|
||||
bitcoin_transaction_revert_operation op;
|
||||
BOOST_CHECK( !checker.check_bitcoin_transaction_revert_operation( op ) );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue