Merge branch 'confidential'

This commit is contained in:
Daniel Larimer 2015-07-22 16:24:07 -04:00
commit 6f19268ffd
15 changed files with 807 additions and 8 deletions

View file

@ -24,6 +24,7 @@
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/smart_ref_impl.hpp>
@ -684,6 +685,13 @@ namespace graphene { namespace app {
result.reserve( impacted.size() );
for( auto& item : impacted ) result.emplace_back(item);
break;
} case impl_blinded_balance_object_type:{
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
assert( aobj != nullptr );
result.reserve( aobj->owner.account_auths.size() );
for( const auto& a : aobj->owner.account_auths )
result.push_back( a.first );
break;
} case impl_block_summary_object_type:{
} case impl_account_transaction_history_object_type:{
} case impl_witness_schedule_object_type: {
@ -1088,5 +1096,18 @@ namespace graphene { namespace app {
_db.get_global_properties().parameters.max_authority_depth );
return true;
}
vector<blinded_balance_object> database_api::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;
}
} } // graphene::app

View file

@ -26,6 +26,7 @@
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/balance_object.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/net/node.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
@ -319,6 +320,12 @@ namespace graphene { namespace app {
bool verify_authority( const signed_transaction& trx )const;
/**
* @return the set of blinded balance objects by commitment ID
*/
vector<blinded_balance_object> get_blinded_balances( const flat_set<commitment_type>& commitments )const;
private:
/** called every time a block is applied to report the objects that were changed */
void on_objects_changed(const vector<object_id_type>& ids);

View file

@ -21,6 +21,7 @@ add_library( graphene_chain
protocol/transaction.cpp
protocol/block.cpp
protocol/fee_schedule.cpp
protocol/confidential.cpp
pts_address.cpp
@ -37,6 +38,7 @@ add_library( graphene_chain
vesting_balance_evaluator.cpp
withdraw_permission_evaluator.cpp
worker_evaluator.cpp
confidential_evaluator.cpp
account_object.cpp
asset_object.cpp

View file

@ -0,0 +1,137 @@
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/database.hpp>
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.enforce_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_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_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) ) }
} } // graphene::chain

View file

@ -44,6 +44,7 @@
#include <graphene/chain/witness_evaluator.hpp>
#include <graphene/chain/worker_evaluator.hpp>
#include <graphene/chain/balance_evaluator.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -150,6 +151,9 @@ void database::initialize_evaluators()
register_evaluator<withdraw_permission_delete_evaluator>();
register_evaluator<worker_create_evaluator>();
register_evaluator<balance_claim_evaluator>();
register_evaluator<transfer_to_blind_evaluator>();
register_evaluator<transfer_from_blind_evaluator>();
register_evaluator<blind_transfer_evaluator>();
}
void database::initialize_indexes()
@ -177,6 +181,7 @@ void database::initialize_indexes()
add_index< primary_index<vesting_balance_index> >();
add_index< primary_index<worker_index> >();
add_index< primary_index<balance_index> >();
add_index< primary_index<blinded_balance_index> >();
//Implementation object indexes
add_index< primary_index<transaction_index > >();

View file

@ -56,6 +56,7 @@ namespace graphene { namespace chain {
/// The number of shares currently in existence
share_type current_supply;
share_type confidential_supply; ///< total asset held in confidential balances
share_type accumulated_fees; ///< fees accumulate to be paid out over time
share_type fee_pool; ///< in core asset
};
@ -91,6 +92,7 @@ namespace graphene { namespace chain {
/// @return true if this asset may only be transferred to/from the issuer or market orders
bool is_transfer_restricted()const { return options.flags & transfer_restricted; }
bool can_override()const { return options.flags & override_authority; }
bool allow_confidential()const { return !(options.flags & asset_issuer_permission_flags::disable_confidential); }
/// Helper function to get an asset object with the given amount in this asset's type
asset amount(share_type a)const { return asset(a, id); }
@ -234,7 +236,7 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::object),
(current_supply)(accumulated_fees)(fee_pool) )
(current_supply)(confidential_supply)(accumulated_fees)(fee_pool) )
FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::object),
(feeds)

View file

@ -0,0 +1,71 @@
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene { namespace chain {
/**
* @class blinded_balance_object
* @brief tracks a blinded balance commitment
* @ingroup object
* @ingroup protocol
*/
class blinded_balance_object : public graphene::db::abstract_object<blinded_balance_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_blinded_balance_object_type;
fc::ecc::commitment_type commitment;
asset_id_type asset_id;
authority owner;
};
struct by_asset;
struct by_owner;
struct by_commitment;
/**
* @ingroup object_index
*/
typedef multi_index_container<
blinded_balance_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_commitment>, member<blinded_balance_object, commitment_type, &blinded_balance_object::commitment> >
>
> blinded_balance_object_multi_index_type;
typedef generic_index<blinded_balance_object, blinded_balance_object_multi_index_type> blinded_balance_index;
class transfer_to_blind_evaluator : public evaluator<transfer_to_blind_evaluator>
{
public:
typedef transfer_to_blind_operation operation_type;
void_result do_evaluate( const transfer_to_blind_operation& o );
void_result do_apply( const transfer_to_blind_operation& o ) ;
};
class transfer_from_blind_evaluator : public evaluator<transfer_from_blind_evaluator>
{
public:
typedef transfer_from_blind_operation operation_type;
void_result do_evaluate( const transfer_from_blind_operation& o );
void_result do_apply( const transfer_from_blind_operation& o ) ;
};
class blind_transfer_evaluator : public evaluator<blind_transfer_evaluator>
{
public:
typedef blind_transfer_operation operation_type;
void_result do_evaluate( const blind_transfer_operation& o );
void_result do_apply( const blind_transfer_operation& o ) ;
};
} } // namespace graphene::chain
FC_REFLECT( graphene::chain::blinded_balance_object, (commitment)(asset_id)(owner) )

View file

@ -143,6 +143,10 @@ namespace graphene { namespace chain {
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" )

View file

@ -83,6 +83,14 @@ namespace graphene { namespace chain {
result.push_back(k.first);
return result;
}
friend bool operator == ( const authority& a, const authority& b )
{
return (a.weight_threshold == b.weight_threshold) &&
(a.account_auths == b.account_auths) &&
(a.key_auths == b.key_auths) &&
(a.address_auths == b.address_auths);
}
uint32_t num_auths()const { return account_auths.size() + key_auths.size() + address_auths.size(); }
void clear() { account_auths.clear(); key_auths.clear(); }

View file

@ -0,0 +1,284 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*/
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
using fc::ecc::blind_factor_type;
/**
* @defgroup stealth Stealth Transfer
* @brief Operations related to stealth transfer of value
*
* Stealth Transfers enable users to maintain their finanical privacy against even
* though all transactions are public. Every account has three balances:
*
* 1. Public Balance - everyone can see the balance changes and the parties involved
* 2. Blinded Balance - everyone can see who is transacting but not the amounts involved
* 3. Stealth Balance - both the amounts and parties involved are obscured
*
* Account owners may set a flag that allows their account to receive(or not) transfers of these kinds
* Asset issuers can enable or disable the use of each of these types of accounts.
*
* Using the "temp account" which has no permissions required, users can transfer a
* stealth balance to the temp account and then use the temp account to register a new
* account. In this way users can use stealth funds to create anonymous accounts with which
* they can perform other actions that are not compatible with blinded balances (such as market orders)
*
* @section referral_program Referral Progam
*
* Stealth transfers that do not specify any account id cannot pay referral fees so 100% of the
* transaction fee is paid to the network.
*
* @section transaction_fees Fees
*
* Stealth transfers can have an arbitrarylly large size and therefore the transaction fee for
* stealth transfers is based purley on the data size of the transaction.
*/
///@{
/**
* @ingroup stealth
* This data is encrypted and stored in the
* encrypted memo portion of the blind output.
*/
struct blind_memo
{
account_id_type from;
share_type amount;
string message;
/** set to the first 4 bytes of the shared secret
* used to encrypt the memo. Used to verify that
* decryption was successful.
*/
uint32_t check= 0;
};
/**
* @ingroup stealth
*/
struct blind_input
{
fc::ecc::commitment_type commitment;
/** provided to maintain the invariant that all authority
* required by an operation is explicit in the operation. Must
* match blinded_balance_id->owner
*/
authority owner;
};
/**
* When sending a stealth tranfer we assume users are unable to scan
* the full blockchain; therefore, payments require confirmation data
* to be passed out of band. We assume this out-of-band channel is
* not secure and therefore the contents of the confirmation must be
* encrypted.
*/
struct stealth_confirmation
{
struct memo_data
{
public_key_type from;
asset amount;
fc::ecc::commitment_type commitment;
uint32_t check = 0;
};
/**
* Packs *this then encodes as base58 encoded string.
*/
operator string()const;
/**
* Unpacks from a base58 string
*/
stealth_confirmation( const std::string& base58 );
stealth_confirmation(){}
public_key_type one_time_key;
vector<char> encrypted_memo;
};
/**
* @class blind_output
* @brief Defines data required to create a new blind commitment
* @ingroup stealth
*
* The blinded output that must be proven to be greater than 0
*/
struct blind_output
{
fc::ecc::commitment_type commitment;
/** only required if there is more than one blind output */
range_proof_type range_proof;
authority owner;
optional<stealth_confirmation> stealth_memo;
};
/**
* @class transfer_to_blind_operation
* @ingroup stealth
* @brief Converts public account balance to a blinded or stealth balance
*/
struct transfer_to_blind_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kb = 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;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(from);
for( const auto& out : outputs )
add_authority_accounts( i, out.owner );
}
};
/**
* @ingroup stealth
* @brief Converts blinded/stealth balance to a public account balance
*/
struct transfer_from_blind_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
};
asset fee;
asset amount;
account_id_type to;
blind_factor_type blinding_factor;
vector<blind_input> inputs;
account_id_type fee_payer()const { return GRAPHENE_TEMP_ACCOUNT; }
void validate()const;
void get_impacted_accounts( flat_set<account_id_type>& i )const
{
i.insert(to);
for( const auto& in : inputs )
add_authority_accounts( i, in.owner );
}
void get_required_authorities( vector<authority>& a )const
{
for( const auto& in : inputs )
a.push_back( in.owner );
}
};
/**
* @ingroup stealth
* @brief Transfers from blind to blind
*
* There are two ways to transfer value while maintaining privacy:
* 1. account to account with amount kept secret
* 2. stealth transfers with amount sender/receiver kept secret
*
* When doing account to account transfers, everyone with access to the
* memo key can see the amounts, but they will not have access to the funds.
*
* When using stealth transfers the same key is used for control and reading
* the memo.
*
* This operation is more expensive than a normal transfer and has
* a fee proportional to the size of the operation.
*
* All assets in a blind transfer must be of the same type: fee.asset_id
* The fee_payer is the temp account and can be funded from the blinded values.
*
* Using this operation you can transfer from an account and/or blinded balances
* to an account and/or blinded balances.
*
* Stealth Transfers:
*
* Assuming Receiver has key pair R,r and has shared public key R with Sender
* Assuming Sender has key pair S,s
* Generate one time key pair O,o as s.child(nonce) where nonce can be inferred from transaction
* Calculate secret V = o*R
* blinding_factor = sha256(V)
* memo is encrypted via aes of V
* owner = R.child(sha256(blinding_factor))
*
* Sender gives Receiver output ID to complete the payment.
*
* This process can also be used to send money to a cold wallet without having to
* pre-register any accounts.
*
* Outputs are assigned the same IDs as the inputs until no more input IDs are available,
* in which case a the return value will be the *first* ID allocated for an output. Additional
* output IDs are allocated sequentially thereafter. If there are fewer outputs than inputs
* then the input IDs are freed and never used again.
*/
struct blind_transfer_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 5*GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to register the cheapest non-free account
uint32_t price_per_output = 5*GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kb = 5*GRAPHENE_BLOCKCHAIN_PRECISION;
};
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_impacted_accounts( flat_set<account_id_type>& i )const
{
for( const auto& in : inputs )
add_authority_accounts( i, in.owner );
for( const auto& out : outputs )
add_authority_accounts( i, out.owner );
}
void get_required_authorities( vector<authority>& a )const
{
for( const auto& in : inputs )
a.push_back( in.owner );
}
};
///@} endgroup stealth
} } // graphene::chain
FC_REFLECT( graphene::chain::stealth_confirmation,
(one_time_key)(encrypted_memo) )
FC_REFLECT( graphene::chain::stealth_confirmation::memo_data,
(from)(amount)(commitment)(check) );
FC_REFLECT( graphene::chain::blind_memo,
(from)(amount)(message)(check) )
FC_REFLECT( graphene::chain::blind_input,
(commitment)(owner) )
FC_REFLECT( graphene::chain::blind_output,
(commitment)(range_proof)(owner)(stealth_memo) )
FC_REFLECT( graphene::chain::transfer_to_blind_operation,
(fee)(amount)(from)(blinding_factor)(outputs) )
FC_REFLECT( graphene::chain::transfer_from_blind_operation,
(fee)(amount)(to)(blinding_factor)(inputs) )
FC_REFLECT( graphene::chain::blind_transfer_operation,
(fee)(inputs)(outputs) )
FC_REFLECT( graphene::chain::transfer_to_blind_operation::fee_parameters_type, (fee)(price_per_output)(price_per_kb) )
FC_REFLECT( graphene::chain::transfer_from_blind_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::blind_transfer_operation::fee_parameters_type, (fee)(price_per_output)(price_per_kb) )

View file

@ -14,6 +14,7 @@
#include <graphene/chain/protocol/withdraw_permission.hpp>
#include <graphene/chain/protocol/witness.hpp>
#include <graphene/chain/protocol/worker.hpp>
#include <graphene/chain/protocol/confidential.hpp>
namespace graphene { namespace chain {
@ -59,7 +60,10 @@ namespace graphene { namespace chain {
custom_operation,
assert_operation,
balance_claim_operation,
override_transfer_operation
override_transfer_operation,
transfer_to_blind_operation,
blind_transfer_operation,
transfer_from_blind_operation
> operation;
/// @} // operations group

View file

@ -84,10 +84,11 @@ namespace graphene { namespace chain {
override_authority = 0x04, /**< issuer may transfer asset back to himself */
transfer_restricted = 0x08, /**< require the issuer to be one party to every transfer */
disable_force_settle = 0x10, /**< disable force settling */
global_settle = 0x20 /**< allow the bitasset issuer to force a global settling -- this may be set in permissions, but not flags */
global_settle = 0x20, /**< allow the bitasset issuer to force a global settling -- this may be set in permissions, but not flags */
disable_confidential = 0x40 /**< allow the asset to be used with confidential transactions */
};
const static uint32_t ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_force_settle|global_settle;
const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted;
const static uint32_t ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_force_settle|global_settle|disable_confidential;
const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential;
enum reserved_spaces
{
@ -138,7 +139,8 @@ namespace graphene { namespace chain {
impl_transaction_object_type,
impl_block_summary_object_type,
impl_account_transaction_history_object_type,
impl_witness_schedule_object_type
impl_witness_schedule_object_type,
impl_blinded_balance_object_type
};
enum meta_info_object_type
@ -386,6 +388,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_block_summary_object_type)
(impl_account_transaction_history_object_type)
(impl_witness_schedule_object_type)
(impl_blinded_balance_object_type)
)
FC_REFLECT_ENUM( graphene::chain::meta_info_object_type, (meta_account_object_type)(meta_asset_object_type) )
@ -417,4 +420,4 @@ FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::witness_schedule_id_type )
FC_REFLECT( graphene::chain::void_t, )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (charge_market_fee)(white_list)(transfer_restricted)(override_authority)(disable_force_settle)(global_settle) )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (charge_market_fee)(white_list)(transfer_restricted)(override_authority)(disable_force_settle)(global_settle)(disable_confidential) )

View file

@ -0,0 +1,122 @@
#include <graphene/chain/protocol/confidential.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/chain/database.hpp>
namespace graphene { namespace chain {
void transfer_to_blind_operation::validate()const
{
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( amount.amount > 0 );
vector<commitment_type> in;
vector<commitment_type> out(outputs.size());
int64_t net_public = amount.amount.value;
for( uint32_t i = 0; i < out.size(); ++i )
{
out[i] = outputs[i].commitment;
/// require all outputs to be sorted prevents duplicates AND prevents implementations
/// from accidentally leaking information by how they arrange commitments.
if( i > 0 ) FC_ASSERT( out[i-1] < out[i], "all outputs must be sorted by commitment id" );
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
{
return k.fee + outputs.size() * k.price_per_output + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kb );
}
void transfer_from_blind_operation::validate()const
{
FC_ASSERT( amount.amount > 0 );
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( inputs.size() > 0 );
FC_ASSERT( amount.asset_id == fee.asset_id );
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());
vector<commitment_type> out(outputs.size());
int64_t net_public = fee.amount.value;//from_amount.value - to_amount.value;
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] );
}
for( uint32_t i = 0; i < out.size(); ++i )
{
out[i] = outputs[i].commitment;
if( i > 0 ) FC_ASSERT( out[i-1] < out[i] );
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_CAPTURE_AND_RETHROW( (*this) ) }
share_type blind_transfer_operation::calculate_fee( const fee_parameters_type& k )const
{
return k.fee + outputs.size() * k.price_per_output + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kb );;
}
} } // graphene::chain

View file

@ -192,7 +192,7 @@ void database_fixture::verify_asset_supplies( const database& db )
}
BOOST_CHECK_EQUAL( core_in_orders.value , reported_core_in_orders.value );
BOOST_CHECK_EQUAL( total_balances[asset_id_type()].value , core_asset_data.current_supply.value );
BOOST_CHECK_EQUAL( total_balances[asset_id_type()].value , core_asset_data.current_supply.value - core_asset_data.confidential_supply.value);
// wlog("*** End asset supply verification ***");
}

View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#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 OutB = 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};
trx.sign( dan_private_key );
db.push_transaction(trx);
trx.signatures.clear();
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};
trx.sign( 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.signatures.clear();
db.push_transaction(trx);
BOOST_REQUIRE_EQUAL( get_balance( nathan, core ), 750-300-10-10 );
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()