From 1e2fafe9c3ebfcf6a41fe389984b0c1d45dca439 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 15 Jun 2015 14:45:55 -0400 Subject: [PATCH 1/9] Remove confidential transfer operations --- .../graphene/chain/blinded_balance_object.hpp | 58 ------ .../include/graphene/chain/operations.hpp | 189 ------------------ libraries/chain/operations.cpp | 122 ----------- 3 files changed, 369 deletions(-) delete mode 100644 libraries/chain/include/graphene/chain/blinded_balance_object.hpp diff --git a/libraries/chain/include/graphene/chain/blinded_balance_object.hpp b/libraries/chain/include/graphene/chain/blinded_balance_object.hpp deleted file mode 100644 index 1a0db331..00000000 --- a/libraries/chain/include/graphene/chain/blinded_balance_object.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 - -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; - static_variant 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, member< object, object_id_type, &object::id > >, - hashed_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/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 0484cb95..d53b649a 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -465,182 +465,6 @@ namespace graphene { namespace chain { acc.adjust( to, amount ); } }; - /** - * @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 - every 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 - */ - static_variant owner; - }; - - /** - * @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; - 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; - }; - - /** - * @class transfer_to_blind_operation - * @ingroup stealth - * @brief Converts public account balance to a blinded or stealth balance - */ - struct transfer_to_blind_operation - { - asset fee; - asset amount; - account_id_type from; - 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 stealth - * @brief Converts blinded/stealth balance to a public account balance - */ - struct transfer_from_blind_operation - { - asset fee; - asset amount; - account_id_type to; - vector inputs; - - 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 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 - { - - asset fee; - account_id_type fee_payer_id; - 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; - }; - ///@} endgroup stealth /** * @ingroup operations @@ -2044,18 +1868,5 @@ 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_memo, - (from)(amount)(message)(check) ) -FC_REFLECT( graphene::chain::blind_input, - (commitment)(owner) ) -FC_REFLECT( graphene::chain::blind_output, - (commitment)(range_proof)(owner)(one_time_key)(encrypted_memo) ) -FC_REFLECT( graphene::chain::transfer_to_blind_operation, - (fee)(amount)(from)(outputs) ) -FC_REFLECT( graphene::chain::transfer_from_blind_operation, - (fee)(amount)(to)(inputs) ) -FC_REFLECT( graphene::chain::blind_transfer_operation, - (fee)(fee_payer_id)(inputs)(outputs) ) - FC_REFLECT_TYPENAME( graphene::chain::operation ) FC_REFLECT_TYPENAME( fc::flat_set ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index e09714f8..8805895f 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -935,126 +935,4 @@ share_type account_upgrade_operation::calculate_fee(const fee_schedule_type& k) return k.membership_annual_fee; } -account_id_type transfer_to_blind_operation::fee_payer()const -{ - // TODO - return from; -} - -void transfer_to_blind_operation::get_required_auth( flat_set& active_auth_set, flat_set& )const -{ - // TODO -} - -void transfer_to_blind_operation::validate()const -{ - // TODO -} - -share_type transfer_to_blind_operation::calculate_fee( const fee_schedule_type& k )const -{ - // TODO - return 0; -} - -void transfer_to_blind_operation::get_balance_delta( balance_accumulator& acc, const operation_result& result )const -{ - // TODO -} - -account_id_type transfer_from_blind_operation::fee_payer()const -{ - // TODO - return to; -} - -void transfer_from_blind_operation::get_required_auth( flat_set& active_auth_set, flat_set& )const -{ - // TODO -} - -void transfer_from_blind_operation::validate()const -{ - // TODO -} - -share_type transfer_from_blind_operation::calculate_fee( const fee_schedule_type& k )const -{ - // TODO - return 0; -} - -void transfer_from_blind_operation::get_balance_delta( balance_accumulator& acc, const operation_result& result )const -{ - // TODO -} - -/** - * 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 -{ - // TODO - return fee_payer_id; -} - -void blind_transfer_operation::get_required_auth( flat_set& active_auth_set, flat_set& )const -{ - // TODO - /* - 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 -{ - // TODO - /* - 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 -{ - // TODO - return 0; -} - -void blind_transfer_operation::get_balance_delta( balance_accumulator& acc, const operation_result& result)const -{ - // TODO - /* - 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 From 9b3220f13afa40b8d4b72cb1f681c63dffe01aad Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 15 Jun 2015 14:59:52 -0400 Subject: [PATCH 2/9] Remove file write operation --- libraries/chain/db_init.cpp | 2 - .../include/graphene/chain/file_object.hpp | 117 ------------------ .../include/graphene/chain/operations.hpp | 78 ------------ .../chain/include/graphene/chain/types.hpp | 7 -- libraries/chain/operations.cpp | 14 --- .../account_history_plugin.cpp | 3 - programs/js_operation_serializer/main.cpp | 17 ++- 7 files changed, 8 insertions(+), 230 deletions(-) delete mode 100644 libraries/chain/include/graphene/chain/file_object.hpp diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 7125932c..1e1d8fac 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -121,7 +120,6 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); add_index< primary_index > >(); add_index< primary_index >(); diff --git a/libraries/chain/include/graphene/chain/file_object.hpp b/libraries/chain/include/graphene/chain/file_object.hpp deleted file mode 100644 index 33fc902a..00000000 --- a/libraries/chain/include/graphene/chain/file_object.hpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 - -namespace graphene { namespace chain { - - - /** - * @brief provides for persistant storage of arbitrary data - * - * Smart contracts need data to be stored persistantly that can be shared with - * other smart contracts. There is a cost associated with storing data, especially if - * that data will be kept in RAM. - * - * File objects allow smart contracts to interact with persistant storage much like - * traditional programs interact with files on disk. The cost of accessing a file - * object to modify it is much higher than the cost to simply read it because the - * the database must make a backup of the file for the undo history in the event - * of a blockchain reorganization or failure in evaluation. For this reason files - * are limited to 2^16 bytes and smart contracts will have to use multiple files if - * they need to store additional data. - * - * Every file has an automatic expiration date at which point in time it will be - * deleted unless a fee is paid to extend its life time. - * - * The contents of all files are public, but not to scripts. A smart contract attempting - * to access the contents of a file must have permission to read the file. The purpose - * of this restriction is to help users monetize the trust associated with publishing - * data. Anyone could re-publish the data under a new file, but the trust in the - * quality of the data would not be the same as the original file. - */ - class file_object : public graphene::db::annotated_object - { - public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = file_object_type; - - /** - * @brief sets bits that control the permissions granted to smart contracts regarding this data. - */ - enum permision_flags - { - owner_read = 0x01, - owner_write = 0x02, - group_read = 0x04, - group_write = 0x08, - all_read = 0x10, - execute = 0x20 ///< set if data contains virtual machine instructions - }; - - /** - * The owner can access this file based upon the @ref permissions flags - * - * @note - if the owner removes write permission from himself then the file - * will be imutable thereafter. - */ - account_id_type owner; - /** any account that has been white listed by this group can read/write - * @ref data based upon the @ref permissions flags. - */ - account_id_type group; - /** - * Bits set according to @ref permission_flags - */ - uint8_t permissions = owner_read | owner_write | all_read; - - /** - * Files consume memory and thus are cleaned up unless a fee is paid to - * keep them alive. - */ - time_point_sec expiration; - - /** - * The maximum data size for a file is 2^16 bytes so that the - * undo history doesn't have to backup larger files. If a smart contract - * requires more data then it can create more file objects. - */ - vector data; - }; - - struct by_expiration; - struct by_owner; - struct by_group; - /** - * @ingroup object_index - */ - typedef multi_index_container< - file_object, - indexed_by< - hashed_unique< tag, member< object, object_id_type, &object::id > >, - hashed_non_unique< tag, member< file_object, account_id_type, &file_object::owner > >, - hashed_non_unique< tag, member< file_object, account_id_type, &file_object::group > >, - ordered_non_unique< tag, member > - > - > file_object_multi_index_type; - - typedef generic_index file_object_index; - -} } - -FC_REFLECT_ENUM( graphene::chain::file_object::permision_flags, (owner_read)(owner_write)(group_read)(group_write)(all_read)(execute) ) -FC_REFLECT_DERIVED( graphene::chain::file_object, (graphene::db::object), (owner)(group)(permissions)(expiration)(data) ) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index d53b649a..5b3cf85b 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -1248,81 +1248,6 @@ namespace graphene { namespace chain { } }; - /** - * @brief create/update the contents of a file. - * - * Any account may pay a fee and write no data to extend the lease seconds - * on a file. - * - * If the file size increasees, the current lease_seconds will be adjusted downward to maintain - * the same byte-days-leased. Then any new leased seconds will be added based upon the - * new file size. - * - * @see file_object - */ - struct file_write_operation - { - public: - /** - * The fee charges is proportional to @ref file_size * @ref lease_seconds - */ - asset fee; - /** - * THe account that is paying the update fee - */ - account_id_type payer; - - /** file_id 0 indicates a new file should be created */ - file_id_type file_id; - - /** may read/write accoding to flags, write permission is required to change owner/group/flags */ - account_id_type owner; - - /** may read/write according fo flags, but may not update flags or owner */ - account_id_type group; - - /** - * Must be less than or equal to 0x2f - */ - uint8_t flags = 0; - - /** - * If the file doesn't exist, it will be intialized to file_size with 0 - * before writing data. - * - * @pre data.size() + offset <= 2^16 - */ - uint16_t offset = 0; - vector data; - - /** - * The length of time to extend the lease on the file, must be less - * than 10 years. - */ - uint32_t lease_seconds = 0; - - /** - * File size must be greater than 0 - */ - uint16_t file_size = 0; - - /** - * If file_id is not 0, then precondition checksum verifies that - * the file contents are as expected prior to writing data. - */ - optional precondition_checksum; - - account_id_type fee_payer()const { return payer; } - void get_required_auth(flat_set& active_auth_set, flat_set&)const { active_auth_set.insert(fee_payer()); } - 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 - { - acc.adjust( fee_payer(), -fee ); - } - }; - /** * @ingroup operations * @@ -1617,7 +1542,6 @@ namespace graphene { namespace chain { withdraw_permission_delete_operation, fill_order_operation, global_parameters_update_operation, - file_write_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, bond_create_offer_operation, @@ -1852,8 +1776,6 @@ FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account) (withdrawal_permission) ) -FC_REFLECT( graphene::chain::file_write_operation, (fee)(payer)(file_id)(owner)(group)(flags)(offset)(data)(lease_seconds)(file_size)(precondition_checksum) ) - FC_REFLECT( graphene::chain::bond_create_offer_operation, (fee)(creator)(offer_to_borrow)(amount)(min_match)(collateral_rate)(min_loan_period_sec)(loan_period_sec)(interest_apr) ) FC_REFLECT( graphene::chain::bond_cancel_offer_operation, (fee)(creator)(offer_id)(refund) ) FC_REFLECT( graphene::chain::bond_accept_offer_operation, (fee)(claimer)(lender)(borrower)(offer_id)(amount_borrowed)(amount_collateral) ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 4cad1f0f..92c00b3a 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -116,7 +116,6 @@ namespace graphene { namespace chain { withdraw_permission_object_type, bond_offer_object_type, bond_object_type, - file_object_type, vesting_balance_object_type, worker_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types @@ -163,7 +162,6 @@ namespace graphene { namespace chain { class withdraw_permission_object; class bond_object; class bond_offer_object; - class file_object; class vesting_balance_object; class witness_schedule_object; class worker_object; @@ -183,7 +181,6 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type; typedef object_id< protocol_ids, bond_offer_object_type, bond_offer_object> bond_offer_id_type; typedef object_id< protocol_ids, bond_object_type, bond_object> bond_id_type; - typedef object_id< protocol_ids, file_object_type, file_object> file_id_type; typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; @@ -386,7 +383,6 @@ namespace graphene { namespace chain { uint32_t cancel_bond_offer_fee; uint32_t accept_bond_offer_fee; uint32_t claim_bond_collateral_fee; - uint32_t file_storage_fee_per_day; ///< the cost of leasing a file with 2^16 bytes for 1 day uint32_t vesting_balance_create_fee; uint32_t vesting_balance_withdraw_fee; uint32_t global_settle_fee; @@ -509,7 +505,6 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (withdraw_permission_object_type) (bond_offer_object_type) (bond_object_type) - (file_object_type) (vesting_balance_object_type) (worker_object_type) (OBJECT_TYPE_COUNT) @@ -568,7 +563,6 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (cancel_bond_offer_fee) (accept_bond_offer_fee) (claim_bond_collateral_fee) - (file_storage_fee_per_day) (vesting_balance_create_fee) (vesting_balance_withdraw_fee) (global_settle_fee) @@ -617,7 +611,6 @@ FC_REFLECT_TYPENAME( graphene::chain::operation_history_id_type ) FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type ) FC_REFLECT_TYPENAME( graphene::chain::bond_offer_id_type ) FC_REFLECT_TYPENAME( graphene::chain::bond_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::file_id_type ) FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type ) FC_REFLECT_TYPENAME( graphene::chain::worker_id_type ) FC_REFLECT_TYPENAME( graphene::chain::relative_key_id_type ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 8805895f..86073a71 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -717,20 +717,6 @@ void asset_update_feed_producers_operation::validate() const { FC_ASSERT( fee.amount >= 0 ); } -void file_write_operation::validate()const -{ - FC_ASSERT( uint32_t(offset) + data.size() <= file_size ); - FC_ASSERT( flags <= 0x2f ); - FC_ASSERT( file_size > 0 ); - /** less than 10 years to prevent overflow of 64 bit numbers in the value*lease_seconds*file_size calculation */ - FC_ASSERT( lease_seconds < 60*60*24*365*10 ); -} - -share_type file_write_operation::calculate_fee( const fee_schedule_type& k )const -{ - return ((((k.file_storage_fee_per_day * lease_seconds)/(60*60*24))*file_size)/0xff) + ((data.size() * k.data_fee)/1024); -} - void vesting_balance_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const { diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 6ecc2c4f..77badee4 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -173,9 +173,6 @@ struct operation_get_impacted_accounts } void operator()( const asset_create_operation& o )const { } - void operator()( const file_write_operation& o )const { - _impacted.insert( o.owner ); - } void operator()( const asset_update_operation& o )const { if( o.new_issuer ) diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 048ed44a..deefe0a1 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -69,7 +68,7 @@ string remove_namespace( string str ) template void generate_serializer(); -template +template void register_serializer(); @@ -96,7 +95,7 @@ struct js_name> }; template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; -template struct js_name< fc::optional > { static std::string name(){ return "optional " + js_name::name(); } }; +template struct js_name< fc::optional > { static std::string name(){ return "optional " + js_name::name(); } }; template<> struct js_name< object_id_type > { static std::string name(){ return "object_id_type"; } }; template struct js_name< fc::flat_set > { static std::string name(){ return "set " + js_name::name(); } }; template struct js_name< std::vector > { static std::string name(){ return "array " + js_name::name(); } }; @@ -115,8 +114,8 @@ template<> struct js_name< time_point_sec > { static std::string name(){ retu template struct js_name > { - static std::string name(){ - return "protocol_id_type \"" + remove_namespace(fc::get_typename::name()) + "\""; + static std::string name(){ + return "protocol_id_type \"" + remove_namespace(fc::get_typename::name()) + "\""; }; }; @@ -132,7 +131,7 @@ struct js_name< fc::flat_map > { static std::string name(){ return "map (" template struct js_sv_name; -template struct js_sv_name +template struct js_sv_name { static std::string name(){ return "\n " + js_name::name(); } }; template @@ -142,10 +141,10 @@ struct js_sv_name { static std::string name(){ return "\n " + js_nam template struct js_name< fc::static_variant > { - static std::string name( std::string n = ""){ + static std::string name( std::string n = ""){ static const std::string name = n; if( name == "" ) - return "static_variant [" + js_sv_name::name() + "\n]"; + return "static_variant [" + js_sv_name::name() + "\n]"; else return name; } }; @@ -290,7 +289,7 @@ class register_member_visitor } }; -template +template struct serializer { static_assert( fc::reflector::is_defined::value == reflected, "invalid template arguments" ); From 96efc4d838cf66b492e00a217de5773d061661fb Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 15 Jun 2015 15:00:16 -0400 Subject: [PATCH 3/9] Revert "Remove file write operation" This reverts commit 9b3220f13afa40b8d4b72cb1f681c63dffe01aad. --- libraries/chain/db_init.cpp | 2 + .../include/graphene/chain/file_object.hpp | 117 ++++++++++++++++++ .../include/graphene/chain/operations.hpp | 78 ++++++++++++ .../chain/include/graphene/chain/types.hpp | 7 ++ libraries/chain/operations.cpp | 14 +++ .../account_history_plugin.cpp | 3 + programs/js_operation_serializer/main.cpp | 17 +-- 7 files changed, 230 insertions(+), 8 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/file_object.hpp diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 1e1d8fac..7125932c 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); add_index< primary_index > >(); add_index< primary_index >(); diff --git a/libraries/chain/include/graphene/chain/file_object.hpp b/libraries/chain/include/graphene/chain/file_object.hpp new file mode 100644 index 00000000..33fc902a --- /dev/null +++ b/libraries/chain/include/graphene/chain/file_object.hpp @@ -0,0 +1,117 @@ +/* + * 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 + +namespace graphene { namespace chain { + + + /** + * @brief provides for persistant storage of arbitrary data + * + * Smart contracts need data to be stored persistantly that can be shared with + * other smart contracts. There is a cost associated with storing data, especially if + * that data will be kept in RAM. + * + * File objects allow smart contracts to interact with persistant storage much like + * traditional programs interact with files on disk. The cost of accessing a file + * object to modify it is much higher than the cost to simply read it because the + * the database must make a backup of the file for the undo history in the event + * of a blockchain reorganization or failure in evaluation. For this reason files + * are limited to 2^16 bytes and smart contracts will have to use multiple files if + * they need to store additional data. + * + * Every file has an automatic expiration date at which point in time it will be + * deleted unless a fee is paid to extend its life time. + * + * The contents of all files are public, but not to scripts. A smart contract attempting + * to access the contents of a file must have permission to read the file. The purpose + * of this restriction is to help users monetize the trust associated with publishing + * data. Anyone could re-publish the data under a new file, but the trust in the + * quality of the data would not be the same as the original file. + */ + class file_object : public graphene::db::annotated_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = file_object_type; + + /** + * @brief sets bits that control the permissions granted to smart contracts regarding this data. + */ + enum permision_flags + { + owner_read = 0x01, + owner_write = 0x02, + group_read = 0x04, + group_write = 0x08, + all_read = 0x10, + execute = 0x20 ///< set if data contains virtual machine instructions + }; + + /** + * The owner can access this file based upon the @ref permissions flags + * + * @note - if the owner removes write permission from himself then the file + * will be imutable thereafter. + */ + account_id_type owner; + /** any account that has been white listed by this group can read/write + * @ref data based upon the @ref permissions flags. + */ + account_id_type group; + /** + * Bits set according to @ref permission_flags + */ + uint8_t permissions = owner_read | owner_write | all_read; + + /** + * Files consume memory and thus are cleaned up unless a fee is paid to + * keep them alive. + */ + time_point_sec expiration; + + /** + * The maximum data size for a file is 2^16 bytes so that the + * undo history doesn't have to backup larger files. If a smart contract + * requires more data then it can create more file objects. + */ + vector data; + }; + + struct by_expiration; + struct by_owner; + struct by_group; + /** + * @ingroup object_index + */ + typedef multi_index_container< + file_object, + indexed_by< + hashed_unique< tag, member< object, object_id_type, &object::id > >, + hashed_non_unique< tag, member< file_object, account_id_type, &file_object::owner > >, + hashed_non_unique< tag, member< file_object, account_id_type, &file_object::group > >, + ordered_non_unique< tag, member > + > + > file_object_multi_index_type; + + typedef generic_index file_object_index; + +} } + +FC_REFLECT_ENUM( graphene::chain::file_object::permision_flags, (owner_read)(owner_write)(group_read)(group_write)(all_read)(execute) ) +FC_REFLECT_DERIVED( graphene::chain::file_object, (graphene::db::object), (owner)(group)(permissions)(expiration)(data) ) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 5b3cf85b..d53b649a 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -1248,6 +1248,81 @@ namespace graphene { namespace chain { } }; + /** + * @brief create/update the contents of a file. + * + * Any account may pay a fee and write no data to extend the lease seconds + * on a file. + * + * If the file size increasees, the current lease_seconds will be adjusted downward to maintain + * the same byte-days-leased. Then any new leased seconds will be added based upon the + * new file size. + * + * @see file_object + */ + struct file_write_operation + { + public: + /** + * The fee charges is proportional to @ref file_size * @ref lease_seconds + */ + asset fee; + /** + * THe account that is paying the update fee + */ + account_id_type payer; + + /** file_id 0 indicates a new file should be created */ + file_id_type file_id; + + /** may read/write accoding to flags, write permission is required to change owner/group/flags */ + account_id_type owner; + + /** may read/write according fo flags, but may not update flags or owner */ + account_id_type group; + + /** + * Must be less than or equal to 0x2f + */ + uint8_t flags = 0; + + /** + * If the file doesn't exist, it will be intialized to file_size with 0 + * before writing data. + * + * @pre data.size() + offset <= 2^16 + */ + uint16_t offset = 0; + vector data; + + /** + * The length of time to extend the lease on the file, must be less + * than 10 years. + */ + uint32_t lease_seconds = 0; + + /** + * File size must be greater than 0 + */ + uint16_t file_size = 0; + + /** + * If file_id is not 0, then precondition checksum verifies that + * the file contents are as expected prior to writing data. + */ + optional precondition_checksum; + + account_id_type fee_payer()const { return payer; } + void get_required_auth(flat_set& active_auth_set, flat_set&)const { active_auth_set.insert(fee_payer()); } + 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 + { + acc.adjust( fee_payer(), -fee ); + } + }; + /** * @ingroup operations * @@ -1542,6 +1617,7 @@ namespace graphene { namespace chain { withdraw_permission_delete_operation, fill_order_operation, global_parameters_update_operation, + file_write_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, bond_create_offer_operation, @@ -1776,6 +1852,8 @@ FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account) (withdrawal_permission) ) +FC_REFLECT( graphene::chain::file_write_operation, (fee)(payer)(file_id)(owner)(group)(flags)(offset)(data)(lease_seconds)(file_size)(precondition_checksum) ) + FC_REFLECT( graphene::chain::bond_create_offer_operation, (fee)(creator)(offer_to_borrow)(amount)(min_match)(collateral_rate)(min_loan_period_sec)(loan_period_sec)(interest_apr) ) FC_REFLECT( graphene::chain::bond_cancel_offer_operation, (fee)(creator)(offer_id)(refund) ) FC_REFLECT( graphene::chain::bond_accept_offer_operation, (fee)(claimer)(lender)(borrower)(offer_id)(amount_borrowed)(amount_collateral) ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 92c00b3a..4cad1f0f 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -116,6 +116,7 @@ namespace graphene { namespace chain { withdraw_permission_object_type, bond_offer_object_type, bond_object_type, + file_object_type, vesting_balance_object_type, worker_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types @@ -162,6 +163,7 @@ namespace graphene { namespace chain { class withdraw_permission_object; class bond_object; class bond_offer_object; + class file_object; class vesting_balance_object; class witness_schedule_object; class worker_object; @@ -181,6 +183,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type; typedef object_id< protocol_ids, bond_offer_object_type, bond_offer_object> bond_offer_id_type; typedef object_id< protocol_ids, bond_object_type, bond_object> bond_id_type; + typedef object_id< protocol_ids, file_object_type, file_object> file_id_type; typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; @@ -383,6 +386,7 @@ namespace graphene { namespace chain { uint32_t cancel_bond_offer_fee; uint32_t accept_bond_offer_fee; uint32_t claim_bond_collateral_fee; + uint32_t file_storage_fee_per_day; ///< the cost of leasing a file with 2^16 bytes for 1 day uint32_t vesting_balance_create_fee; uint32_t vesting_balance_withdraw_fee; uint32_t global_settle_fee; @@ -505,6 +509,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (withdraw_permission_object_type) (bond_offer_object_type) (bond_object_type) + (file_object_type) (vesting_balance_object_type) (worker_object_type) (OBJECT_TYPE_COUNT) @@ -563,6 +568,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (cancel_bond_offer_fee) (accept_bond_offer_fee) (claim_bond_collateral_fee) + (file_storage_fee_per_day) (vesting_balance_create_fee) (vesting_balance_withdraw_fee) (global_settle_fee) @@ -611,6 +617,7 @@ FC_REFLECT_TYPENAME( graphene::chain::operation_history_id_type ) FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type ) FC_REFLECT_TYPENAME( graphene::chain::bond_offer_id_type ) FC_REFLECT_TYPENAME( graphene::chain::bond_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::file_id_type ) FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type ) FC_REFLECT_TYPENAME( graphene::chain::worker_id_type ) FC_REFLECT_TYPENAME( graphene::chain::relative_key_id_type ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 86073a71..8805895f 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -717,6 +717,20 @@ void asset_update_feed_producers_operation::validate() const { FC_ASSERT( fee.amount >= 0 ); } +void file_write_operation::validate()const +{ + FC_ASSERT( uint32_t(offset) + data.size() <= file_size ); + FC_ASSERT( flags <= 0x2f ); + FC_ASSERT( file_size > 0 ); + /** less than 10 years to prevent overflow of 64 bit numbers in the value*lease_seconds*file_size calculation */ + FC_ASSERT( lease_seconds < 60*60*24*365*10 ); +} + +share_type file_write_operation::calculate_fee( const fee_schedule_type& k )const +{ + return ((((k.file_storage_fee_per_day * lease_seconds)/(60*60*24))*file_size)/0xff) + ((data.size() * k.data_fee)/1024); +} + void vesting_balance_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const { diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 77badee4..6ecc2c4f 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -173,6 +173,9 @@ struct operation_get_impacted_accounts } void operator()( const asset_create_operation& o )const { } + void operator()( const file_write_operation& o )const { + _impacted.insert( o.owner ); + } void operator()( const asset_update_operation& o )const { if( o.new_issuer ) diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index deefe0a1..048ed44a 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ string remove_namespace( string str ) template void generate_serializer(); -template +template void register_serializer(); @@ -95,7 +96,7 @@ struct js_name> }; template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; template struct js_name> { static std::string name(){ return "bytes "+ fc::to_string(N); }; }; -template struct js_name< fc::optional > { static std::string name(){ return "optional " + js_name::name(); } }; +template struct js_name< fc::optional > { static std::string name(){ return "optional " + js_name::name(); } }; template<> struct js_name< object_id_type > { static std::string name(){ return "object_id_type"; } }; template struct js_name< fc::flat_set > { static std::string name(){ return "set " + js_name::name(); } }; template struct js_name< std::vector > { static std::string name(){ return "array " + js_name::name(); } }; @@ -114,8 +115,8 @@ template<> struct js_name< time_point_sec > { static std::string name(){ retu template struct js_name > { - static std::string name(){ - return "protocol_id_type \"" + remove_namespace(fc::get_typename::name()) + "\""; + static std::string name(){ + return "protocol_id_type \"" + remove_namespace(fc::get_typename::name()) + "\""; }; }; @@ -131,7 +132,7 @@ struct js_name< fc::flat_map > { static std::string name(){ return "map (" template struct js_sv_name; -template struct js_sv_name +template struct js_sv_name { static std::string name(){ return "\n " + js_name::name(); } }; template @@ -141,10 +142,10 @@ struct js_sv_name { static std::string name(){ return "\n " + js_nam template struct js_name< fc::static_variant > { - static std::string name( std::string n = ""){ + static std::string name( std::string n = ""){ static const std::string name = n; if( name == "" ) - return "static_variant [" + js_sv_name::name() + "\n]"; + return "static_variant [" + js_sv_name::name() + "\n]"; else return name; } }; @@ -289,7 +290,7 @@ class register_member_visitor } }; -template +template struct serializer { static_assert( fc::reflector::is_defined::value == reflected, "invalid template arguments" ); From d12d9cdb0c802a1c8754a0a5f1c39cbe72008218 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 15 Jun 2015 15:10:26 -0400 Subject: [PATCH 4/9] Remove bond operations --- libraries/chain/CMakeLists.txt | 1 - libraries/chain/bond_evaluator.cpp | 218 ------------------ libraries/chain/db_init.cpp | 8 - libraries/chain/db_market.cpp | 34 --- .../include/graphene/chain/bond_evaluator.hpp | 68 ------ .../include/graphene/chain/bond_object.hpp | 111 --------- .../include/graphene/chain/operations.hpp | 126 ---------- .../chain/include/graphene/chain/types.hpp | 18 -- libraries/chain/operations.cpp | 71 ------ .../account_history_plugin.cpp | 12 - libraries/wallet/cache.cpp | 5 - programs/js_operation_serializer/main.cpp | 1 - tests/common/database_fixture.cpp | 7 - tests/tests/operation_tests.cpp | 64 ----- 14 files changed, 744 deletions(-) delete mode 100644 libraries/chain/bond_evaluator.cpp delete mode 100644 libraries/chain/include/graphene/chain/bond_evaluator.hpp delete mode 100644 libraries/chain/include/graphene/chain/bond_object.hpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 4d4c3abe..17635897 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -18,7 +18,6 @@ add_library( graphene_chain proposal_evaluator.cpp short_order_evaluator.cpp limit_order_evaluator.cpp - bond_evaluator.cpp vesting_balance_evaluator.cpp withdraw_permission_evaluator.cpp worker_evaluator.cpp diff --git a/libraries/chain/bond_evaluator.cpp b/libraries/chain/bond_evaluator.cpp deleted file mode 100644 index c3108771..00000000 --- a/libraries/chain/bond_evaluator.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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 -#include -#include - -namespace graphene { namespace chain { - -object_id_type bond_create_offer_evaluator::do_evaluate( const bond_create_offer_operation& op ) -{ - const auto& d = db(); - - const auto& creator_account = op.creator( d ); - const auto& base_asset = op.collateral_rate.base.asset_id( d ); - const auto& quote_asset = op.collateral_rate.quote.asset_id( d ); - - // TODO: Check asset authorizations and withdrawals - - const auto& amount_asset = (op.amount.asset_id == op.collateral_rate.base.asset_id) ? base_asset : quote_asset; - - FC_ASSERT( !base_asset.is_transfer_restricted() && !quote_asset.is_transfer_restricted() ); - - if( base_asset.options.whitelist_markets.size() ) - FC_ASSERT( base_asset.options.whitelist_markets.find( quote_asset.id ) != base_asset.options.whitelist_markets.end() ); - if( base_asset.options.blacklist_markets.size() ) - FC_ASSERT( base_asset.options.blacklist_markets.find( quote_asset.id ) == base_asset.options.blacklist_markets.end() ); - - FC_ASSERT( d.get_balance( creator_account, amount_asset ) >= op.amount ); - - return object_id_type(); -} - -object_id_type bond_create_offer_evaluator::do_apply( const bond_create_offer_operation& op ) -{ - db().adjust_balance( op.creator, -op.amount ); - db().adjust_core_in_orders( op.creator(db()), op.amount ); - - const auto& offer = db().create( [&]( bond_offer_object& obj ) - { - obj.offered_by_account = op.creator; - obj.offer_to_borrow = op.offer_to_borrow; - obj.amount = op.amount; - obj.min_match = op.min_match; - obj.collateral_rate = op.collateral_rate; - obj.min_loan_period_sec = op.min_loan_period_sec; - obj.loan_period_sec = op.loan_period_sec; - obj.interest_apr = op.interest_apr; - } ); - - return offer.id; -} - - -object_id_type bond_cancel_offer_evaluator::do_evaluate( const bond_cancel_offer_operation& op ) -{ - _offer = &op.offer_id(db()); - FC_ASSERT( op.creator == _offer->offered_by_account ); - FC_ASSERT( _offer->amount == op.refund ); - return object_id_type(); -} - -object_id_type bond_cancel_offer_evaluator::do_apply( const bond_cancel_offer_operation& op ) -{ - assert( _offer != nullptr ); - db().adjust_balance( op.creator, op.refund ); - db().adjust_core_in_orders( op.creator(db()), -op.refund ); - db().remove( *_offer ); - return object_id_type(); -} - -object_id_type bond_accept_offer_evaluator::do_evaluate( const bond_accept_offer_operation& op ) -{ try { - _offer = &op.offer_id(db()); - - if( _offer->offer_to_borrow ) - FC_ASSERT( op.amount_borrowed.amount >= _offer->min_match ); - else - FC_ASSERT( op.amount_collateral.amount >= _offer->min_match ); - - FC_ASSERT( (op.amount_borrowed / op.amount_collateral == _offer->collateral_rate) || - (op.amount_collateral / op.amount_borrowed == _offer->collateral_rate) ); - - return object_id_type(); -} FC_CAPTURE_AND_RETHROW((op)) } - -object_id_type bond_accept_offer_evaluator::do_apply( const bond_accept_offer_operation& op ) -{ try { - - if( op.claimer == op.lender ) - { - db().adjust_balance( op.lender, -op.amount_borrowed ); - } - else // claimer == borrower - { - db().adjust_balance( op.borrower, -op.amount_collateral ); - db().adjust_core_in_orders( op.borrower(db()), op.amount_collateral ); - } - db().adjust_balance( op.borrower, op.amount_borrowed ); - - const auto& bond = db().create( [&]( bond_object& obj ) - { - obj.borrowed = op.amount_borrowed; - obj.collateral = op.amount_collateral; - obj.borrower = op.borrower; - obj.lender = op.lender; - - auto head_time = db().get_dynamic_global_properties().time; - obj.interest_apr = _offer->interest_apr; - obj.start_date = head_time; - obj.due_date = head_time + fc::seconds( _offer->loan_period_sec ); - obj.earliest_payoff_date = head_time + fc::seconds( _offer->min_loan_period_sec ); - } ); - - if( _offer->offer_to_borrow && op.amount_borrowed < _offer->amount ) - { - db().modify( *_offer, [&]( bond_offer_object& offer ){ - offer.amount -= op.amount_borrowed; - }); - } - else if( !_offer->offer_to_borrow && op.amount_collateral < _offer->amount ) - { - db().modify( *_offer, [&]( bond_offer_object& offer ){ - offer.amount -= op.amount_collateral; - }); - } - else - { - db().remove( *_offer ); - } - return bond.id; -} FC_CAPTURE_AND_RETHROW((op)) } - - - -object_id_type bond_claim_collateral_evaluator::do_evaluate( const bond_claim_collateral_operation& op ) -{ - _bond = &op.bond_id(db()); - auto head_time = db().get_dynamic_global_properties().time; - FC_ASSERT( head_time > _bond->earliest_payoff_date ); - - - FC_ASSERT( op.collateral_claimed <= _bond->collateral ); - if( _bond->borrower == op.claimer ) - { - auto elapsed_time = head_time - _bond->start_date; - auto elapsed_days = 1 + elapsed_time.to_seconds() / (60*60*24); - - fc::uint128 tmp = _bond->borrowed.amount.value; - tmp *= elapsed_days; - tmp *= _bond->interest_apr; - tmp /= (365 * GRAPHENE_100_PERCENT); - FC_ASSERT( tmp < GRAPHENE_MAX_SHARE_SUPPLY ); - _interest_due = asset(tmp.to_uint64(), _bond->borrowed.asset_id); - - FC_ASSERT( _interest_due + _bond->borrowed <= op.payoff_amount ); - - auto total_debt = _interest_due + _bond->borrowed; - - fc::uint128 max_claim = _bond->collateral.amount.value; - max_claim *= op.payoff_amount.amount.value; - max_claim /= total_debt.amount.value; - - FC_ASSERT( op.collateral_claimed.amount.value == max_claim.to_uint64() ); - } - else - { - FC_ASSERT( _bond->lender == op.claimer ); - FC_ASSERT( head_time > _bond->due_date ); - FC_ASSERT( _bond->collateral == op.collateral_claimed ); - FC_ASSERT( op.payoff_amount == asset(0,_bond->borrowed.asset_id ) ); - } - return object_id_type(); -} - -object_id_type bond_claim_collateral_evaluator::do_apply( const bond_claim_collateral_operation& op ) -{ - assert( _bond != nullptr ); - - const account_object& claimer = op.claimer(db()); - - db().adjust_core_in_orders( _bond->borrower(db()), -op.collateral_claimed ); - - if( op.payoff_amount.amount > 0 ) - { - db().adjust_balance( claimer, -op.payoff_amount ); - db().adjust_balance( op.lender, op.payoff_amount ); - } - db().adjust_balance( claimer, op.collateral_claimed ); - - if( op.collateral_claimed == _bond->collateral ) - db().remove(*_bond); - else - db().modify( *_bond, [&]( bond_object& bond ){ - bond.borrowed -= op.payoff_amount + _interest_due; - bond.collateral -= op.collateral_claimed; - bond.start_date = db().get_dynamic_global_properties().time; - }); - - return object_id_type(); -} - -} } // graphene::chain diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 1e1d8fac..57c3cd37 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -37,7 +36,6 @@ #include #include -#include #include #include #include @@ -89,10 +87,6 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); - register_evaluator(); - register_evaluator(); - register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); @@ -118,8 +112,6 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); - add_index< primary_index >(); add_index< primary_index > >(); add_index< primary_index >(); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 3f035dbc..22fb778d 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -33,7 +32,6 @@ namespace graphene { namespace chain { calculate the USD->CORE price and convert all USD balances to CORE at that price and subtract CORE from total - any fees accumulated by the issuer in the bitasset are forfeit / not redeemed - cancel all open orders with bitasset in it - - any bonds with the bitasset as collateral get converted to CORE as collateral - any bitassets that use this bitasset as collateral are immediately settled at their feed price - convert all balances in bitasset to CORE and subtract from total - any prediction markets with usd as the backing get converted to CORE as the backing @@ -111,38 +109,6 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett // settle all balances asset total_mia_settled = mia.amount(0); - // convert collateral held in bonds - const auto& bond_idx = get_index_type().indices().get(); - auto bond_itr = bond_idx.find( bitasset.id ); - while( bond_itr != bond_idx.end() ) - { - if( bond_itr->collateral.asset_id == bitasset.id ) - { - auto settled_amount = bond_itr->collateral * settlement_price; - total_mia_settled += bond_itr->collateral; - collateral_gathered -= settled_amount; - modify( *bond_itr, [&]( bond_object& obj ) { - obj.collateral = settled_amount; - }); - } - else break; - } - - // cancel all bond offers holding the bitasset and refund the offer - const auto& bond_offer_idx = get_index_type().indices().get(); - auto bond_offer_itr = bond_offer_idx.find( bitasset.id ); - while( bond_offer_itr != bond_offer_idx.end() ) - { - if( bond_offer_itr->amount.asset_id == bitasset.id ) - { - adjust_balance( bond_offer_itr->offered_by_account, bond_offer_itr->amount ); - auto old_itr = bond_offer_itr; - bond_offer_itr++; - remove( *old_itr ); - } - else break; - } - const auto& index = get_index_type().indices().get(); auto range = index.equal_range(mia.get_id()); for( auto itr = range.first; itr != range.second; ++itr ) diff --git a/libraries/chain/include/graphene/chain/bond_evaluator.hpp b/libraries/chain/include/graphene/chain/bond_evaluator.hpp deleted file mode 100644 index 224c3527..00000000 --- a/libraries/chain/include/graphene/chain/bond_evaluator.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 - -namespace graphene { namespace chain { - -class bond_create_offer_evaluator : public evaluator -{ - public: - typedef bond_create_offer_operation operation_type; - - object_id_type do_evaluate( const bond_create_offer_operation& op ); - object_id_type do_apply( const bond_create_offer_operation& op ); -}; - -class bond_cancel_offer_evaluator : public evaluator -{ - public: - typedef bond_cancel_offer_operation operation_type; - - object_id_type do_evaluate( const bond_cancel_offer_operation& op ); - object_id_type do_apply( const bond_cancel_offer_operation& op ); - - const bond_offer_object* _offer = nullptr; -}; - -class bond_accept_offer_evaluator : public evaluator -{ - public: - typedef bond_accept_offer_operation operation_type; - - object_id_type do_evaluate( const bond_accept_offer_operation& op ); - object_id_type do_apply( const bond_accept_offer_operation& op ); - - const bond_offer_object* _offer = nullptr; - asset _fill_amount; -}; - -class bond_claim_collateral_evaluator : public evaluator -{ - public: - typedef bond_claim_collateral_operation operation_type; - - object_id_type do_evaluate( const bond_claim_collateral_operation& op ); - object_id_type do_apply( const bond_claim_collateral_operation& op ); - - const bond_object* _bond = nullptr; - asset _interest_due; -}; - -} } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/bond_object.hpp b/libraries/chain/include/graphene/chain/bond_object.hpp deleted file mode 100644 index 3c005c28..00000000 --- a/libraries/chain/include/graphene/chain/bond_object.hpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 - -#pragma once -#include -#include -#include - -namespace graphene { namespace chain { - - /** - * @ingroup object - */ - class bond_object : public graphene::db::abstract_object - { - public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = bond_object_type; - - asset_id_type collateral_type()const { return collateral.asset_id; } - - account_id_type borrower; - account_id_type lender; - asset borrowed; - /** if collateral is the core asset, then voting rights belong to the borrower - * because the borrower is owner of the collateral until they default - */ - asset collateral; - uint16_t interest_apr = 0; - time_point_sec start_date; - /** after this date the lender can collect the collateral at will or let it float */ - time_point_sec due_date; - /** the loan cannot be paid off before this date */ - time_point_sec earliest_payoff_date; - }; - - /** - * @ingroup object - */ - class bond_offer_object : public graphene::db::abstract_object - { - public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = bond_offer_object_type; - - asset_id_type asset_type()const { return amount.asset_id; } - - account_id_type offered_by_account; - bool offer_to_borrow = false; // Offer to borrow if true, and offer to lend otherwise - asset amount; - share_type min_match; ///< asset type same as ammount.asset_id - price collateral_rate; - uint32_t min_loan_period_sec = 0; - uint32_t loan_period_sec = 0; - uint16_t interest_apr = 0; - }; - - struct by_borrower; - struct by_lender; - struct by_offerer; - struct by_collateral; /// needed for blackswan resolution - struct by_asset; /// needed for blackswan resolution - - typedef multi_index_container< - bond_object, - indexed_by< - hashed_unique< tag, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, member >, - ordered_non_unique< tag, member >, - hashed_non_unique< tag, const_mem_fun > - > - > bond_object_multi_index_type; - - typedef generic_index bond_index; - - /** - * Todo: consider adding index of tuple - * Todo: consider adding index of tuple - */ - typedef multi_index_container< - bond_offer_object, - indexed_by< - hashed_unique< tag, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, member >, - hashed_non_unique< tag, const_mem_fun > - > - > bond_offer_object_multi_index_type; - - typedef generic_index bond_offer_index; - -}} // graphene::chain - -FC_REFLECT_DERIVED( graphene::chain::bond_object, (graphene::db::object), - (borrower)(lender)(borrowed)(collateral)(interest_apr)(start_date)(due_date)(earliest_payoff_date) ) -FC_REFLECT_DERIVED( graphene::chain::bond_offer_object, (graphene::db::object), (offered_by_account)(offer_to_borrow)(amount)(min_match)(collateral_rate)(min_loan_period_sec)(loan_period_sec)(interest_apr) ) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index 5b3cf85b..b670be1d 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -1248,123 +1248,6 @@ namespace graphene { namespace chain { } }; - /** - * @ingroup operations - * - * Bond offers are objects that exist on the blockchain and can be - * filled in full or in part by someone using the accept_bond_offer - * operation. When the offer is accepted a new bond_object is - * created that defines the terms of the loan. - * - * @return bond_offer_id - */ - struct bond_create_offer_operation - { - asset fee; - account_id_type creator; - bool offer_to_borrow = false; ///< Offer to borrow if true, and offer to lend otherwise - asset amount; ///< Amount to lend or secure depending on above - share_type min_match; ///< asset id same as amount.asset_id and sets the minimum match that will be accepted - price collateral_rate; ///< To derive amount of collateral or principle based on above - /** after this time the lender can let the loan float or collect the collateral at will */ - uint32_t min_loan_period_sec = 0; ///< the earliest the loan may be paid off - uint32_t loan_period_sec = 0; - uint16_t interest_apr = 0; ///< MAX_INTEREST_APR == 100% and is max value - - account_id_type fee_payer()const { return creator; } - 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 - { - acc.adjust( fee_payer(), -fee ); - acc.adjust( creator, -amount ); - } - }; - - /** - * @ingroup operations - * Subtracts refund from bond_offer.amount and frees bond_offer if refund == bond_offer.amount - */ - struct bond_cancel_offer_operation - { - asset fee; - account_id_type creator; - bond_offer_id_type offer_id; - asset refund; - - account_id_type fee_payer()const { return creator; } - 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 - { - acc.adjust( fee_payer(), -fee ); - acc.adjust( creator, refund ); - } - }; - - /** - * @ingroup operations - * @return new bond_id - */ - struct bond_accept_offer_operation - { - asset fee; - account_id_type claimer; - account_id_type lender; - account_id_type borrower; ///< included in case of offer to borrow, because borrower will receive funds - bond_offer_id_type offer_id; - asset amount_borrowed; ///< should equal amount_collateral * offer_id->collateral_rate - asset amount_collateral; ///< should equal amount_borrowed * offer_id->collateral_rate - - account_id_type fee_payer()const { return claimer; } - 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 - { - acc.adjust( fee_payer(), -fee ); - if( claimer == lender ) - acc.adjust( claimer, -amount_borrowed ); - else // claimer == borrower - acc.adjust( claimer, -amount_collateral ); - acc.adjust( borrower, amount_borrowed ); - } - }; - - /** - * @ingroup operations - * After the loan period the lender can claim - * the collateral, prior to the loan period expiring - * the borrower can claim it by paying off the loan - */ - struct bond_claim_collateral_operation - { - asset fee; - account_id_type claimer; ///< must be bond_id->lender or bond_id->borrower - account_id_type lender; ///< must be bond_id->lender - bond_id_type bond_id; - asset payoff_amount; - - /** the borrower can claim a percentage of the collateral propotional to the - * percentage of the debt+interest that was paid off - */ - asset collateral_claimed; - - account_id_type fee_payer()const { return claimer; } - 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 - { - acc.adjust( fee_payer(), -fee ); - acc.adjust( claimer, -payoff_amount ); - acc.adjust( claimer, collateral_claimed ); - acc.adjust( lender, payoff_amount ); - } - }; - /** * @brief Create a vesting balance. * @ingroup operations @@ -1544,10 +1427,6 @@ namespace graphene { namespace chain { global_parameters_update_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, - bond_create_offer_operation, - bond_cancel_offer_operation, - bond_accept_offer_operation, - bond_claim_collateral_operation, worker_create_operation, custom_operation > operation; @@ -1776,11 +1655,6 @@ FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account) (withdrawal_permission) ) -FC_REFLECT( graphene::chain::bond_create_offer_operation, (fee)(creator)(offer_to_borrow)(amount)(min_match)(collateral_rate)(min_loan_period_sec)(loan_period_sec)(interest_apr) ) -FC_REFLECT( graphene::chain::bond_cancel_offer_operation, (fee)(creator)(offer_id)(refund) ) -FC_REFLECT( graphene::chain::bond_accept_offer_operation, (fee)(claimer)(lender)(borrower)(offer_id)(amount_borrowed)(amount_collateral) ) -FC_REFLECT( graphene::chain::bond_claim_collateral_operation, (fee)(claimer)(lender)(bond_id)(payoff_amount)(collateral_claimed) ) - FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(vesting_seconds) ) FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 92c00b3a..7a7d15d1 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -114,8 +114,6 @@ namespace graphene { namespace chain { proposal_object_type, operation_history_object_type, withdraw_permission_object_type, - bond_offer_object_type, - bond_object_type, vesting_balance_object_type, worker_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types @@ -160,8 +158,6 @@ namespace graphene { namespace chain { class proposal_object; class operation_history_object; class withdraw_permission_object; - class bond_object; - class bond_offer_object; class vesting_balance_object; class witness_schedule_object; class worker_object; @@ -179,8 +175,6 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type; typedef object_id< protocol_ids, operation_history_object_type, operation_history_object> operation_history_id_type; typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type; - typedef object_id< protocol_ids, bond_offer_object_type, bond_offer_object> bond_offer_id_type; - typedef object_id< protocol_ids, bond_object_type, bond_object> bond_id_type; typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; @@ -379,10 +373,6 @@ namespace graphene { namespace chain { uint32_t membership_annual_fee; ///< the annual cost of a membership subscription uint32_t membership_lifetime_fee; ///< the cost to upgrade to a lifetime member uint32_t withdraw_permission_update_fee; ///< the cost to create/update a withdraw permission - uint32_t create_bond_offer_fee; - uint32_t cancel_bond_offer_fee; - uint32_t accept_bond_offer_fee; - uint32_t claim_bond_collateral_fee; uint32_t vesting_balance_create_fee; uint32_t vesting_balance_withdraw_fee; uint32_t global_settle_fee; @@ -503,8 +493,6 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (proposal_object_type) (operation_history_object_type) (withdraw_permission_object_type) - (bond_offer_object_type) - (bond_object_type) (vesting_balance_object_type) (worker_object_type) (OBJECT_TYPE_COUNT) @@ -559,10 +547,6 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (membership_annual_fee) (membership_lifetime_fee) (withdraw_permission_update_fee) - (create_bond_offer_fee) - (cancel_bond_offer_fee) - (accept_bond_offer_fee) - (claim_bond_collateral_fee) (vesting_balance_create_fee) (vesting_balance_withdraw_fee) (global_settle_fee) @@ -609,8 +593,6 @@ FC_REFLECT_TYPENAME( graphene::chain::custom_id_type ) FC_REFLECT_TYPENAME( graphene::chain::proposal_id_type ) FC_REFLECT_TYPENAME( graphene::chain::operation_history_id_type ) FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::bond_offer_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::bond_id_type ) FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type ) FC_REFLECT_TYPENAME( graphene::chain::worker_id_type ) FC_REFLECT_TYPENAME( graphene::chain::relative_key_id_type ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index 86073a71..c08027e2 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -804,77 +804,6 @@ share_type custom_operation::calculate_fee( const fee_schedule_type& k )const return (data.size() * k.data_fee)/1024; } -void bond_create_offer_operation::get_required_auth( flat_set& active_auth_set, flat_set& )const -{ - active_auth_set.insert( creator ); -} - -void bond_create_offer_operation::validate()const -{ try { - FC_ASSERT( fee.amount >= 0 ); - FC_ASSERT( amount.amount > 0 ); - collateral_rate.validate(); - FC_ASSERT( (amount * collateral_rate).amount > 0 ); - FC_ASSERT( min_loan_period_sec > 0 ); - FC_ASSERT( loan_period_sec >= min_loan_period_sec ); - FC_ASSERT( interest_apr <= GRAPHENE_MAX_INTEREST_APR ); -} FC_CAPTURE_AND_RETHROW((*this)) } - -share_type bond_create_offer_operation::calculate_fee( const fee_schedule_type& schedule )const -{ - return schedule.create_bond_offer_fee; -} - - -void bond_cancel_offer_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const -{ - active_auth_set.insert( creator ); -} -void bond_cancel_offer_operation::validate()const -{ - FC_ASSERT( fee.amount > 0 ); - FC_ASSERT( refund.amount > 0 ); -} -share_type bond_cancel_offer_operation::calculate_fee( const fee_schedule_type& k )const -{ - return k.cancel_bond_offer_fee; -} - -void bond_accept_offer_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const -{ - active_auth_set.insert( claimer ); -} - -void bond_accept_offer_operation::validate()const -{ - FC_ASSERT( fee.amount > 0 ); - (amount_collateral / amount_borrowed).validate(); - FC_ASSERT( claimer == borrower || claimer == lender ); - FC_ASSERT( borrower != lender ); -} - -share_type bond_accept_offer_operation::calculate_fee( const fee_schedule_type& k )const -{ - return k.accept_bond_offer_fee; -} -void bond_claim_collateral_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const -{ - active_auth_set.insert( claimer ); -} - -void bond_claim_collateral_operation::validate()const -{ - FC_ASSERT( fee.amount > 0 ); - FC_ASSERT(payoff_amount.amount >= 0 ); - FC_ASSERT(collateral_claimed.amount >= 0 ); - FC_ASSERT( payoff_amount.asset_id != collateral_claimed.asset_id ); -} - -share_type bond_claim_collateral_operation::calculate_fee( const fee_schedule_type& k )const -{ - return k.claim_bond_collateral_fee; -} - void worker_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const { active_auth_set.insert(owner); diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 77badee4..d8e1148e 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -232,18 +232,6 @@ struct operation_get_impacted_accounts _impacted.insert( account_id_type() ); } - void operator()( const bond_create_offer_operation& o )const { } - void operator()( const bond_cancel_offer_operation& o )const { } - void operator()( const bond_accept_offer_operation& o )const { - _impacted.insert( o.borrower ); - _impacted.insert( o.lender ); - } - void operator()( const bond_claim_collateral_operation& o )const - { - _impacted.insert( o.lender ); - _impacted.insert( o.claimer ); - } - void operator()( const vesting_balance_create_operation& o )const { _impacted.insert( o.creator ); diff --git a/libraries/wallet/cache.cpp b/libraries/wallet/cache.cpp index 3c68e708..d5c3f382 100644 --- a/libraries/wallet/cache.cpp +++ b/libraries/wallet/cache.cpp @@ -26,7 +26,6 @@ #include #include #include -#include using namespace fc; using namespace graphene::chain; @@ -87,10 +86,6 @@ object* create_object( const variant& v ) return create_object_of_type< operation_history_object >( v ); case withdraw_permission_object_type: return create_object_of_type< withdraw_permission_object >( v ); - case bond_offer_object_type: - return create_object_of_type< bond_offer_object >( v ); - case bond_object_type: - return create_object_of_type< bond_object >( v ); default: ; } diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index deefe0a1..21db9e66 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -16,7 +16,6 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -#include #include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 1717a04a..27d57445 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -151,12 +150,6 @@ void database_fixture::verify_asset_supplies( )const { total_balances[asset_id_type()] += witness_obj.accumulated_income; } - for( const bond_offer_object& bond_offer : db.get_index_type().indices() ) - { - total_balances[ bond_offer.amount.asset_id ] += bond_offer.amount.amount; - if( bond_offer.amount.asset_id == asset_id_type() ) - core_in_orders += bond_offer.amount.amount; - } for( const vesting_balance_object& vbo : db.get_index_type< simple_index >() ) total_balances[ vbo.balance.asset_id ] += vbo.balance.amount; diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 27d4e5d2..e3bc8c00 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2080,7 +2080,6 @@ BOOST_AUTO_TEST_CASE( margin_call_black_swan ) * 2) Short Orders for BitAsset backed by BitUSD * 3) Call Orders for BitAsset backed by BitUSD * 4) Issuer Fees - * 5) Bond Market Collateral * * This test should fail until the black swan handling code can * perform a recursive blackswan for any other BitAssets that use @@ -2138,69 +2137,6 @@ BOOST_AUTO_TEST_CASE( unimp_transfer_cashback_test ) } } -BOOST_AUTO_TEST_CASE( bond_create_offer_test ) -{ try { - bond_create_offer_operation op; - op.fee = asset( 0, 0 ); - op.creator = account_id_type(); - op.amount = asset( 1, 0 ); - op.collateral_rate = price( asset( 1, 0 ), asset( 1, 1 ) ); - op.min_loan_period_sec = 1; - op.loan_period_sec = 1; - - // Fee must be non-negative - REQUIRE_OP_VALIDATION_SUCCESS( op, fee, asset( 1, 0 ) ); - REQUIRE_OP_VALIDATION_SUCCESS( op, fee, asset( 0, 0 ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, fee, asset( -1, 0 ) ); - - // Amount must be positive - REQUIRE_OP_VALIDATION_SUCCESS( op, amount, asset( 1, 0 ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, amount, asset( 0, 0 ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, amount, asset( -1, 0 ) ); - - // Collateral rate must be valid - REQUIRE_OP_VALIDATION_SUCCESS( op, collateral_rate, price( asset( 1, 0 ), asset( 1, 1 ) ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, collateral_rate, price( asset( 0, 0 ), asset( 1, 1 ) ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, collateral_rate, price( asset( 1, 0 ), asset( 0, 1 ) ) ); - REQUIRE_OP_VALIDATION_FAILURE( op, collateral_rate, price( asset( 1, 0 ), asset( 1, 0 ) ) ); - - // Min loan period must be at least 1 sec - REQUIRE_OP_VALIDATION_SUCCESS( op, min_loan_period_sec, 1 ); - REQUIRE_OP_VALIDATION_FAILURE( op, min_loan_period_sec, 0 ); - - // Loan period must be greater than min load period - REQUIRE_OP_VALIDATION_SUCCESS( op, loan_period_sec, op.min_loan_period_sec + 1 ); - REQUIRE_OP_VALIDATION_FAILURE( op, loan_period_sec, 0 ); - - // Interest APR cannot be greater than max - REQUIRE_OP_VALIDATION_FAILURE( op, interest_apr, GRAPHENE_MAX_INTEREST_APR + 1 ); - REQUIRE_OP_VALIDATION_SUCCESS( op, interest_apr, GRAPHENE_MAX_INTEREST_APR ); - REQUIRE_OP_VALIDATION_SUCCESS( op, interest_apr, 0 ); - - // Setup world state we will need to test actual evaluation - INVOKE( create_uia ); - const auto& test_asset = get_asset( "TEST" ); - const auto& nathan_account = create_account( "nathan" ); - transfer( account_id_type()( db ), nathan_account, asset( 1, 0 ) ); - - op.creator = nathan_account.get_id(); - op.collateral_rate.quote.asset_id = test_asset.get_id(); - trx.operations.emplace_back( op ); - - // Insufficient funds in creator account - REQUIRE_THROW_WITH_VALUE( op, creator, account_id_type( 1 ) ); - - // Insufficient principle - REQUIRE_THROW_WITH_VALUE( op, amount, asset( 2, 0 ) ); - - // Insufficient collateral - op.offer_to_borrow = true; - REQUIRE_THROW_WITH_VALUE( op, amount, asset( 1, test_asset.get_id() ) ); - - // This op should be fully valid - REQUIRE_OP_EVALUATION_SUCCESS( op, offer_to_borrow, false ); -} FC_LOG_AND_RETHROW() } - BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) { try { INVOKE( create_uia ); From 5f79b662dcb28f9ab3e9fa5424f03ad73be185a0 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 15 Jun 2015 15:10:40 -0400 Subject: [PATCH 5/9] Revert "Remove bond operations" This reverts commit d12d9cdb0c802a1c8754a0a5f1c39cbe72008218. --- libraries/chain/CMakeLists.txt | 1 + libraries/chain/bond_evaluator.cpp | 218 ++++++++++++++++++ libraries/chain/db_init.cpp | 8 + libraries/chain/db_market.cpp | 34 +++ .../include/graphene/chain/bond_evaluator.hpp | 68 ++++++ .../include/graphene/chain/bond_object.hpp | 111 +++++++++ .../include/graphene/chain/operations.hpp | 126 ++++++++++ .../chain/include/graphene/chain/types.hpp | 18 ++ libraries/chain/operations.cpp | 71 ++++++ .../account_history_plugin.cpp | 12 + libraries/wallet/cache.cpp | 5 + programs/js_operation_serializer/main.cpp | 1 + tests/common/database_fixture.cpp | 7 + tests/tests/operation_tests.cpp | 64 +++++ 14 files changed, 744 insertions(+) create mode 100644 libraries/chain/bond_evaluator.cpp create mode 100644 libraries/chain/include/graphene/chain/bond_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/bond_object.hpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 17635897..4d4c3abe 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -18,6 +18,7 @@ add_library( graphene_chain proposal_evaluator.cpp short_order_evaluator.cpp limit_order_evaluator.cpp + bond_evaluator.cpp vesting_balance_evaluator.cpp withdraw_permission_evaluator.cpp worker_evaluator.cpp diff --git a/libraries/chain/bond_evaluator.cpp b/libraries/chain/bond_evaluator.cpp new file mode 100644 index 00000000..c3108771 --- /dev/null +++ b/libraries/chain/bond_evaluator.cpp @@ -0,0 +1,218 @@ +/* + * 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 +#include +#include + +namespace graphene { namespace chain { + +object_id_type bond_create_offer_evaluator::do_evaluate( const bond_create_offer_operation& op ) +{ + const auto& d = db(); + + const auto& creator_account = op.creator( d ); + const auto& base_asset = op.collateral_rate.base.asset_id( d ); + const auto& quote_asset = op.collateral_rate.quote.asset_id( d ); + + // TODO: Check asset authorizations and withdrawals + + const auto& amount_asset = (op.amount.asset_id == op.collateral_rate.base.asset_id) ? base_asset : quote_asset; + + FC_ASSERT( !base_asset.is_transfer_restricted() && !quote_asset.is_transfer_restricted() ); + + if( base_asset.options.whitelist_markets.size() ) + FC_ASSERT( base_asset.options.whitelist_markets.find( quote_asset.id ) != base_asset.options.whitelist_markets.end() ); + if( base_asset.options.blacklist_markets.size() ) + FC_ASSERT( base_asset.options.blacklist_markets.find( quote_asset.id ) == base_asset.options.blacklist_markets.end() ); + + FC_ASSERT( d.get_balance( creator_account, amount_asset ) >= op.amount ); + + return object_id_type(); +} + +object_id_type bond_create_offer_evaluator::do_apply( const bond_create_offer_operation& op ) +{ + db().adjust_balance( op.creator, -op.amount ); + db().adjust_core_in_orders( op.creator(db()), op.amount ); + + const auto& offer = db().create( [&]( bond_offer_object& obj ) + { + obj.offered_by_account = op.creator; + obj.offer_to_borrow = op.offer_to_borrow; + obj.amount = op.amount; + obj.min_match = op.min_match; + obj.collateral_rate = op.collateral_rate; + obj.min_loan_period_sec = op.min_loan_period_sec; + obj.loan_period_sec = op.loan_period_sec; + obj.interest_apr = op.interest_apr; + } ); + + return offer.id; +} + + +object_id_type bond_cancel_offer_evaluator::do_evaluate( const bond_cancel_offer_operation& op ) +{ + _offer = &op.offer_id(db()); + FC_ASSERT( op.creator == _offer->offered_by_account ); + FC_ASSERT( _offer->amount == op.refund ); + return object_id_type(); +} + +object_id_type bond_cancel_offer_evaluator::do_apply( const bond_cancel_offer_operation& op ) +{ + assert( _offer != nullptr ); + db().adjust_balance( op.creator, op.refund ); + db().adjust_core_in_orders( op.creator(db()), -op.refund ); + db().remove( *_offer ); + return object_id_type(); +} + +object_id_type bond_accept_offer_evaluator::do_evaluate( const bond_accept_offer_operation& op ) +{ try { + _offer = &op.offer_id(db()); + + if( _offer->offer_to_borrow ) + FC_ASSERT( op.amount_borrowed.amount >= _offer->min_match ); + else + FC_ASSERT( op.amount_collateral.amount >= _offer->min_match ); + + FC_ASSERT( (op.amount_borrowed / op.amount_collateral == _offer->collateral_rate) || + (op.amount_collateral / op.amount_borrowed == _offer->collateral_rate) ); + + return object_id_type(); +} FC_CAPTURE_AND_RETHROW((op)) } + +object_id_type bond_accept_offer_evaluator::do_apply( const bond_accept_offer_operation& op ) +{ try { + + if( op.claimer == op.lender ) + { + db().adjust_balance( op.lender, -op.amount_borrowed ); + } + else // claimer == borrower + { + db().adjust_balance( op.borrower, -op.amount_collateral ); + db().adjust_core_in_orders( op.borrower(db()), op.amount_collateral ); + } + db().adjust_balance( op.borrower, op.amount_borrowed ); + + const auto& bond = db().create( [&]( bond_object& obj ) + { + obj.borrowed = op.amount_borrowed; + obj.collateral = op.amount_collateral; + obj.borrower = op.borrower; + obj.lender = op.lender; + + auto head_time = db().get_dynamic_global_properties().time; + obj.interest_apr = _offer->interest_apr; + obj.start_date = head_time; + obj.due_date = head_time + fc::seconds( _offer->loan_period_sec ); + obj.earliest_payoff_date = head_time + fc::seconds( _offer->min_loan_period_sec ); + } ); + + if( _offer->offer_to_borrow && op.amount_borrowed < _offer->amount ) + { + db().modify( *_offer, [&]( bond_offer_object& offer ){ + offer.amount -= op.amount_borrowed; + }); + } + else if( !_offer->offer_to_borrow && op.amount_collateral < _offer->amount ) + { + db().modify( *_offer, [&]( bond_offer_object& offer ){ + offer.amount -= op.amount_collateral; + }); + } + else + { + db().remove( *_offer ); + } + return bond.id; +} FC_CAPTURE_AND_RETHROW((op)) } + + + +object_id_type bond_claim_collateral_evaluator::do_evaluate( const bond_claim_collateral_operation& op ) +{ + _bond = &op.bond_id(db()); + auto head_time = db().get_dynamic_global_properties().time; + FC_ASSERT( head_time > _bond->earliest_payoff_date ); + + + FC_ASSERT( op.collateral_claimed <= _bond->collateral ); + if( _bond->borrower == op.claimer ) + { + auto elapsed_time = head_time - _bond->start_date; + auto elapsed_days = 1 + elapsed_time.to_seconds() / (60*60*24); + + fc::uint128 tmp = _bond->borrowed.amount.value; + tmp *= elapsed_days; + tmp *= _bond->interest_apr; + tmp /= (365 * GRAPHENE_100_PERCENT); + FC_ASSERT( tmp < GRAPHENE_MAX_SHARE_SUPPLY ); + _interest_due = asset(tmp.to_uint64(), _bond->borrowed.asset_id); + + FC_ASSERT( _interest_due + _bond->borrowed <= op.payoff_amount ); + + auto total_debt = _interest_due + _bond->borrowed; + + fc::uint128 max_claim = _bond->collateral.amount.value; + max_claim *= op.payoff_amount.amount.value; + max_claim /= total_debt.amount.value; + + FC_ASSERT( op.collateral_claimed.amount.value == max_claim.to_uint64() ); + } + else + { + FC_ASSERT( _bond->lender == op.claimer ); + FC_ASSERT( head_time > _bond->due_date ); + FC_ASSERT( _bond->collateral == op.collateral_claimed ); + FC_ASSERT( op.payoff_amount == asset(0,_bond->borrowed.asset_id ) ); + } + return object_id_type(); +} + +object_id_type bond_claim_collateral_evaluator::do_apply( const bond_claim_collateral_operation& op ) +{ + assert( _bond != nullptr ); + + const account_object& claimer = op.claimer(db()); + + db().adjust_core_in_orders( _bond->borrower(db()), -op.collateral_claimed ); + + if( op.payoff_amount.amount > 0 ) + { + db().adjust_balance( claimer, -op.payoff_amount ); + db().adjust_balance( op.lender, op.payoff_amount ); + } + db().adjust_balance( claimer, op.collateral_claimed ); + + if( op.collateral_claimed == _bond->collateral ) + db().remove(*_bond); + else + db().modify( *_bond, [&]( bond_object& bond ){ + bond.borrowed -= op.payoff_amount + _interest_due; + bond.collateral -= op.collateral_claimed; + bond.start_date = db().get_dynamic_global_properties().time; + }); + + return object_id_type(); +} + +} } // graphene::chain diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 57c3cd37..1e1d8fac 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include +#include #include #include #include @@ -87,6 +89,10 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); @@ -112,6 +118,8 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); add_index< primary_index > >(); add_index< primary_index >(); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 22fb778d..3f035dbc 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ namespace graphene { namespace chain { calculate the USD->CORE price and convert all USD balances to CORE at that price and subtract CORE from total - any fees accumulated by the issuer in the bitasset are forfeit / not redeemed - cancel all open orders with bitasset in it + - any bonds with the bitasset as collateral get converted to CORE as collateral - any bitassets that use this bitasset as collateral are immediately settled at their feed price - convert all balances in bitasset to CORE and subtract from total - any prediction markets with usd as the backing get converted to CORE as the backing @@ -109,6 +111,38 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett // settle all balances asset total_mia_settled = mia.amount(0); + // convert collateral held in bonds + const auto& bond_idx = get_index_type().indices().get(); + auto bond_itr = bond_idx.find( bitasset.id ); + while( bond_itr != bond_idx.end() ) + { + if( bond_itr->collateral.asset_id == bitasset.id ) + { + auto settled_amount = bond_itr->collateral * settlement_price; + total_mia_settled += bond_itr->collateral; + collateral_gathered -= settled_amount; + modify( *bond_itr, [&]( bond_object& obj ) { + obj.collateral = settled_amount; + }); + } + else break; + } + + // cancel all bond offers holding the bitasset and refund the offer + const auto& bond_offer_idx = get_index_type().indices().get(); + auto bond_offer_itr = bond_offer_idx.find( bitasset.id ); + while( bond_offer_itr != bond_offer_idx.end() ) + { + if( bond_offer_itr->amount.asset_id == bitasset.id ) + { + adjust_balance( bond_offer_itr->offered_by_account, bond_offer_itr->amount ); + auto old_itr = bond_offer_itr; + bond_offer_itr++; + remove( *old_itr ); + } + else break; + } + const auto& index = get_index_type().indices().get(); auto range = index.equal_range(mia.get_id()); for( auto itr = range.first; itr != range.second; ++itr ) diff --git a/libraries/chain/include/graphene/chain/bond_evaluator.hpp b/libraries/chain/include/graphene/chain/bond_evaluator.hpp new file mode 100644 index 00000000..224c3527 --- /dev/null +++ b/libraries/chain/include/graphene/chain/bond_evaluator.hpp @@ -0,0 +1,68 @@ +/* + * 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 + +namespace graphene { namespace chain { + +class bond_create_offer_evaluator : public evaluator +{ + public: + typedef bond_create_offer_operation operation_type; + + object_id_type do_evaluate( const bond_create_offer_operation& op ); + object_id_type do_apply( const bond_create_offer_operation& op ); +}; + +class bond_cancel_offer_evaluator : public evaluator +{ + public: + typedef bond_cancel_offer_operation operation_type; + + object_id_type do_evaluate( const bond_cancel_offer_operation& op ); + object_id_type do_apply( const bond_cancel_offer_operation& op ); + + const bond_offer_object* _offer = nullptr; +}; + +class bond_accept_offer_evaluator : public evaluator +{ + public: + typedef bond_accept_offer_operation operation_type; + + object_id_type do_evaluate( const bond_accept_offer_operation& op ); + object_id_type do_apply( const bond_accept_offer_operation& op ); + + const bond_offer_object* _offer = nullptr; + asset _fill_amount; +}; + +class bond_claim_collateral_evaluator : public evaluator +{ + public: + typedef bond_claim_collateral_operation operation_type; + + object_id_type do_evaluate( const bond_claim_collateral_operation& op ); + object_id_type do_apply( const bond_claim_collateral_operation& op ); + + const bond_object* _bond = nullptr; + asset _interest_due; +}; + +} } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/bond_object.hpp b/libraries/chain/include/graphene/chain/bond_object.hpp new file mode 100644 index 00000000..3c005c28 --- /dev/null +++ b/libraries/chain/include/graphene/chain/bond_object.hpp @@ -0,0 +1,111 @@ +/* + * 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 + +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + + /** + * @ingroup object + */ + class bond_object : public graphene::db::abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = bond_object_type; + + asset_id_type collateral_type()const { return collateral.asset_id; } + + account_id_type borrower; + account_id_type lender; + asset borrowed; + /** if collateral is the core asset, then voting rights belong to the borrower + * because the borrower is owner of the collateral until they default + */ + asset collateral; + uint16_t interest_apr = 0; + time_point_sec start_date; + /** after this date the lender can collect the collateral at will or let it float */ + time_point_sec due_date; + /** the loan cannot be paid off before this date */ + time_point_sec earliest_payoff_date; + }; + + /** + * @ingroup object + */ + class bond_offer_object : public graphene::db::abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = bond_offer_object_type; + + asset_id_type asset_type()const { return amount.asset_id; } + + account_id_type offered_by_account; + bool offer_to_borrow = false; // Offer to borrow if true, and offer to lend otherwise + asset amount; + share_type min_match; ///< asset type same as ammount.asset_id + price collateral_rate; + uint32_t min_loan_period_sec = 0; + uint32_t loan_period_sec = 0; + uint16_t interest_apr = 0; + }; + + struct by_borrower; + struct by_lender; + struct by_offerer; + struct by_collateral; /// needed for blackswan resolution + struct by_asset; /// needed for blackswan resolution + + typedef multi_index_container< + bond_object, + indexed_by< + hashed_unique< tag, member< object, object_id_type, &object::id > >, + ordered_non_unique< tag, member >, + ordered_non_unique< tag, member >, + hashed_non_unique< tag, const_mem_fun > + > + > bond_object_multi_index_type; + + typedef generic_index bond_index; + + /** + * Todo: consider adding index of tuple + * Todo: consider adding index of tuple + */ + typedef multi_index_container< + bond_offer_object, + indexed_by< + hashed_unique< tag, member< object, object_id_type, &object::id > >, + ordered_non_unique< tag, member >, + hashed_non_unique< tag, const_mem_fun > + > + > bond_offer_object_multi_index_type; + + typedef generic_index bond_offer_index; + +}} // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::bond_object, (graphene::db::object), + (borrower)(lender)(borrowed)(collateral)(interest_apr)(start_date)(due_date)(earliest_payoff_date) ) +FC_REFLECT_DERIVED( graphene::chain::bond_offer_object, (graphene::db::object), (offered_by_account)(offer_to_borrow)(amount)(min_match)(collateral_rate)(min_loan_period_sec)(loan_period_sec)(interest_apr) ) diff --git a/libraries/chain/include/graphene/chain/operations.hpp b/libraries/chain/include/graphene/chain/operations.hpp index b670be1d..5b3cf85b 100644 --- a/libraries/chain/include/graphene/chain/operations.hpp +++ b/libraries/chain/include/graphene/chain/operations.hpp @@ -1248,6 +1248,123 @@ namespace graphene { namespace chain { } }; + /** + * @ingroup operations + * + * Bond offers are objects that exist on the blockchain and can be + * filled in full or in part by someone using the accept_bond_offer + * operation. When the offer is accepted a new bond_object is + * created that defines the terms of the loan. + * + * @return bond_offer_id + */ + struct bond_create_offer_operation + { + asset fee; + account_id_type creator; + bool offer_to_borrow = false; ///< Offer to borrow if true, and offer to lend otherwise + asset amount; ///< Amount to lend or secure depending on above + share_type min_match; ///< asset id same as amount.asset_id and sets the minimum match that will be accepted + price collateral_rate; ///< To derive amount of collateral or principle based on above + /** after this time the lender can let the loan float or collect the collateral at will */ + uint32_t min_loan_period_sec = 0; ///< the earliest the loan may be paid off + uint32_t loan_period_sec = 0; + uint16_t interest_apr = 0; ///< MAX_INTEREST_APR == 100% and is max value + + account_id_type fee_payer()const { return creator; } + 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 + { + acc.adjust( fee_payer(), -fee ); + acc.adjust( creator, -amount ); + } + }; + + /** + * @ingroup operations + * Subtracts refund from bond_offer.amount and frees bond_offer if refund == bond_offer.amount + */ + struct bond_cancel_offer_operation + { + asset fee; + account_id_type creator; + bond_offer_id_type offer_id; + asset refund; + + account_id_type fee_payer()const { return creator; } + 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 + { + acc.adjust( fee_payer(), -fee ); + acc.adjust( creator, refund ); + } + }; + + /** + * @ingroup operations + * @return new bond_id + */ + struct bond_accept_offer_operation + { + asset fee; + account_id_type claimer; + account_id_type lender; + account_id_type borrower; ///< included in case of offer to borrow, because borrower will receive funds + bond_offer_id_type offer_id; + asset amount_borrowed; ///< should equal amount_collateral * offer_id->collateral_rate + asset amount_collateral; ///< should equal amount_borrowed * offer_id->collateral_rate + + account_id_type fee_payer()const { return claimer; } + 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 + { + acc.adjust( fee_payer(), -fee ); + if( claimer == lender ) + acc.adjust( claimer, -amount_borrowed ); + else // claimer == borrower + acc.adjust( claimer, -amount_collateral ); + acc.adjust( borrower, amount_borrowed ); + } + }; + + /** + * @ingroup operations + * After the loan period the lender can claim + * the collateral, prior to the loan period expiring + * the borrower can claim it by paying off the loan + */ + struct bond_claim_collateral_operation + { + asset fee; + account_id_type claimer; ///< must be bond_id->lender or bond_id->borrower + account_id_type lender; ///< must be bond_id->lender + bond_id_type bond_id; + asset payoff_amount; + + /** the borrower can claim a percentage of the collateral propotional to the + * percentage of the debt+interest that was paid off + */ + asset collateral_claimed; + + account_id_type fee_payer()const { return claimer; } + 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 + { + acc.adjust( fee_payer(), -fee ); + acc.adjust( claimer, -payoff_amount ); + acc.adjust( claimer, collateral_claimed ); + acc.adjust( lender, payoff_amount ); + } + }; + /** * @brief Create a vesting balance. * @ingroup operations @@ -1427,6 +1544,10 @@ namespace graphene { namespace chain { global_parameters_update_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, + bond_create_offer_operation, + bond_cancel_offer_operation, + bond_accept_offer_operation, + bond_claim_collateral_operation, worker_create_operation, custom_operation > operation; @@ -1655,6 +1776,11 @@ FC_REFLECT( graphene::chain::withdraw_permission_claim_operation, (fee)(withdraw FC_REFLECT( graphene::chain::withdraw_permission_delete_operation, (fee)(withdraw_from_account)(authorized_account) (withdrawal_permission) ) +FC_REFLECT( graphene::chain::bond_create_offer_operation, (fee)(creator)(offer_to_borrow)(amount)(min_match)(collateral_rate)(min_loan_period_sec)(loan_period_sec)(interest_apr) ) +FC_REFLECT( graphene::chain::bond_cancel_offer_operation, (fee)(creator)(offer_id)(refund) ) +FC_REFLECT( graphene::chain::bond_accept_offer_operation, (fee)(claimer)(lender)(borrower)(offer_id)(amount_borrowed)(amount_collateral) ) +FC_REFLECT( graphene::chain::bond_claim_collateral_operation, (fee)(claimer)(lender)(bond_id)(payoff_amount)(collateral_claimed) ) + FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(vesting_seconds) ) FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) ) diff --git a/libraries/chain/include/graphene/chain/types.hpp b/libraries/chain/include/graphene/chain/types.hpp index 7a7d15d1..92c00b3a 100644 --- a/libraries/chain/include/graphene/chain/types.hpp +++ b/libraries/chain/include/graphene/chain/types.hpp @@ -114,6 +114,8 @@ namespace graphene { namespace chain { proposal_object_type, operation_history_object_type, withdraw_permission_object_type, + bond_offer_object_type, + bond_object_type, vesting_balance_object_type, worker_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types @@ -158,6 +160,8 @@ namespace graphene { namespace chain { class proposal_object; class operation_history_object; class withdraw_permission_object; + class bond_object; + class bond_offer_object; class vesting_balance_object; class witness_schedule_object; class worker_object; @@ -175,6 +179,8 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type; typedef object_id< protocol_ids, operation_history_object_type, operation_history_object> operation_history_id_type; typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type; + typedef object_id< protocol_ids, bond_offer_object_type, bond_offer_object> bond_offer_id_type; + typedef object_id< protocol_ids, bond_object_type, bond_object> bond_id_type; typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; @@ -373,6 +379,10 @@ namespace graphene { namespace chain { uint32_t membership_annual_fee; ///< the annual cost of a membership subscription uint32_t membership_lifetime_fee; ///< the cost to upgrade to a lifetime member uint32_t withdraw_permission_update_fee; ///< the cost to create/update a withdraw permission + uint32_t create_bond_offer_fee; + uint32_t cancel_bond_offer_fee; + uint32_t accept_bond_offer_fee; + uint32_t claim_bond_collateral_fee; uint32_t vesting_balance_create_fee; uint32_t vesting_balance_withdraw_fee; uint32_t global_settle_fee; @@ -493,6 +503,8 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (proposal_object_type) (operation_history_object_type) (withdraw_permission_object_type) + (bond_offer_object_type) + (bond_object_type) (vesting_balance_object_type) (worker_object_type) (OBJECT_TYPE_COUNT) @@ -547,6 +559,10 @@ FC_REFLECT( graphene::chain::fee_schedule_type, (membership_annual_fee) (membership_lifetime_fee) (withdraw_permission_update_fee) + (create_bond_offer_fee) + (cancel_bond_offer_fee) + (accept_bond_offer_fee) + (claim_bond_collateral_fee) (vesting_balance_create_fee) (vesting_balance_withdraw_fee) (global_settle_fee) @@ -593,6 +609,8 @@ FC_REFLECT_TYPENAME( graphene::chain::custom_id_type ) FC_REFLECT_TYPENAME( graphene::chain::proposal_id_type ) FC_REFLECT_TYPENAME( graphene::chain::operation_history_id_type ) FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::bond_offer_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::bond_id_type ) FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type ) FC_REFLECT_TYPENAME( graphene::chain::worker_id_type ) FC_REFLECT_TYPENAME( graphene::chain::relative_key_id_type ) diff --git a/libraries/chain/operations.cpp b/libraries/chain/operations.cpp index c08027e2..86073a71 100644 --- a/libraries/chain/operations.cpp +++ b/libraries/chain/operations.cpp @@ -804,6 +804,77 @@ share_type custom_operation::calculate_fee( const fee_schedule_type& k )const return (data.size() * k.data_fee)/1024; } +void bond_create_offer_operation::get_required_auth( flat_set& active_auth_set, flat_set& )const +{ + active_auth_set.insert( creator ); +} + +void bond_create_offer_operation::validate()const +{ try { + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( amount.amount > 0 ); + collateral_rate.validate(); + FC_ASSERT( (amount * collateral_rate).amount > 0 ); + FC_ASSERT( min_loan_period_sec > 0 ); + FC_ASSERT( loan_period_sec >= min_loan_period_sec ); + FC_ASSERT( interest_apr <= GRAPHENE_MAX_INTEREST_APR ); +} FC_CAPTURE_AND_RETHROW((*this)) } + +share_type bond_create_offer_operation::calculate_fee( const fee_schedule_type& schedule )const +{ + return schedule.create_bond_offer_fee; +} + + +void bond_cancel_offer_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const +{ + active_auth_set.insert( creator ); +} +void bond_cancel_offer_operation::validate()const +{ + FC_ASSERT( fee.amount > 0 ); + FC_ASSERT( refund.amount > 0 ); +} +share_type bond_cancel_offer_operation::calculate_fee( const fee_schedule_type& k )const +{ + return k.cancel_bond_offer_fee; +} + +void bond_accept_offer_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const +{ + active_auth_set.insert( claimer ); +} + +void bond_accept_offer_operation::validate()const +{ + FC_ASSERT( fee.amount > 0 ); + (amount_collateral / amount_borrowed).validate(); + FC_ASSERT( claimer == borrower || claimer == lender ); + FC_ASSERT( borrower != lender ); +} + +share_type bond_accept_offer_operation::calculate_fee( const fee_schedule_type& k )const +{ + return k.accept_bond_offer_fee; +} +void bond_claim_collateral_operation::get_required_auth(flat_set& active_auth_set, flat_set&)const +{ + active_auth_set.insert( claimer ); +} + +void bond_claim_collateral_operation::validate()const +{ + FC_ASSERT( fee.amount > 0 ); + FC_ASSERT(payoff_amount.amount >= 0 ); + FC_ASSERT(collateral_claimed.amount >= 0 ); + FC_ASSERT( payoff_amount.asset_id != collateral_claimed.asset_id ); +} + +share_type bond_claim_collateral_operation::calculate_fee( const fee_schedule_type& k )const +{ + return k.claim_bond_collateral_fee; +} + void worker_create_operation::get_required_auth(flat_set& active_auth_set, flat_set&) const { active_auth_set.insert(owner); diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index d8e1148e..77badee4 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -232,6 +232,18 @@ struct operation_get_impacted_accounts _impacted.insert( account_id_type() ); } + void operator()( const bond_create_offer_operation& o )const { } + void operator()( const bond_cancel_offer_operation& o )const { } + void operator()( const bond_accept_offer_operation& o )const { + _impacted.insert( o.borrower ); + _impacted.insert( o.lender ); + } + void operator()( const bond_claim_collateral_operation& o )const + { + _impacted.insert( o.lender ); + _impacted.insert( o.claimer ); + } + void operator()( const vesting_balance_create_operation& o )const { _impacted.insert( o.creator ); diff --git a/libraries/wallet/cache.cpp b/libraries/wallet/cache.cpp index d5c3f382..3c68e708 100644 --- a/libraries/wallet/cache.cpp +++ b/libraries/wallet/cache.cpp @@ -26,6 +26,7 @@ #include #include #include +#include using namespace fc; using namespace graphene::chain; @@ -86,6 +87,10 @@ object* create_object( const variant& v ) return create_object_of_type< operation_history_object >( v ); case withdraw_permission_object_type: return create_object_of_type< withdraw_permission_object >( v ); + case bond_offer_object_type: + return create_object_of_type< bond_offer_object >( v ); + case bond_object_type: + return create_object_of_type< bond_object >( v ); default: ; } diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 21db9e66..deefe0a1 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -16,6 +16,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 27d57445..1717a04a 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -150,6 +151,12 @@ void database_fixture::verify_asset_supplies( )const { total_balances[asset_id_type()] += witness_obj.accumulated_income; } + for( const bond_offer_object& bond_offer : db.get_index_type().indices() ) + { + total_balances[ bond_offer.amount.asset_id ] += bond_offer.amount.amount; + if( bond_offer.amount.asset_id == asset_id_type() ) + core_in_orders += bond_offer.amount.amount; + } for( const vesting_balance_object& vbo : db.get_index_type< simple_index >() ) total_balances[ vbo.balance.asset_id ] += vbo.balance.amount; diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index e3bc8c00..27d4e5d2 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2080,6 +2080,7 @@ BOOST_AUTO_TEST_CASE( margin_call_black_swan ) * 2) Short Orders for BitAsset backed by BitUSD * 3) Call Orders for BitAsset backed by BitUSD * 4) Issuer Fees + * 5) Bond Market Collateral * * This test should fail until the black swan handling code can * perform a recursive blackswan for any other BitAssets that use @@ -2137,6 +2138,69 @@ BOOST_AUTO_TEST_CASE( unimp_transfer_cashback_test ) } } +BOOST_AUTO_TEST_CASE( bond_create_offer_test ) +{ try { + bond_create_offer_operation op; + op.fee = asset( 0, 0 ); + op.creator = account_id_type(); + op.amount = asset( 1, 0 ); + op.collateral_rate = price( asset( 1, 0 ), asset( 1, 1 ) ); + op.min_loan_period_sec = 1; + op.loan_period_sec = 1; + + // Fee must be non-negative + REQUIRE_OP_VALIDATION_SUCCESS( op, fee, asset( 1, 0 ) ); + REQUIRE_OP_VALIDATION_SUCCESS( op, fee, asset( 0, 0 ) ); + REQUIRE_OP_VALIDATION_FAILURE( op, fee, asset( -1, 0 ) ); + + // Amount must be positive + REQUIRE_OP_VALIDATION_SUCCESS( op, amount, asset( 1, 0 ) ); + REQUIRE_OP_VALIDATION_FAILURE( op, amount, asset( 0, 0 ) ); + REQUIRE_OP_VALIDATION_FAILURE( op, amount, asset( -1, 0 ) ); + + // Collateral rate must be valid + REQUIRE_OP_VALIDATION_SUCCESS( op, collateral_rate, price( asset( 1, 0 ), asset( 1, 1 ) ) ); + REQUIRE_OP_VALIDATION_FAILURE( op, collateral_rate, price( asset( 0, 0 ), asset( 1, 1 ) ) ); + REQUIRE_OP_VALIDATION_FAILURE( op, collateral_rate, price( asset( 1, 0 ), asset( 0, 1 ) ) ); + REQUIRE_OP_VALIDATION_FAILURE( op, collateral_rate, price( asset( 1, 0 ), asset( 1, 0 ) ) ); + + // Min loan period must be at least 1 sec + REQUIRE_OP_VALIDATION_SUCCESS( op, min_loan_period_sec, 1 ); + REQUIRE_OP_VALIDATION_FAILURE( op, min_loan_period_sec, 0 ); + + // Loan period must be greater than min load period + REQUIRE_OP_VALIDATION_SUCCESS( op, loan_period_sec, op.min_loan_period_sec + 1 ); + REQUIRE_OP_VALIDATION_FAILURE( op, loan_period_sec, 0 ); + + // Interest APR cannot be greater than max + REQUIRE_OP_VALIDATION_FAILURE( op, interest_apr, GRAPHENE_MAX_INTEREST_APR + 1 ); + REQUIRE_OP_VALIDATION_SUCCESS( op, interest_apr, GRAPHENE_MAX_INTEREST_APR ); + REQUIRE_OP_VALIDATION_SUCCESS( op, interest_apr, 0 ); + + // Setup world state we will need to test actual evaluation + INVOKE( create_uia ); + const auto& test_asset = get_asset( "TEST" ); + const auto& nathan_account = create_account( "nathan" ); + transfer( account_id_type()( db ), nathan_account, asset( 1, 0 ) ); + + op.creator = nathan_account.get_id(); + op.collateral_rate.quote.asset_id = test_asset.get_id(); + trx.operations.emplace_back( op ); + + // Insufficient funds in creator account + REQUIRE_THROW_WITH_VALUE( op, creator, account_id_type( 1 ) ); + + // Insufficient principle + REQUIRE_THROW_WITH_VALUE( op, amount, asset( 2, 0 ) ); + + // Insufficient collateral + op.offer_to_borrow = true; + REQUIRE_THROW_WITH_VALUE( op, amount, asset( 1, test_asset.get_id() ) ); + + // This op should be fully valid + REQUIRE_OP_EVALUATION_SUCCESS( op, offer_to_borrow, false ); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE( vesting_balance_create_test ) { try { INVOKE( create_uia ); From 33fe8e9d7cb8c80ddd289d1886d64292b033d685 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Mon, 6 Jul 2015 18:29:03 -0400 Subject: [PATCH 6/9] brain storm line of credit operations --- .../include/graphene/chain/line_of_credit.hpp | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 libraries/chain/include/graphene/chain/line_of_credit.hpp diff --git a/libraries/chain/include/graphene/chain/line_of_credit.hpp b/libraries/chain/include/graphene/chain/line_of_credit.hpp new file mode 100644 index 00000000..d75a4318 --- /dev/null +++ b/libraries/chain/include/graphene/chain/line_of_credit.hpp @@ -0,0 +1,146 @@ +#pragma once + +namespace graphene { namespace chain { + + struct line_of_credit + { + enum flag + { + /** + * Debt may be increased via credit_passthrough operation, controlled by borrower + **/ + bool allow_lender_passthrough = 0x01, + bool allow_borrower_passthrough = 0x02, + /** borrower may pull BitUSD from lender */ + bool allow_cash_advance = 0x04, + /** the lender may change the interest rate */ + bool allow_variable_interest = 0x08 + }; + + account_id_type borrower; + account_id_type lender; + share_type debt; + share_type credit_limit; + share_type passthrough_fee; + asset_id_type asset_type; + /** accumulated daily, compounded every update */ + uint16_t interest_apr = 0; + uint8_t flags = 0; + /** requires borrower and lender to approve update */ + digest_type loan_contract_digest; + }; + + /** + * Index on borrower/asset_type and lender/asset_type + */ + class line_of_credit_object : public db::abstract_object + { + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = line_of_credit_object_type; + + account_id_type borrower()const { return terms.borrower; } + account_id_type lender()const { return terms.lender; } + asset debt()const { return asset( terms.debt, terms.asset_type ); } + asset credit_limit()const { return asset( terms.credit_limit, terms.asset_type ); } + asset_id_type asset_type()const { return term.asset_type; } + + line_of_credit terms; + time_point_sec last_update; + + /** + * Both borrower and lender must approve of the line of credit before any + * debts may accrue. + */ + bool borrower_approved = false; + bool borrower_lender_approved = false; + }; + + struct by_borrower; + struct by_lender; + + /** + * @ingroup object_index + */ + typedef multi_index_container< + line_of_credit_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_non_unique< tag, composite_key< + line_of_credit_object, + const_member_fun, + const_member_fun > + >, + ordered_non_unique< tag, composite_key< + line_of_credit_object, + const_member_fun, + const_member_fun > + > + > + > line_of_credit_object_multi_index_type; + typedef generic_index line_of_credit_index; + + struct line_of_credit_create_operation + { + asset fee; + account_id_type creator; ///< pays the transaction fee and approves terms by default + line_of_credit terms; + }; + + struct line_of_credit_accept_terms_operation + { + asset fee; + line_of_credit_id_type id; + account_id_type acceptor; + }; + + struct line_of_credit_update_operation + { + asset fee; ///< paid for by terms.lender + account_id_type updater; ///< account performing the update + line_of_credit_id_type id; + /** + * Lender may increase or lower, borrower may increase + */ + uint16_t new_interest_rate = 0; + uint8_t flags; + /** + * Lender may increase or lower, borrower may lower + */ + asset new_credit_limit; + }; + + /** + * This operation is used to transfer the debt to a debt + * collector. + */ + struct line_of_credit_transfer_debt_operation + { + asset fee; ///< paid for by current_lender + line_of_credit_id_type loc; + account_id_type current_lender; + account_id_type new_lender; + }; + + + /** + * @brief enables payment of 3rd parties by rebalancing debts among users + */ + struct line_of_credit_passthrough_operation + { + struct transfer_node + { + line_of_credit_id_type loc; + account_id_type borrower; + account_id_type lender; + share_type delta_debt; + share_type fee_paid; ///< always paid to the lender + }; + + asset fee; + account_id_type fee_payer; ///< accout with actual asset to pay fee + account_id_type from; ///< account funding the init_amount + asset init_amount; + vector passthrough; ///< pass through nodes + }; + +} } // graphene / chain From c6b848ef18ed80607bfa4e0fde6fbcf38c0f6862 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Sat, 18 Jul 2015 00:52:28 -0400 Subject: [PATCH 7/9] implement payment splitter proposal --- libraries/chain/db_init.cpp | 7 + .../graphene/chain/protocol/operations.hpp | 8 +- .../graphene/chain/protocol/splitter.hpp | 147 +++++++++++ .../include/graphene/chain/protocol/types.hpp | 6 +- .../graphene/chain/splitter_evaluator.hpp | 232 ++++++++++++++++++ programs/js_operation_serializer/main.cpp | 1 + 6 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/protocol/splitter.hpp create mode 100644 libraries/chain/include/graphene/chain/splitter_evaluator.hpp diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 32a724cf..eeb5c984 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -150,6 +151,11 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -177,6 +183,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes add_index< primary_index >(); diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 853e9c5c..5c1a0b51 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -59,7 +60,12 @@ namespace graphene { namespace chain { custom_operation, assert_operation, balance_claim_operation, - override_transfer_operation + override_transfer_operation, + splitter_create_operation, + splitter_update_operation, + splitter_pay_operation, + splitter_payout_operation, + splitter_delete_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/splitter.hpp b/libraries/chain/include/graphene/chain/protocol/splitter.hpp new file mode 100644 index 00000000..36cafa35 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/splitter.hpp @@ -0,0 +1,147 @@ +/* Copyright (c) 2015, Cryptonomex, Inc. */ +#pragma once +#include + +namespace graphene { namespace chain { + + struct market_buyback + { + asset_id_type asset_to_buy; + price limit_price; + void validate()const { + limit_price.validate(); + FC_ASSERT( limit_price.quote.asset_id == asset_to_buy ); + } + }; + + typedef static_variant payment_target_type; + + struct payment_target + { + uint16_t weight = 0; + payment_target_type target; + }; + + struct payment_target_validate + { + typedef void result_type; + void operator()( const account_id_type& id )const { } + void operator()( const market_buyback& t )const + { + FC_ASSERT( t.asset_to_buy == t.limit_price.quote.asset_id ); + t.limit_price.validate(); + } + }; + + struct splitter_create_operation : public base_operation + { + /// TODO: charge fee based upon size + struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + + asset fee; + account_id_type payer; + account_id_type owner; + vector targets; + asset min_payment; + share_type max_payment; ///< same asset_id as min_payment + share_type payout_threshold; ///< same asset_id as min_payment + + void validate()const + { + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( min_payment.amount > 0 ); + FC_ASSERT( min_payment.amount <= max_payment ); + FC_ASSERT( payout_threshold >= 0 ); + for( const auto& t : targets ) + { + FC_ASSERT( t.weight > 0 ); + t.target.visit( payment_target_validate() ); + } + } + account_id_type fee_payer()const { return payer; } + }; + + struct splitter_update_operation : public base_operation + { + /// TODO: charge fee based upon size + struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + + asset fee; + splitter_id_type splitter_id; + account_id_type owner; ///< must match splitter_id->owner + account_id_type new_owner; + vector targets; + asset min_payment; + share_type max_payment; ///< same asset_id as min_payment + share_type payout_threshold; ///< same asset_id as min_payment + + void validate()const + { + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( min_payment.amount > 0 ); + FC_ASSERT( min_payment.amount <= max_payment ); + FC_ASSERT( payout_threshold >= 0 ); + for( const auto& t : targets ) FC_ASSERT( t.weight > 0 ); + } + + account_id_type fee_payer()const { return owner; } + }; + + struct splitter_pay_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + + asset fee; + splitter_id_type splitter_id; + account_id_type paying_account; ///< also fee payer + asset payment; + + void validate()const + { + FC_ASSERT( payment.amount > 0 ); + FC_ASSERT( fee.amount >= 0 ); + } + + account_id_type fee_payer()const { return paying_account; } + }; + + struct splitter_payout_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + + asset fee; + splitter_id_type splitter_id; + account_id_type owner; ///< must match splitter_id->owner + + void validate()const { FC_ASSERT( fee.amount >= 0 ); } + account_id_type fee_payer()const { return owner; } + }; + + + struct splitter_delete_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + + asset fee; + splitter_id_type splitter_id; + account_id_type owner; ///< must match splitter_id->owner + + void validate()const { FC_ASSERT( fee.amount >= 0 ); } + account_id_type fee_payer()const { return owner; } + }; + +} } + +FC_REFLECT( graphene::chain::market_buyback, (asset_to_buy)(limit_price) ) +FC_REFLECT( graphene::chain::payment_target, (weight)(target) ) +FC_REFLECT( graphene::chain::splitter_create_operation, (fee)(payer)(owner)(targets)(min_payment)(max_payment)(payout_threshold) ) +FC_REFLECT( graphene::chain::splitter_update_operation, (fee)(owner)(new_owner)(targets)(min_payment)(max_payment)(payout_threshold) ) +FC_REFLECT( graphene::chain::splitter_pay_operation, (fee)(splitter_id)(paying_account)(payment) ) +FC_REFLECT( graphene::chain::splitter_payout_operation, (fee)(splitter_id)(owner) ) +FC_REFLECT( graphene::chain::splitter_delete_operation, (fee)(splitter_id)(owner) ) +FC_REFLECT( graphene::chain::splitter_create_operation::fee_parameters_type, (fee) ); +FC_REFLECT( graphene::chain::splitter_update_operation::fee_parameters_type, (fee) ); +FC_REFLECT( graphene::chain::splitter_pay_operation::fee_parameters_type, (fee) ); +FC_REFLECT( graphene::chain::splitter_payout_operation::fee_parameters_type, (fee) ); +FC_REFLECT( graphene::chain::splitter_delete_operation::fee_parameters_type, (fee) ); + diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 64d49325..419accde 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -123,6 +123,7 @@ namespace graphene { namespace chain { vesting_balance_object_type, worker_object_type, balance_object_type, + splitter_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -169,11 +170,12 @@ namespace graphene { namespace chain { class witness_schedule_object; class worker_object; class balance_object; + class splitter_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type; - typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type; + typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type; typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type; typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type; typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type; @@ -184,6 +186,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type; typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type; typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type; + typedef object_id< protocol_ids, splitter_object_type, splitter_object> splitter_id_type; // implementation types class global_property_object; @@ -376,6 +379,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (vesting_balance_object_type) (worker_object_type) (balance_object_type) + (splitter_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, diff --git a/libraries/chain/include/graphene/chain/splitter_evaluator.hpp b/libraries/chain/include/graphene/chain/splitter_evaluator.hpp new file mode 100644 index 00000000..d8d822f2 --- /dev/null +++ b/libraries/chain/include/graphene/chain/splitter_evaluator.hpp @@ -0,0 +1,232 @@ +/* Copyright (c) 2015, Cryptonomex, Inc. */ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + + class splitter_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = splitter_object_type; + + account_id_type owner; + asset balance; + vector targets; + asset min_payment; + share_type max_payment; ///< same asset_id as min_payment + share_type payout_threshold; ///< same asset_id as min_payment + + + struct payout_visitor + { + typedef void result_type; + database& db; + asset amount; + + payout_visitor( database& d, asset a ):db(d),amount(a){} + + void operator()( const account_id_type& id )const + { + db.adjust_balance( id(db), amount ); + } + void operator()( const market_buyback& t )const + { + const auto& new_order_object = db.create([&](limit_order_object& obj){ + obj.seller = GRAPHENE_NULL_ACCOUNT; + obj.for_sale = amount.amount; + obj.sell_price = t.limit_price; + assert( amount.asset_id == t.limit_price.base.asset_id ); + }); + db.apply_order(new_order_object); + } + }; + void payout( database& db )const + { + uint64_t total_weight = 0; + for( auto& t : targets ) total_weight += t.weight; + + asset remaining = balance; + + for( uint32_t i = 0 ; i < targets.size(); ++i ) + { + const auto& t = targets[i]; + + fc::uint128 tmp( balance.amount.value ); + tmp *= t.weight; + tmp /= total_weight; + + asset payout_amount( tmp.to_uint64(), balance.asset_id ); + + if( payout_amount > remaining || (i == (targets.size() - 1)) ) + payout_amount = remaining; + + if( payout_amount.amount > 0 ) + { + t.target.visit( payout_visitor( db, payout_amount ) ); + } + remaining -= payout_amount; + } + } + }; + + struct by_account; + + typedef multi_index_container< + splitter_object, + indexed_by< + hashed_unique< tag, member< object, object_id_type, &object::id > >, + ordered_non_unique< tag, member< splitter_object, account_id_type, &splitter_object::owner > > + > + > splitter_multi_index_type; + typedef generic_index splitter_index; + + + struct target_evalautor + { + typedef void result_type; + const database& db; + + target_evalautor( database& d ):db(d){} + + void operator()( const account_id_type& id )const { id(db); } + void operator()( const market_buyback& t )const + { + /// dereference these objects to verify they exist + t.asset_to_buy(db); + t.limit_price.base.asset_id(db); + } + + }; + + class splitter_create_evaluator : public evaluator + { + public: + typedef splitter_create_operation operation_type; + + void_result do_evaluate( const splitter_create_operation& o ) + { + o.owner(db()); // dereference to prove it exists + for( auto& t : o.targets ) + t.target.visit( target_evalautor(db()) ); + return void_result(); + } + + object_id_type do_apply( const splitter_create_operation& o ) + { + const auto& new_splitter_object = db().create( [&]( splitter_object& obj ){ + obj.owner = o.owner; + obj.targets = o.targets; + obj.min_payment = o.min_payment; + obj.max_payment = o.max_payment; + obj.payout_threshold = o.payout_threshold; + obj.balance.asset_id = o.min_payment.asset_id; + }); + return new_splitter_object.id; + } + }; + + class splitter_update_evaluator : public evaluator + { + public: + typedef splitter_update_operation operation_type; + + void_result do_evaluate( const splitter_update_operation& o ) + { + const auto& sp = o.splitter_id(db()); // dereference to prove it exists + FC_ASSERT( sp.balance.amount == 0 ); + FC_ASSERT( sp.owner == o.owner ); + + for( auto& t : o.targets ) + t.target.visit( target_evalautor(db()) ); + return void_result(); + } + void_result do_apply( const splitter_update_operation& o ) + { + const auto& sp = o.splitter_id(db()); // dereference to prove it exists + db().modify( sp, [&]( splitter_object& obj ){ + obj.targets = o.targets; + obj.owner = o.new_owner; + obj.min_payment = o.min_payment; + obj.max_payment = o.max_payment; + obj.payout_threshold = o.payout_threshold; + obj.balance.asset_id = o.min_payment.asset_id; + }); + return void_result(); + } + }; + + class splitter_pay_evaluator : public evaluator + { + public: + typedef splitter_pay_operation operation_type; + + void_result do_evaluate( const splitter_pay_operation& o ) + { + const auto& sp = o.splitter_id(db()); // dereference to prove it exists + FC_ASSERT( o.payment.asset_id == sp.min_payment.asset_id ); + FC_ASSERT( o.payment >= sp.min_payment ); + FC_ASSERT( o.payment.amount <= sp.max_payment ); + return void_result(); + } + void_result do_apply( const splitter_pay_operation& o ) + { + db().adjust_balance( o.paying_account, -o.payment ); + const auto& sp = o.splitter_id(db()); // dereference to prove it exists + db().modify( sp, [&]( splitter_object& obj ){ + obj.balance += o.payment; + }); + + if( sp.balance.amount > sp.payout_threshold ) + sp.payout(db()); + + return void_result(); + } + }; + class splitter_payout_evaluator : public evaluator + { + public: + typedef splitter_payout_operation operation_type; + + void_result do_evaluate( const splitter_payout_operation& o ) + { + const auto& sp = o.splitter_id(db()); // dereference to prove it exists + FC_ASSERT( sp.owner == o.owner ); + FC_ASSERT( sp.balance.amount > 0 ); + return void_result(); + } + + void_result do_apply( const splitter_payout_operation& o ) + { + const auto& sp = o.splitter_id(db()); // dereference to prove it exists + sp.payout(db()); + return void_result(); + } + }; + class splitter_delete_evaluator : public evaluator + { + public: + typedef splitter_delete_operation operation_type; + + void_result do_evaluate( const splitter_delete_operation& o ) + { + const auto& sp = o.splitter_id(db()); // dereference to prove it exists + FC_ASSERT( sp.owner == o.owner ); + FC_ASSERT( sp.balance.amount == 0 ); + return void_result(); + } + void_result do_apply( const splitter_delete_operation& o ) + { + db().remove( o.splitter_id(db()) ); + return void_result(); + } + }; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::splitter_object, + (graphene::db::object), + (owner)(balance)(targets)(min_payment)(max_payment)(payout_threshold) + ) diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 6022b9fe..6deb8755 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace graphene::chain; From 8b8d35fad190becb283e847474de2dd25a02a74c Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Sat, 18 Jul 2015 01:36:31 -0400 Subject: [PATCH 8/9] Update how fees are paid - price per kb for splitters (based on ram usage and number of parties) - pay payout fee when threshold is reached --- .../graphene/chain/protocol/splitter.hpp | 20 +++++++---- .../graphene/chain/splitter_evaluator.hpp | 35 ++++++++++++++++++- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/graphene/chain/protocol/splitter.hpp b/libraries/chain/include/graphene/chain/protocol/splitter.hpp index 36cafa35..5ce1f34d 100644 --- a/libraries/chain/include/graphene/chain/protocol/splitter.hpp +++ b/libraries/chain/include/graphene/chain/protocol/splitter.hpp @@ -35,8 +35,10 @@ namespace graphene { namespace chain { struct splitter_create_operation : public base_operation { - /// TODO: charge fee based upon size - struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + struct fee_parameters_type { + uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; + uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; + }; asset fee; account_id_type payer; @@ -59,12 +61,16 @@ namespace graphene { namespace chain { } } account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k )const + { return calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte ) + k.fee; } }; struct splitter_update_operation : public base_operation { - /// TODO: charge fee based upon size - struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; }; + struct fee_parameters_type { + uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; + uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; + }; asset fee; splitter_id_type splitter_id; @@ -85,6 +91,8 @@ namespace graphene { namespace chain { } account_id_type fee_payer()const { return owner; } + share_type calculate_fee(const fee_parameters_type& k )const + { return calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte ) + k.fee; } }; struct splitter_pay_operation : public base_operation @@ -139,8 +147,8 @@ FC_REFLECT( graphene::chain::splitter_update_operation, (fee)(owner)(new_owner)( FC_REFLECT( graphene::chain::splitter_pay_operation, (fee)(splitter_id)(paying_account)(payment) ) FC_REFLECT( graphene::chain::splitter_payout_operation, (fee)(splitter_id)(owner) ) FC_REFLECT( graphene::chain::splitter_delete_operation, (fee)(splitter_id)(owner) ) -FC_REFLECT( graphene::chain::splitter_create_operation::fee_parameters_type, (fee) ); -FC_REFLECT( graphene::chain::splitter_update_operation::fee_parameters_type, (fee) ); +FC_REFLECT( graphene::chain::splitter_create_operation::fee_parameters_type, (fee)(price_per_kbyte) ); +FC_REFLECT( graphene::chain::splitter_update_operation::fee_parameters_type, (fee)(price_per_kbyte) ); FC_REFLECT( graphene::chain::splitter_pay_operation::fee_parameters_type, (fee) ); FC_REFLECT( graphene::chain::splitter_payout_operation::fee_parameters_type, (fee) ); FC_REFLECT( graphene::chain::splitter_delete_operation::fee_parameters_type, (fee) ); diff --git a/libraries/chain/include/graphene/chain/splitter_evaluator.hpp b/libraries/chain/include/graphene/chain/splitter_evaluator.hpp index d8d822f2..c5d60f57 100644 --- a/libraries/chain/include/graphene/chain/splitter_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/splitter_evaluator.hpp @@ -43,8 +43,41 @@ namespace graphene { namespace chain { db.apply_order(new_order_object); } }; - void payout( database& db )const + void payout( database& db, bool pay_fee = true )const { + /** this happens when the threshold is used as the trigger, but not when the payout operation is used */ + if( pay_fee ) + { + const auto& fee_config = db.get_global_properties().parameters.current_fees->get(); + asset fee_in_aobj_units = asset(fee_config.fee); + const asset_object& aobj = min_payment.asset_id(db); + const asset_dynamic_data_object& aobj_dyn = aobj.dynamic_asset_data_id(db); + if( aobj.id != asset_id_type() ) + { + fee_in_aobj_units = asset(fee_config.fee) * aobj.options.core_exchange_rate; + /// not enough in fee pool to cover + if( aobj_dyn.fee_pool < fee_config.fee ) return; + } + /// not enough to cover payout fee, so don't payout. + if( fee_in_aobj_units > balance ) return; + + + db.modify( *this, [&]( splitter_object& obj ){ obj.balance-= fee_in_aobj_units; } ); + + if( aobj.id != asset_id_type() ) + { + db.modify( aobj_dyn, [&]( asset_dynamic_data_object& obj ){ + obj.accumulated_fees += fee_in_aobj_units.amount; + obj.fee_pool -= fee_config.fee; + }); + } + + db.modify( aobj_dyn, [&]( asset_dynamic_data_object& obj ){ + obj.current_supply -= fee_config.fee; + }); + } + + uint64_t total_weight = 0; for( auto& t : targets ) total_weight += t.weight; From b21fb0c3a8e7143fd9f34281cf9fa6c16ba7fad5 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Sat, 18 Jul 2015 01:58:23 -0400 Subject: [PATCH 9/9] adding some documentation --- .../include/graphene/chain/protocol/splitter.hpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/chain/include/graphene/chain/protocol/splitter.hpp b/libraries/chain/include/graphene/chain/protocol/splitter.hpp index 5ce1f34d..1aeff0c8 100644 --- a/libraries/chain/include/graphene/chain/protocol/splitter.hpp +++ b/libraries/chain/include/graphene/chain/protocol/splitter.hpp @@ -1,4 +1,18 @@ -/* Copyright (c) 2015, Cryptonomex, Inc. */ +/* Copyright (c) 2015, Cryptonomex, Inc. + * + * The proposed feature is the Payment Splitter, which is capable of receiving + * payments and disseminating these payments to a pre- defined list of weighted + * targets proportionally to the weight assigned each target. The targets + * defined in this document are accounts, and asset markets. + * + * A payment sent to the object may optionally be limited by a + * minimum and maximum. The object will collect payments until the total + * balance of the object exceeds a threshold, at which point it will auto- + * matically pay to the targets, subtracting fees from the balance prior to + * processing. Alternatively, a payout can be manually triggered with an + * operation that pays the fee explicitly. + * + * */ #pragma once #include