#357 secp256k1 lib from libbitcoin
This commit is contained in:
parent
09579fbab1
commit
3b5e928094
20 changed files with 65 additions and 1440 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -5,5 +5,5 @@
|
|||
[submodule "libraries/fc"]
|
||||
path = libraries/fc
|
||||
url = https://gitlab.com/PBSA/tools-libs/peerplays-fc.git
|
||||
branch = latest-fc
|
||||
branch = develop
|
||||
ignore = dirty
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ template class fc::api<graphene::app::block_api>;
|
|||
template class fc::api<graphene::app::network_broadcast_api>;
|
||||
template class fc::api<graphene::app::network_node_api>;
|
||||
template class fc::api<graphene::app::history_api>;
|
||||
template class fc::api<graphene::app::crypto_api>;
|
||||
template class fc::api<graphene::app::asset_api>;
|
||||
template class fc::api<graphene::debug_witness::debug_api>;
|
||||
template class fc::api<graphene::app::login_api>;
|
||||
|
|
@ -90,8 +89,6 @@ void login_api::enable_api(const std::string &api_name) {
|
|||
_history_api = std::make_shared<history_api>(_app);
|
||||
} else if (api_name == "network_node_api") {
|
||||
_network_node_api = std::make_shared<network_node_api>(std::ref(_app));
|
||||
} else if (api_name == "crypto_api") {
|
||||
_crypto_api = std::make_shared<crypto_api>();
|
||||
} else if (api_name == "asset_api") {
|
||||
_asset_api = std::make_shared<asset_api>(_app);
|
||||
} else if (api_name == "debug_api") {
|
||||
|
|
@ -289,11 +286,6 @@ fc::api<history_api> login_api::history() const {
|
|||
return *_history_api;
|
||||
}
|
||||
|
||||
fc::api<crypto_api> login_api::crypto() const {
|
||||
FC_ASSERT(_crypto_api);
|
||||
return *_crypto_api;
|
||||
}
|
||||
|
||||
fc::api<asset_api> login_api::asset() const {
|
||||
FC_ASSERT(_asset_api);
|
||||
return *_asset_api;
|
||||
|
|
@ -522,55 +514,6 @@ vector<bucket_object> history_api::get_market_history(std::string asset_a, std::
|
|||
FC_CAPTURE_AND_RETHROW((asset_a)(asset_b)(bucket_seconds)(start)(end))
|
||||
}
|
||||
|
||||
crypto_api::crypto_api(){};
|
||||
|
||||
commitment_type crypto_api::blind(const blind_factor_type &blind, uint64_t value) {
|
||||
return fc::ecc::blind(blind, value);
|
||||
}
|
||||
|
||||
blind_factor_type crypto_api::blind_sum(const std::vector<blind_factor_type> &blinds_in, uint32_t non_neg) {
|
||||
return fc::ecc::blind_sum(blinds_in, non_neg);
|
||||
}
|
||||
|
||||
bool crypto_api::verify_sum(const std::vector<commitment_type> &commits_in, const std::vector<commitment_type> &neg_commits_in, int64_t excess) {
|
||||
return fc::ecc::verify_sum(commits_in, neg_commits_in, excess);
|
||||
}
|
||||
|
||||
verify_range_result crypto_api::verify_range(const commitment_type &commit, const std::vector<char> &proof) {
|
||||
verify_range_result result;
|
||||
result.success = fc::ecc::verify_range(result.min_val, result.max_val, commit, proof);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<char> crypto_api::range_proof_sign(uint64_t min_value,
|
||||
const commitment_type &commit,
|
||||
const blind_factor_type &commit_blind,
|
||||
const blind_factor_type &nonce,
|
||||
int8_t base10_exp,
|
||||
uint8_t min_bits,
|
||||
uint64_t actual_value) {
|
||||
return fc::ecc::range_proof_sign(min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value);
|
||||
}
|
||||
|
||||
verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind(const blind_factor_type &nonce,
|
||||
const commitment_type &commit,
|
||||
const std::vector<char> &proof) {
|
||||
verify_range_proof_rewind_result result;
|
||||
result.success = fc::ecc::verify_range_proof_rewind(result.blind_out,
|
||||
result.value_out,
|
||||
result.message_out,
|
||||
nonce,
|
||||
result.min_val,
|
||||
result.max_val,
|
||||
const_cast<commitment_type &>(commit),
|
||||
proof);
|
||||
return result;
|
||||
}
|
||||
|
||||
range_proof_info crypto_api::range_get_info(const std::vector<char> &proof) {
|
||||
return fc::ecc::range_get_info(proof);
|
||||
}
|
||||
|
||||
// asset_api
|
||||
asset_api::asset_api(graphene::app::application &app) :
|
||||
_app(app),
|
||||
|
|
|
|||
|
|
@ -362,7 +362,6 @@ public:
|
|||
wild_access.allowed_apis.push_back("database_api");
|
||||
wild_access.allowed_apis.push_back("network_broadcast_api");
|
||||
wild_access.allowed_apis.push_back("history_api");
|
||||
wild_access.allowed_apis.push_back("crypto_api");
|
||||
wild_access.allowed_apis.push_back("bookie_api");
|
||||
wild_access.allowed_apis.push_back("affiliate_stats_api");
|
||||
wild_access.allowed_apis.push_back("sidechain_api");
|
||||
|
|
|
|||
|
|
@ -237,9 +237,6 @@ public:
|
|||
// Proposed transactions
|
||||
vector<proposal_object> get_proposed_transactions(const std::string account_id_or_name) const;
|
||||
|
||||
// Blinded balances
|
||||
vector<blinded_balance_object> get_blinded_balances(const flat_set<commitment_type> &commitments) const;
|
||||
|
||||
// Tournaments
|
||||
vector<tournament_object> get_tournaments_in_state(tournament_state state, uint32_t limit) const;
|
||||
vector<tournament_object> get_tournaments(tournament_id_type stop, unsigned limit, tournament_id_type start);
|
||||
|
|
@ -2650,29 +2647,6 @@ vector<proposal_object> database_api_impl::get_proposed_transactions(const std::
|
|||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Blinded balances //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<blinded_balance_object> database_api::get_blinded_balances(const flat_set<commitment_type> &commitments) const {
|
||||
return my->get_blinded_balances(commitments);
|
||||
}
|
||||
|
||||
vector<blinded_balance_object> database_api_impl::get_blinded_balances(const flat_set<commitment_type> &commitments) const {
|
||||
vector<blinded_balance_object> result;
|
||||
result.reserve(commitments.size());
|
||||
const auto &bal_idx = _db.get_index_type<blinded_balance_index>();
|
||||
const auto &by_commitment_idx = bal_idx.indices().get<by_commitment>();
|
||||
for (const auto &c : commitments) {
|
||||
auto itr = by_commitment_idx.find(c);
|
||||
if (itr != by_commitment_idx.end())
|
||||
result.push_back(*itr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Tournament methods //
|
||||
|
|
|
|||
|
|
@ -289,33 +289,6 @@ private:
|
|||
std::function<void(const variant &)> _on_pending_transaction;
|
||||
};
|
||||
|
||||
class crypto_api {
|
||||
public:
|
||||
crypto_api();
|
||||
|
||||
fc::ecc::commitment_type blind(const fc::ecc::blind_factor_type &blind, uint64_t value);
|
||||
|
||||
fc::ecc::blind_factor_type blind_sum(const std::vector<blind_factor_type> &blinds_in, uint32_t non_neg);
|
||||
|
||||
bool verify_sum(const std::vector<commitment_type> &commits_in, const std::vector<commitment_type> &neg_commits_in, int64_t excess);
|
||||
|
||||
verify_range_result verify_range(const fc::ecc::commitment_type &commit, const std::vector<char> &proof);
|
||||
|
||||
std::vector<char> range_proof_sign(uint64_t min_value,
|
||||
const commitment_type &commit,
|
||||
const blind_factor_type &commit_blind,
|
||||
const blind_factor_type &nonce,
|
||||
int8_t base10_exp,
|
||||
uint8_t min_bits,
|
||||
uint64_t actual_value);
|
||||
|
||||
verify_range_proof_rewind_result verify_range_proof_rewind(const blind_factor_type &nonce,
|
||||
const fc::ecc::commitment_type &commit,
|
||||
const std::vector<char> &proof);
|
||||
|
||||
range_proof_info range_get_info(const std::vector<char> &proof);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
|
|
@ -359,7 +332,6 @@ extern template class fc::api<graphene::app::block_api>;
|
|||
extern template class fc::api<graphene::app::network_broadcast_api>;
|
||||
extern template class fc::api<graphene::app::network_node_api>;
|
||||
extern template class fc::api<graphene::app::history_api>;
|
||||
extern template class fc::api<graphene::app::crypto_api>;
|
||||
extern template class fc::api<graphene::app::asset_api>;
|
||||
extern template class fc::api<graphene::debug_witness::debug_api>;
|
||||
|
||||
|
|
@ -394,8 +366,6 @@ public:
|
|||
fc::api<history_api> history() const;
|
||||
/// @brief Retrieve the network node API
|
||||
fc::api<network_node_api> network_node() const;
|
||||
/// @brief Retrieve the cryptography API
|
||||
fc::api<crypto_api> crypto() const;
|
||||
/// @brief Retrieve the asset API
|
||||
fc::api<asset_api> asset() const;
|
||||
/// @brief Retrieve the debug API (if available)
|
||||
|
|
@ -417,7 +387,6 @@ private:
|
|||
optional<fc::api<network_broadcast_api>> _network_broadcast_api;
|
||||
optional<fc::api<network_node_api>> _network_node_api;
|
||||
optional<fc::api<history_api>> _history_api;
|
||||
optional<fc::api<crypto_api>> _crypto_api;
|
||||
optional<fc::api<asset_api>> _asset_api;
|
||||
optional<fc::api<graphene::debug_witness::debug_api>> _debug_api;
|
||||
optional<fc::api<graphene::bookie::bookie_api>> _bookie_api;
|
||||
|
|
@ -475,15 +444,6 @@ FC_API(graphene::app::network_node_api,
|
|||
(subscribe_to_pending_transactions)
|
||||
(unsubscribe_from_pending_transactions))
|
||||
|
||||
FC_API(graphene::app::crypto_api,
|
||||
(blind)
|
||||
(blind_sum)
|
||||
(verify_sum)
|
||||
(verify_range)
|
||||
(range_proof_sign)
|
||||
(verify_range_proof_rewind)
|
||||
(range_get_info))
|
||||
|
||||
FC_API(graphene::app::asset_api,
|
||||
(get_asset_holders)
|
||||
(get_asset_holders_count)
|
||||
|
|
@ -496,7 +456,6 @@ FC_API(graphene::app::login_api,
|
|||
(database)
|
||||
(history)
|
||||
(network_node)
|
||||
(crypto)
|
||||
(asset)
|
||||
(debug)
|
||||
(bookie)
|
||||
|
|
|
|||
|
|
@ -877,15 +877,6 @@ public:
|
|||
*/
|
||||
vector<proposal_object> get_proposed_transactions(const std::string account_id_or_name) const;
|
||||
|
||||
//////////////////////
|
||||
// Blinded balances //
|
||||
//////////////////////
|
||||
|
||||
/**
|
||||
* @return the set of blinded balance objects by commitment ID
|
||||
*/
|
||||
vector<blinded_balance_object> get_blinded_balances(const flat_set<commitment_type> &commitments) const;
|
||||
|
||||
/////////////////
|
||||
// Tournaments //
|
||||
/////////////////
|
||||
|
|
@ -1198,9 +1189,6 @@ FC_API(graphene::app::database_api,
|
|||
// Proposed transactions
|
||||
(get_proposed_transactions)
|
||||
|
||||
// Blinded balances
|
||||
(get_blinded_balances)
|
||||
|
||||
// Tournaments
|
||||
(get_tournaments_in_state)
|
||||
(get_tournaments_by_state)
|
||||
|
|
|
|||
|
|
@ -33,149 +33,45 @@ namespace graphene { namespace chain {
|
|||
|
||||
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )
|
||||
{ try {
|
||||
const auto& d = db();
|
||||
|
||||
const auto& atype = o.amount.asset_id(db());
|
||||
FC_ASSERT( atype.allow_confidential() );
|
||||
FC_ASSERT( !atype.is_transfer_restricted() );
|
||||
FC_ASSERT( !(atype.options.flags & white_list) );
|
||||
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
for( const auto& a : out.owner.account_auths )
|
||||
a.first(d); // verify all accounts exist and are valid
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
||||
void_result transfer_to_blind_evaluator::do_apply( const transfer_to_blind_operation& o )
|
||||
{ try {
|
||||
db().adjust_balance( o.from, -o.amount );
|
||||
|
||||
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
|
||||
db().modify( add, [&]( asset_dynamic_data_object& obj ){
|
||||
obj.confidential_supply += o.amount.amount;
|
||||
FC_ASSERT( obj.confidential_supply >= 0 );
|
||||
});
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
|
||||
obj.asset_id = o.amount.asset_id;
|
||||
obj.owner = out.owner;
|
||||
obj.commitment = out.commitment;
|
||||
});
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void transfer_to_blind_evaluator::pay_fee()
|
||||
{
|
||||
if( db().head_block_time() >= HARDFORK_563_TIME )
|
||||
pay_fba_fee( fba_accumulator_id_transfer_to_blind );
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
|
||||
void_result transfer_from_blind_evaluator::do_evaluate( const transfer_from_blind_operation& o )
|
||||
{ try {
|
||||
const auto& d = db();
|
||||
o.fee.asset_id(d); // verify fee is a legit asset
|
||||
const auto& bbi = d.get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
FC_ASSERT( itr != cidx.end() );
|
||||
FC_ASSERT( itr->asset_id == o.fee.asset_id );
|
||||
FC_ASSERT( itr->owner == in.owner );
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result transfer_from_blind_evaluator::do_apply( const transfer_from_blind_operation& o )
|
||||
{ try {
|
||||
db().adjust_balance( o.fee_payer(), o.fee );
|
||||
db().adjust_balance( o.to, o.amount );
|
||||
const auto& bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
FC_ASSERT( itr != cidx.end() );
|
||||
db().remove( *itr );
|
||||
}
|
||||
const auto& add = o.amount.asset_id(db()).dynamic_asset_data_id(db()); // verify fee is a legit asset
|
||||
db().modify( add, [&]( asset_dynamic_data_object& obj ){
|
||||
obj.confidential_supply -= o.amount.amount + o.fee.amount;
|
||||
FC_ASSERT( obj.confidential_supply >= 0 );
|
||||
});
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void transfer_from_blind_evaluator::pay_fee()
|
||||
{
|
||||
if( db().head_block_time() >= HARDFORK_563_TIME )
|
||||
pay_fba_fee( fba_accumulator_id_transfer_from_blind );
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
|
||||
void_result blind_transfer_evaluator::do_evaluate( const blind_transfer_operation& o )
|
||||
{ try {
|
||||
const auto& d = db();
|
||||
o.fee.asset_id(db()); // verify fee is a legit asset
|
||||
const auto& bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
for( const auto& a : out.owner.account_auths )
|
||||
a.first(d); // verify all accounts exist and are valid
|
||||
}
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
|
||||
FC_ASSERT( itr->asset_id == o.fee.asset_id );
|
||||
FC_ASSERT( itr->owner == in.owner );
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result blind_transfer_evaluator::do_apply( const blind_transfer_operation& o )
|
||||
{ try {
|
||||
db().adjust_balance( o.fee_payer(), o.fee ); // deposit the fee to the temp account
|
||||
const auto& bbi = db().get_index_type<blinded_balance_index>();
|
||||
const auto& cidx = bbi.indices().get<by_commitment>();
|
||||
for( const auto& in : o.inputs )
|
||||
{
|
||||
auto itr = cidx.find( in.commitment );
|
||||
GRAPHENE_ASSERT( itr != cidx.end(), blind_transfer_unknown_commitment, "", ("commitment",in.commitment) );
|
||||
db().remove( *itr );
|
||||
}
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
db().create<blinded_balance_object>( [&]( blinded_balance_object& obj ){
|
||||
obj.asset_id = o.fee.asset_id;
|
||||
obj.owner = out.owner;
|
||||
obj.commitment = out.commitment;
|
||||
});
|
||||
}
|
||||
const auto& add = o.fee.asset_id(db()).dynamic_asset_data_id(db());
|
||||
db().modify( add, [&]( asset_dynamic_data_object& obj ){
|
||||
obj.confidential_supply -= o.fee.amount;
|
||||
FC_ASSERT( obj.confidential_supply >= 0 );
|
||||
});
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void blind_transfer_evaluator::pay_fee()
|
||||
{
|
||||
if( db().head_block_time() >= HARDFORK_563_TIME )
|
||||
pay_fba_fee( fba_accumulator_id_blind_transfer );
|
||||
else
|
||||
generic_evaluator::pay_fee();
|
||||
}
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -203,27 +203,10 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.issuer );
|
||||
}
|
||||
|
||||
void operator()( const transfer_to_blind_operation& op )
|
||||
{
|
||||
_impacted.insert( op.from );
|
||||
for( const auto& out : op.outputs )
|
||||
add_authority_accounts( _impacted, out.owner );
|
||||
}
|
||||
|
||||
void operator()( const blind_transfer_operation& op )
|
||||
{
|
||||
for( const auto& in : op.inputs )
|
||||
add_authority_accounts( _impacted, in.owner );
|
||||
for( const auto& out : op.outputs )
|
||||
add_authority_accounts( _impacted, out.owner );
|
||||
}
|
||||
|
||||
void operator()( const transfer_from_blind_operation& op )
|
||||
{
|
||||
_impacted.insert( op.to );
|
||||
for( const auto& in : op.inputs )
|
||||
add_authority_accounts( _impacted, in.owner );
|
||||
}
|
||||
//! We don't use this operations
|
||||
void operator()( const transfer_to_blind_operation& op ){}
|
||||
void operator()( const blind_transfer_operation& op ){}
|
||||
void operator()( const transfer_from_blind_operation& op ){}
|
||||
|
||||
void operator()( const asset_settle_cancel_operation& op )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -182,9 +182,6 @@ namespace graphene { namespace chain {
|
|||
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer );
|
||||
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" )
|
||||
|
||||
GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer );
|
||||
GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" );
|
||||
|
||||
/*
|
||||
FC_DECLARE_DERIVED_EXCEPTION( addition_overflow, graphene::chain::chain_exception, 30002, "addition overflow" )
|
||||
FC_DECLARE_DERIVED_EXCEPTION( subtraction_overflow, graphene::chain::chain_exception, 30003, "subtraction overflow" )
|
||||
|
|
|
|||
|
|
@ -111,12 +111,12 @@ struct stealth_confirmation
|
|||
/**
|
||||
* Packs *this then encodes as base58 encoded string.
|
||||
*/
|
||||
operator string()const;
|
||||
//operator string()const;
|
||||
/**
|
||||
* Unpacks from a base58 string
|
||||
*/
|
||||
stealth_confirmation( const std::string& base58 );
|
||||
stealth_confirmation(){}
|
||||
//stealth_confirmation( const std::string& base58 );
|
||||
//stealth_confirmation(){}
|
||||
|
||||
public_key_type one_time_key;
|
||||
optional<public_key_type> to;
|
||||
|
|
@ -152,16 +152,17 @@ struct transfer_to_blind_operation : public base_operation
|
|||
uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
|
||||
asset fee;
|
||||
asset amount;
|
||||
account_id_type from;
|
||||
blind_factor_type blinding_factor;
|
||||
vector<blind_output> outputs;
|
||||
|
||||
account_id_type fee_payer()const { return from; }
|
||||
void validate()const;
|
||||
share_type calculate_fee(const fee_parameters_type& )const;
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
//account_id_type fee_payer()const { return from; }
|
||||
//void validate()const;
|
||||
//share_type calculate_fee(const fee_parameters_type& )const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -180,14 +181,15 @@ struct transfer_from_blind_operation : public base_operation
|
|||
blind_factor_type blinding_factor;
|
||||
vector<blind_input> inputs;
|
||||
|
||||
account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
|
||||
void validate()const;
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
void get_required_authorities( vector<authority>& a )const
|
||||
{
|
||||
for( const auto& in : inputs )
|
||||
a.push_back( in.owner );
|
||||
}
|
||||
//account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
|
||||
//void validate()const;
|
||||
//void get_required_authorities( vector<authority>& a )const
|
||||
//{
|
||||
// for( const auto& in : inputs )
|
||||
// a.push_back( in.owner );
|
||||
//}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -243,17 +245,18 @@ struct blind_transfer_operation : public base_operation
|
|||
asset fee;
|
||||
vector<blind_input> inputs;
|
||||
vector<blind_output> outputs;
|
||||
|
||||
/** graphene TEMP account */
|
||||
account_id_type fee_payer()const;
|
||||
void validate()const;
|
||||
share_type calculate_fee( const fee_parameters_type& k )const;
|
||||
|
||||
void get_required_authorities( vector<authority>& a )const
|
||||
{
|
||||
for( const auto& in : inputs )
|
||||
a.push_back( in.owner );
|
||||
}
|
||||
account_id_type fee_payer()const { return account_id_type{}; }
|
||||
|
||||
/** graphene TEMP account */
|
||||
//account_id_type fee_payer()const;
|
||||
//void validate()const;
|
||||
//share_type calculate_fee( const fee_parameters_type& k )const;
|
||||
//void get_required_authorities( vector<authority>& a )const
|
||||
//{
|
||||
// for( const auto& in : inputs )
|
||||
// a.push_back( in.owner );
|
||||
//}
|
||||
};
|
||||
|
||||
///@} endgroup stealth
|
||||
|
|
|
|||
|
|
@ -106,9 +106,9 @@ namespace graphene { namespace chain {
|
|||
assert_operation,
|
||||
balance_claim_operation,
|
||||
override_transfer_operation,
|
||||
transfer_to_blind_operation,
|
||||
blind_transfer_operation,
|
||||
transfer_from_blind_operation,
|
||||
transfer_to_blind_operation, //! We don't use this operation
|
||||
blind_transfer_operation, //! We don't use this operation
|
||||
transfer_from_blind_operation, //! We don't use this operation
|
||||
asset_settle_cancel_operation, // VIRTUAL
|
||||
asset_claim_fees_operation,
|
||||
fba_distribute_operation, // VIRTUAL
|
||||
|
|
|
|||
|
|
@ -22,12 +22,10 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <graphene/chain/protocol/confidential.hpp>
|
||||
#include <graphene/chain/confidential_evaluator.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
|
||||
/*
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void transfer_to_blind_operation::validate()const
|
||||
|
|
@ -47,19 +45,6 @@ void transfer_to_blind_operation::validate()const
|
|||
FC_ASSERT( !outputs[i].owner.is_impossible() );
|
||||
}
|
||||
FC_ASSERT( out.size(), "there must be at least one output" );
|
||||
|
||||
auto public_c = fc::ecc::blind(blinding_factor,net_public);
|
||||
|
||||
FC_ASSERT( fc::ecc::verify_sum( {public_c}, out, 0 ), "", ("net_public",net_public) );
|
||||
|
||||
if( outputs.size() > 1 )
|
||||
{
|
||||
for( auto out : outputs )
|
||||
{
|
||||
auto info = fc::ecc::range_get_info( out.range_proof );
|
||||
FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
share_type transfer_to_blind_operation::calculate_fee( const fee_parameters_type& k )const
|
||||
|
|
@ -79,31 +64,15 @@ void transfer_from_blind_operation::validate()const
|
|||
vector<commitment_type> in(inputs.size());
|
||||
vector<commitment_type> out;
|
||||
int64_t net_public = fee.amount.value + amount.amount.value;
|
||||
out.push_back( fc::ecc::blind( blinding_factor, net_public ) );
|
||||
for( uint32_t i = 0; i < in.size(); ++i )
|
||||
{
|
||||
in[i] = inputs[i].commitment;
|
||||
/// by requiring all inputs to be sorted we also prevent duplicate commitments on the input
|
||||
if( i > 0 ) FC_ASSERT( in[i-1] < in[i], "all inputs must be sorted by commitment id" );
|
||||
}
|
||||
FC_ASSERT( in.size(), "there must be at least one input" );
|
||||
FC_ASSERT( fc::ecc::verify_sum( in, out, 0 ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If fee_payer = temp_account_id, then the fee is paid by the surplus balance of inputs-outputs and
|
||||
* 100% of the fee goes to the network.
|
||||
*/
|
||||
account_id_type blind_transfer_operation::fee_payer()const
|
||||
{
|
||||
return GRAPHENE_TEMP_ACCOUNT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method can be computationally intensive because it verifies that input commitments - output commitments add up to 0
|
||||
*/
|
||||
void blind_transfer_operation::validate()const
|
||||
{ try {
|
||||
vector<commitment_type> in(inputs.size());
|
||||
|
|
@ -122,17 +91,6 @@ void blind_transfer_operation::validate()const
|
|||
FC_ASSERT( !outputs[i].owner.is_impossible() );
|
||||
}
|
||||
FC_ASSERT( in.size(), "there must be at least one input" );
|
||||
FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), "", ("net_public", net_public) );
|
||||
|
||||
if( outputs.size() > 1 )
|
||||
{
|
||||
for( auto out : outputs )
|
||||
{
|
||||
auto info = fc::ecc::range_get_info( out.range_proof );
|
||||
FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY );
|
||||
}
|
||||
}
|
||||
FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ), "", ("net_public", net_public) );
|
||||
} FC_CAPTURE_AND_RETHROW( (*this) ) }
|
||||
|
||||
share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k )const
|
||||
|
|
@ -140,16 +98,12 @@ share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k
|
|||
return k.fee + outputs.size() * k.price_per_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs *this then encodes as base58 encoded string.
|
||||
*/
|
||||
|
||||
stealth_confirmation::operator string()const
|
||||
{
|
||||
return fc::to_base58( fc::raw::pack( *this ) );
|
||||
}
|
||||
/**
|
||||
* Unpacks from a base58 string
|
||||
*/
|
||||
|
||||
stealth_confirmation::stealth_confirmation( const std::string& base58 )
|
||||
{
|
||||
*this = fc::raw::unpack<stealth_confirmation>( fc::from_base58( base58 ) );
|
||||
|
|
@ -157,6 +111,8 @@ stealth_confirmation::stealth_confirmation( const std::string& base58 )
|
|||
|
||||
} } // graphene::chain
|
||||
|
||||
*/
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_to_blind_operation::fee_parameters_type )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::transfer_from_blind_operation::fee_parameters_type )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::blind_transfer_operation::fee_parameters_type )
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit e7369949bea26f3201d8442ba78286a88df74762
|
||||
Subproject commit 156b0c4e41c9215eadb2af8009b05e0f38c16dda
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
#include <fc/io/raw.hpp>
|
||||
|
||||
|
|
@ -6,8 +7,8 @@
|
|||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
const secp256k1_context_t *btc_context() {
|
||||
static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
const secp256k1_context *btc_context() {
|
||||
static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
@ -31,20 +32,14 @@ fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &script
|
|||
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) {
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context *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
|
||||
|
||||
secp256k1_ecdsa_signature sign;
|
||||
FC_ASSERT(secp256k1_ecdsa_sign(context_sign, &sign, (const unsigned char *)hash.data(), (const unsigned char *)privkey.data(), secp256k1_nonce_function_rfc6979, nullptr));
|
||||
FC_ASSERT(secp256k1_ecdsa_signature_serialize_der(context_sign, (unsigned char *)sig.data(), (size_t *)&sig_len, &sign));
|
||||
sig.resize(sig_len);
|
||||
|
||||
return sig;
|
||||
|
|
@ -52,7 +47,7 @@ std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, con
|
|||
|
||||
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) {
|
||||
const secp256k1_context *context_sign, int hash_type) {
|
||||
FC_ASSERT(tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size());
|
||||
FC_ASSERT(!privkey.empty());
|
||||
|
||||
|
|
@ -77,17 +72,23 @@ void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vecto
|
|||
}
|
||||
}
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context) {
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context *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());
|
||||
secp256k1_pubkey pub_key;
|
||||
FC_ASSERT(secp256k1_ec_pubkey_parse(context, &pub_key, (const unsigned char *)pubkey_temp.data(), pubkey_temp.size()));
|
||||
|
||||
secp256k1_ecdsa_signature sign;
|
||||
FC_ASSERT(secp256k1_ecdsa_signature_parse_der(context, &sign, (const unsigned char *)sig_temp.data(), sig_temp.size()));
|
||||
|
||||
int result = secp256k1_ecdsa_verify(context, &sign, (const unsigned char *)msg_temp.data(), &pub_key);
|
||||
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) {
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context *context) {
|
||||
FC_ASSERT(redeem_scripts.size() == amounts.size());
|
||||
|
||||
using data = std::pair<size_t, bytes>;
|
||||
|
|
|
|||
|
|
@ -8,23 +8,23 @@ namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
|||
|
||||
class bitcoin_transaction;
|
||||
|
||||
const secp256k1_context_t *btc_context();
|
||||
const secp256k1_context *btc_context();
|
||||
|
||||
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<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context *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);
|
||||
const secp256k1_context *context_sign = nullptr, int hash_type = 1);
|
||||
|
||||
void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts, bool use_mulisig_workaround = true);
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context);
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context *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);
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context *context);
|
||||
|
||||
void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
|
|
|
|||
|
|
@ -68,80 +68,12 @@ enum authority_type
|
|||
active
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains the confirmation receipt the sender must give the receiver and
|
||||
* the meta data about the receipt that helps the sender identify which receipt is
|
||||
* for the receiver and which is for the change address.
|
||||
*/
|
||||
struct blind_confirmation
|
||||
{
|
||||
struct output
|
||||
{
|
||||
string label;
|
||||
public_key_type pub_key;
|
||||
stealth_confirmation::memo_data decrypted_memo;
|
||||
stealth_confirmation confirmation;
|
||||
authority auth;
|
||||
string confirmation_receipt;
|
||||
};
|
||||
|
||||
signed_transaction trx;
|
||||
vector<output> outputs;
|
||||
};
|
||||
|
||||
struct blind_balance
|
||||
{
|
||||
asset amount;
|
||||
public_key_type from; ///< the account this balance came from
|
||||
public_key_type to; ///< the account this balance is logically associated with
|
||||
public_key_type one_time_key; ///< used to derive the authority key and blinding factor
|
||||
fc::sha256 blinding_factor;
|
||||
fc::ecc::commitment_type commitment;
|
||||
bool used = false;
|
||||
};
|
||||
|
||||
struct blind_receipt
|
||||
{
|
||||
std::pair<public_key_type,fc::time_point> from_date()const { return std::make_pair(from_key,date); }
|
||||
std::pair<public_key_type,fc::time_point> to_date()const { return std::make_pair(to_key,date); }
|
||||
std::tuple<public_key_type,asset_id_type,bool> to_asset_used()const { return std::make_tuple(to_key,amount.asset_id,used); }
|
||||
const commitment_type& commitment()const { return data.commitment; }
|
||||
|
||||
fc::time_point date;
|
||||
public_key_type from_key;
|
||||
string from_label;
|
||||
public_key_type to_key;
|
||||
string to_label;
|
||||
asset amount;
|
||||
string memo;
|
||||
authority control_authority;
|
||||
stealth_confirmation::memo_data data;
|
||||
bool used = false;
|
||||
stealth_confirmation conf;
|
||||
};
|
||||
|
||||
struct by_from;
|
||||
struct by_to;
|
||||
struct by_to_asset_used;
|
||||
struct by_commitment;
|
||||
|
||||
typedef multi_index_container< blind_receipt,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_commitment>, const_mem_fun< blind_receipt, const commitment_type&, &blind_receipt::commitment > >,
|
||||
ordered_unique< tag<by_to>, const_mem_fun< blind_receipt, std::pair<public_key_type,fc::time_point>, &blind_receipt::to_date > >,
|
||||
ordered_non_unique< tag<by_to_asset_used>, const_mem_fun< blind_receipt, std::tuple<public_key_type,asset_id_type,bool>, &blind_receipt::to_asset_used > >,
|
||||
ordered_unique< tag<by_from>, const_mem_fun< blind_receipt, std::pair<public_key_type,fc::time_point>, &blind_receipt::from_date > >
|
||||
>
|
||||
> blind_receipt_index_type;
|
||||
|
||||
|
||||
struct key_label
|
||||
{
|
||||
string label;
|
||||
public_key_type key;
|
||||
};
|
||||
|
||||
|
||||
struct by_label;
|
||||
struct by_key;
|
||||
typedef multi_index_container<
|
||||
|
|
@ -195,7 +127,6 @@ struct wallet_data
|
|||
map<string, string> pending_witness_registrations;
|
||||
|
||||
key_label_index_type labeled_keys;
|
||||
blind_receipt_index_type blind_receipts;
|
||||
|
||||
std::map<rock_paper_scissors_throw_commit, rock_paper_scissors_throw_reveal> committed_game_moves;
|
||||
|
||||
|
|
@ -872,74 +803,10 @@ class wallet_api
|
|||
bool set_key_label( public_key_type, string label );
|
||||
string get_key_label( public_key_type )const;
|
||||
|
||||
/**
|
||||
* Generates a new blind account for the given brain key and assigns it the given label.
|
||||
*/
|
||||
public_key_type create_blind_account( string label, string brain_key );
|
||||
|
||||
/**
|
||||
* @return the total balance of all blinded commitments that can be claimed by the
|
||||
* given account key or label
|
||||
*/
|
||||
vector<asset> get_blind_balances( string key_or_label );
|
||||
/** @return all blind accounts */
|
||||
map<string,public_key_type> get_blind_accounts()const;
|
||||
/** @return all blind accounts for which this wallet has the private key */
|
||||
map<string,public_key_type> get_my_blind_accounts()const;
|
||||
/** @return the public key associated with the given label */
|
||||
public_key_type get_public_key( string label )const;
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @return all blind receipts to/form a particular account
|
||||
*/
|
||||
vector<blind_receipt> blind_history( string key_or_account );
|
||||
|
||||
/**
|
||||
* Given a confirmation receipt, this method will parse it for a blinded balance and confirm
|
||||
* that it exists in the blockchain. If it exists then it will report the amount received and
|
||||
* who sent it.
|
||||
*
|
||||
* @param confirmation_receipt - a base58 encoded stealth confirmation
|
||||
* @param opt_from - if not empty and the sender is a unknown public key, then the unknown public key will be given the label opt_from
|
||||
* @param opt_memo - optional memo
|
||||
*/
|
||||
blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo );
|
||||
|
||||
/**
|
||||
* Transfers a public balance from from_account_id_or_name to one or more blinded balances using a
|
||||
* stealth transfer.
|
||||
*
|
||||
* @param from_account_id_or_name account id or name
|
||||
* @param asset_symbol asset symbol
|
||||
* @param to_amounts map from key or label to amount
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns blind confirmation structure
|
||||
*/
|
||||
blind_confirmation transfer_to_blind( string from_account_id_or_name,
|
||||
string asset_symbol,
|
||||
vector<pair<string, string>> to_amounts,
|
||||
bool broadcast = false );
|
||||
|
||||
/**
|
||||
* Transfers funds from a set of blinded balances to a public account balance.
|
||||
*/
|
||||
blind_confirmation transfer_from_blind(
|
||||
string from_blind_account_key_or_label,
|
||||
string to_account_id_or_name,
|
||||
string amount,
|
||||
string asset_symbol,
|
||||
bool broadcast = false );
|
||||
|
||||
/**
|
||||
* Used to transfer from one set of blinded balances to another
|
||||
*/
|
||||
blind_confirmation blind_transfer( string from_key_or_label,
|
||||
string to_key_or_label,
|
||||
string amount,
|
||||
string symbol,
|
||||
bool broadcast = false );
|
||||
|
||||
/** Place a limit order attempting to sell one asset for another.
|
||||
*
|
||||
* Buying and selling are the same operation on Graphene; if you want to buy BTS
|
||||
|
|
@ -2547,16 +2414,6 @@ class wallet_api
|
|||
void network_add_nodes( const vector<string>& nodes );
|
||||
vector< variant > network_get_connected_peers();
|
||||
|
||||
/**
|
||||
* Used to transfer from one set of blinded balances to another
|
||||
*/
|
||||
blind_confirmation blind_transfer_help( string from_key_or_label,
|
||||
string to_key_or_label,
|
||||
string amount,
|
||||
string symbol,
|
||||
bool broadcast = false,
|
||||
bool to_temp = false );
|
||||
|
||||
std::map<string,std::function<string(fc::variant,const fc::variants&)>> get_result_formatters() const;
|
||||
|
||||
/**
|
||||
|
|
@ -2607,9 +2464,6 @@ class wallet_api
|
|||
extern template class fc::api<graphene::wallet::wallet_api>;
|
||||
|
||||
FC_REFLECT( graphene::wallet::key_label, (label)(key) )
|
||||
FC_REFLECT( graphene::wallet::blind_balance, (amount)(from)(to)(one_time_key)(blinding_factor)(commitment)(used) )
|
||||
FC_REFLECT( graphene::wallet::blind_confirmation::output, (label)(pub_key)(decrypted_memo)(confirmation)(auth)(confirmation_receipt) )
|
||||
FC_REFLECT( graphene::wallet::blind_confirmation, (trx)(outputs) )
|
||||
|
||||
FC_REFLECT( graphene::wallet::plain_keys, (keys)(checksum) )
|
||||
|
||||
|
|
@ -2620,7 +2474,6 @@ FC_REFLECT( graphene::wallet::wallet_data,
|
|||
(extra_keys)
|
||||
(pending_account_registrations)(pending_witness_registrations)
|
||||
(labeled_keys)
|
||||
(blind_receipts)
|
||||
(committed_game_moves)
|
||||
(ws_server)
|
||||
(ws_user)
|
||||
|
|
@ -2639,9 +2492,6 @@ FC_REFLECT( graphene::wallet::exported_account_keys, (account_name)(encrypted_pr
|
|||
|
||||
FC_REFLECT( graphene::wallet::exported_keys, (password_checksum)(account_keys) )
|
||||
|
||||
FC_REFLECT( graphene::wallet::blind_receipt,
|
||||
(date)(from_key)(from_label)(to_key)(to_label)(amount)(memo)(control_authority)(data)(used)(conf) )
|
||||
|
||||
FC_REFLECT( graphene::wallet::approval_delta,
|
||||
(active_approvals_to_add)
|
||||
(active_approvals_to_remove)
|
||||
|
|
@ -2814,15 +2664,6 @@ FC_API( graphene::wallet::wallet_api,
|
|||
(set_key_label)
|
||||
(get_key_label)
|
||||
(get_public_key)
|
||||
(get_blind_accounts)
|
||||
(get_my_blind_accounts)
|
||||
(get_blind_balances)
|
||||
(create_blind_account)
|
||||
(transfer_to_blind)
|
||||
(transfer_from_blind)
|
||||
(blind_transfer)
|
||||
(blind_history)
|
||||
(receive_blind_transfer)
|
||||
(list_sports)
|
||||
(list_event_groups)
|
||||
(list_betting_market_groups)
|
||||
|
|
|
|||
|
|
@ -134,8 +134,6 @@ public:
|
|||
std::string operator()(const T& op)const;
|
||||
|
||||
std::string operator()(const transfer_operation& op)const;
|
||||
std::string operator()(const transfer_from_blind_operation& op)const;
|
||||
std::string operator()(const transfer_to_blind_operation& op)const;
|
||||
std::string operator()(const account_create_operation& op)const;
|
||||
std::string operator()(const account_update_operation& op)const;
|
||||
std::string operator()(const asset_create_operation& op)const;
|
||||
|
|
@ -3472,70 +3470,6 @@ public:
|
|||
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
m["get_blind_balances"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
auto r = result.as<vector<asset>>( GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
vector<asset_object> asset_recs;
|
||||
std::transform(r.begin(), r.end(), std::back_inserter(asset_recs), [this](const asset& a) {
|
||||
return get_asset(a.asset_id);
|
||||
});
|
||||
|
||||
std::stringstream ss;
|
||||
for( unsigned i = 0; i < asset_recs.size(); ++i )
|
||||
ss << asset_recs[i].amount_to_pretty_string(r[i]) << "\n";
|
||||
|
||||
return ss.str();
|
||||
};
|
||||
m["transfer_to_blind"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
auto r = result.as<blind_confirmation>( GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
std::stringstream ss;
|
||||
r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) );
|
||||
ss << "\n";
|
||||
for( const auto& out : r.outputs )
|
||||
{
|
||||
asset_object a = get_asset( out.decrypted_memo.amount.asset_id );
|
||||
ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n";
|
||||
}
|
||||
return ss.str();
|
||||
};
|
||||
m["blind_transfer"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
auto r = result.as<blind_confirmation>( GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
std::stringstream ss;
|
||||
r.trx.operations[0].visit( operation_printer( ss, *this, operation_result() ) );
|
||||
ss << "\n";
|
||||
for( const auto& out : r.outputs )
|
||||
{
|
||||
asset_object a = get_asset( out.decrypted_memo.amount.asset_id );
|
||||
ss << a.amount_to_pretty_string( out.decrypted_memo.amount ) << " to " << out.label << "\n\t receipt: " << out.confirmation_receipt <<"\n\n";
|
||||
}
|
||||
return ss.str();
|
||||
};
|
||||
m["receive_blind_transfer"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
auto r = result.as<blind_receipt>( GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
std::stringstream ss;
|
||||
asset_object as = get_asset( r.amount.asset_id );
|
||||
ss << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n";
|
||||
return ss.str();
|
||||
};
|
||||
m["blind_history"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
auto records = result.as<vector<blind_receipt>>( GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
std::stringstream ss;
|
||||
ss << "WHEN "
|
||||
<< " " << "AMOUNT" << " " << "FROM" << " => " << "TO" << " " << "MEMO" <<"\n";
|
||||
ss << "====================================================================================\n";
|
||||
for( auto& r : records )
|
||||
{
|
||||
asset_object as = get_asset( r.amount.asset_id );
|
||||
ss << fc::get_approximate_relative_time_string( r.date )
|
||||
<< " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n";
|
||||
}
|
||||
return ss.str();
|
||||
};
|
||||
m["get_upcoming_tournaments"] = m["get_tournaments"] = m["get_tournaments_by_state"] = [this](variant result, const fc::variants& a)
|
||||
{
|
||||
const vector<tournament_object> tournaments = result.as<vector<tournament_object> >( GRAPHENE_MAX_NESTED_OBJECTS );
|
||||
|
|
@ -4396,26 +4330,6 @@ std::string operation_printer::operator()(const T& op)const
|
|||
out << " result: " << str_result;
|
||||
return "";
|
||||
}
|
||||
std::string operation_printer::operator()(const transfer_from_blind_operation& op)const
|
||||
{
|
||||
auto a = wallet.get_asset( op.fee.asset_id );
|
||||
auto receiver = wallet.get_account( op.to );
|
||||
|
||||
out << receiver.name
|
||||
<< " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance";
|
||||
return "";
|
||||
}
|
||||
std::string operation_printer::operator()(const transfer_to_blind_operation& op)const
|
||||
{
|
||||
auto fa = wallet.get_asset( op.fee.asset_id );
|
||||
auto a = wallet.get_asset( op.amount.asset_id );
|
||||
auto sender = wallet.get_account( op.from );
|
||||
|
||||
out << sender.name
|
||||
<< " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"")
|
||||
<< " fee: " << fa.amount_to_pretty_string( op.fee );
|
||||
return "";
|
||||
}
|
||||
string operation_printer::operator()(const transfer_operation& op) const
|
||||
{
|
||||
out << "Transfer " << wallet.get_asset(op.amount.asset_id).amount_to_pretty_string(op.amount)
|
||||
|
|
@ -6186,495 +6100,6 @@ bool wallet_api::set_key_label( public_key_type key, string label
|
|||
}
|
||||
return false;
|
||||
}
|
||||
map<string,public_key_type> wallet_api::get_blind_accounts()const
|
||||
{
|
||||
map<string,public_key_type> result;
|
||||
for( const auto& item : my->_wallet.labeled_keys )
|
||||
result[item.label] = item.key;
|
||||
return result;
|
||||
}
|
||||
map<string,public_key_type> wallet_api::get_my_blind_accounts()const
|
||||
{
|
||||
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
|
||||
map<string,public_key_type> result;
|
||||
for( const auto& item : my->_wallet.labeled_keys )
|
||||
{
|
||||
if( my->_keys.find(item.key) != my->_keys.end() )
|
||||
result[item.label] = item.key;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public_key_type wallet_api::create_blind_account( string label, string brain_key )
|
||||
{
|
||||
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
|
||||
|
||||
auto label_itr = my->_wallet.labeled_keys.get<by_label>().find(label);
|
||||
if( label_itr != my->_wallet.labeled_keys.get<by_label>().end() )
|
||||
FC_ASSERT( !"Key with label already exists" );
|
||||
brain_key = fc::trim_and_normalize_spaces( brain_key );
|
||||
auto secret = fc::sha256::hash( brain_key.c_str(), brain_key.size() );
|
||||
auto priv_key = fc::ecc::private_key::regenerate( secret );
|
||||
public_key_type pub_key = priv_key.get_public_key();
|
||||
|
||||
FC_ASSERT( set_key_label( pub_key, label ) );
|
||||
|
||||
my->_keys[pub_key] = graphene::utilities::key_to_wif( priv_key );
|
||||
|
||||
save_wallet_file();
|
||||
return pub_key;
|
||||
}
|
||||
|
||||
vector<asset> wallet_api::get_blind_balances( string key_or_label )
|
||||
{
|
||||
vector<asset> result;
|
||||
map<asset_id_type, share_type> balances;
|
||||
|
||||
vector<commitment_type> used;
|
||||
|
||||
auto pub_key = get_public_key( key_or_label );
|
||||
auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();
|
||||
auto start = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(0),false) );
|
||||
auto end = to_asset_used_idx.lower_bound( std::make_tuple(pub_key,asset_id_type(uint32_t(0xffffffff)),true) );
|
||||
while( start != end )
|
||||
{
|
||||
if( !start->used )
|
||||
{
|
||||
auto answer = my->_remote_db->get_blinded_balances( {start->commitment()} );
|
||||
if( answer.size() )
|
||||
balances[start->amount.asset_id] += start->amount.amount;
|
||||
else
|
||||
used.push_back( start->commitment() );
|
||||
}
|
||||
++start;
|
||||
}
|
||||
for( const auto& u : used )
|
||||
{
|
||||
auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );
|
||||
my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );
|
||||
}
|
||||
for( auto item : balances )
|
||||
result.push_back( asset( item.second, item.first ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label,
|
||||
string to_account_id_or_name,
|
||||
string amount_in,
|
||||
string symbol,
|
||||
bool broadcast )
|
||||
{ try {
|
||||
transfer_from_blind_operation from_blind;
|
||||
|
||||
|
||||
auto fees = my->_remote_db->get_global_properties().parameters.current_fees;
|
||||
fc::optional<asset_object> asset_obj = get_asset(symbol);
|
||||
FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol));
|
||||
auto amount = asset_obj->amount_from_string(amount_in);
|
||||
|
||||
from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate );
|
||||
|
||||
auto blind_in = asset_obj->amount_to_string( from_blind.fee + amount );
|
||||
|
||||
|
||||
auto conf = blind_transfer_help( from_blind_account_key_or_label,
|
||||
from_blind_account_key_or_label,
|
||||
blind_in, symbol, false, true/*to_temp*/ );
|
||||
FC_ASSERT( conf.outputs.size() > 0 );
|
||||
|
||||
auto to_account = my->get_account( to_account_id_or_name );
|
||||
from_blind.to = to_account.id;
|
||||
from_blind.amount = amount;
|
||||
from_blind.blinding_factor = conf.outputs.back().decrypted_memo.blinding_factor;
|
||||
from_blind.inputs.push_back( {conf.outputs.back().decrypted_memo.commitment, authority() } );
|
||||
from_blind.fee = fees->calculate_fee( from_blind, asset_obj->options.core_exchange_rate );
|
||||
|
||||
idump( (from_blind) );
|
||||
conf.trx.operations.push_back(from_blind);
|
||||
ilog( "about to validate" );
|
||||
conf.trx.validate();
|
||||
|
||||
if( broadcast && conf.outputs.size() == 2 ) {
|
||||
|
||||
// Save the change
|
||||
blind_confirmation::output conf_output;
|
||||
blind_confirmation::output change_output = conf.outputs[0];
|
||||
|
||||
// The wallet must have a private key for confirmation.to, this is used to decrypt the memo
|
||||
public_key_type from_key = get_public_key(from_blind_account_key_or_label);
|
||||
conf_output.confirmation.to = from_key;
|
||||
conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key;
|
||||
conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo;
|
||||
conf_output.confirmation_receipt = conf_output.confirmation;
|
||||
//try {
|
||||
receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name );
|
||||
//} catch ( ... ){}
|
||||
}
|
||||
|
||||
ilog( "about to broadcast" );
|
||||
conf.trx = sign_transaction( conf.trx, broadcast );
|
||||
|
||||
return conf;
|
||||
} FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) }
|
||||
|
||||
blind_confirmation wallet_api::blind_transfer( string from_key_or_label,
|
||||
string to_key_or_label,
|
||||
string amount_in,
|
||||
string symbol,
|
||||
bool broadcast )
|
||||
{
|
||||
return blind_transfer_help( from_key_or_label, to_key_or_label, amount_in, symbol, broadcast, false );
|
||||
}
|
||||
blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
|
||||
string to_key_or_label,
|
||||
string amount_in,
|
||||
string symbol,
|
||||
bool broadcast,
|
||||
bool to_temp )
|
||||
{
|
||||
blind_confirmation confirm;
|
||||
try {
|
||||
|
||||
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
|
||||
public_key_type from_key = get_public_key(from_key_or_label);
|
||||
public_key_type to_key = get_public_key(to_key_or_label);
|
||||
|
||||
fc::optional<asset_object> asset_obj = get_asset(symbol);
|
||||
FC_ASSERT(asset_obj.valid(), "Could not find asset matching ${asset}", ("asset", symbol));
|
||||
|
||||
blind_transfer_operation blind_tr;
|
||||
blind_tr.outputs.resize(2);
|
||||
|
||||
auto fees = my->_remote_db->get_global_properties().parameters.current_fees;
|
||||
|
||||
auto amount = asset_obj->amount_from_string(amount_in);
|
||||
|
||||
asset total_amount = asset_obj->amount(0);
|
||||
|
||||
vector<fc::sha256> blinding_factors;
|
||||
|
||||
//auto from_priv_key = my->get_private_key( from_key );
|
||||
|
||||
blind_tr.fee = fees->calculate_fee( blind_tr, asset_obj->options.core_exchange_rate );
|
||||
|
||||
vector<commitment_type> used;
|
||||
|
||||
auto& to_asset_used_idx = my->_wallet.blind_receipts.get<by_to_asset_used>();
|
||||
auto start = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,false) );
|
||||
auto end = to_asset_used_idx.lower_bound( std::make_tuple(from_key,amount.asset_id,true) );
|
||||
while( start != end )
|
||||
{
|
||||
auto result = my->_remote_db->get_blinded_balances( {start->commitment() } );
|
||||
if( result.size() == 0 )
|
||||
{
|
||||
used.push_back( start->commitment() );
|
||||
}
|
||||
else
|
||||
{
|
||||
blind_tr.inputs.push_back({start->commitment(), start->control_authority});
|
||||
blinding_factors.push_back( start->data.blinding_factor );
|
||||
total_amount += start->amount;
|
||||
|
||||
if( total_amount >= amount + blind_tr.fee )
|
||||
break;
|
||||
}
|
||||
++start;
|
||||
}
|
||||
for( const auto& u : used )
|
||||
{
|
||||
auto itr = my->_wallet.blind_receipts.get<by_commitment>().find( u );
|
||||
my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } );
|
||||
}
|
||||
|
||||
FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficent Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) );
|
||||
|
||||
auto one_time_key = fc::ecc::private_key::generate();
|
||||
auto secret = one_time_key.get_shared_secret( to_key );
|
||||
auto child = fc::sha256::hash( secret );
|
||||
auto nonce = fc::sha256::hash( one_time_key.get_secret() );
|
||||
auto blind_factor = fc::sha256::hash( child );
|
||||
|
||||
auto from_secret = one_time_key.get_shared_secret( from_key );
|
||||
auto from_child = fc::sha256::hash( from_secret );
|
||||
auto from_nonce = fc::sha256::hash( nonce );
|
||||
|
||||
auto change = total_amount - amount - blind_tr.fee;
|
||||
fc::sha256 change_blind_factor;
|
||||
fc::sha256 to_blind_factor;
|
||||
if( change.amount > 0 )
|
||||
{
|
||||
idump(("to_blind_factor")(blind_factor) );
|
||||
blinding_factors.push_back( blind_factor );
|
||||
change_blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() - 1 );
|
||||
wdump(("change_blind_factor")(change_blind_factor) );
|
||||
}
|
||||
else // change == 0
|
||||
{
|
||||
blind_tr.outputs.resize(1);
|
||||
blind_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );
|
||||
idump(("to_sum_blind_factor")(blind_factor) );
|
||||
blinding_factors.push_back( blind_factor );
|
||||
idump(("nochange to_blind_factor")(blind_factor) );
|
||||
}
|
||||
fc::ecc::public_key from_pub_key = from_key;
|
||||
fc::ecc::public_key to_pub_key = to_key;
|
||||
|
||||
blind_output to_out;
|
||||
to_out.owner = to_temp ? authority() : authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );
|
||||
to_out.commitment = fc::ecc::blind( blind_factor, amount.amount.value );
|
||||
idump(("to_out.blind")(blind_factor)(to_out.commitment) );
|
||||
|
||||
|
||||
if( blind_tr.outputs.size() > 1 )
|
||||
{
|
||||
to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, 0, amount.amount.value );
|
||||
|
||||
blind_output change_out;
|
||||
change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 );
|
||||
change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value );
|
||||
change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, 0, change.amount.value );
|
||||
blind_tr.outputs[1] = change_out;
|
||||
|
||||
|
||||
blind_confirmation::output conf_output;
|
||||
conf_output.label = from_key_or_label;
|
||||
conf_output.pub_key = from_key;
|
||||
conf_output.decrypted_memo.from = from_key;
|
||||
conf_output.decrypted_memo.amount = change;
|
||||
conf_output.decrypted_memo.blinding_factor = change_blind_factor;
|
||||
conf_output.decrypted_memo.commitment = change_out.commitment;
|
||||
conf_output.decrypted_memo.check = from_secret._hash[0];
|
||||
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
|
||||
conf_output.confirmation.to = from_key;
|
||||
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) );
|
||||
conf_output.auth = change_out.owner;
|
||||
conf_output.confirmation_receipt = conf_output.confirmation;
|
||||
|
||||
confirm.outputs.push_back( conf_output );
|
||||
}
|
||||
blind_tr.outputs[0] = to_out;
|
||||
|
||||
blind_confirmation::output conf_output;
|
||||
conf_output.label = to_key_or_label;
|
||||
conf_output.pub_key = to_key;
|
||||
conf_output.decrypted_memo.from = from_key;
|
||||
conf_output.decrypted_memo.amount = amount;
|
||||
conf_output.decrypted_memo.blinding_factor = blind_factor;
|
||||
conf_output.decrypted_memo.commitment = to_out.commitment;
|
||||
conf_output.decrypted_memo.check = secret._hash[0];
|
||||
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
|
||||
conf_output.confirmation.to = to_key;
|
||||
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
|
||||
conf_output.auth = to_out.owner;
|
||||
conf_output.confirmation_receipt = conf_output.confirmation;
|
||||
|
||||
confirm.outputs.push_back( conf_output );
|
||||
|
||||
/** commitments must be in sorted order */
|
||||
std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(),
|
||||
[&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );
|
||||
std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(),
|
||||
[&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } );
|
||||
|
||||
confirm.trx.operations.emplace_back( std::move(blind_tr) );
|
||||
ilog( "validate before" );
|
||||
confirm.trx.validate();
|
||||
confirm.trx = sign_transaction(confirm.trx, broadcast);
|
||||
|
||||
if( broadcast )
|
||||
{
|
||||
for( const auto& out : confirm.outputs )
|
||||
{
|
||||
try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){}
|
||||
}
|
||||
}
|
||||
|
||||
return confirm;
|
||||
} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Transfers a public balance from @from to one or more blinded balances using a
|
||||
* stealth transfer.
|
||||
*/
|
||||
blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name,
|
||||
string asset_symbol,
|
||||
/** map from key or label to amount */
|
||||
vector<pair<string, string>> to_amounts,
|
||||
bool broadcast )
|
||||
{ try {
|
||||
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
|
||||
idump((to_amounts));
|
||||
|
||||
blind_confirmation confirm;
|
||||
account_object from_account = my->get_account(from_account_id_or_name);
|
||||
|
||||
fc::optional<asset_object> asset_obj = get_asset(asset_symbol);
|
||||
FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol));
|
||||
|
||||
transfer_to_blind_operation bop;
|
||||
bop.from = from_account.id;
|
||||
|
||||
vector<fc::sha256> blinding_factors;
|
||||
|
||||
asset total_amount = asset_obj->amount(0);
|
||||
|
||||
for( auto item : to_amounts )
|
||||
{
|
||||
auto one_time_key = fc::ecc::private_key::generate();
|
||||
auto to_key = get_public_key( item.first );
|
||||
auto secret = one_time_key.get_shared_secret( to_key );
|
||||
auto child = fc::sha256::hash( secret );
|
||||
auto nonce = fc::sha256::hash( one_time_key.get_secret() );
|
||||
auto blind_factor = fc::sha256::hash( child );
|
||||
|
||||
blinding_factors.push_back( blind_factor );
|
||||
|
||||
auto amount = asset_obj->amount_from_string(item.second);
|
||||
total_amount += amount;
|
||||
|
||||
|
||||
fc::ecc::public_key to_pub_key = to_key;
|
||||
blind_output out;
|
||||
out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 );
|
||||
out.commitment = fc::ecc::blind( blind_factor, amount.amount.value );
|
||||
if( to_amounts.size() > 1 )
|
||||
out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, 0, amount.amount.value );
|
||||
|
||||
|
||||
blind_confirmation::output conf_output;
|
||||
conf_output.label = item.first;
|
||||
conf_output.pub_key = to_key;
|
||||
conf_output.decrypted_memo.amount = amount;
|
||||
conf_output.decrypted_memo.blinding_factor = blind_factor;
|
||||
conf_output.decrypted_memo.commitment = out.commitment;
|
||||
conf_output.decrypted_memo.check = secret._hash[0];
|
||||
conf_output.confirmation.one_time_key = one_time_key.get_public_key();
|
||||
conf_output.confirmation.to = to_key;
|
||||
conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) );
|
||||
conf_output.confirmation_receipt = conf_output.confirmation;
|
||||
|
||||
confirm.outputs.push_back( conf_output );
|
||||
|
||||
bop.outputs.push_back(out);
|
||||
}
|
||||
bop.amount = total_amount;
|
||||
bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() );
|
||||
|
||||
/** commitments must be in sorted order */
|
||||
std::sort( bop.outputs.begin(), bop.outputs.end(),
|
||||
[&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } );
|
||||
|
||||
confirm.trx.operations.push_back( bop );
|
||||
my->set_operation_fees( confirm.trx, my->_remote_db->get_global_properties().parameters.current_fees);
|
||||
confirm.trx.validate();
|
||||
confirm.trx = sign_transaction(confirm.trx, broadcast);
|
||||
|
||||
if( broadcast )
|
||||
{
|
||||
for( const auto& out : confirm.outputs )
|
||||
{
|
||||
try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){}
|
||||
}
|
||||
}
|
||||
|
||||
return confirm;
|
||||
} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) }
|
||||
|
||||
blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo )
|
||||
{
|
||||
FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" );
|
||||
stealth_confirmation conf(confirmation_receipt);
|
||||
FC_ASSERT( conf.to );
|
||||
|
||||
blind_receipt result;
|
||||
result.conf = conf;
|
||||
|
||||
auto to_priv_key_itr = my->_keys.find( *conf.to );
|
||||
FC_ASSERT( to_priv_key_itr != my->_keys.end(), "No private key for receiver", ("conf",conf) );
|
||||
|
||||
|
||||
auto to_priv_key = wif_to_key( to_priv_key_itr->second );
|
||||
FC_ASSERT( to_priv_key );
|
||||
|
||||
auto secret = to_priv_key->get_shared_secret( conf.one_time_key );
|
||||
auto child = fc::sha256::hash( secret );
|
||||
|
||||
auto child_priv_key = to_priv_key->child( child );
|
||||
//auto blind_factor = fc::sha256::hash( child );
|
||||
|
||||
auto plain_memo = fc::aes_decrypt( secret, conf.encrypted_memo );
|
||||
auto memo = fc::raw::unpack<stealth_confirmation::memo_data>( plain_memo );
|
||||
|
||||
result.to_key = *conf.to;
|
||||
result.to_label = get_key_label( result.to_key );
|
||||
if( memo.from )
|
||||
{
|
||||
result.from_key = *memo.from;
|
||||
result.from_label = get_key_label( result.from_key );
|
||||
if( result.from_label == string() )
|
||||
{
|
||||
result.from_label = opt_from;
|
||||
set_key_label( result.from_key, result.from_label );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.from_label = opt_from;
|
||||
}
|
||||
result.amount = memo.amount;
|
||||
result.memo = opt_memo;
|
||||
|
||||
// confirm the amount matches the commitment (verify the blinding factor)
|
||||
auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value );
|
||||
FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) );
|
||||
|
||||
blind_balance bal;
|
||||
bal.amount = memo.amount;
|
||||
bal.to = *conf.to;
|
||||
if( memo.from ) bal.from = *memo.from;
|
||||
bal.one_time_key = conf.one_time_key;
|
||||
bal.blinding_factor = memo.blinding_factor;
|
||||
bal.commitment = memo.commitment;
|
||||
bal.used = false;
|
||||
|
||||
auto child_pubkey = child_priv_key.get_public_key();
|
||||
auto owner = authority(1, public_key_type(child_pubkey), 1);
|
||||
result.control_authority = owner;
|
||||
result.data = memo;
|
||||
|
||||
auto child_key_itr = owner.key_auths.find( child_pubkey );
|
||||
if( child_key_itr != owner.key_auths.end() )
|
||||
my->_keys[child_key_itr->first] = key_to_wif( child_priv_key );
|
||||
|
||||
// my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal );
|
||||
|
||||
result.date = fc::time_point::now();
|
||||
my->_wallet.blind_receipts.insert( result );
|
||||
my->_keys[child_pubkey] = key_to_wif( child_priv_key );
|
||||
|
||||
save_wallet_file();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<blind_receipt> wallet_api::blind_history( string key_or_account )
|
||||
{
|
||||
vector<blind_receipt> result;
|
||||
auto pub_key = get_public_key( key_or_account );
|
||||
|
||||
if( pub_key == public_key_type() )
|
||||
return vector<blind_receipt>();
|
||||
|
||||
for( auto& r : my->_wallet.blind_receipts )
|
||||
{
|
||||
if( r.from_key == pub_key || r.to_key == pub_key )
|
||||
result.push_back( r );
|
||||
}
|
||||
std::sort( result.begin(), result.end(), [&]( const blind_receipt& a, const blind_receipt& b ){ return a.date > b.date; } );
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////
|
||||
// peerplays //
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ using namespace fc::ecc;
|
|||
|
||||
BOOST_AUTO_TEST_SUITE(bitcoin_sign_tests)
|
||||
|
||||
const secp256k1_context_t *btc_context() {
|
||||
static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
const secp256k1_context *btc_context() {
|
||||
static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* 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 <boost/test/unit_test.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/committee_member_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
|
||||
#include <graphene/db/simple_index.hpp>
|
||||
|
||||
#include <fc/crypto/digest.hpp>
|
||||
#include "../common/database_fixture.hpp"
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE( confidential_tests, database_fixture )
|
||||
BOOST_AUTO_TEST_CASE( confidential_test )
|
||||
{ try {
|
||||
ACTORS( (dan)(nathan) )
|
||||
const asset_object& core = asset_id_type()(db);
|
||||
|
||||
transfer(account_id_type()(db), dan, core.amount(1000000));
|
||||
|
||||
transfer_to_blind_operation to_blind;
|
||||
to_blind.amount = core.amount(1000);
|
||||
to_blind.from = dan.id;
|
||||
|
||||
auto owner1_key = fc::ecc::private_key::generate();
|
||||
auto owner1_pub = owner1_key.get_public_key();
|
||||
auto owner2_key = fc::ecc::private_key::generate();
|
||||
auto owner2_pub = owner2_key.get_public_key();
|
||||
|
||||
blind_output out1, out2;
|
||||
out1.owner = authority( 1, public_key_type(owner1_pub), 1 );
|
||||
out2.owner = authority( 1, public_key_type(owner2_pub), 1 );
|
||||
|
||||
|
||||
auto InB1 = fc::sha256::hash("InB1");
|
||||
auto InB2 = fc::sha256::hash("InB2");
|
||||
auto nonce1 = fc::sha256::hash("nonce");
|
||||
auto nonce2 = fc::sha256::hash("nonce2");
|
||||
|
||||
out1.commitment = fc::ecc::blind(InB1,250);
|
||||
out1.range_proof = fc::ecc::range_proof_sign( 0, out1.commitment, InB1, nonce1, 0, 0, 250 );
|
||||
|
||||
out2.commitment = fc::ecc::blind(InB2,750);
|
||||
out2.range_proof = fc::ecc::range_proof_sign( 0, out2.commitment, InB1, nonce2, 0, 0, 750 );
|
||||
|
||||
to_blind.blinding_factor = fc::ecc::blind_sum( {InB1,InB2}, 2 );
|
||||
to_blind.outputs = {out2,out1};
|
||||
|
||||
trx.operations = {to_blind};
|
||||
sign( trx, dan_private_key );
|
||||
db.push_transaction(trx);
|
||||
trx.clear_signatures();
|
||||
|
||||
BOOST_TEST_MESSAGE( "Transfering from blind to blind with change address" );
|
||||
auto Out3B = fc::sha256::hash("Out3B");
|
||||
auto Out4B = fc::ecc::blind_sum( {InB2,Out3B}, 1 ); // add InB2 - Out3b
|
||||
blind_output out3, out4;
|
||||
out3.commitment = fc::ecc::blind(Out3B,300);
|
||||
out3.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 300 );
|
||||
out4.commitment = fc::ecc::blind(Out4B,750-300-10);
|
||||
out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-10 );
|
||||
|
||||
|
||||
blind_transfer_operation blind_tr;
|
||||
blind_tr.fee = core.amount(10);
|
||||
blind_tr.inputs.push_back( {out2.commitment, out2.owner} );
|
||||
blind_tr.outputs = {out3,out4};
|
||||
blind_tr.validate();
|
||||
trx.operations = {blind_tr};
|
||||
sign( trx, owner2_key );
|
||||
db.push_transaction(trx);
|
||||
|
||||
BOOST_TEST_MESSAGE( "Attempting to double spend the same commitments" );
|
||||
blind_tr.fee = core.amount(11);
|
||||
|
||||
Out4B = fc::ecc::blind_sum( {InB2,Out3B}, 1 ); // add InB2 - Out3b
|
||||
out4.commitment = fc::ecc::blind(Out4B,750-300-11);
|
||||
auto out4_amount = 750-300-10;
|
||||
out4.range_proof = fc::ecc::range_proof_sign( 0, out3.commitment, InB1, nonce1, 0, 0, 750-300-11 );
|
||||
blind_tr.outputs = {out4,out3};
|
||||
trx.operations = {blind_tr};
|
||||
BOOST_REQUIRE_THROW( db.push_transaction(trx, ~0), graphene::chain::blind_transfer_unknown_commitment );
|
||||
|
||||
|
||||
BOOST_TEST_MESSAGE( "Transfering from blind to nathan public" );
|
||||
out4.commitment = fc::ecc::blind(Out4B,750-300-10);
|
||||
|
||||
transfer_from_blind_operation from_blind;
|
||||
from_blind.fee = core.amount(10);
|
||||
from_blind.to = nathan.id;
|
||||
from_blind.amount = core.amount( out4_amount - 10 );
|
||||
from_blind.blinding_factor = Out4B;
|
||||
from_blind.inputs.push_back( {out4.commitment, out4.owner} );
|
||||
trx.operations = {from_blind};
|
||||
trx.clear_signatures();
|
||||
db.push_transaction(trx);
|
||||
|
||||
BOOST_REQUIRE_EQUAL( get_balance( nathan, core ), 750-300-10-10 );
|
||||
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -741,212 +741,6 @@ BOOST_AUTO_TEST_CASE( account_create_fee_scaling )
|
|||
// FC_LOG_AND_RETHROW()
|
||||
// }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stealth_fba_test )
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTORS( (alice)(bob)(chloe)(dan)(izzy)(philbin)(tom) );
|
||||
upgrade_to_lifetime_member(philbin_id);
|
||||
|
||||
generate_blocks( HARDFORK_538_TIME );
|
||||
generate_blocks( HARDFORK_555_TIME );
|
||||
generate_blocks( HARDFORK_563_TIME );
|
||||
generate_blocks( HARDFORK_572_TIME );
|
||||
generate_blocks( HARDFORK_599_TIME );
|
||||
|
||||
// Philbin (registrar who registers Rex)
|
||||
|
||||
// Izzy (initial issuer of stealth asset, will later transfer to Tom)
|
||||
// Alice, Bob, Chloe, Dan (ABCD)
|
||||
// Rex (recycler -- buyback account for stealth asset)
|
||||
// Tom (owner of stealth asset who will be set as top_n authority)
|
||||
|
||||
// Izzy creates STEALTH
|
||||
asset_id_type stealth_id = create_user_issued_asset( "STEALTH", izzy_id(db),
|
||||
disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee ).id;
|
||||
|
||||
/*
|
||||
// this is disabled because it doesn't work, our modify() is probably being overwritten by undo
|
||||
|
||||
//
|
||||
// Init blockchain with stealth ID's
|
||||
// On a real chain, this would be done with #define GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET
|
||||
// causing the designated_asset fields of these objects to be set at genesis, but for
|
||||
// this test we modify the db directly.
|
||||
//
|
||||
auto set_fba_asset = [&]( uint64_t fba_acc_id, asset_id_type asset_id )
|
||||
{
|
||||
db.modify( fba_accumulator_id_type(fba_acc_id)(db), [&]( fba_accumulator_object& fba )
|
||||
{
|
||||
fba.designated_asset = asset_id;
|
||||
} );
|
||||
};
|
||||
|
||||
set_fba_asset( fba_accumulator_id_transfer_to_blind , stealth_id );
|
||||
set_fba_asset( fba_accumulator_id_blind_transfer , stealth_id );
|
||||
set_fba_asset( fba_accumulator_id_transfer_from_blind, stealth_id );
|
||||
*/
|
||||
|
||||
// Izzy kills some permission bits (this somehow happened to the real STEALTH in production)
|
||||
{
|
||||
asset_update_operation update_op;
|
||||
update_op.issuer = izzy_id;
|
||||
update_op.asset_to_update = stealth_id;
|
||||
asset_options new_options;
|
||||
new_options = stealth_id(db).options;
|
||||
new_options.issuer_permissions = charge_market_fee;
|
||||
new_options.flags = disable_confidential | transfer_restricted | override_authority | white_list | charge_market_fee;
|
||||
// after fixing #579 you should be able to delete the following line
|
||||
new_options.core_exchange_rate = price( asset( 1, stealth_id ), asset( 1, asset_id_type() ) );
|
||||
update_op.new_options = new_options;
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( update_op );
|
||||
set_expiration( db, tx );
|
||||
sign( tx, izzy_private_key );
|
||||
PUSH_TX( db, tx );
|
||||
}
|
||||
|
||||
// Izzy transfers issuer duty to Tom
|
||||
{
|
||||
asset_update_operation update_op;
|
||||
update_op.issuer = izzy_id;
|
||||
update_op.asset_to_update = stealth_id;
|
||||
update_op.new_issuer = tom_id;
|
||||
// new_options should be optional, but isn't...the following line should be unnecessary #580
|
||||
update_op.new_options = stealth_id(db).options;
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( update_op );
|
||||
set_expiration( db, tx );
|
||||
sign( tx, izzy_private_key );
|
||||
PUSH_TX( db, tx );
|
||||
}
|
||||
|
||||
// Tom re-enables the permission bits to clear the flags, then clears them again
|
||||
// Allowed by #572 when current_supply == 0
|
||||
{
|
||||
asset_update_operation update_op;
|
||||
update_op.issuer = tom_id;
|
||||
update_op.asset_to_update = stealth_id;
|
||||
asset_options new_options;
|
||||
new_options = stealth_id(db).options;
|
||||
new_options.issuer_permissions = new_options.flags | charge_market_fee;
|
||||
update_op.new_options = new_options;
|
||||
signed_transaction tx;
|
||||
// enable perms is one op
|
||||
tx.operations.push_back( update_op );
|
||||
|
||||
new_options.issuer_permissions = charge_market_fee;
|
||||
new_options.flags = charge_market_fee;
|
||||
update_op.new_options = new_options;
|
||||
// reset wrongly set flags and reset permissions can be done in a single op
|
||||
tx.operations.push_back( update_op );
|
||||
|
||||
set_expiration( db, tx );
|
||||
sign( tx, tom_private_key );
|
||||
PUSH_TX( db, tx );
|
||||
}
|
||||
|
||||
// Philbin registers Rex who will be the asset's buyback, including sig from the new issuer (Tom)
|
||||
account_id_type rex_id;
|
||||
{
|
||||
buyback_account_options bbo;
|
||||
bbo.asset_to_buy = stealth_id;
|
||||
bbo.asset_to_buy_issuer = tom_id;
|
||||
bbo.markets.emplace( asset_id_type() );
|
||||
account_create_operation create_op = make_account( "rex" );
|
||||
create_op.registrar = philbin_id;
|
||||
create_op.extensions.value.buyback_options = bbo;
|
||||
create_op.owner = authority::null_authority();
|
||||
create_op.active = authority::null_authority();
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( create_op );
|
||||
set_expiration( db, tx );
|
||||
sign( tx, philbin_private_key );
|
||||
sign( tx, tom_private_key );
|
||||
|
||||
processed_transaction ptx = PUSH_TX( db, tx );
|
||||
rex_id = ptx.operation_results.back().get< object_id_type >();
|
||||
}
|
||||
|
||||
// Tom issues some asset to Alice and Bob
|
||||
set_expiration( db, trx ); // #11
|
||||
issue_uia( alice_id, asset( 1000, stealth_id ) );
|
||||
issue_uia( bob_id, asset( 1000, stealth_id ) );
|
||||
|
||||
// Tom sets his authority to the top_n of the asset
|
||||
{
|
||||
top_holders_special_authority top2;
|
||||
top2.num_top_holders = 2;
|
||||
top2.asset = stealth_id;
|
||||
|
||||
account_update_operation op;
|
||||
op.account = tom_id;
|
||||
op.extensions.value.active_special_authority = top2;
|
||||
op.extensions.value.owner_special_authority = top2;
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
|
||||
set_expiration( db, tx );
|
||||
sign( tx, tom_private_key );
|
||||
|
||||
PUSH_TX( db, tx );
|
||||
}
|
||||
|
||||
// Wait until the next maintenance interval for top_n to take effect
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
|
||||
// Do a blind op to add some fees to the pool.
|
||||
fund( chloe_id(db), asset( 100000, asset_id_type() ) );
|
||||
|
||||
auto create_transfer_to_blind = [&]( account_id_type account, asset amount, const std::string& key ) -> transfer_to_blind_operation
|
||||
{
|
||||
fc::ecc::private_key blind_key = fc::ecc::private_key::regenerate( fc::sha256::hash( key+"-privkey" ) );
|
||||
public_key_type blind_pub = blind_key.get_public_key();
|
||||
|
||||
fc::sha256 secret = fc::sha256::hash( key+"-secret" );
|
||||
fc::sha256 nonce = fc::sha256::hash( key+"-nonce" );
|
||||
|
||||
transfer_to_blind_operation op;
|
||||
blind_output blind_out;
|
||||
blind_out.owner = authority( 1, blind_pub, 1 );
|
||||
blind_out.commitment = fc::ecc::blind( secret, amount.amount.value );
|
||||
blind_out.range_proof = fc::ecc::range_proof_sign( 0, blind_out.commitment, secret, nonce, 0, 0, amount.amount.value );
|
||||
|
||||
op.amount = amount;
|
||||
op.from = account;
|
||||
op.blinding_factor = fc::ecc::blind_sum( {secret}, 1 );
|
||||
op.outputs = {blind_out};
|
||||
|
||||
return op;
|
||||
};
|
||||
|
||||
{
|
||||
transfer_to_blind_operation op = create_transfer_to_blind( chloe_id, asset( 5000, asset_id_type() ), "chloe-key" );
|
||||
op.fee = asset( 1000, asset_id_type() );
|
||||
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_expiration( db, tx );
|
||||
sign( tx, chloe_private_key );
|
||||
|
||||
PUSH_TX( db, tx );
|
||||
}
|
||||
|
||||
// wait until next maint interval
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
|
||||
idump( ( get_operation_history( chloe_id ) ) );
|
||||
idump( ( get_operation_history( rex_id ) ) );
|
||||
idump( ( get_operation_history( tom_id ) ) );
|
||||
}
|
||||
catch( const fc::exception& e )
|
||||
{
|
||||
elog( "caught exception ${e}", ("e", e.to_detail_string()) );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
// added test from bitshares for issues:
|
||||
// https://github.com/bitshares/bitshares-core/issues/429
|
||||
// https://github.com/bitshares/bitshares-core/issues/433
|
||||
|
|
|
|||
Loading…
Reference in a new issue