Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c4116860d | ||
|
|
5f5a1a1860 | ||
|
|
c226924ef9 | ||
|
|
6f437e7be9 |
13 changed files with 729 additions and 1 deletions
|
|
@ -337,6 +337,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 )
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ add_library( graphene_chain
|
|||
son_wallet_transfer_evaluator.cpp
|
||||
|
||||
sidechain_address_evaluator.cpp
|
||||
sidechain_transaction_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_transfer_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>
|
||||
|
|
@ -84,6 +85,7 @@
|
|||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_transfer_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -262,6 +264,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()
|
||||
|
|
@ -310,6 +315,7 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<son_wallet_transfer_index> >();
|
||||
|
||||
add_index< primary_index<sidechain_address_index> >();
|
||||
add_index< primary_index<bitcoin_transaction_index> >();
|
||||
|
||||
//Implementation object indexes
|
||||
add_index< primary_index<transaction_index > >();
|
||||
|
|
|
|||
|
|
@ -324,6 +324,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 )
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include <graphene/chain/protocol/sidechain_address.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet_transfer.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -152,7 +153,10 @@ namespace graphene { namespace chain {
|
|||
son_wallet_transfer_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) )
|
||||
|
|
@ -150,6 +150,7 @@ namespace graphene { namespace chain {
|
|||
son_wallet_object_type,
|
||||
son_wallet_transfer_object_type,
|
||||
sidechain_address_object_type,
|
||||
bitcoin_transaction_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -216,6 +217,7 @@ namespace graphene { namespace chain {
|
|||
class son_wallet_object;
|
||||
class son_wallet_transfer_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;
|
||||
|
|
@ -247,6 +249,7 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_transfer_object_type, son_wallet_transfer_object> son_wallet_transfer_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;
|
||||
|
|
@ -436,6 +439,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(son_wallet_object_type)
|
||||
(son_wallet_transfer_object_type)
|
||||
(sidechain_address_object_type)
|
||||
(bitcoin_transaction_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -512,6 +516,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_transfer_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
Normal file
136
libraries/chain/sidechain_transaction_evaluator.cpp
Normal 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"); // can be removed after HF date pass
|
||||
FC_ASSERT(op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "Payer should be the son btc account");
|
||||
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"); // can be removed after HF date pass
|
||||
FC_ASSERT(op.payer == db().get_global_properties().parameters.get_son_btc_account_id(), "Payer should be the son btc account");
|
||||
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
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_transfer_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/protocol/transfer.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
|
@ -43,6 +44,7 @@ class peerplays_sidechain_plugin_impl
|
|||
void create_son_down_proposals();
|
||||
void recreate_primary_wallet();
|
||||
void process_deposits();
|
||||
void process_send_transactions();
|
||||
//void process_withdrawals();
|
||||
void on_block_applied( const signed_block& b );
|
||||
void on_objects_new(const vector<object_id_type>& new_object_ids);
|
||||
|
|
@ -382,6 +384,43 @@ void peerplays_sidechain_plugin_impl::process_deposits() {
|
|||
});
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_send_transactions()
|
||||
{
|
||||
const auto& idx = plugin.database().get_index_type<bitcoin_transaction_index>().indices().get<by_processed>();
|
||||
const auto& idx_range = idx.equal_range(false);
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second,
|
||||
[&] (const bitcoin_transaction_object& bto) {
|
||||
|
||||
// TODO: Do the sending transaction stuff here after PW Script and Classes are available.
|
||||
const chain::global_property_object& gpo = plugin.database().get_global_properties();
|
||||
|
||||
chain::bitcoin_send_transaction_process_operation process_op;
|
||||
process_op.payer = gpo.parameters.get_son_btc_account_id();
|
||||
process_op.bitcoin_transaction_id = bto.id;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = get_son_object().son_account;
|
||||
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( plugin.database().head_block_time().sec_since_epoch() + lifetime );
|
||||
|
||||
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_keys().begin()->second, proposal_op);
|
||||
fc::future<bool> fut = fc::async( [&](){
|
||||
try {
|
||||
plugin.database().push_transaction(trx);
|
||||
if(plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch(fc::exception e){
|
||||
ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}",("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
});
|
||||
}
|
||||
|
||||
//void peerplays_sidechain_plugin_impl::process_withdrawals() {
|
||||
//}
|
||||
|
||||
|
|
@ -407,6 +446,8 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b )
|
|||
|
||||
//process_withdrawals();
|
||||
|
||||
//process_send_transactions();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +493,12 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector<object_id_type
|
|||
fut.wait(fc::seconds(10));
|
||||
};
|
||||
|
||||
auto sign_proposal = [ & ]( const chain::proposal_id_type& id )
|
||||
{
|
||||
// TODO: To be done after PW Script and transaction signing is checked in.
|
||||
return true;
|
||||
};
|
||||
|
||||
for(auto object_id: new_object_ids) {
|
||||
if( object_id.is<chain::proposal_object>() ) {
|
||||
const object* obj = d.find_object(object_id);
|
||||
|
|
@ -474,6 +521,16 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vector<object_id_type
|
|||
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::transfer_operation>::value) {
|
||||
approve_proposal( proposal->id );
|
||||
}
|
||||
|
||||
if(proposal->proposed_transaction.operations.size() == 1
|
||||
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::bitcoin_transaction_send_operation>::value) {
|
||||
sign_proposal( proposal->id );
|
||||
}
|
||||
|
||||
if(proposal->proposed_transaction.operations.size() == 1
|
||||
&& proposal->proposed_transaction.operations[0].which() == chain::operation::tag<chain::bitcoin_send_transaction_process_operation>::value) {
|
||||
approve_proposal( proposal->id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_transfer_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <iostream>
|
||||
|
|
|
|||
366
tests/tests/sidechain_transaction_tests.cpp
Normal file
366
tests/tests/sidechain_transaction_tests.cpp
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
#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 &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 = db.get_global_properties().parameters.get_son_btc_account_id();
|
||||
|
||||
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<char> a1 = {'a', 'l', 'i', 'c', 'e', '1'};
|
||||
std::vector<char> a2 = {'a', 'l', 'i', 'c', 'e', '2'};
|
||||
std::vector<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<char> b1 = {'b', 'o', 'b', '1'};
|
||||
std::vector<char> b2 = {'b', 'o', 'b', '2'};
|
||||
std::vector<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 = db.get_global_properties().parameters.get_son_btc_account_id();
|
||||
|
||||
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