/* * Copyright (c) 2015 Cryptonomex, Inc., and contributors. * * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include #include namespace graphene { namespace chain { class database; /** * @class account_statistics_object * @ingroup object * @ingroup implementation * * This object contains regularly updated statistical data about an account. It is provided for the purpose of * separating the account data that changes frequently from the account data that is mostly static, which will * minimize the amount of data that must be backed up as part of the undo history everytime a transfer is made. */ class account_statistics_object : public graphene::db::abstract_object { public: static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_account_statistics_object_type; account_id_type owner; /** * Keep the most recent operation as a root pointer to a linked list of the transaction history. */ account_transaction_history_id_type most_recent_op; /** Total operations related to this account. */ uint32_t total_ops = 0; /** Total operations related to this account that has been removed from the database. */ uint32_t removed_ops = 0; /** * When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for * transfers). Rather than maintaining an index of [asset,owner,order_id] we will simply maintain the running * total here and update it every time an order is created or modified. */ share_type total_core_in_orders; /** * Tracks the total fees paid by this account for the purpose of calculating bulk discounts. */ share_type lifetime_fees_paid; /** * Tracks the fees paid by this account which have not been disseminated to the various parties that receive * them yet (registrar, referrer, lifetime referrer, network, etc). This is used as an optimization to avoid * doing massive amounts of uint128 arithmetic on each and every operation. * * These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance * interval. */ share_type pending_fees; /** * Same as @ref pending_fees, except these fees will be paid out as pre-vested cash-back (immediately * available for withdrawal) rather than requiring the normal vesting period. */ share_type pending_vested_fees; /// @brief Split up and pay out @ref pending_fees and @ref pending_vested_fees void process_fees(const account_object& a, database& d) const; /** * Core fees are paid into the account_statistics_object by this method */ void pay_fee( share_type core_fee, share_type cashback_vesting_threshold ); }; /** * @brief Tracks the balance of a single account/asset pair * @ingroup object * * This object is indexed on owner and asset_type so that black swan * events in asset_type can be processed quickly. */ class account_balance_object : public abstract_object { public: static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_account_balance_object_type; account_id_type owner; asset_id_type asset_type; share_type balance; asset get_balance()const { return asset(balance, asset_type); } void adjust_balance(const asset& delta); }; /** * @brief This class represents an account on the object graph * @ingroup object * @ingroup protocol * * Accounts are the primary unit of authority on the graphene system. Users must have an account in order to use * assets, trade in the markets, vote for committee_members, etc. */ class account_object : public graphene::db::abstract_object { public: static const uint8_t space_id = protocol_ids; static const uint8_t type_id = account_object_type; /** * The time at which this account's membership expires. * If set to any time in the past, the account is a basic account. * If set to time_point_sec::maximum(), the account is a lifetime member. * If set to any time not in the past less than time_point_sec::maximum(), the account is an annual member. * * See @ref is_lifetime_member, @ref is_basic_account, @ref is_annual_member, and @ref is_member */ time_point_sec membership_expiration_date; ///The account that paid the fee to register this account. Receives a percentage of referral rewards. account_id_type registrar; /// The account credited as referring this account. Receives a percentage of referral rewards. account_id_type referrer; /// The lifetime member at the top of the referral tree. Receives a percentage of referral rewards. account_id_type lifetime_referrer; /// Percentage of fee which should go to network. uint16_t network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; /// Percentage of fee which should go to lifetime referrer. uint16_t lifetime_referrer_fee_percentage = 0; /// Percentage of referral rewards (leftover fee after paying network and lifetime referrer) which should go /// to referrer. The remainder of referral rewards goes to the registrar. uint16_t referrer_rewards_percentage = 0; /// The account's name. This name must be unique among all account names on the graph. May not be empty. string name; /** * The owner authority represents absolute control over the account. Usually the keys in this authority will * be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes * complete and irrevocable loss of the account. Generally the only time the owner authority is required is to * update the active authority. */ authority owner; /// The owner authority contains the hot keys of the account. This authority has control over nearly all /// operations the account may perform. authority active; typedef account_options options_type; account_options options; /// The reference implementation records the account's statistics in a separate object. This field contains the /// ID of that object. account_statistics_id_type statistics; /** * This is a set of all accounts which have 'whitelisted' this account. Whitelisting is only used in core * validation for the purpose of authorizing accounts to hold and transact in whitelisted assets. This * account cannot update this set, except by transferring ownership of the account, which will clear it. Other * accounts may add or remove their IDs from this set. */ flat_set whitelisting_accounts; /** * Optionally track all of the accounts this account has whitelisted or blacklisted, these should * be made Immutable so that when the account object is cloned no deep copy is required. This state is * tracked for GUI display purposes. * * TODO: move white list tracking to its own multi-index container rather than having 4 fields on an * account. This will scale better because under the current design if you whitelist 2000 accounts, * then every time someone fetches this account object they will get the full list of 2000 accounts. */ ///@{ set whitelisted_accounts; set blacklisted_accounts; ///@} /** * This is a set of all accounts which have 'blacklisted' this account. Blacklisting is only used in core * validation for the purpose of forbidding accounts from holding and transacting in whitelisted assets. This * account cannot update this set, and it will be preserved even if the account is transferred. Other accounts * may add or remove their IDs from this set. */ flat_set blacklisting_accounts; /** * Vesting balance which receives cashback_reward deposits. */ optional cashback_vb; special_authority owner_special_authority = no_special_authority(); special_authority active_special_authority = no_special_authority(); /** * This flag is set when the top_n logic sets both authorities, * and gets reset when authority or special_authority is set. */ uint8_t top_n_control_flags = 0; static const uint8_t top_n_control_owner = 1; static const uint8_t top_n_control_active = 2; /** * This is a set of assets which the account is allowed to have. * This is utilized to restrict buyback accounts to the assets that trade in their markets. * In the future we may expand this to allow accounts to e.g. voluntarily restrict incoming transfers. */ optional< flat_set > allowed_assets; optional< affiliate_reward_distributions > affiliate_distributions; bool has_special_authority()const { return (owner_special_authority.which() != special_authority::tag< no_special_authority >::value) || (active_special_authority.which() != special_authority::tag< no_special_authority >::value); } template const vesting_balance_object& cashback_balance(const DB& db)const { FC_ASSERT(cashback_vb); return db.get(*cashback_vb); } /// @return true if this is a lifetime member account; false otherwise. bool is_lifetime_member()const { return membership_expiration_date == time_point_sec::maximum(); } /// @return true if this is a basic account; false otherwise. bool is_basic_account(time_point_sec now)const { return now > membership_expiration_date; } /// @return true if the account is an unexpired annual member; false otherwise. /// @note This method will return false for lifetime members. bool is_annual_member(time_point_sec now)const { return !is_lifetime_member() && !is_basic_account(now); } /// @return true if the account is an annual or lifetime member; false otherwise. bool is_member(time_point_sec now)const { return !is_basic_account(now); } account_id_type get_id()const { return id; } }; /** * @brief This secondary index will allow a reverse lookup of all accounts that a particular key or account * is an potential signing authority. */ class account_member_index : public secondary_index { public: virtual void object_inserted( const object& obj ) override; virtual void object_removed( const object& obj ) override; virtual void about_to_modify( const object& before ) override; virtual void object_modified( const object& after ) override; /** given an account or key, map it to the set of accounts that reference it in an active or owner authority */ map< account_id_type, set > account_to_account_memberships; map< public_key_type, set > account_to_key_memberships; /** some accounts use address authorities in the genesis block */ map< address, set > account_to_address_memberships; protected: set get_account_members( const account_object& a )const; set get_key_members( const account_object& a )const; set
get_address_members( const account_object& a )const; set before_account_members; set before_key_members; set
before_address_members; }; /** * @brief This secondary index will allow a reverse lookup of all accounts that have been referred by * a particular account. */ class account_referrer_index : public secondary_index { public: virtual void object_inserted( const object& obj ) override; virtual void object_removed( const object& obj ) override; virtual void about_to_modify( const object& before ) override; virtual void object_modified( const object& after ) override; /** maps the referrer to the set of accounts that they have referred */ map< account_id_type, set > referred_by; }; /** * @brief Tracks a pending payout of a single dividend payout asset * from a single dividend holder asset to a holder's account. * * Each maintenance interval, this will be adjusted to account for * any new transfers to the dividend distribution account. * @ingroup object * */ class pending_dividend_payout_balance_for_holder_object : public abstract_object { public: static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_pending_dividend_payout_balance_for_holder_object_type; account_id_type owner; asset_id_type dividend_holder_asset_type; asset_id_type dividend_payout_asset_type; share_type pending_balance; asset get_pending_balance()const { return asset(pending_balance, dividend_payout_asset_type); } void adjust_balance(const asset& delta); }; struct by_account_asset; struct by_asset_balance; /** * @ingroup object_index */ typedef multi_index_container< account_balance_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_unique< tag, composite_key< account_balance_object, member, member > >, ordered_unique< tag, composite_key< account_balance_object, member, member, member >, composite_key_compare< std::less< asset_id_type >, std::greater< share_type >, std::less< account_id_type > > > > > account_balance_object_multi_index_type; /** * @ingroup object_index */ typedef generic_index account_balance_index; struct by_name{}; /** * @ingroup object_index */ typedef multi_index_container< account_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_unique< tag, member > > > account_multi_index_type; /** * @ingroup object_index */ typedef generic_index account_index; struct by_dividend_payout_account{}; // use when calculating pending payouts struct by_dividend_account_payout{}; // use when doing actual payouts struct by_account_dividend_payout{}; // use in get_full_accounts() /** * @ingroup object_index */ typedef multi_index_container< pending_dividend_payout_balance_for_holder_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_unique< tag, composite_key< pending_dividend_payout_balance_for_holder_object, member, member, member > >, ordered_unique< tag, composite_key< pending_dividend_payout_balance_for_holder_object, member, member, member > >, ordered_unique< tag, composite_key< pending_dividend_payout_balance_for_holder_object, member, member, member > > > > pending_dividend_payout_balance_for_holder_object_multi_index_type; /** * @ingroup object_index */ typedef generic_index pending_dividend_payout_balance_for_holder_object_index; }} FC_REFLECT_DERIVED( graphene::chain::account_object, (graphene::db::object), (membership_expiration_date)(registrar)(referrer)(lifetime_referrer) (network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage) (name)(owner)(active)(options)(statistics)(whitelisting_accounts)(blacklisting_accounts) (whitelisted_accounts)(blacklisted_accounts) (cashback_vb) (owner_special_authority)(active_special_authority) (top_n_control_flags) (allowed_assets)(affiliate_distributions) ) FC_REFLECT_DERIVED( graphene::chain::account_balance_object, (graphene::db::object), (owner)(asset_type)(balance) ) FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object), (owner) (most_recent_op) (total_ops)(removed_ops) (total_core_in_orders) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) ) FC_REFLECT_DERIVED( graphene::chain::pending_dividend_payout_balance_for_holder_object, (graphene::db::object), (owner)(dividend_holder_asset_type)(dividend_payout_asset_type)(pending_balance) )