bool database::fill_order(const force_settlement_object& settle, const asset& pays, const asset& receives) { try { bool filled = false; auto issuer_fees = pay_market_fees(get(receives.asset_id), receives); if( pays < settle.balance ) { modify(settle, [&pays](force_settlement_object& s) { s.balance -= pays; }); filled = false; } else { filled = true; } adjust_balance(settle.owner, receives - issuer_fees); assert( pays.asset_id != receives.asset_id ); push_applied_operation( fill_order_operation{ settle.id, settle.owner, pays, receives, issuer_fees } ); if (filled) remove(settle); return filled; } FC_CAPTURE_AND_RETHROW( (settle)(pays)(receives) ) }
void database::pay_order( const account_object& receiver, const asset& receives, const asset& pays ) { const auto& balances = receiver.statistics(*this); modify( balances, [&]( account_statistics_object& b ){ if( pays.asset_id == asset_id_type() ) b.total_core_in_orders -= pays.amount; }); adjust_balance(receiver.get_id(), receives); }
bool database::fill_order( const call_order_object& order, const asset& pays, const asset& receives ) { try { //idump((pays)(receives)(order)); FC_ASSERT( order.get_debt().asset_id == receives.asset_id ); FC_ASSERT( order.get_collateral().asset_id == pays.asset_id ); FC_ASSERT( order.get_collateral() >= pays ); optional<asset> collateral_freed; modify( order, [&]( call_order_object& o ){ o.debt -= receives.amount; o.collateral -= pays.amount; if( o.debt == 0 ) { collateral_freed = o.get_collateral(); o.collateral = 0; } }); const asset_object& mia = receives.asset_id(*this); assert( mia.is_market_issued() ); const asset_dynamic_data_object& mia_ddo = mia.dynamic_asset_data_id(*this); modify( mia_ddo, [&]( asset_dynamic_data_object& ao ){ //idump((receives)); ao.current_supply -= receives.amount; }); const account_object& borrower = order.borrower(*this); if( collateral_freed || pays.asset_id == asset_id_type() ) { const account_statistics_object& borrower_statistics = borrower.statistics(*this); if( collateral_freed ) adjust_balance(borrower.get_id(), *collateral_freed); modify( borrower_statistics, [&]( account_statistics_object& b ){ if( collateral_freed && collateral_freed->amount > 0 ) b.total_core_in_orders -= collateral_freed->amount; if( pays.asset_id == asset_id_type() ) b.total_core_in_orders -= pays.amount; assert( b.total_core_in_orders >= 0 ); }); } assert( pays.asset_id != receives.asset_id ); push_applied_operation( fill_order_operation{ order.id, order.borrower, pays, receives, asset(0, pays.asset_id) } ); if( collateral_freed ) remove( order ); return collateral_freed.valid(); } FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) }
void database::init_genesis(const genesis_state_type& genesis_state) { try { FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." ); FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % GRAPHENE_DEFAULT_BLOCK_INTERVAL == 0, "Genesis timestamp must be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL." ); FC_ASSERT(genesis_state.initial_witness_candidates.size() > 0, "Cannot start a chain with zero witnesses."); FC_ASSERT(genesis_state.initial_active_witnesses <= genesis_state.initial_witness_candidates.size(), "initial_active_witnesses is larger than the number of candidate witnesses."); _undo_db.disable(); struct auth_inhibitor { auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags) { db.node_properties().skip_flags |= skip_authority_check; } ~auth_inhibitor() { db.node_properties().skip_flags = old_flags; } private: database& db; uint32_t old_flags; } inhibitor(*this); transaction_evaluation_state genesis_eval_state(this); flat_index<block_summary_object>& bsi = get_mutable_index_type< flat_index<block_summary_object> >(); bsi.resize(0xffff+1); // Create blockchain accounts fc::ecc::private_key null_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); create<account_balance_object>([](account_balance_object& b) { b.balance = GRAPHENE_MAX_SHARE_SUPPLY; }); const account_object& committee_account = create<account_object>( [&](account_object& n) { n.membership_expiration_date = time_point_sec::maximum(); n.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; n.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; n.owner.weight_threshold = 1; n.active.weight_threshold = 1; n.name = "committee-account"; n.statistics = create<account_statistics_object>( [&](account_statistics_object& s){ s.owner = n.id; }).id; }); FC_ASSERT(committee_account.get_id() == GRAPHENE_COMMITTEE_ACCOUNT); FC_ASSERT(create<account_object>([this](account_object& a) { a.name = "witness-account"; a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_WITNESS_ACCOUNT; a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; }).get_id() == GRAPHENE_WITNESS_ACCOUNT); FC_ASSERT(create<account_object>([this](account_object& a) { a.name = "relaxed-committee-account"; a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT; a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; }).get_id() == GRAPHENE_RELAXED_COMMITTEE_ACCOUNT); FC_ASSERT(create<account_object>([this](account_object& a) { a.name = "null-account"; a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = 0; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT; }).get_id() == GRAPHENE_NULL_ACCOUNT); FC_ASSERT(create<account_object>([this](account_object& a) { a.name = "temp-account"; a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 0; a.active.weight_threshold = 0; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_TEMP_ACCOUNT; a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; }).get_id() == GRAPHENE_TEMP_ACCOUNT); FC_ASSERT(create<account_object>([this](account_object& a) { a.name = "proxy-to-self"; a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = GRAPHENE_NULL_ACCOUNT; a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = 0; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT; }).get_id() == GRAPHENE_PROXY_TO_SELF_ACCOUNT); // Create more special accounts while( true ) { uint64_t id = get_index<account_object>().get_next_id().instance(); if( id >= genesis_state.immutable_parameters.num_special_accounts ) break; const account_object& acct = create<account_object>([&](account_object& a) { a.name = "special-account-" + std::to_string(id); a.statistics = create<account_statistics_object>([&](account_statistics_object& s){s.owner = a.id;}).id; a.owner.weight_threshold = 1; a.active.weight_threshold = 1; a.registrar = a.lifetime_referrer = a.referrer = id; a.membership_expiration_date = time_point_sec::maximum(); a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; }); FC_ASSERT( acct.get_id() == account_id_type(id) ); remove( acct ); } // Create core asset const asset_dynamic_data_object& dyn_asset = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) { a.current_supply = GRAPHENE_MAX_SHARE_SUPPLY; }); const asset_object& core_asset = create<asset_object>( [&]( asset_object& a ) { a.symbol = GRAPHENE_SYMBOL; a.options.max_supply = genesis_state.max_core_supply; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; a.options.flags = 0; a.options.issuer_permissions = 0; a.issuer = GRAPHENE_NULL_ACCOUNT; a.options.core_exchange_rate.base.amount = 1; a.options.core_exchange_rate.base.asset_id = 0; a.options.core_exchange_rate.quote.amount = 1; a.options.core_exchange_rate.quote.asset_id = 0; a.dynamic_asset_data_id = dyn_asset.id; }); assert( asset_id_type(core_asset.id) == asset().asset_id ); assert( get_balance(account_id_type(), asset_id_type()) == asset(dyn_asset.current_supply) ); // Create more special assets while( true ) { uint64_t id = get_index<asset_object>().get_next_id().instance(); if( id >= genesis_state.immutable_parameters.num_special_assets ) break; const asset_dynamic_data_object& dyn_asset = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& a) { a.current_supply = 0; }); const asset_object& asset_obj = create<asset_object>( [&]( asset_object& a ) { a.symbol = "SPECIAL" + std::to_string( id ); a.options.max_supply = 0; a.precision = GRAPHENE_BLOCKCHAIN_PRECISION_DIGITS; a.options.flags = 0; a.options.issuer_permissions = 0; a.issuer = GRAPHENE_NULL_ACCOUNT; a.options.core_exchange_rate.base.amount = 1; a.options.core_exchange_rate.base.asset_id = 0; a.options.core_exchange_rate.quote.amount = 1; a.options.core_exchange_rate.quote.asset_id = 0; a.dynamic_asset_data_id = dyn_asset.id; }); FC_ASSERT( asset_obj.get_id() == asset_id_type(id) ); remove( asset_obj ); } chain_id_type chain_id = genesis_state.compute_chain_id(); // Create global properties create<global_property_object>([&](global_property_object& p) { p.parameters = genesis_state.initial_parameters; // Set fees to zero initially, so that genesis initialization needs not pay them // We'll fix it at the end of the function p.parameters.current_fees->zero_all_fees(); }); create<dynamic_global_property_object>([&](dynamic_global_property_object& p) { p.time = genesis_state.initial_timestamp; p.dynamic_flags = 0; p.witness_budget = 0; p.recent_slots_filled = fc::uint128::max_value(); }); FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, "min_witness_count must be odd" ); FC_ASSERT( (genesis_state.immutable_parameters.min_committee_member_count & 1) == 1, "min_committee_member_count must be odd" ); create<chain_property_object>([&](chain_property_object& p) { p.chain_id = chain_id; p.immutable_parameters = genesis_state.immutable_parameters; } ); create<block_summary_object>([&](block_summary_object&) {}); // Create initial accounts for( const auto& account : genesis_state.initial_accounts ) { account_create_operation cop; cop.name = account.name; cop.registrar = GRAPHENE_TEMP_ACCOUNT; cop.owner = authority(1, account.owner_key, 1); if( account.active_key == public_key_type() ) { cop.active = cop.owner; cop.options.memo_key = account.owner_key; } else { cop.active = authority(1, account.active_key, 1); cop.options.memo_key = account.active_key; } account_id_type account_id(apply_operation(genesis_eval_state, cop).get<object_id_type>()); if( account.is_lifetime_member ) { account_upgrade_operation op; op.account_to_upgrade = account_id; op.upgrade_to_lifetime_member = true; apply_operation(genesis_eval_state, op); } } // Helper function to get account ID by name const auto& accounts_by_name = get_index_type<account_index>().indices().get<by_name>(); auto get_account_id = [&accounts_by_name](const string& name) { auto itr = accounts_by_name.find(name); FC_ASSERT(itr != accounts_by_name.end(), "Unable to find account '${acct}'. Did you forget to add a record for it to initial_accounts?", ("acct", name)); return itr->get_id(); }; // Helper function to get asset ID by symbol const auto& assets_by_symbol = get_index_type<asset_index>().indices().get<by_symbol>(); const auto get_asset_id = [&assets_by_symbol](const string& symbol) { auto itr = assets_by_symbol.find(symbol); // TODO: This is temporary for handling BTS snapshot if( symbol == "BTS" ) itr = assets_by_symbol.find(GRAPHENE_SYMBOL); FC_ASSERT(itr != assets_by_symbol.end(), "Unable to find asset '${sym}'. Did you forget to add a record for it to initial_assets?", ("sym", symbol)); return itr->get_id(); }; map<asset_id_type, share_type> total_supplies; map<asset_id_type, share_type> total_debts; // Create initial assets for( const genesis_state_type::initial_asset_type& asset : genesis_state.initial_assets ) { asset_id_type new_asset_id = get_index_type<asset_index>().get_next_id(); total_supplies[ new_asset_id ] = 0; asset_dynamic_data_id_type dynamic_data_id; optional<asset_bitasset_data_id_type> bitasset_data_id; if( asset.is_bitasset ) { int collateral_holder_number = 0; total_debts[ new_asset_id ] = 0; for( const auto& collateral_rec : asset.collateral_records ) { account_create_operation cop; cop.name = asset.symbol + "-collateral-holder-" + std::to_string(collateral_holder_number); boost::algorithm::to_lower(cop.name); cop.registrar = GRAPHENE_TEMP_ACCOUNT; cop.owner = authority(1, collateral_rec.owner, 1); cop.active = cop.owner; account_id_type owner_account_id = apply_operation(genesis_eval_state, cop).get<object_id_type>(); modify( owner_account_id(*this).statistics(*this), [&]( account_statistics_object& o ) { o.total_core_in_orders = collateral_rec.collateral; }); create<call_order_object>([&](call_order_object& c) { c.borrower = owner_account_id; c.collateral = collateral_rec.collateral; c.debt = collateral_rec.debt; c.call_price = price::call_price(chain::asset(c.debt, new_asset_id), chain::asset(c.collateral, core_asset.id), GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO); }); total_supplies[ 0 ] += collateral_rec.collateral; total_debts[ new_asset_id ] += collateral_rec.debt; ++collateral_holder_number; } bitasset_data_id = create<asset_bitasset_data_object>([&](asset_bitasset_data_object& b) { b.options.short_backing_asset = core_asset.id; b.options.minimum_feeds = GRAPHENE_DEFAULT_MINIMUM_FEEDS; }).id; } dynamic_data_id = create<asset_dynamic_data_object>([&](asset_dynamic_data_object& d) { d.accumulated_fees = asset.accumulated_fees; }).id; total_supplies[ new_asset_id ] += asset.accumulated_fees; create<asset_object>([&](asset_object& a) { a.symbol = asset.symbol; a.options.description = asset.description; a.precision = asset.precision; string issuer_name = asset.issuer_name; a.issuer = get_account_id(issuer_name); a.options.max_supply = asset.max_supply; a.options.flags = witness_fed_asset; a.options.issuer_permissions = charge_market_fee | global_settle | witness_fed_asset | committee_fed_asset; a.dynamic_asset_data_id = dynamic_data_id; a.bitasset_data_id = bitasset_data_id; }); } // Create initial balances share_type total_allocation; for( const auto& handout : genesis_state.initial_balances ) { const auto asset_id = get_asset_id(handout.asset_symbol); create<balance_object>([&handout,&get_asset_id,total_allocation,asset_id](balance_object& b) { b.balance = asset(handout.amount, asset_id); b.owner = handout.owner; }); total_supplies[ asset_id ] += handout.amount; } // Create initial vesting balances for( const genesis_state_type::initial_vesting_balance_type& vest : genesis_state.initial_vesting_balances ) { const auto asset_id = get_asset_id(vest.asset_symbol); create<balance_object>([&](balance_object& b) { b.owner = vest.owner; b.balance = asset(vest.amount, asset_id); linear_vesting_policy policy; policy.begin_timestamp = vest.begin_timestamp; policy.vesting_cliff_seconds = 0; policy.vesting_duration_seconds = vest.vesting_duration_seconds; policy.begin_balance = vest.begin_balance; b.vesting_policy = std::move(policy); }); total_supplies[ asset_id ] += vest.amount; } if( total_supplies[ 0 ] > 0 ) { adjust_balance(GRAPHENE_COMMITTEE_ACCOUNT, -get_balance(GRAPHENE_COMMITTEE_ACCOUNT,{})); } else { total_supplies[ 0 ] = GRAPHENE_MAX_SHARE_SUPPLY; } const auto& idx = get_index_type<asset_index>().indices().get<by_symbol>(); auto it = idx.begin(); bool has_imbalanced_assets = false; while( it != idx.end() ) { if( it->bitasset_data_id.valid() ) { auto supply_itr = total_supplies.find( it->id ); auto debt_itr = total_debts.find( it->id ); FC_ASSERT( supply_itr != total_supplies.end() ); FC_ASSERT( debt_itr != total_debts.end() ); if( supply_itr->second != debt_itr->second ) { has_imbalanced_assets = true; elog( "Genesis for asset ${aname} is not balanced\n" " Debt is ${debt}\n" " Supply is ${supply}\n", ("debt", debt_itr->second) ("supply", supply_itr->second) ); } } ++it; } FC_ASSERT( !has_imbalanced_assets ); // Save tallied supplies for( const auto& item : total_supplies ) { const auto asset_id = item.first; const auto total_supply = item.second; modify( get( asset_id ), [ & ]( asset_object& asset ) { modify( get( asset.dynamic_asset_data_id ), [ & ]( asset_dynamic_data_object& asset_data ) { asset_data.current_supply = total_supply; } ); } ); } // Create special witness account const witness_object& wit = create<witness_object>([&](witness_object& w) {}); FC_ASSERT( wit.id == GRAPHENE_NULL_WITNESS ); remove(wit); // Create initial witnesses std::for_each(genesis_state.initial_witness_candidates.begin(), genesis_state.initial_witness_candidates.end(), [&](const genesis_state_type::initial_witness_type& witness) { witness_create_operation op; op.witness_account = get_account_id(witness.owner_name); op.block_signing_key = witness.block_signing_key; apply_operation(genesis_eval_state, op); }); // Create initial committee members std::for_each(genesis_state.initial_committee_candidates.begin(), genesis_state.initial_committee_candidates.end(), [&](const genesis_state_type::initial_committee_member_type& member) { committee_member_create_operation op; op.committee_member_account = get_account_id(member.owner_name); apply_operation(genesis_eval_state, op); }); // Create initial workers std::for_each(genesis_state.initial_worker_candidates.begin(), genesis_state.initial_worker_candidates.end(), [&](const genesis_state_type::initial_worker_type& worker) { worker_create_operation op; op.owner = get_account_id(worker.owner_name); op.work_begin_date = genesis_state.initial_timestamp; op.work_end_date = time_point_sec::maximum(); op.daily_pay = worker.daily_pay; op.name = "Genesis-Worker-" + worker.owner_name; op.initializer = vesting_balance_worker_initializer{uint16_t(0)}; apply_operation(genesis_eval_state, std::move(op)); }); // Set active witnesses modify(get_global_properties(), [&](global_property_object& p) { for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i ) { p.active_witnesses.insert(i); p.witness_accounts.insert(get(witness_id_type(i)).witness_account); } }); // Enable fees modify(get_global_properties(), [&genesis_state](global_property_object& p) { p.parameters.current_fees = genesis_state.initial_parameters.current_fees; }); // Create witness scheduler create<witness_schedule_object>([&]( witness_schedule_object& wso ) { for( const witness_id_type& wid : get_global_properties().active_witnesses ) wso.current_shuffled_witnesses.push_back( wid ); }); debug_dump(); _undo_db.enable(); } FC_CAPTURE_AND_RETHROW() }