Merge branch 'SON261' into feature/SON-264
Fix conflicts
This commit is contained in:
commit
1e27c0b4ba
17 changed files with 1000 additions and 8 deletions
|
|
@ -343,6 +343,15 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sidechain_address_delete_operation& op ){
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
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_send_transaction_process_operation& op ){
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ add_library( graphene_chain
|
|||
son_wallet_withdraw_evaluator.cpp
|
||||
|
||||
sidechain_address_evaluator.cpp
|
||||
sidechain_transaction_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
#include <graphene/chain/asset_evaluator.hpp>
|
||||
|
|
@ -86,6 +87,7 @@
|
|||
#include <graphene/chain/son_wallet_deposit_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -267,6 +269,9 @@ void database::initialize_evaluators()
|
|||
register_evaluator<add_sidechain_address_evaluator>();
|
||||
register_evaluator<update_sidechain_address_evaluator>();
|
||||
register_evaluator<delete_sidechain_address_evaluator>();
|
||||
register_evaluator<bitcoin_transaction_send_evaluator>();
|
||||
register_evaluator<bitcoin_transaction_sign_evaluator>();
|
||||
register_evaluator<bitcoin_send_transaction_process_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -316,6 +321,7 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<son_wallet_withdraw_index> >();
|
||||
|
||||
add_index< primary_index<sidechain_address_index> >();
|
||||
add_index< primary_index<bitcoin_transaction_index> >();
|
||||
|
||||
//Implementation object indexes
|
||||
add_index< primary_index<transaction_index > >();
|
||||
|
|
|
|||
|
|
@ -330,6 +330,15 @@ struct get_impacted_account_visitor
|
|||
void operator()( const sidechain_address_delete_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
}
|
||||
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_send_transaction_process_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
|
||||
|
|
|
|||
6
libraries/chain/include/graphene/chain/protocol/operations.hpp
Normal file → Executable file
6
libraries/chain/include/graphene/chain/protocol/operations.hpp
Normal file → Executable file
|
|
@ -50,6 +50,7 @@
|
|||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet_deposit.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet_withdraw.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -155,7 +156,10 @@ namespace graphene { namespace chain {
|
|||
son_wallet_withdraw_process_operation,
|
||||
sidechain_address_add_operation,
|
||||
sidechain_address_update_operation,
|
||||
sidechain_address_delete_operation
|
||||
sidechain_address_delete_operation,
|
||||
bitcoin_transaction_send_operation,
|
||||
bitcoin_transaction_sign_operation,
|
||||
bitcoin_send_transaction_process_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct bitcoin_transaction_send_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
// TODO: BTC Transaction Structs go here
|
||||
fc::flat_map<son_id_type, std::vector<peerplays_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 bitcoin_transaction_sign_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
proposal_id_type proposal_id;
|
||||
std::vector<peerplays_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 bitcoin_send_transaction_process_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
bitcoin_transaction_id_type bitcoin_transaction_id;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
void validate()const {}
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation, (fee)(payer)(signatures) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(proposal_id)(signatures) )
|
||||
|
||||
FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation, (fee)(payer)(bitcoin_transaction_id) )
|
||||
|
|
@ -151,6 +151,7 @@ namespace graphene { namespace chain {
|
|||
son_wallet_deposit_object_type,
|
||||
son_wallet_withdraw_object_type,
|
||||
sidechain_address_object_type,
|
||||
bitcoin_transaction_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -218,6 +219,7 @@ namespace graphene { namespace chain {
|
|||
class son_wallet_deposit_object;
|
||||
class son_wallet_withdraw_object;
|
||||
class sidechain_address_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;
|
||||
|
|
@ -250,6 +252,7 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type;
|
||||
typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type;
|
||||
typedef object_id< protocol_ids, bitcoin_transaction_object_type,bitcoin_transaction_object> bitcoin_transaction_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -440,6 +443,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(son_wallet_deposit_object_type)
|
||||
(son_wallet_withdraw_object_type)
|
||||
(sidechain_address_object_type)
|
||||
(bitcoin_transaction_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -517,6 +521,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::bitcoin_transaction_id_type )
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||
|
||||
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& o);
|
||||
object_id_type do_apply(const bitcoin_transaction_send_operation& o);
|
||||
};
|
||||
|
||||
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& o);
|
||||
object_id_type do_apply(const bitcoin_transaction_sign_operation& o);
|
||||
void update_proposal( const bitcoin_transaction_sign_operation& o );
|
||||
};
|
||||
|
||||
class bitcoin_send_transaction_process_evaluator : public evaluator<bitcoin_send_transaction_process_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef bitcoin_send_transaction_process_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const bitcoin_send_transaction_process_operation& o);
|
||||
object_id_type do_apply(const bitcoin_send_transaction_process_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class bitcoin_transaction_object
|
||||
* @brief tracks state of bitcoin transaction.
|
||||
* @ingroup object
|
||||
*/
|
||||
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 structs go here.
|
||||
bool processed = false;
|
||||
fc::flat_map<son_id_type, std::vector<peerplays_sidechain::bytes>> signatures;
|
||||
};
|
||||
|
||||
struct by_processed;
|
||||
using bitcoin_transaction_multi_index_type = multi_index_container<
|
||||
bitcoin_transaction_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_non_unique< tag<by_processed>,
|
||||
member<bitcoin_transaction_object, bool, &bitcoin_transaction_object::processed>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using bitcoin_transaction_index = generic_index<bitcoin_transaction_object, bitcoin_transaction_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::bitcoin_transaction_object, (graphene::db::object),
|
||||
(processed)(signatures) )
|
||||
136
libraries/chain/sidechain_transaction_evaluator.cpp
Executable file
136
libraries/chain/sidechain_transaction_evaluator.cpp
Executable file
|
|
@ -0,0 +1,136 @@
|
|||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene
|
||||
{
|
||||
namespace chain
|
||||
{
|
||||
|
||||
void_result bitcoin_transaction_send_evaluator::do_evaluate(const bitcoin_transaction_send_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." );
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
object_id_type bitcoin_transaction_send_evaluator::do_apply(const bitcoin_transaction_send_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto &new_bitcoin_transaction_object = db().create<bitcoin_transaction_object>([&](bitcoin_transaction_object &obj) {
|
||||
obj.processed = false;
|
||||
obj.signatures = op.signatures;
|
||||
});
|
||||
return new_bitcoin_transaction_object.id;
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
void_result bitcoin_transaction_sign_evaluator::do_evaluate(const bitcoin_transaction_sign_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
const auto &proposal_idx = db().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");
|
||||
// Checks can this SON approve this proposal
|
||||
auto can_this_son_approve_this_proposal = [&]() {
|
||||
const auto &sidx = db().get_index_type<son_index>().indices().get<graphene::chain::by_account>();
|
||||
auto son_obj = sidx.find(op.payer);
|
||||
if (son_obj == sidx.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// TODO: Check if the SON is included in the PW script.
|
||||
return true;
|
||||
};
|
||||
|
||||
FC_ASSERT(can_this_son_approve_this_proposal(), "Invalid approval received");
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
object_id_type bitcoin_transaction_sign_evaluator::do_apply(const bitcoin_transaction_sign_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto &proposal = op.proposal_id(db());
|
||||
const auto &sidx = db().get_index_type<son_index>().indices().get<graphene::chain::by_account>();
|
||||
auto son_obj = sidx.find(op.payer);
|
||||
|
||||
db().modify(proposal, [&](proposal_object &po) {
|
||||
auto bitcoin_transaction_send_op = po.proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();
|
||||
bitcoin_transaction_send_op.signatures[son_obj->id] = op.signatures;
|
||||
po.proposed_transaction.operations[0] = bitcoin_transaction_send_op;
|
||||
});
|
||||
|
||||
update_proposal(op);
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void_result bitcoin_send_transaction_process_evaluator::do_evaluate(const bitcoin_send_transaction_process_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == GRAPHENE_SON_ACCOUNT, "SON paying account must be set as payer." );
|
||||
const auto& btidx = db().get_index_type<bitcoin_transaction_index>().indices().get<by_id>();
|
||||
const auto btobj = btidx.find(op.bitcoin_transaction_id);
|
||||
FC_ASSERT(btobj != btidx.end(), "Bitcoin Transaction Object not found");
|
||||
FC_ASSERT(btobj->processed == false, "Bitcoin Transaction already processed");
|
||||
return void_result();
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
object_id_type bitcoin_send_transaction_process_evaluator::do_apply(const bitcoin_send_transaction_process_operation &op)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto &btidx = db().get_index_type<bitcoin_transaction_index>().indices().get<by_id>();
|
||||
auto btobj = btidx.find(op.bitcoin_transaction_id);
|
||||
if (btobj != btidx.end())
|
||||
{
|
||||
db().modify(*btobj, [&op](bitcoin_transaction_object &bto) {
|
||||
bto.processed = true;
|
||||
});
|
||||
}
|
||||
return op.bitcoin_transaction_id;
|
||||
}
|
||||
FC_CAPTURE_AND_RETHROW((op))
|
||||
}
|
||||
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
2
libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp
Normal file → Executable file
2
libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp
Normal file → Executable file
|
|
@ -36,6 +36,8 @@ protected:
|
|||
virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0;
|
||||
virtual std::string sign_transaction( const std::string& transaction ) = 0;
|
||||
virtual std::string send_transaction( const std::string& transaction ) = 0;
|
||||
//virtual std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed) = 0;
|
||||
//virtual std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) = 0;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,12 @@ public:
|
|||
void send_btc_tx( const std::string& tx_hex );
|
||||
std::string add_multisig_address( const std::vector<std::string> public_keys );
|
||||
bool connection_is_not_defined() const;
|
||||
std::string create_raw_transaction(const std::string& txid, const std::string& vout, const std::string& out_address, double transfer_amount);
|
||||
std::string sign_raw_transaction_with_wallet(const std::string& tx_hash);
|
||||
std::string sign_raw_transaction_with_privkey(const std::string& tx_hash, const std::string& private_key);
|
||||
void import_address( const std::string& address_or_script);
|
||||
std::vector<btc_txout> list_unspent();
|
||||
std::vector<btc_txout> list_unspent_by_address_and_amount(const std::string& address, double transfer_amount);
|
||||
std::string prepare_tx(const std::vector<btc_txout>& ins, const fc::flat_map<std::string, double> outs);
|
||||
|
||||
private:
|
||||
|
|
@ -80,6 +84,11 @@ public:
|
|||
std::string transfer( const std::string& from, const std::string& to, const uint64_t amount );
|
||||
std::string sign_transaction( const std::string& transaction );
|
||||
std::string send_transaction( const std::string& transaction );
|
||||
std::string sign_and_send_transaction_with_wallet ( const std::string& tx_json );
|
||||
std::string transfer_all_btc(const std::string& from_address, const std::string& to_address);
|
||||
std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed);
|
||||
std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount);
|
||||
|
||||
|
||||
private:
|
||||
std::string ip;
|
||||
|
|
@ -87,6 +96,7 @@ private:
|
|||
uint32_t rpc_port;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::map<std::string, std::string> _private_keys;
|
||||
|
||||
std::unique_ptr<zmq_listener> listener;
|
||||
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
|
||||
|
|
|
|||
|
|
@ -109,6 +109,9 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
|
|||
("bitcoin-address", bpo::value<string>()->default_value("2N911a7smwDzUGARg8s7Q1ViizFCw6gWcbR"), "Bitcoin address")
|
||||
("bitcoin-public-key", bpo::value<string>()->default_value("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772"), "Bitcoin public key")
|
||||
("bitcoin-private-key", bpo::value<string>()->default_value("cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr"), "Bitcoin private key")
|
||||
("bitcoin-private-keys", bpo::value<vector<string>>()->composing()->multitoken()->
|
||||
DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")),
|
||||
"Tuple of [Bitcoin PublicKey, Bitcoin Private key] (may specify multiple times)")
|
||||
;
|
||||
cfg.add(cli);
|
||||
}
|
||||
|
|
|
|||
0
libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp
Normal file → Executable file
0
libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp
Normal file → Executable file
306
libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp
Normal file → Executable file
306
libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp
Normal file → Executable file
|
|
@ -112,6 +112,8 @@ 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("] }");
|
||||
|
||||
ilog(body);
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
if( reply.body.empty() )
|
||||
|
|
@ -170,6 +172,72 @@ std::string bitcoin_rpc_client::add_multisig_address( const std::vector<std::str
|
|||
return "";
|
||||
}
|
||||
|
||||
std::string bitcoin_rpc_client::create_raw_transaction(const std::string& txid, const std::string& nvout, const std::string& out_address, double transfer_amount)
|
||||
{
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", \"method\": \"createrawtransaction\", \"params\": [");
|
||||
std::string params = "";
|
||||
std::string input = std::string("[{\"txid\":\"") + txid + std::string("\",\"vout\":")+ nvout +std::string("}]");
|
||||
std::string output = std::string("[{\"") + out_address + std::string("\":") + std::to_string(transfer_amount) + std::string("}]");
|
||||
params = params + input + std::string(",") + output;
|
||||
body = body + params + std::string("]}");
|
||||
|
||||
ilog(body);
|
||||
|
||||
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 ) {
|
||||
return reply_str;
|
||||
}
|
||||
|
||||
if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
|
||||
wlog( "BTC createrawtransaction failed! Reply: ${msg}", ("msg", reply_str) );
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string bitcoin_rpc_client::sign_raw_transaction_with_wallet(const std::string& tx_hash)
|
||||
{
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"signrawtransactionwithwallet\", \"method\": \"signrawtransactionwithwallet\", \"params\": [");
|
||||
std::string params = "\"" + tx_hash + "\"";
|
||||
body = body + params + std::string("]}");
|
||||
|
||||
ilog(body);
|
||||
|
||||
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 ) {
|
||||
return reply_str;
|
||||
}
|
||||
|
||||
if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
|
||||
wlog( "BTC sign_raw_transaction_with_wallet failed! Reply: ${msg}", ("msg", reply_str) );
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string bitcoin_rpc_client::sign_raw_transaction_with_privkey(const std::string& tx_hash, const std::string& private_key)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
bool bitcoin_rpc_client::connection_is_not_defined() const
|
||||
{
|
||||
return ip.empty() || rpc_port == 0 || user.empty() || password.empty();
|
||||
|
|
@ -240,6 +308,51 @@ std::vector<btc_txout> bitcoin_rpc_client::list_unspent()
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<btc_txout> bitcoin_rpc_client::list_unspent_by_address_and_amount(const std::string& address, double minimum_amount)
|
||||
{
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"listunspent\", \"params\": [");
|
||||
body += std::string("1,999999,[\"");
|
||||
body += address;
|
||||
body += std::string("\"],true,{\"minimumAmount\":");
|
||||
body += std::to_string(minimum_amount);
|
||||
body += std::string("}] }");
|
||||
|
||||
ilog(body);
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
std::vector<btc_txout> result;
|
||||
if( reply.body.empty() )
|
||||
{
|
||||
wlog("Failed to list unspent txo");
|
||||
return result;
|
||||
}
|
||||
|
||||
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((reply_str));
|
||||
if( json.count( "result" ) )
|
||||
{
|
||||
for(auto& entry: json.get_child("result"))
|
||||
{
|
||||
btc_txout txo;
|
||||
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
|
||||
txo.out_num_ = entry.second.get_child("vout").get_value<unsigned int>();
|
||||
txo.amount_ = entry.second.get_child("amount").get_value<double>();
|
||||
result.push_back(txo);
|
||||
}
|
||||
}
|
||||
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
|
||||
wlog( "Failed to list unspent txo! Reply: ${msg}", ("msg", reply_str) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string bitcoin_rpc_client::prepare_tx(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs)
|
||||
{
|
||||
std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"createrawtransaction\", \"params\": [");
|
||||
|
|
@ -249,21 +362,21 @@ std::string bitcoin_rpc_client::prepare_tx(const std::vector<btc_txout> &ins, co
|
|||
{
|
||||
if(!first)
|
||||
body += ",";
|
||||
body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":\"" + fc::to_string(entry.out_num_) + "\"}";
|
||||
body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}";
|
||||
first = false;
|
||||
}
|
||||
body += "]";
|
||||
body += "],[";
|
||||
first = true;
|
||||
body += "{";
|
||||
for(const auto& entry: outs)
|
||||
{
|
||||
if(!first)
|
||||
body += ",";
|
||||
body += "\"" + entry.first + "\":\"" + fc::to_string(entry.second) + "\"";
|
||||
body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}";
|
||||
first = false;
|
||||
}
|
||||
body += "}";
|
||||
body += std::string("] }");
|
||||
body += std::string("]] }");
|
||||
|
||||
ilog(body);
|
||||
|
||||
const auto reply = send_post_request( body );
|
||||
|
||||
|
|
@ -282,7 +395,7 @@ std::string bitcoin_rpc_client::prepare_tx(const std::vector<btc_txout> &ins, co
|
|||
if( reply.status == 200 ) {
|
||||
idump((reply_str));
|
||||
if( json.count( "result" ) )
|
||||
return json.get_child("result").get_value<std::string>();
|
||||
return reply_str;
|
||||
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
|
||||
wlog( "Failed to create raw transaction: [${body}]! Reply: ${msg}", ("body", body)("msg", reply_str) );
|
||||
}
|
||||
|
|
@ -351,6 +464,21 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
|
|||
rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>();
|
||||
rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>();
|
||||
|
||||
if( options.count("bitcoin-private-keys") )
|
||||
{
|
||||
const std::vector<std::string> pub_priv_keys = options["bitcoin-private-keys"].as<std::vector<std::string>>();
|
||||
for (const std::string& itr_key_pair : pub_priv_keys)
|
||||
{
|
||||
auto key_pair = graphene::app::dejsonify<std::pair<std::string, std::string> >(itr_key_pair, 5);
|
||||
ilog("Public Key: ${public}", ("public", key_pair.first));
|
||||
if(!key_pair.first.length() || !key_pair.second.length())
|
||||
{
|
||||
FC_THROW("Invalid public private key pair.");
|
||||
}
|
||||
_private_keys[key_pair.first] = key_pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
fc::http::connection conn;
|
||||
try {
|
||||
conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) );
|
||||
|
|
@ -454,6 +582,170 @@ std::string sidechain_net_handler_bitcoin::send_transaction( const std::string&
|
|||
return "";
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet ( const std::string& tx_json )
|
||||
{
|
||||
std::string reply_str = tx_json;
|
||||
|
||||
ilog(reply_str);
|
||||
|
||||
std::stringstream ss_utx(reply_str);
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json( ss_utx, pt );
|
||||
|
||||
if( !(pt.count( "error" ) && pt.get_child( "error" ).empty()) || !pt.count("result") ) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string unsigned_tx_hex = pt.get<std::string>("result");
|
||||
|
||||
reply_str = bitcoin_client->sign_raw_transaction_with_wallet(unsigned_tx_hex);
|
||||
ilog(reply_str);
|
||||
std::stringstream ss_stx(reply_str);
|
||||
boost::property_tree::ptree stx_json;
|
||||
boost::property_tree::read_json( ss_stx, stx_json );
|
||||
|
||||
if( !(stx_json.count( "error" ) && stx_json.get_child( "error" ).empty()) || !stx_json.count("result") || !stx_json.get_child("result").count("hex") ) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string signed_tx_hex = stx_json.get<std::string>("result.hex");
|
||||
|
||||
bitcoin_client->send_btc_tx(signed_tx_hex);
|
||||
|
||||
return reply_str;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string& from_address, const std::string& to_address)
|
||||
{
|
||||
uint64_t fee_rate = bitcoin_client->receive_estimated_fee();
|
||||
uint64_t min_fee_rate = 1000;
|
||||
fee_rate = std::max(fee_rate, min_fee_rate);
|
||||
|
||||
double min_amount = ((double)fee_rate/100000000.0); // Account only for relay fee for now
|
||||
double total_amount = 0.0;
|
||||
std::vector<btc_txout> unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(from_address, 0);
|
||||
|
||||
if(unspent_utxo.size() == 0)
|
||||
{
|
||||
wlog("Failed to find UTXOs to spend for ${pw}",("pw", from_address));
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
for(const auto& utx: unspent_utxo)
|
||||
{
|
||||
total_amount += utx.amount_;
|
||||
}
|
||||
|
||||
if(min_amount >= total_amount)
|
||||
{
|
||||
wlog("Failed not enough BTC to transfer from ${fa}",("fa", from_address));
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
fc::flat_map<std::string, double> outs;
|
||||
outs[to_address] = total_amount - min_amount;
|
||||
|
||||
std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs);
|
||||
return sign_and_send_transaction_with_wallet(reply_str);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet ( const sidechain_event_data& sed )
|
||||
{
|
||||
const auto& idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx.rbegin();
|
||||
if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second;
|
||||
|
||||
std::stringstream ss(pw_address_json);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json( ss, json );
|
||||
|
||||
std::string pw_address = json.get<std::string>("address");
|
||||
|
||||
std::string txid = sed.sidechain_transaction_id;
|
||||
std::string suid = sed.sidechain_uid;
|
||||
std::string nvout = suid.substr(suid.find_last_of("-")+1);
|
||||
uint64_t deposit_amount = sed.sidechain_amount.value;
|
||||
uint64_t fee_rate = bitcoin_client->receive_estimated_fee();
|
||||
uint64_t min_fee_rate = 1000;
|
||||
fee_rate = std::max(fee_rate, min_fee_rate);
|
||||
deposit_amount -= fee_rate; // Deduct minimum relay fee
|
||||
double transfer_amount = (double)deposit_amount/100000000.0;
|
||||
|
||||
std::vector<btc_txout> ins;
|
||||
fc::flat_map<std::string, double> outs;
|
||||
|
||||
btc_txout utxo;
|
||||
utxo.txid_ = txid;
|
||||
utxo.out_num_ = std::stoul(nvout);
|
||||
|
||||
ins.push_back(utxo);
|
||||
|
||||
outs[pw_address] = transfer_amount;
|
||||
|
||||
std::string reply_str = bitcoin_client->prepare_tx(ins, outs);
|
||||
return sign_and_send_transaction_with_wallet(reply_str);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) {
|
||||
const auto& idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx.rbegin();
|
||||
if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second;
|
||||
|
||||
std::stringstream ss(pw_address_json);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json( ss, json );
|
||||
|
||||
std::string pw_address = json.get<std::string>("address");
|
||||
|
||||
uint64_t fee_rate = bitcoin_client->receive_estimated_fee();
|
||||
uint64_t min_fee_rate = 1000;
|
||||
fee_rate = std::max(fee_rate, min_fee_rate);
|
||||
|
||||
double min_amount = sidechain_amount + ((double)fee_rate/100000000.0); // Account only for relay fee for now
|
||||
double total_amount = 0.0;
|
||||
std::vector<btc_txout> unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(pw_address, 0);
|
||||
|
||||
if(unspent_utxo.size() == 0)
|
||||
{
|
||||
wlog("Failed to find UTXOs to spend for ${pw}",("pw", pw_address));
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
for(const auto& utx: unspent_utxo)
|
||||
{
|
||||
total_amount += utx.amount_;
|
||||
}
|
||||
|
||||
if(min_amount > total_amount)
|
||||
{
|
||||
wlog("Failed not enough BTC to spend for ${pw}",("pw", pw_address));
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
fc::flat_map<std::string, double> outs;
|
||||
outs[user_address] = sidechain_amount;
|
||||
if((total_amount - min_amount) > 0.0)
|
||||
{
|
||||
outs[pw_address] = total_amount - min_amount;
|
||||
}
|
||||
|
||||
std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs);
|
||||
return sign_and_send_transaction_with_wallet(reply_str);
|
||||
}
|
||||
|
||||
void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) {
|
||||
std::string block = bitcoin_client->receive_full_block( event_data );
|
||||
if( block != "" ) {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <iostream>
|
||||
|
|
|
|||
380
tests/tests/sidechain_transaction_tests.cpp
Executable file
380
tests/tests/sidechain_transaction_tests.cpp
Executable file
|
|
@ -0,0 +1,380 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
using namespace graphene;
|
||||
using namespace graphene::chain;
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(sidechain_transaction_tests, database_fixture)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
BOOST_TEST_MESSAGE("bitcoin_transaction_send_test");
|
||||
|
||||
generate_blocks(HARDFORK_SON_TIME);
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
ACTORS((alice)(bob));
|
||||
|
||||
upgrade_to_lifetime_member(alice);
|
||||
upgrade_to_lifetime_member(bob);
|
||||
|
||||
transfer(committee_account, alice_id, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));
|
||||
transfer(committee_account, bob_id, asset(500000 * GRAPHENE_BLOCKCHAIN_PRECISION));
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
std::string test_url = "https://create_son_test";
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit_alice;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer{};
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit_alice = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create payment normal vesting
|
||||
vesting_balance_id_type payment_alice;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = alice_id;
|
||||
op.owner = alice_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment_alice = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// alice becomes son
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address";
|
||||
|
||||
son_create_operation op;
|
||||
op.owner_account = alice_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit_alice;
|
||||
op.pay_vb = payment_alice;
|
||||
op.signing_key = alice_public_key;
|
||||
op.sidechain_public_keys = sidechain_public_keys;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create deposit vesting
|
||||
vesting_balance_id_type deposit_bob;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::son;
|
||||
op.policy = dormant_vesting_policy_initializer{};
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
deposit_bob = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// create payment normal vesting
|
||||
vesting_balance_id_type payment_bob;
|
||||
{
|
||||
vesting_balance_create_operation op;
|
||||
op.creator = bob_id;
|
||||
op.owner = bob_id;
|
||||
op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION);
|
||||
op.balance_type = vesting_balance_type::normal;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, bob_private_key);
|
||||
processed_transaction ptx = PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
payment_bob = ptx.operation_results[0].get<object_id_type>();
|
||||
}
|
||||
generate_block();
|
||||
set_expiration(db, trx);
|
||||
|
||||
// bob becomes son
|
||||
{
|
||||
flat_map<graphene::peerplays_sidechain::sidechain_type, string> sidechain_public_keys;
|
||||
sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address";
|
||||
|
||||
son_create_operation op;
|
||||
op.owner_account = bob_id;
|
||||
op.url = test_url;
|
||||
op.deposit = deposit_bob;
|
||||
op.pay_vb = payment_bob;
|
||||
op.signing_key = bob_public_key;
|
||||
op.sidechain_public_keys = sidechain_public_keys;
|
||||
trx.operations.push_back(op);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
generate_block();
|
||||
|
||||
const auto& acc_idx = db.get_index_type<account_index>().indices().get<by_id>();
|
||||
auto acc_itr = acc_idx.find(GRAPHENE_SON_ACCOUNT);
|
||||
BOOST_REQUIRE(acc_itr != acc_idx.end());
|
||||
db.modify(*acc_itr, [&](account_object &obj) {
|
||||
obj.active.account_auths.clear();
|
||||
obj.active.add_authority(bob_id, 1);
|
||||
obj.active.add_authority(alice_id, 1);
|
||||
obj.active.weight_threshold = 2;
|
||||
obj.owner.account_auths.clear();
|
||||
obj.owner.add_authority(bob_id, 1);
|
||||
obj.owner.add_authority(alice_id, 1);
|
||||
obj.owner.weight_threshold = 2;
|
||||
});
|
||||
|
||||
/*const auto &son_btc_account = db.create<account_object>([&](account_object &obj) {
|
||||
obj.name = "son_btc_account";
|
||||
obj.statistics = db.create<account_statistics_object>([&](account_statistics_object &acc_stat) { acc_stat.owner = obj.id; }).id;
|
||||
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;
|
||||
|
||||
obj.owner.add_authority(bob_id, 1);
|
||||
obj.active.add_authority(bob_id, 1);
|
||||
obj.owner.add_authority(alice_id, 1);
|
||||
obj.active.add_authority(alice_id, 1);
|
||||
obj.active.weight_threshold = 2;
|
||||
obj.owner.weight_threshold = 2;
|
||||
});
|
||||
|
||||
db.modify( db.get_global_properties(), [&]( global_property_object& _gpo )
|
||||
{
|
||||
_gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id();
|
||||
if( _gpo.pending_parameters )
|
||||
_gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id();
|
||||
});*/
|
||||
|
||||
generate_block();
|
||||
|
||||
const global_property_object &gpo = db.get_global_properties();
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send bitcoin_transaction_send_operation");
|
||||
|
||||
bitcoin_transaction_send_operation send_op;
|
||||
|
||||
send_op.payer = GRAPHENE_SON_ACCOUNT;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = alice_id;
|
||||
proposal_op.proposed_ops.push_back(op_wrapper(send_op));
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
trx.operations.push_back(proposal_op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
|
||||
BOOST_TEST_MESSAGE("Check proposal results");
|
||||
|
||||
const auto &idx = db.get_index_type<proposal_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(idx.size() == 1);
|
||||
auto obj = idx.find(proposal_id_type(0));
|
||||
BOOST_REQUIRE(obj != idx.end());
|
||||
|
||||
const auto& btidx = db.get_index_type<bitcoin_transaction_index>().indices().get<by_id>();
|
||||
BOOST_REQUIRE(btidx.size() == 0);
|
||||
|
||||
std::vector<unsigned char> a1 = {'a', 'l', 'i', 'c', 'e', '1'};
|
||||
std::vector<unsigned char> a2 = {'a', 'l', 'i', 'c', 'e', '2'};
|
||||
std::vector<unsigned char> a3 = {'a', 'l', 'i', 'c', 'e', '3'};
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation");
|
||||
|
||||
bitcoin_transaction_sign_operation sign_op;
|
||||
|
||||
sign_op.payer = alice_id;
|
||||
sign_op.proposal_id = proposal_id_type(0);
|
||||
sign_op.signatures.push_back(a1);
|
||||
sign_op.signatures.push_back(a2);
|
||||
sign_op.signatures.push_back(a3);
|
||||
|
||||
trx.operations.push_back(sign_op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
|
||||
BOOST_REQUIRE(idx.size() == 1);
|
||||
BOOST_REQUIRE(btidx.size() == 0);
|
||||
auto pobj = idx.find(proposal_id_type(0));
|
||||
BOOST_REQUIRE(pobj != idx.end());
|
||||
|
||||
const auto& sidx = db.get_index_type<son_index>().indices().get<graphene::chain::by_account>();
|
||||
const auto son_obj1 = sidx.find( alice_id );
|
||||
BOOST_REQUIRE(son_obj1 != sidx.end());
|
||||
|
||||
auto bitcoin_transaction_send_op = pobj->proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();
|
||||
BOOST_REQUIRE(bitcoin_transaction_send_op.signatures.size() == 1);
|
||||
BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][0] == a1);
|
||||
BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][1] == a2);
|
||||
BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][2] == a3);
|
||||
|
||||
std::vector<unsigned char> b1 = {'b', 'o', 'b', '1'};
|
||||
std::vector<unsigned char> b2 = {'b', 'o', 'b', '2'};
|
||||
std::vector<unsigned char> b3 = {'b', 'o', 'b', '3'};
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation");
|
||||
|
||||
bitcoin_transaction_sign_operation sign_op;
|
||||
|
||||
sign_op.payer = bob_id;
|
||||
sign_op.proposal_id = proposal_id_type(0);
|
||||
sign_op.signatures.push_back(b1);
|
||||
sign_op.signatures.push_back(b2);
|
||||
sign_op.signatures.push_back(b3);
|
||||
|
||||
trx.operations.push_back(sign_op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, bob_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
|
||||
BOOST_REQUIRE(idx.size() == 0);
|
||||
|
||||
const auto son_obj2 = sidx.find( bob_id );
|
||||
BOOST_REQUIRE(son_obj2 != sidx.end());
|
||||
|
||||
BOOST_REQUIRE(btidx.size() == 1);
|
||||
|
||||
const auto btobj = btidx.find(bitcoin_transaction_id_type(0));
|
||||
BOOST_REQUIRE(btobj != btidx.end());
|
||||
|
||||
BOOST_REQUIRE(btobj->processed == false);
|
||||
|
||||
auto sigs = btobj->signatures;
|
||||
|
||||
BOOST_REQUIRE(sigs[son_obj1->id][0] == a1);
|
||||
BOOST_REQUIRE(sigs[son_obj1->id][1] == a2);
|
||||
BOOST_REQUIRE(sigs[son_obj1->id][2] == a3);
|
||||
|
||||
BOOST_REQUIRE(sigs[son_obj2->id][0] == b1);
|
||||
BOOST_REQUIRE(sigs[son_obj2->id][1] == b2);
|
||||
BOOST_REQUIRE(sigs[son_obj2->id][2] == b3);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send bitcoin_send_transaction_process_operation");
|
||||
|
||||
bitcoin_send_transaction_process_operation process_op;
|
||||
process_op.bitcoin_transaction_id = bitcoin_transaction_id_type(0);
|
||||
process_op.payer = GRAPHENE_SON_ACCOUNT;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = alice_id;
|
||||
proposal_op.proposed_ops.push_back(op_wrapper(process_op));
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
trx.operations.push_back(proposal_op);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
generate_block();
|
||||
BOOST_REQUIRE(idx.size() == 1);
|
||||
obj = idx.find(proposal_id_type(1));
|
||||
BOOST_REQUIRE(obj != idx.end());
|
||||
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send proposal_update_operation");
|
||||
|
||||
proposal_update_operation puo;
|
||||
puo.fee_paying_account = bob_id;
|
||||
puo.proposal = obj->id;
|
||||
puo.active_approvals_to_add = { bob_id };
|
||||
|
||||
trx.operations.push_back(puo);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, bob_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
BOOST_REQUIRE(idx.size() == 1);
|
||||
obj = idx.find(proposal_id_type(1));
|
||||
BOOST_REQUIRE(obj != idx.end());
|
||||
|
||||
BOOST_REQUIRE(btobj != btidx.end());
|
||||
BOOST_REQUIRE(btobj->processed == false);
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send proposal_update_operation");
|
||||
|
||||
proposal_update_operation puo;
|
||||
puo.fee_paying_account = alice_id;
|
||||
puo.proposal = obj->id;
|
||||
puo.active_approvals_to_add = { alice_id };
|
||||
|
||||
trx.operations.push_back(puo);
|
||||
set_expiration(db, trx);
|
||||
sign(trx, alice_private_key);
|
||||
PUSH_TX(db, trx, ~0);
|
||||
trx.clear();
|
||||
}
|
||||
generate_block();
|
||||
BOOST_REQUIRE(idx.size() == 0);
|
||||
|
||||
BOOST_REQUIRE(btobj != btidx.end());
|
||||
BOOST_REQUIRE(btobj->processed == true);
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue