diff --git a/.gitignore b/.gitignore index 2b57e790..7d1ac33e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.a *.sw* +*.cmake CMakeCache.txt CMakeFiles Makefile @@ -12,4 +13,10 @@ tests/app_test tests/chain_bench tests/chain_test -/doxygen +doxygen + +programs/cli_wallet/cli_wallet +programs/js_operation_serializer/js_operation_serializer +programs/witness_node/witness_node +tests/intense_test +tests/performance_test diff --git a/libraries/chain/include/graphene/chain/blinded_balance_object.hpp b/libraries/chain/include/graphene/chain/blinded_balance_object.hpp new file mode 100644 index 00000000..4d4ea044 --- /dev/null +++ b/libraries/chain/include/graphene/chain/blinded_balance_object.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + */ +#pragma once + +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 + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = blinded_balance_object_type; + + fc::ecc::commitment_type commitment; + asset_id_type asset_id; + optional owner; + address key; + }; + + struct by_asset; + struct by_owner; + struct by_commitment; + + /** + * @ingroup object_index + */ + typedef multi_index_container< + blinded_balance_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + hashed_unique< tag, member >, + ordered_non_unique< tag, member >, + ordered_non_unique< tag, member > + > + > blinded_balance_object_multi_index_type; + typedef generic_index balance_index; + + +} } // graphene::chain + +FC_REFLECT( graphene::chain::blinded_balance_object, (commitment)(asset_id)(last_update_block_num)(owner) ) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 74ae5163..630ccfef 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -112,3 +112,13 @@ #define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 ) #define GRAPHENE_LEGACY_NAME_IMPORT_PERIOD 3000000 /** 3 million blocks */ +/** + * Reserved Account IDs with special meaning + */ +///@{ +#define GRAPHENE_GENESIS_ACCOUNT (graphene::chain::account_id_type(0)) +#define GRAPHENE_WITNESS_ACCOUNT (graphene::chain::account_id_type(1)) +#define GRAPHENE_DELEGATE_ACCOUNT (graphene::chain::account_id_type(2)) +#define GRAPHENE_NULL_ACCOUNT (graphene::chain::account_id_type(3)) +#define GRAPHENE_TEMP_ACCOUNT (graphene::chain::account_id_type(4)) +///@} diff --git a/libraries/chain/include/graphene/chain/key_object.hpp b/libraries/chain/include/graphene/chain/key_object.hpp index 3a9d07da..6500e34f 100644 --- a/libraries/chain/include/graphene/chain/key_object.hpp +++ b/libraries/chain/include/graphene/chain/key_object.hpp @@ -1,19 +1,6 @@ /* * 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. */ #pragma once #include diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 9353568b..4d40090e 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -275,6 +275,7 @@ namespace graphene { namespace chain { void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); } }; + /** * @brief Create a witness object, as a bid to hold a witness position on the network. * @ingroup operations @@ -445,6 +446,114 @@ namespace graphene { namespace chain { } }; + /** + * 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. + * + * The sum of the blind_inputs + public inputs - public outputs - blind_outputs - fee must + * be 0. + * + * 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 + { + /** + * 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; + }; + + 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 + */ + static_variant owner; + }; + + /** + * 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; + static_variant owner; + public_key_type one_time_key; + /** encrypted via aes with shared secret derived from + * one_time_key and (owner or owner.memo_key) + */ + vector encrypted_memo; + }; + + asset fee; + account_id_type fee_payer_id; + account_id_type from_account; + /** unblinded amount transfered from from account */ + share_type from_amount; + account_id_type to_account; + /** unblinded amount transfered to to_account */ + share_type to_amount; + string to_account_name; + optional
to_address; + vector inputs; + vector outputs; + + account_id_type fee_payer()const; + void get_required_auth( flat_set& active_auth_set, + flat_set& )const; + void validate()const; + share_type calculate_fee( const fee_schedule_type& k )const; + void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const; + }; + /** * @ingroup operations */ @@ -1482,6 +1591,7 @@ namespace graphene { namespace chain { } }; + /** * @defgroup workers The Blockchain Worker System * @ingroup operations @@ -1846,4 +1956,17 @@ FC_REFLECT( graphene::chain::worker_create_operation, FC_REFLECT( graphene::chain::custom_operation, (fee)(payer)(required_auths)(id)(data) ) FC_REFLECT( graphene::chain::void_result, ) +FC_REFLECT( graphene::chain::blind_transfer_operation::blind_memo, + (from)(amount)(message)(check) ) +FC_REFLECT( graphene::chain::blind_transfer_operation::blind_input, + (commitment)(owner) ) +FC_REFLECT( graphene::chain::blind_transfer_operation::blind_output, + (commitment)(range_proof)(owner)(one_time_key)(encrypted_memo) ) +FC_REFLECT( graphene::chain::blind_transfer_operation, + (fee)(fee_payer_id) + (from_account)(from_amount) + (to_account)(to_account_name)(to_address)(to_amount) ) + FC_REFLECT_TYPENAME( graphene::chain::operation ) + + diff --git a/libraries/chain/include/graphene/chain/transaction.hpp b/libraries/chain/include/graphene/chain/transaction.hpp index 7f947b70..62322fd8 100644 --- a/libraries/chain/include/graphene/chain/transaction.hpp +++ b/libraries/chain/include/graphene/chain/transaction.hpp @@ -1,19 +1,6 @@ /* * 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. */ #pragma once #include @@ -140,10 +127,17 @@ namespace graphene { namespace chain { : transaction(trx){} void sign( key_id_type id, const private_key_type& key ); + flat_map signatures; + /** some operations may depend only upon a signature and not + * require account approval. This allows those extra signatures + * to be added to the transaction. + */ + flat_map extra_signatures; + /// Removes all operations and signatures - void clear() { operations.clear(); signatures.clear(); } + void clear() { operations.clear(); signatures.clear(); extra_signatures.clear(); } }; /** @@ -175,6 +169,8 @@ namespace graphene { namespace chain { } } FC_REFLECT( graphene::chain::transaction, (ref_block_num)(ref_block_prefix)(relative_expiration)(operations) ) -FC_REFLECT_DERIVED( graphene::chain::signed_transaction, (graphene::chain::transaction), (signatures) ) +FC_REFLECT_DERIVED( graphene::chain::signed_transaction, + (graphene::chain::transaction), + (signatures)(extra_signatures) ) FC_REFLECT_DERIVED( graphene::chain::processed_transaction, (graphene::chain::signed_transaction), (operation_results) ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 2fe3e855..0a9fb29b 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -63,6 +63,9 @@ namespace graphene { namespace chain { using fc::flat_map; using fc::flat_set; using fc::static_variant; + using fc::ecc::range_proof_type; + using fc::ecc::range_proof_info; + using fc::ecc::commitment_type; typedef fc::ecc::private_key private_key_type; @@ -131,6 +134,7 @@ namespace graphene { namespace chain { global_settle_fee_type, worker_create_fee_type, ///< the cost to create a new worker worker_delete_fee_type, ///< the cost to delete a worker + blind_transfer_fee_type, ///< the cost per killobyte of the blinded transfer size FEE_TYPE_COUNT ///< Sentry value which contains the number of different fee types }; @@ -272,6 +276,8 @@ namespace graphene { namespace chain { typedef fc::sha224 secret_hash_type; typedef uint16_t weight_type; + + /** * @brief An ID for some votable object * @@ -417,6 +423,7 @@ namespace graphene { namespace chain { uint32_t global_settle_fee; uint32_t worker_create_fee; ///< the cost to create a new worker uint32_t worker_delete_fee; ///< the cost to delete a worker + uint32_t blind_transfer_fee; ///< cost per kb for blind transfers }; @@ -639,6 +646,7 @@ FC_REFLECT_ENUM( graphene::chain::fee_type, (global_settle_fee_type) (worker_create_fee_type) (worker_delete_fee_type) + (blind_transfer_fee_type) (FEE_TYPE_COUNT) ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index ddd63008..c30ff546 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -1,19 +1,6 @@ /* * 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 #include @@ -912,4 +899,63 @@ memo_message memo_message::deserialize(const string& serial) return result; } +/** + * 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 fee_payer_id; +} + +void blind_transfer_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const +{ + active_auth_set.insert( fee_payer_id ); + active_auth_set.insert( from_account ); + for( auto input : inputs ) + { + if( input.owner.which() == static_variant::tag::value ) + active_auth_set.insert( input.owner.get() ); + } +} + +/** + * This method can be computationally intensive because it verifies that input commitments - output commitments add up to 0 + */ +void blind_transfer_operation::validate()const +{ + vector in(inputs.size()); + vector out(outputs.size()); + int64_t net_public = from_amount.value - to_amount.value; + for( uint32_t i = 0; i < in.size(); ++i ) in[i] = inputs[i].commitment; + for( uint32_t i = 0; i < out.size(); ++i ) out[i] = outputs[i].commitment; + FC_ASSERT( in.size() + out.size() || net_public == 0 ); + if( fee_payer_id == GRAPHENE_TEMP_ACCOUNT ) net_public -= fee.amount.value; + FC_ASSERT( fc::ecc::verify_sum( in, out, net_public ) ); + + if( outputs.size() > 1 ) + { + for( auto out : outputs ) + { + auto info = fc::ecc::range_get_info( out.range_proof ); + FC_ASSERT( info.min_value >= 0 ); + FC_ASSERT( info.max_value <= GRAPHENE_MAX_SHARE_SUPPLY ); + } + } +} + +share_type blind_transfer_operation::calculate_fee( const fee_schedule_type& k )const +{ + auto size = 1024 + fc::raw::pack_size(*this); + return (k.blind_transfer_fee * size)/1024; +} + +void blind_transfer_operation::get_balance_delta( balance_accumulator& acc, + const operation_result& result)const +{ + acc.adjust( fee_payer(), -fee ); + acc.adjust( from_account, asset(-from_amount,fee.asset_id) ); + acc.adjust( to_account, asset(to_amount,fee.asset_id) ); +} + } } // namespace graphene::chain diff --git a/libraries/fc b/libraries/fc index 1bbb748c..dde8ed9d 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 1bbb748c4ebbaaf42440cec220562c8c2027cb80 +Subproject commit dde8ed9d7ab49807f2556488c0815f3741b11e00