From 889a50939090d3974dc0b8b47e3c4d1c161e20d6 Mon Sep 17 00:00:00 2001 From: dtaghavi Date: Wed, 11 Dec 2024 23:39:30 +0000 Subject: [PATCH] Native action sysio.roa reducepolicy implemented Implmented sysio.roas native action 'reducepolicy' and all pieces necessary for that. Replaced userres and got roa usage tracking added Replaced userres in chain_plugin to reference sysio.roa's reslimit table. Adjusted the flow of resource usage tracking: RAM isn't charged during the action play through instead incrementing our new total_ram_usage In finalize we do our check to determine who to charge contract or caller where we use total_ram_usage to charge the appropriate person. Cleaner overall flow and maintains Leap resource management architecture. Also added a check, to not allow nodeowners to remove the sysio or sysio.* policies that are established on registration as these are system requirements. They can expand them but not lower them. Modified newaccount so only sysio can call it. sysio is charged for resources used for account creation. all accounts are created with no resources. Removed delegatebw and buyram from account creation. Fixed finalize() fallback logic Fixed an issue in finalize() logic when it checks contracts resources. Logic was incorrect for measuring accounts with unlimited CPU / NET. Fixed fallback edge case There was an edge case where contract == user and so it would SYS_ASSERT instead since that is to be expected sometimes. We attempt to charge the contract and if it doesn't have enough we reject without trying the user. modified the "free resources" check to accomodate bios boot. toggles after block 200. Fixed chain plugin get_account Previous edits didn't fully adjust to account for our sysio.roa resource management changes It now gets and populates properly. Resolved unlimited CPU / NET on clio get account --- libraries/chain/controller.cpp | 10 + .../include/sysio/chain/contract_types.hpp | 22 ++ .../chain/include/sysio/chain/controller.hpp | 1 - .../include/sysio/chain/resource_limits.hpp | 4 +- .../include/sysio/chain/sysio_contract.hpp | 2 + .../include/sysio/chain/sysio_roa_objects.hpp | 129 +++++++ .../sysio/chain/transaction_context.hpp | 3 + libraries/chain/include/sysio/chain/types.hpp | 2 + libraries/chain/resource_limits.cpp | 4 +- libraries/chain/sysio_contract.cpp | 347 +++++++++++++++--- libraries/chain/transaction_context.cpp | 176 +++++++-- plugins/chain_plugin/chain_plugin.cpp | 160 ++++---- programs/clio/main.cpp | 116 +++--- tests/abieos | 2 +- 14 files changed, 758 insertions(+), 220 deletions(-) create mode 100644 libraries/chain/include/sysio/chain/sysio_roa_objects.hpp diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index df214f0274..73851a89b2 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -12,6 +12,7 @@ #include #include #include +#include // **Roa Change** Added for db initialization of sysio.roa tables. #include #include #include @@ -334,6 +335,8 @@ struct controller_impl { wasmif.current_lib(bsp->block_num); }); + + #define SET_APP_HANDLER( receiver, contract, action) \ set_apply_handler( account_name(#receiver), account_name(#contract), action_name(#action), \ @@ -346,6 +349,8 @@ struct controller_impl { SET_APP_HANDLER( sysio, sysio, deleteauth ); SET_APP_HANDLER( sysio, sysio, linkauth ); SET_APP_HANDLER( sysio, sysio, unlinkauth ); + // **Roa Change** Handler for native action reducepolicy + SET_APP_HANDLER( sysio.roa, roa, reducepolicy ); /* SET_APP_HANDLER( sysio, sysio, postrecovery ); SET_APP_HANDLER( sysio, sysio, passrecovery ); @@ -984,6 +989,11 @@ struct controller_impl { authorization.initialize_database(); resource_limits.initialize_database(); + // **Roa Changes** New sysio.roa contract multi indexes for resource management. + db.add_index(); + db.add_index(); + db.add_index(); + authority system_auth(genesis.initial_key); create_native_account( genesis.initial_timestamp, config::system_account_name, system_auth, system_auth, true ); diff --git a/libraries/chain/include/sysio/chain/contract_types.hpp b/libraries/chain/include/sysio/chain/contract_types.hpp index bb58923d10..5ff617c5ab 100644 --- a/libraries/chain/include/sysio/chain/contract_types.hpp +++ b/libraries/chain/include/sysio/chain/contract_types.hpp @@ -2,6 +2,7 @@ #include #include +#include // Included for reducepolicy namespace sysio { namespace chain { @@ -152,6 +153,24 @@ struct onerror { } }; +// **Roa change** to facilitate native action +struct reducepolicy { + account_name owner; + account_name issuer; + asset net_weight; + asset cpu_weight; + asset ram_weight; + uint8_t network_gen; + + static account_name get_account() { + return "sysio.roa"_n; // The account this contract is deployed to. + } + + static action_name get_name() { + return "reducepolicy"_n; // The action name. + } +}; + } } /// namespace sysio::chain FC_REFLECT( sysio::chain::newaccount , (creator)(name)(owner)(active) ) @@ -163,3 +182,6 @@ FC_REFLECT( sysio::chain::linkauth , (account)(code)(typ FC_REFLECT( sysio::chain::unlinkauth , (account)(code)(type) ) FC_REFLECT( sysio::chain::canceldelay , (canceling_auth)(trx_id) ) FC_REFLECT( sysio::chain::onerror , (sender_id)(sent_trx) ) +// **Roa change** +FC_REFLECT( sysio::chain::reducepolicy , (owner)(issuer)(net_weight)(cpu_weight)(ram_weight)(network_gen)) + diff --git a/libraries/chain/include/sysio/chain/controller.hpp b/libraries/chain/include/sysio/chain/controller.hpp index ead62bf6d2..304124b0f3 100644 --- a/libraries/chain/include/sysio/chain/controller.hpp +++ b/libraries/chain/include/sysio/chain/controller.hpp @@ -380,7 +380,6 @@ namespace sysio { namespace chain { chainbase::database& mutable_db()const; std::unique_ptr my; - }; } } /// sysio::chain diff --git a/libraries/chain/include/sysio/chain/resource_limits.hpp b/libraries/chain/include/sysio/chain/resource_limits.hpp index 5a55481108..580dbf9a9a 100644 --- a/libraries/chain/include/sysio/chain/resource_limits.hpp +++ b/libraries/chain/include/sysio/chain/resource_limits.hpp @@ -62,7 +62,7 @@ namespace sysio { namespace chain { public: explicit resource_limits_manager(chainbase::database& db, std::function get_deep_mind_logger) - :_db(db),_get_deep_mind_logger(get_deep_mind_logger) + :_db(db), _get_deep_mind_logger(get_deep_mind_logger) { } @@ -115,4 +115,4 @@ namespace sysio { namespace chain { FC_REFLECT( sysio::chain::resource_limits::account_resource_limit, (used)(available)(max) ) FC_REFLECT( sysio::chain::resource_limits::ratio, (numerator)(denominator)) -FC_REFLECT( sysio::chain::resource_limits::elastic_limit_parameters, (target)(max)(periods)(max_multiplier)(contract_rate)(expand_rate)) +FC_REFLECT( sysio::chain::resource_limits::elastic_limit_parameters, (target)(max)(periods)(max_multiplier)(contract_rate)(expand_rate)) \ No newline at end of file diff --git a/libraries/chain/include/sysio/chain/sysio_contract.hpp b/libraries/chain/include/sysio/chain/sysio_contract.hpp index 9469c1ab37..2664b15aae 100644 --- a/libraries/chain/include/sysio/chain/sysio_contract.hpp +++ b/libraries/chain/include/sysio/chain/sysio_contract.hpp @@ -16,6 +16,8 @@ namespace sysio { namespace chain { void apply_sysio_deleteauth(apply_context&); void apply_sysio_linkauth(apply_context&); void apply_sysio_unlinkauth(apply_context&); + // **Roa changes** + void apply_roa_reducepolicy(apply_context&); /* void apply_sysio_postrecovery(apply_context&); diff --git a/libraries/chain/include/sysio/chain/sysio_roa_objects.hpp b/libraries/chain/include/sysio/chain/sysio_roa_objects.hpp new file mode 100644 index 0000000000..a781f56e63 --- /dev/null +++ b/libraries/chain/include/sysio/chain/sysio_roa_objects.hpp @@ -0,0 +1,129 @@ +#pragma once +#include +#include +#include +#include +#include + +/** + * @brief This file represents the structure of the system contract sysio.roa which replaces the old resource management system. + * The tables defined here MUST match the structure in the system contract ( sysio.roa.hpp ). + */ +namespace sysio { namespace chain { + + // Note: The object_type IDs below start at 200 to avoid conflicts with core SYSIO object types. + // Check sysio/chain/types.hpp for core definitions. + // Do not modify sysio/chain/types.hpp. Keep custom object types in separate files. + enum sysio_roa_object_type { + nodeowners_object_type = 200, + policies_object_type, + reslimit_object_type + }; + + class nodeowners_object : public chainbase::object { + CHAINBASE_DEFAULT_CONSTRUCTOR(nodeowners_object) + + public: + id_type id; + name owner; // Node Owner's account name. + uint8_t tier; + asset total_sys; + asset allocated_sys; + asset allocated_bw; + asset allocated_ram; + uint8_t network_gen; // Added field for scoping + + // Primary key for chainbase + id_type primary_key() const { return id; } + + // Accessors for indexing by (network_gen, owner) + name get_owner() const { return owner; } + uint8_t get_network_gen() const { return network_gen; } + + // No need for get_tier() since we are not indexing or filtering by tier in native code + }; + + struct by_network_gen_owner; + + using nodeowners_index = chainbase::shared_multi_index_container< + nodeowners_object, + indexed_by< + ordered_unique, member>, + // Composite key using network_gen and owner for scoping + ordered_unique, + composite_key, + const_mem_fun + > + > + > + >; + + class policies_object : public chainbase::object { + CHAINBASE_DEFAULT_CONSTRUCTOR(policies_object) + + public: + id_type id; + name owner; // The account this policy applies to. + name issuer; // The Node Owner who issued this policy. + asset net_weight; + asset cpu_weight; + asset ram_weight; + uint64_t bytes_per_unit; + uint32_t time_block; + + id_type primary_key() const { return id; } + name get_owner() const { return owner; } + name get_issuer() const { return issuer; } + }; + + struct by_issuer_owner; + // struct by_owner; // Remove if not used + + using policies_index = chainbase::shared_multi_index_container< + policies_object, + indexed_by< + ordered_unique, member>, + ordered_unique, + composite_key, + const_mem_fun + > + > + > + >; + + class reslimit_object : public chainbase::object { + CHAINBASE_DEFAULT_CONSTRUCTOR(reslimit_object) + + public: + id_type id; + name owner; + asset net_weight; + asset cpu_weight; + uint64_t ram_bytes; + + id_type primary_key() const { return id; } + name get_owner() const { return owner; } + }; + + struct by_reslimit_owner; + + using reslimit_index = chainbase::shared_multi_index_container< + reslimit_object, + indexed_by< + ordered_unique, member>, + ordered_unique, const_mem_fun> + > + >; + +}} // namespace sysio::chain + +CHAINBASE_SET_INDEX_TYPE(sysio::chain::nodeowners_object, sysio::chain::nodeowners_index) +CHAINBASE_SET_INDEX_TYPE(sysio::chain::policies_object, sysio::chain::policies_index) +CHAINBASE_SET_INDEX_TYPE(sysio::chain::reslimit_object, sysio::chain::reslimit_index) + +FC_REFLECT(sysio::chain::nodeowners_object, (id)(owner)(tier)(total_sys)(allocated_sys)(allocated_bw)(allocated_ram)(network_gen)) +FC_REFLECT(sysio::chain::policies_object, (id)(owner)(issuer)(net_weight)(cpu_weight)(ram_weight)(bytes_per_unit)(time_block)) +FC_REFLECT(sysio::chain::reslimit_object, (id)(owner)(net_weight)(cpu_weight)(ram_bytes)) + diff --git a/libraries/chain/include/sysio/chain/transaction_context.hpp b/libraries/chain/include/sysio/chain/transaction_context.hpp index a8f17a303d..0ad203628f 100644 --- a/libraries/chain/include/sysio/chain/transaction_context.hpp +++ b/libraries/chain/include/sysio/chain/transaction_context.hpp @@ -169,6 +169,9 @@ namespace sysio { namespace chain { int64_t billing_timer_exception_code = block_cpu_usage_exceeded::code_value; fc::time_point pseudo_start; fc::microseconds billed_time; + + // Roa Change: Store total RAM usage accumulated during actions here. transaction_context::finalize() will use to finalize charges. + int64_t total_ram_usage = 0; }; } } diff --git a/libraries/chain/include/sysio/chain/types.hpp b/libraries/chain/include/sysio/chain/types.hpp index 36784de248..dc2401b1d2 100644 --- a/libraries/chain/include/sysio/chain/types.hpp +++ b/libraries/chain/include/sysio/chain/types.hpp @@ -131,6 +131,8 @@ namespace sysio { namespace chain { * * UNUSED_ enums can be taken for new purposes but otherwise the offsets * in this enumeration are potentially shared_memory breaking + * + * Note: sysio_roa_objects.hpp defines usage of: 200 - 202 */ enum object_type { diff --git a/libraries/chain/resource_limits.cpp b/libraries/chain/resource_limits.cpp index 9752a0ff6a..4edcba1820 100644 --- a/libraries/chain/resource_limits.cpp +++ b/libraries/chain/resource_limits.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace sysio { namespace chain { namespace resource_limits { @@ -240,7 +241,6 @@ int64_t resource_limits_manager::get_account_ram_usage( const account_name& name return _db.get( name ).ram_usage; } - bool resource_limits_manager::set_account_limits( const account_name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight) { //const auto& usage = _db.get( account ); /* @@ -516,4 +516,4 @@ std::pair resource_limits_manager::get_account_net return {arl, greylisted}; } -} } } /// sysio::chain::resource_limits +} } } /// sysio::chain::resource_limits \ No newline at end of file diff --git a/libraries/chain/sysio_contract.cpp b/libraries/chain/sysio_contract.cpp index 77165533bc..da91d10772 100644 --- a/libraries/chain/sysio_contract.cpp +++ b/libraries/chain/sysio_contract.cpp @@ -20,6 +20,7 @@ #include #include +#include // **Roa Change** needed for multi index access in reduce policy namespace sysio { namespace chain { @@ -67,64 +68,75 @@ void validate_authority_precondition( const apply_context& context, const author void apply_sysio_newaccount(apply_context& context) { auto create = context.get_action().data_as(); try { - context.require_authorization(create.creator); -// context.require_write_lock( config::sysio_auth_scope ); - auto& authorization = context.control.get_mutable_authorization_manager(); + context.require_authorization(create.creator); + // context.require_write_lock( config::sysio_auth_scope ); // If not needed, can remain commented out - SYS_ASSERT( validate(create.owner), action_validate_exception, "Invalid owner authority"); - SYS_ASSERT( validate(create.active), action_validate_exception, "Invalid active authority"); + auto& authorization = context.control.get_mutable_authorization_manager(); - auto& db = context.db; + SYS_ASSERT(validate(create.owner), action_validate_exception, "Invalid owner authority"); + SYS_ASSERT(validate(create.active), action_validate_exception, "Invalid active authority"); - auto name_str = name(create.name).to_string(); + auto& db = context.db; - SYS_ASSERT( !create.name.empty(), action_validate_exception, "account name cannot be empty" ); - SYS_ASSERT( name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long" ); + auto name_str = name(create.name).to_string(); - // Check if the creator is privileged - const auto &creator = db.get(create.creator); - if( !creator.is_privileged() ) { - SYS_ASSERT( name_str.find( "sysio." ) != 0, action_validate_exception, - "only privileged accounts can have names that start with 'sysio.'" ); - } + SYS_ASSERT(!create.name.empty(), action_validate_exception, "account name cannot be empty"); + SYS_ASSERT(name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long"); - auto existing_account = db.find(create.name); - SYS_ASSERT(existing_account == nullptr, account_name_exists_exception, - "Cannot create account named ${name}, as that name is already taken", - ("name", create.name)); + // Ensure only sysio can create new accounts at this stage + SYS_ASSERT(create.creator == config::system_account_name, + action_validate_exception, + "Only sysio can create new accounts"); - db.create([&](auto& a) { - a.name = create.name; - a.creation_date = context.control.pending_block_time(); - }); + // Check if the creator is privileged for sysio. prefixed accounts + const auto &creator = db.get(create.creator); + if (!creator.is_privileged()) { + SYS_ASSERT(name_str.find("sysio.") != 0, action_validate_exception, + "only privileged accounts can have names that start with 'sysio.'"); + } - db.create([&](auto& a) { - a.name = create.name; - }); + auto existing_account = db.find(create.name); + SYS_ASSERT(existing_account == nullptr, account_name_exists_exception, + "Cannot create account named ${name}, as that name is already taken", + ("name", create.name)); - for( const auto& auth : { create.owner, create.active } ){ - validate_authority_precondition( context, auth ); - } + db.create([&](auto& a) { + a.name = create.name; + a.creation_date = context.control.pending_block_time(); + }); - const auto& owner_permission = authorization.create_permission( create.name, config::owner_name, 0, - std::move(create.owner) ); - const auto& active_permission = authorization.create_permission( create.name, config::active_name, owner_permission.id, - std::move(create.active) ); + db.create([&](auto& a) { + a.name = create.name; + }); - context.control.get_mutable_resource_limits_manager().initialize_account(create.name); + for (const auto& auth : { create.owner, create.active }) { + validate_authority_precondition(context, auth); + } - int64_t ram_delta = config::overhead_per_account_ram_bytes; - ram_delta += 2*config::billable_size_v; - ram_delta += owner_permission.auth.get_billable_size(); - ram_delta += active_permission.auth.get_billable_size(); + const auto& owner_permission = authorization.create_permission(create.name, config::owner_name, 0, + std::move(create.owner)); + const auto& active_permission = authorization.create_permission(create.name, config::active_name, owner_permission.id, + std::move(create.active)); - if (auto dm_logger = context.control.get_deep_mind_logger()) { - dm_logger->on_ram_trace(RAM_EVENT_ID("${name}", ("name", create.name)), "account", "add", "newaccount"); - } + context.control.get_mutable_resource_limits_manager().initialize_account(create.name); - context.add_ram_usage(create.name, ram_delta); + // Set CPU and NET to 0 for the newly created account + context.control.get_mutable_resource_limits_manager().set_account_limits(create.name, 0, 0, 0); -} FC_CAPTURE_AND_RETHROW( (create) ) } + int64_t ram_delta = config::overhead_per_account_ram_bytes; + ram_delta += 2 * config::billable_size_v; + ram_delta += owner_permission.auth.get_billable_size(); + ram_delta += active_permission.auth.get_billable_size(); + + if (auto dm_logger = context.control.get_deep_mind_logger()) { + dm_logger->on_ram_trace(RAM_EVENT_ID("${name}", ("name", create.name)), "account", "add", "newaccount"); + } + + // Charge the RAM usage to sysio instead of the new account + context.add_ram_usage(config::system_account_name, ram_delta); + + } FC_CAPTURE_AND_RETHROW((create)) +} void apply_sysio_setcode(apply_context& context) { auto& db = context.db; @@ -251,7 +263,7 @@ void apply_sysio_updateauth(apply_context& context) { auto update = context.get_action().data_as(); - // ** NEW ADDED IF STATEMENT ** + // ** Auth.msg change ** if( update.permission != name("auth.ext") && update.permission != name("auth.session") ) { context.require_authorization(update.account); // only here to mark the single authority on this action as used } @@ -451,4 +463,251 @@ void apply_sysio_canceldelay(apply_context& context) { context.cancel_deferred_transaction(transaction_id_to_sender_id(trx_id), account_name()); } +void apply_roa_reducepolicy(apply_context& context) { + // Parse action arguments + auto args = context.get_action().data_as(); + const name& owner = args.owner; + const name& issuer = args.issuer; + const asset& net_weight = args.net_weight; + const asset& cpu_weight = args.cpu_weight; + const asset& ram_weight = args.ram_weight; + uint8_t network_gen = args.network_gen; + + context.require_authorization(issuer); + + auto& db = context.db; // chainbase database reference + + // Fetch Node Owner by (network_gen, issuer) + const auto& nodeowners_by_ng_owner = db.get_index(); + auto node_itr = nodeowners_by_ng_owner.find(boost::make_tuple(network_gen, issuer)); + SYS_ASSERT(node_itr != nodeowners_by_ng_owner.end(), action_validate_exception, "Issuer is not a registered Node Owner"); + + // Fetch Policy by (issuer, owner) + const auto& policies_by_issuer_owner = db.get_index(); + auto policy_itr = policies_by_issuer_owner.find(boost::make_tuple(issuer, owner)); + SYS_ASSERT(policy_itr != policies_by_issuer_owner.end(), action_validate_exception, "Policy does not exist under this issuer for the owner"); + + // Ensure current block >= policy time_block + uint32_t current_block = context.control.head_block_num(); + SYS_ASSERT(current_block >= policy_itr->time_block, action_validate_exception, "Cannot reduce policy before time_block"); + + std::string acc_str = owner.to_string(); + bool sysio_acct = (acc_str == "sysio") || (acc_str.size() > 5 && acc_str.rfind("sysio.", 0) == 0); + if (sysio_acct && policy_itr->time_block == 1 && policy_itr->issuer == issuer) { + SYS_ASSERT(false, action_validate_exception, "Cannot reduce the sysio policies created at node registration"); + } + + // Fetch the reslimit row for this owner + const auto& reslimit_by_owner = db.get_index(); + auto rl_itr = reslimit_by_owner.find(owner); + SYS_ASSERT(rl_itr != reslimit_by_owner.end(), action_validate_exception, "reslimit row does not exist for this owner"); + + // Validate weights + SYS_ASSERT(net_weight.get_amount() >= 0, action_validate_exception, "NET weight cannot be negative"); + SYS_ASSERT(cpu_weight.get_amount() >= 0, action_validate_exception, "CPU weight cannot be negative"); + SYS_ASSERT(ram_weight.get_amount() >= 0, action_validate_exception, "RAM weight cannot be negative"); + SYS_ASSERT(net_weight.get_amount() > 0 || cpu_weight.get_amount() > 0 || ram_weight.get_amount() > 0, action_validate_exception, "All weights are 0, nothing to reduce."); + + // Ensure reductions do not exceed allocations + SYS_ASSERT(net_weight.get_amount() <= policy_itr->net_weight.get_amount(), action_validate_exception, "Cannot reduce NET below zero"); + SYS_ASSERT(cpu_weight.get_amount() <= policy_itr->cpu_weight.get_amount(), action_validate_exception, "Cannot reduce CPU below zero"); + SYS_ASSERT(ram_weight.get_amount() <= policy_itr->ram_weight.get_amount(), action_validate_exception, "Cannot reduce RAM below zero"); + + uint64_t bytes_per_unit = policy_itr->bytes_per_unit; + + auto& resource_manager = context.control.get_mutable_resource_limits_manager(); + + int64_t ram_bytes, net_limit, cpu_limit; + resource_manager.get_account_limits(owner, ram_bytes, net_limit, cpu_limit); + + // If we're not reducing RAM, skip the RAM logic + int64_t divisible_ram_to_reclaim = 0; + asset reclaimed_ram_weight(0, ram_weight.get_symbol()); + + if (ram_weight.get_amount() > 0) { + int64_t ram_usage = resource_manager.get_account_ram_usage(owner); + int64_t ram_unused = ram_bytes - ram_usage; + + // Convert requested ram_weight to bytes + int64_t requested_ram_bytes = ram_weight.get_amount() * (int64_t)bytes_per_unit; + + // Determine how many bytes we can reclaim + int64_t ram_to_reclaim = std::min(ram_unused, requested_ram_bytes); + SYS_ASSERT(ram_to_reclaim >= 0, action_validate_exception, "Invalid RAM reclaim calculation"); + + // Adjust ram_to_reclaim down to the nearest multiple of bytes_per_unit + int64_t remainder = ram_to_reclaim % (int64_t)bytes_per_unit; + divisible_ram_to_reclaim = ram_to_reclaim - remainder; + + int64_t reclaimed_units = divisible_ram_to_reclaim / (int64_t)bytes_per_unit; + reclaimed_ram_weight = asset(reclaimed_units, ram_weight.get_symbol()); + + // Ensure we don't reclaim more RAM than requested + SYS_ASSERT(reclaimed_ram_weight.get_amount() <= ram_weight.get_amount(), action_validate_exception, "Cannot reclaim more RAM than requested"); + } + + // Update resource limits + resource_manager.set_account_limits( + owner, + ram_bytes - divisible_ram_to_reclaim, + net_limit - net_weight.get_amount(), + cpu_limit - cpu_weight.get_amount() + ); + + // Update reslimit row + db.modify(*rl_itr, [&](auto& row) { + row.net_weight -= net_weight; + row.cpu_weight -= cpu_weight; + row.ram_bytes = static_cast(row.ram_bytes - divisible_ram_to_reclaim); + }); + + // Modify the policy object + db.modify(*policy_itr, [&](auto& row) { + row.net_weight -= net_weight; + row.cpu_weight -= cpu_weight; + row.ram_weight -= reclaimed_ram_weight; + }); + + // If policy goes to zero allocation, remove it + if (policy_itr->net_weight.get_amount() == 0 && policy_itr->cpu_weight.get_amount() == 0 && policy_itr->ram_weight.get_amount() == 0) { + db.remove(*policy_itr); + } + + // Update Node Owner's allocations + asset total_reclaimed_sys = net_weight + cpu_weight + reclaimed_ram_weight; + db.modify(*node_itr, [&](auto& row) { + row.allocated_sys -= total_reclaimed_sys; + row.allocated_bw -= (net_weight + cpu_weight); + row.allocated_ram -= reclaimed_ram_weight; + }); + + std::string msg = "Reclaimed resources: owner = " + owner.to_string() + + ", issuer = " + issuer.to_string() + + ", RAM = " + std::to_string(divisible_ram_to_reclaim) + " bytes, NET = " + + net_weight.to_string() + ", CPU = " + cpu_weight.to_string() + "\n"; + + context.console_append(msg); +} + +// Old function that built, remove once new one builds +// void apply_roa_reducepolicy(apply_context& context) { +// // Parse action arguments +// auto args = context.get_action().data_as(); +// const name& owner = args.owner; +// const name& issuer = args.issuer; +// const asset& net_weight = args.net_weight; +// const asset& cpu_weight = args.cpu_weight; +// const asset& ram_weight = args.ram_weight; +// uint8_t network_gen = args.network_gen; // Newly added + +// // Ensure the issuer is authorized +// context.require_authorization(issuer); + +// auto& db = context.db; // chainbase database reference + +// // Fetch Node Owner by (network_gen, issuer) +// const auto& nodeowners_by_ng_owner = db.get_index(); +// auto node_itr = nodeowners_by_ng_owner.find(boost::make_tuple(network_gen, issuer)); +// SYS_ASSERT(node_itr != nodeowners_by_ng_owner.end(), action_validate_exception, "Issuer is not a registered Node Owner"); + +// // Fetch Policy by (issuer, owner) +// const auto& policies_by_issuer_owner = db.get_index(); +// auto policy_itr = policies_by_issuer_owner.find(boost::make_tuple(issuer, owner)); +// SYS_ASSERT(policy_itr != policies_by_issuer_owner.end(), action_validate_exception, "Policy does not exist under this issuer for the owner"); + +// // Ensure time_block has passed +// uint32_t current_block = context.control.head_block_num(); +// SYS_ASSERT(current_block >= policy_itr->time_block, action_validate_exception, "Cannot reduce policy before time_block"); + +// // Fetch the reslimit row for this owner +// const auto& reslimit_by_owner = db.get_index(); +// auto rl_itr = reslimit_by_owner.find(owner); +// SYS_ASSERT(rl_itr != reslimit_by_owner.end(), action_validate_exception, "reslimit row does not exist for this owner"); + +// // Validate weights (allow zero values) +// SYS_ASSERT(net_weight.get_amount() >= 0, action_validate_exception, "NET weight cannot be negative"); +// SYS_ASSERT(cpu_weight.get_amount() >= 0, action_validate_exception, "CPU weight cannot be negative"); +// SYS_ASSERT(ram_weight.get_amount() >= 0, action_validate_exception, "RAM weight cannot be negative"); + +// // Validate at least one weight > 0 +// SYS_ASSERT(net_weight.get_amount() > 0 || cpu_weight.get_amount() > 0 || ram_weight.get_amount() > 0, action_validate_exception, "All weights are 0, nothing to reduce."); + +// // Ensure reductions do not exceed allocations +// SYS_ASSERT(net_weight.get_amount() <= policy_itr->net_weight.get_amount(), action_validate_exception, "Cannot reduce NET below zero"); +// SYS_ASSERT(cpu_weight.get_amount() <= policy_itr->cpu_weight.get_amount(), action_validate_exception, "Cannot reduce CPU below zero"); +// SYS_ASSERT(ram_weight.get_amount() <= policy_itr->ram_weight.get_amount(), action_validate_exception, "Cannot reduce RAM below zero"); + +// asset ram_byte_price = policy_itr->ram_byte_price; + +// // Determine the total bytes allocated by the policy +// int64_t policy_ram_bytes = policy_itr->ram_weight.get_amount() / ram_byte_price.get_amount(); + +// // Get the current resource limits +// auto& resource_manager = context.control.get_mutable_resource_limits_manager(); +// int64_t ram_bytes, net_limit, cpu_limit; +// resource_manager.get_account_limits(owner, ram_bytes, net_limit, cpu_limit); + +// // Calculate unused RAM +// int64_t ram_usage = resource_manager.get_account_ram_usage(owner); +// int64_t ram_unused = ram_bytes - ram_usage; + +// // Cap reclaimable bytes by unused RAM and policy's total allocation +// int64_t ram_bytes_to_reclaim = ram_weight.get_amount() / ram_byte_price.get_amount(); +// int64_t ram_to_reclaim = std::min({ram_unused, ram_bytes_to_reclaim, policy_ram_bytes}); + +// // Calculate reclaimed RAM weight (in SYS) +// asset reclaimed_ram_weight(ram_to_reclaim * ram_byte_price.get_amount(), ram_weight.get_symbol()); + +// // Ensure reclaimed_ram_weight calculation is precise +// SYS_ASSERT( +// reclaimed_ram_weight.get_amount() == ram_to_reclaim * ram_byte_price.get_amount(), +// action_validate_exception, +// "Reclaimed RAM weight calculation is imprecise" +// ); + +// // Ensure we don’t exceed the policy’s ram_weight allocation +// SYS_ASSERT(reclaimed_ram_weight.get_amount() <= policy_itr->ram_weight.get_amount(), action_validate_exception, "Cannot reclaim more RAM than the policy allocates"); + +// // Apply changes to resource limits +// resource_manager.set_account_limits( +// owner, +// ram_bytes - ram_to_reclaim, // Reduce RAM allocation +// net_limit - net_weight.get_amount(), // net_limt (int64_t) +// cpu_limit - cpu_weight.get_amount() // cpu_limt (int64_t) +// ); + +// // We know how many bytes we reclaimed: ram_to_reclaim +// // net and cpu are straightforward: just subtract the same amounts we reduced. +// db.modify(*rl_itr, [&](auto& row) { +// row.net_weight -= net_weight; +// row.cpu_weight -= cpu_weight; +// row.ram_bytes = static_cast(row.ram_bytes - ram_to_reclaim); +// }); + +// // Modify the policy object +// db.modify(*policy_itr, [&](auto& row) { +// row.net_weight -= net_weight; +// row.cpu_weight -= cpu_weight; +// row.ram_weight -= reclaimed_ram_weight; +// }); + +// // Check if policy should be erased (after modification) +// if (policy_itr->net_weight.get_amount() == 0 && policy_itr->cpu_weight.get_amount() == 0 && policy_itr->ram_weight.get_amount() == 0) { +// db.remove(*policy_itr); +// } + +// // Update Node Owner's allocated SYS +// asset total_reclaimed_sys = net_weight + cpu_weight + reclaimed_ram_weight; +// db.modify(*node_itr, [&](auto& row) { +// row.allocated_sys -= total_reclaimed_sys; +// }); + +// std::string msg = "Reclaimed resources: owner = " + owner.to_string() + +// ", issuer = " + issuer.to_string() + +// ", RAM = " + std::to_string(ram_to_reclaim) + " bytes, NET = " + +// net_weight.to_string() + ", CPU = " + cpu_weight.to_string() + "\n"; + +// // Log reclaimed resources +// context.console_append(msg); +// } } } // namespace sysio::chain diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 499ac91791..ec7a964d5f 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -89,12 +89,12 @@ namespace sysio { namespace chain { } } - void transaction_context::init(uint64_t initial_net_usage) - { + void transaction_context::init(uint64_t initial_net_usage) { SYS_ASSERT( !is_initialized, transaction_exception, "cannot initialize twice" ); // set maximum to a semi-valid deadline to allow for pause math and conversion to dates for logging - if( block_deadline == fc::time_point::maximum() ) block_deadline = start + fc::hours(24*7*52); + if( block_deadline == fc::time_point::maximum() ) + block_deadline = start + fc::hours(24*7*52); const auto& cfg = control.get_global_properties().configuration; auto& rl = control.get_mutable_resource_limits_manager(); @@ -118,6 +118,7 @@ namespace sysio { namespace chain { } const transaction& trx = packed_trx.get_transaction(); + // Possibly lower net_limit to optional limit set in the transaction header uint64_t trx_specified_net_usage_limit = static_cast(trx.max_net_usage_words.value) * 8; if( trx_specified_net_usage_limit > 0 && trx_specified_net_usage_limit <= net_limit ) { @@ -150,9 +151,20 @@ namespace sysio { namespace chain { } } } - validate_ram_usage.reserve( bill_to_accounts.size() ); - // Update usage values of accounts to reflect new time + // ---------------------- NEW ADDITION FOR SYSIO.ROA BILLING ---------------------- + // Identify the contract account from the first action if possible + account_name contract_account = trx.actions.empty() ? name() : trx.actions.front().account; + + // Only add contract_account if it's a valid (non-empty) name + if (contract_account.good()) { + bill_to_accounts.insert(contract_account); + } + + validate_ram_usage.reserve(bill_to_accounts.size()); + // ------------------------------------------------------------------------- + + // Update usage windows for all candidate accounts (user + contract) rl.update_account_usage( bill_to_accounts, block_timestamp_type(control.pending_block_time()).slot ); // Calculate the highest network usage and CPU time that all of the billed accounts can afford to be billed @@ -190,7 +202,7 @@ namespace sysio { namespace chain { billing_timer_exception_code = deadline_exception::code_value; } - // Explicit billed_cpu_time_us should be used, block_deadline will be maximum unless in test code + // Explicit billed_cpu_time_us used if( explicit_billed_cpu_time ) { _deadline = block_deadline; deadline_exception_code = deadline_exception::code_value; @@ -207,21 +219,22 @@ namespace sysio { namespace chain { validate_account_cpu_usage_estimate( billed_cpu_time_us, validate_account_cpu_limit ); } - eager_net_limit = (eager_net_limit/8)*8; // Round down to nearest multiple of word size (8 bytes) so check_net_usage can be efficient + eager_net_limit = (eager_net_limit/8)*8; // Round down to nearest multiple of word size (8 bytes) if( initial_net_usage > 0 ) - add_net_usage( initial_net_usage ); // Fail early if current net usage is already greater than the calculated limit + add_net_usage( initial_net_usage ); // Fail early if current net usage exceeds limit if(control.skip_trx_checks()) { transaction_timer.start( fc::time_point::maximum() ); } else { transaction_timer.start( _deadline ); - checktime(); // Fail early if deadline has already been exceeded + checktime(); // Fail early if deadline already exceeded } is_initialized = true; } + void transaction_context::init_for_implicit_trx( uint64_t initial_net_usage ) { const transaction& trx = packed_trx.get_transaction(); @@ -319,61 +332,158 @@ namespace sysio { namespace chain { } void transaction_context::finalize() { - SYS_ASSERT( is_initialized, transaction_exception, "must first initialize" ); + SYS_ASSERT(is_initialized, transaction_exception, "must first initialize"); - if( is_input ) { + // Update permission usage if this is an input transaction + if (is_input) { const transaction& trx = packed_trx.get_transaction(); auto& am = control.get_mutable_authorization_manager(); - for( const auto& act : trx.actions ) { - for( const auto& auth : act.authorization ) { - am.update_permission_usage( am.get_permission(auth) ); + for (const auto& act : trx.actions) { + for (const auto& auth : act.authorization) { + am.update_permission_usage(am.get_permission(auth)); } } } auto& rl = control.get_mutable_resource_limits_manager(); - for( auto a : validate_ram_usage ) { - rl.verify_account_ram_usage( a ); + + // Verify that all accounts that incurred RAM usage in this transaction have valid RAM usage + for (auto a : validate_ram_usage) { + rl.verify_account_ram_usage(a); } - // Calculate the new highest network usage and CPU time that all of the billed accounts can afford to be billed + // Calculate limits based on what billed accounts can afford int64_t account_net_limit = 0; int64_t account_cpu_limit = 0; bool greylisted_net = false, greylisted_cpu = false; - std::tie( account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay(); + std::tie(account_net_limit, account_cpu_limit, greylisted_net, greylisted_cpu) = max_bandwidth_billed_accounts_can_pay(); + net_limit_due_to_greylist |= greylisted_net; cpu_limit_due_to_greylist |= greylisted_cpu; - // Possibly lower net_limit to what the billed accounts can pay - if( static_cast(account_net_limit) <= net_limit ) { - // NOTE: net_limit may possibly not be objective anymore due to net greylisting, but it should still be no greater than the truly objective net_limit + uint32_t greylist_limit = (greylisted_net || greylisted_cpu) ? 1 : config::maximum_elastic_resource_multiplier; + + // Possibly lower net_limit based on what accounts can pay + if (static_cast(account_net_limit) <= net_limit) { net_limit = static_cast(account_net_limit); net_limit_due_to_block = false; } - // Possibly lower objective_duration_limit to what the billed accounts can pay - if( account_cpu_limit <= objective_duration_limit.count() ) { - // NOTE: objective_duration_limit may possibly not be objective anymore due to cpu greylisting, but it should still be no greater than the truly objective objective_duration_limit + // Possibly lower objective_duration_limit based on what accounts can pay + if (account_cpu_limit <= objective_duration_limit.count()) { objective_duration_limit = fc::microseconds(account_cpu_limit); billing_timer_exception_code = tx_cpu_usage_exceeded::code_value; } - net_usage = ((net_usage + 7)/8)*8; // Round up to nearest multiple of word size (8 bytes) - + // Round up net_usage to nearest multiple of 8 bytes and verify + net_usage = ((net_usage + 7)/8)*8; eager_net_limit = net_limit; check_net_usage(); auto now = fc::time_point::now(); trace->elapsed = now - start; - update_billed_cpu_time( now ); + // Update CPU time and validate CPU usage + update_billed_cpu_time(now); + validate_cpu_usage_to_bill(billed_cpu_time_us, account_cpu_limit, true); - validate_cpu_usage_to_bill( billed_cpu_time_us, account_cpu_limit, true ); + // ---------------------- BIOS Mode Check ---------------------- + // Example: If we're before block #500, skip all new billing logic. + uint32_t bios_transition_block = 200; + if (control.head_block_num() < bios_transition_block) { + // BIOS phase: no resource billing, just return + return; + } - rl.add_transaction_usage( bill_to_accounts, static_cast(billed_cpu_time_us), net_usage, - block_timestamp_type(control.pending_block_time()).slot ); // Should never fail + // ---------------------- Fallback Logic for CPU/NET/RAM ---------------------- + const transaction& trx = packed_trx.get_transaction(); + account_name contract_account = trx.actions.empty() ? name() : trx.actions.front().account; + + // Attempt to find a user account distinct from the contract_account + account_name user_account; + for (const auto& acct : bill_to_accounts) { + if (acct != contract_account) { + user_account = acct; + break; + } + } + + // If no separate user found, fallback to using contract_account as payer as well + if (!user_account.good()) { + // Only one account (the contract) is involved, so it must pay if it can. + user_account = contract_account; + } + + // Retrieve contract CPU/NET limits + auto [contract_cpu, contract_cpu_grey] = rl.get_account_cpu_limit_ex(contract_account, greylist_limit); + auto [contract_net, contract_net_grey] = rl.get_account_net_limit_ex(contract_account, greylist_limit); + + int64_t total_ram_used = total_ram_usage; + + // Get contract RAM limits + int64_t c_ram_limit, c_net_w, c_cpu_w; + rl.get_account_limits(contract_account, c_ram_limit, c_net_w, c_cpu_w); + int64_t c_ram_usage = rl.get_account_ram_usage(contract_account); + + // RAM available for contract (no unlimited concept for RAM) + int64_t c_ram_available = (c_ram_limit < 0) ? -1 : (c_ram_limit - c_ram_usage); + + // Check unlimited CPU/NET: negative 'available' means unlimited + bool contract_cpu_unlimited = (contract_cpu.available < 0); + bool contract_net_unlimited = (contract_net.available < 0); + + // Check if contract can pay + bool contract_can_pay_cpu = contract_cpu_unlimited || (contract_cpu.available >= billed_cpu_time_us); + bool contract_can_pay_net = contract_net_unlimited || (contract_net.available >= (int64_t)net_usage); + bool contract_can_pay_ram = (c_ram_available >= total_ram_used); + + bool contract_can_pay = contract_can_pay_cpu && contract_can_pay_net && contract_can_pay_ram; + + if (contract_can_pay) { + // Contract pays + rl.add_pending_ram_usage(contract_account, total_ram_used); + rl.verify_account_ram_usage(contract_account); + rl.add_transaction_usage({contract_account}, static_cast(billed_cpu_time_us), net_usage, + block_timestamp_type(control.pending_block_time()).slot); + } else { + // Contract cannot pay. If user_account == contract_account, no fallback is possible. + if (user_account == contract_account) { + // No distinct user to fallback to, fail transaction + SYS_ASSERT(false, resource_exhausted_exception, + "Contract cannot pay and no distinct user found to fallback"); + } + + // We have a distinct user, try to charge the user now + auto [user_cpu, user_cpu_grey] = rl.get_account_cpu_limit_ex(user_account, greylist_limit); + auto [user_net, user_net_grey] = rl.get_account_net_limit_ex(user_account, greylist_limit); + + int64_t u_ram_limit, u_net_w, u_cpu_w; + rl.get_account_limits(user_account, u_ram_limit, u_net_w, u_cpu_w); + int64_t u_ram_usage = rl.get_account_ram_usage(user_account); + int64_t u_ram_available = (u_ram_limit < 0) ? -1 : (u_ram_limit - u_ram_usage); + + bool user_cpu_unlimited = (user_cpu.available < 0); + bool user_net_unlimited = (user_net.available < 0); + + bool user_can_pay_cpu = user_cpu_unlimited || (user_cpu.available >= billed_cpu_time_us); + bool user_can_pay_net = user_net_unlimited || (user_net.available >= (int64_t)net_usage); + bool user_can_pay_ram = (u_ram_available >= total_ram_used); + + bool user_can_pay = user_can_pay_cpu && user_can_pay_net && user_can_pay_ram; + + SYS_ASSERT(user_can_pay, resource_exhausted_exception, "Neither contract nor user can pay for this transaction"); + + // User pays + rl.add_pending_ram_usage(user_account, total_ram_used); + rl.verify_account_ram_usage(user_account); + rl.add_transaction_usage({user_account}, static_cast(billed_cpu_time_us), net_usage, + block_timestamp_type(control.pending_block_time()).slot); + } + // ---------------------------------------------------------------------------- } + + void transaction_context::squash() { if (undo_session) undo_session->squash(); } @@ -536,8 +646,10 @@ namespace sysio { namespace chain { } void transaction_context::add_ram_usage( account_name account, int64_t ram_delta ) { - auto& rl = control.get_mutable_resource_limits_manager(); - rl.add_pending_ram_usage( account, ram_delta ); + // ---------------------- NEW ADDITION FOR SYSIO.ROA BILLING ---------------------- + total_ram_usage += ram_delta; + + // If ram_delta > 0, we may still track the account in validate_ram_usage if needed. if( ram_delta > 0 ) { validate_ram_usage.insert( account ); } diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 209b610a00..9ae28faaf3 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -2436,38 +2436,43 @@ read_only::get_account_results read_only::get_account( const get_account_params& result.head_block_num = db.head_block_num(); result.head_block_time = db.head_block_time(); - rm.get_account_limits( result.account_name, result.ram_quota, result.net_weight, result.cpu_weight ); + // Get baseline resource limits from the resource manager + rm.get_account_limits(result.account_name, result.ram_quota, result.net_weight, result.cpu_weight); - const auto& accnt_obj = db.get_account( result.account_name ); - const auto& accnt_metadata_obj = db.db().get( result.account_name ); + const auto& accnt_obj = db.get_account(result.account_name); + const auto& accnt_metadata_obj = d.get(result.account_name); result.privileged = accnt_metadata_obj.is_privileged(); result.last_code_update = accnt_metadata_obj.last_code_update; result.created = accnt_obj.creation_date; uint32_t greylist_limit = db.is_resource_greylisted(result.account_name) ? 1 : config::maximum_elastic_resource_multiplier; - result.net_limit = rm.get_account_net_limit_ex( result.account_name, greylist_limit).first; - result.cpu_limit = rm.get_account_cpu_limit_ex( result.account_name, greylist_limit).first; - result.ram_usage = rm.get_account_ram_usage( result.account_name ); + auto [net_limit, net_grey] = rm.get_account_net_limit_ex(result.account_name, greylist_limit); + auto [cpu_limit, cpu_grey] = rm.get_account_cpu_limit_ex(result.account_name, greylist_limit); - if ( producer_plug ) { // producer_plug is null when called from chain_plugin_tests.cpp and get_table_tests.cpp + result.net_limit = net_limit; + result.cpu_limit = cpu_limit; + result.ram_usage = rm.get_account_ram_usage(result.account_name); + + if (producer_plug) { account_resource_limit subjective_cpu_bill_limit; - subjective_cpu_bill_limit.used = producer_plug->get_subjective_bill( result.account_name, fc::time_point::now() ); + subjective_cpu_bill_limit.used = producer_plug->get_subjective_bill(result.account_name, fc::time_point::now()); result.subjective_cpu_bill_limit = subjective_cpu_bill_limit; } + // Gather permission and linked actions info as before const auto linked_action_map = ([&](){ const auto& links = d.get_index(); - auto iter = links.lower_bound( boost::make_tuple( params.account_name ) ); + auto iter = links.lower_bound(boost::make_tuple(params.account_name)); - std::multimap result; + std::multimap result_map; while (iter != links.end() && iter->account == params.account_name ) { auto action = iter->message_type.empty() ? std::optional() : std::optional(iter->message_type); - result.emplace(std::make_pair(iter->required_permission, linked_action{iter->code, std::move(action)})); + result_map.emplace(std::make_pair(iter->required_permission, linked_action{iter->code, std::move(action)})); ++iter; } - return result; + return result_map; })(); auto get_linked_actions = [&](chain::name perm_name) { @@ -2481,115 +2486,94 @@ read_only::get_account_results read_only::get_account( const get_account_params& }; const auto& permissions = d.get_index(); - auto perm = permissions.lower_bound( boost::make_tuple( params.account_name ) ); - while( perm != permissions.end() && perm->owner == params.account_name ) { - /// TODO: lookup perm->parent name + auto perm = permissions.lower_bound(boost::make_tuple(params.account_name)); + while (perm != permissions.end() && perm->owner == params.account_name) { name parent; - - // Don't lookup parent if null - if( perm->parent._id ) { - const auto* p = d.find( perm->parent ); - if( p ) { - SYS_ASSERT(perm->owner == p->owner, invalid_parent_permission, "Invalid parent permission"); - parent = p->name; - } + if (perm->parent._id) { + const auto* p = d.find(perm->parent); + SYS_ASSERT(p && perm->owner == p->owner, invalid_parent_permission, "Invalid parent permission"); + parent = p->name; } auto linked_actions = get_linked_actions(perm->name); - - result.permissions.push_back( permission{ perm->name, parent, perm->auth.to_authority(), std::move(linked_actions)} ); + result.permissions.push_back(permission{ perm->name, parent, perm->auth.to_authority(), std::move(linked_actions) }); ++perm; } - // add sysio.any linked authorizations result.sysio_any_linked_actions = get_linked_actions(chain::config::sysio_any_name); - const auto& code_account = db.db().get( config::system_account_name ); - - abi_def abi; - if( abi_serializer::to_abi(code_account.abi, abi) ) { - abi_serializer abis( abi, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + // Load system account code/ABI for potentially core symbol extraction + const auto& code_account = d.get(config::system_account_name); - const auto token_code = "sysio.token"_n; + abi_def system_abi; + fc::microseconds abi_serializer_max_time = fc::microseconds(config::default_abi_serializer_max_time_us); + if (abi_serializer::to_abi(code_account.abi, system_abi)) { + abi_serializer system_abis(system_abi, abi_serializer::create_yield_function(abi_serializer_max_time)); auto core_symbol = extract_core_symbol(); - if (params.expected_core_symbol) core_symbol = *(params.expected_core_symbol); - const auto* t_id = d.find(boost::make_tuple( token_code, params.account_name, "accounts"_n )); - if( t_id != nullptr ) { + const auto token_code = "sysio.token"_n; + + // Check balance of core symbol in sysio.token::accounts table + if (const auto* t_id = d.find(boost::make_tuple(token_code, params.account_name, "accounts"_n))) { const auto &idx = d.get_index(); - auto it = idx.find(boost::make_tuple( t_id->id, core_symbol.to_symbol_code() )); - if( it != idx.end() && it->value.size() >= sizeof(asset) ) { + auto it = idx.find(boost::make_tuple(t_id->id, core_symbol.to_symbol_code())); + if (it != idx.end() && it->value.size() >= sizeof(asset)) { asset bal; fc::datastream ds(it->value.data(), it->value.size()); fc::raw::unpack(ds, bal); - - if( bal.get_symbol().valid() && bal.get_symbol() == core_symbol ) { + if (bal.get_symbol().valid() && bal.get_symbol() == core_symbol) { result.core_liquid_balance = bal; } } } - t_id = d.find(boost::make_tuple( config::system_account_name, params.account_name, "userres"_n )); - if (t_id != nullptr) { - const auto &idx = d.get_index(); - auto it = idx.find(boost::make_tuple( t_id->id, params.account_name.to_uint64_t() )); - if ( it != idx.end() ) { - vector data; - copy_inline_row(*it, data); - result.total_resources = abis.binary_to_variant( "user_resources", data, abi_serializer::create_yield_function( abi_serializer_max_time ), shorten_abi_errors ); - } - } - - t_id = d.find(boost::make_tuple( config::system_account_name, params.account_name, "delband"_n )); - if (t_id != nullptr) { - const auto &idx = d.get_index(); - auto it = idx.find(boost::make_tuple( t_id->id, params.account_name.to_uint64_t() )); - if ( it != idx.end() ) { - vector data; - copy_inline_row(*it, data); - result.self_delegated_bandwidth = abis.binary_to_variant( "delegated_bandwidth", data, abi_serializer::create_yield_function( abi_serializer_max_time ), shorten_abi_errors ); - } - } + // ---------------------- New Code for Reslimit from sysio.roa ---------------------- + // Accessing sysio.roa's ABI + const auto& roa_account = d.get("sysio.roa"_n); + abi_def roa_abi; + if (abi_serializer::to_abi(roa_account.abi, roa_abi)) { + abi_serializer roa_abis(roa_abi, abi_serializer::create_yield_function(abi_serializer_max_time)); - t_id = d.find(boost::make_tuple( config::system_account_name, params.account_name, "refunds"_n )); - if (t_id != nullptr) { - const auto &idx = d.get_index(); - auto it = idx.find(boost::make_tuple( t_id->id, params.account_name.to_uint64_t() )); - if ( it != idx.end() ) { - vector data; - copy_inline_row(*it, data); - result.refund_request = abis.binary_to_variant( "refund_request", data, abi_serializer::create_yield_function( abi_serializer_max_time ), shorten_abi_errors ); - } - } + // Lookup 'reslimit' table under sysio.roa for the given account + auto t_id_reslimit = d.find( + boost::make_tuple("sysio.roa"_n, params.account_name, "reslimit"_n) + ); - t_id = d.find(boost::make_tuple( config::system_account_name, config::system_account_name, "voters"_n )); - if (t_id != nullptr) { - const auto &idx = d.get_index(); - auto it = idx.find(boost::make_tuple( t_id->id, params.account_name.to_uint64_t() )); - if ( it != idx.end() ) { - vector data; - copy_inline_row(*it, data); - result.voter_info = abis.binary_to_variant( "voter_info", data, abi_serializer::create_yield_function( abi_serializer_max_time ), shorten_abi_errors ); + if (t_id_reslimit != nullptr) { + const auto &idx = d.get_index(); + auto it = idx.find(boost::make_tuple(t_id_reslimit->id, params.account_name.to_uint64_t())); + if (it != idx.end()) { + vector data; + copy_inline_row(*it, data); + result.total_resources = roa_abis.binary_to_variant("reslimit", data, abi_serializer::create_yield_function(abi_serializer_max_time), shorten_abi_errors); + + // Extract fields from total_resources + const auto& res_obj = result.total_resources.get_object(); + auto net_weight_str = res_obj["net_weight"].as_string(); + auto cpu_weight_str = res_obj["cpu_weight"].as_string(); + auto ram_bytes = res_obj["ram_bytes"].as_uint64(); + + asset net_asset = asset::from_string(net_weight_str); + asset cpu_asset = asset::from_string(cpu_weight_str); + + // Set these to result fields (overwrite baseline values from resource_limits_manager) + result.ram_quota = (int64_t)ram_bytes; + result.net_weight = (int64_t)net_asset.get_amount(); + result.cpu_weight = (int64_t)cpu_asset.get_amount(); + } } } - t_id = d.find(boost::make_tuple( config::system_account_name, config::system_account_name, "rexbal"_n )); - if (t_id != nullptr) { - const auto &idx = d.get_index(); - auto it = idx.find(boost::make_tuple( t_id->id, params.account_name.to_uint64_t() )); - if( it != idx.end() ) { - vector data; - copy_inline_row(*it, data); - result.rex_info = abis.binary_to_variant( "rex_balance", data, abi_serializer::create_yield_function( abi_serializer_max_time ), shorten_abi_errors ); - } - } + // ROA Change: Since we no longer need legacy system contract tables, we skip refunds, voters, delband, rex_info, etc. } + return result; } + static fc::variant action_abi_to_variant( const abi_def& abi, type_name action_type ) { fc::variant v; auto it = std::find_if(abi.structs.begin(), abi.structs.end(), [&](auto& x){return x.name == action_type;}); diff --git a/programs/clio/main.cpp b/programs/clio/main.cpp index 1ec9e804e2..79435def32 100644 --- a/programs/clio/main.cpp +++ b/programs/clio/main.cpp @@ -1149,69 +1149,85 @@ struct create_account_subcommand { createAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key or permission level for the new account"))->required(); createAccount->add_option("ActiveKey", active_key_str, localized("The active public key or permission level for the new account")); - if (!simple) { - createAccount->add_option("--stake-net", stake_net, - (localized("The amount of tokens delegated for net bandwidth")))->required(); - createAccount->add_option("--stake-cpu", stake_cpu, - (localized("The amount of tokens delegated for CPU bandwidth")))->required(); - createAccount->add_option("--buy-ram-kbytes", buy_ram_bytes_in_kbytes, - (localized("The amount of RAM bytes to purchase for the new account in kibibytes (KiB)"))); - createAccount->add_option("--buy-ram-bytes", buy_ram_bytes, - (localized("The amount of RAM bytes to purchase for the new account in bytes"))); - createAccount->add_option("--buy-ram", buy_ram_eos, - (localized("The amount of RAM bytes to purchase for the new account in tokens"))); - createAccount->add_flag("--transfer", transfer, - (localized("Transfer voting power and right to unstake tokens to receiver"))); - } + // Commented out for now for reference, these will not be used any longer. + // if (!simple) { + // // These options remain defined, but we will not use them at runtime. + // createAccount->add_option("--stake-net", stake_net, + // (localized("The amount of tokens delegated for net bandwidth")))->required(); + // createAccount->add_option("--stake-cpu", stake_cpu, + // (localized("The amount of tokens delegated for CPU bandwidth")))->required(); + // createAccount->add_option("--buy-ram-kbytes", buy_ram_bytes_in_kbytes, + // (localized("The amount of RAM bytes to purchase for the new account in kibibytes (KiB)"))); + // createAccount->add_option("--buy-ram-bytes", buy_ram_bytes, + // (localized("The amount of RAM bytes to purchase for the new account in bytes"))); + // createAccount->add_option("--buy-ram", buy_ram_eos, + // (localized("The amount of RAM bytes to purchase for the new account in tokens"))); + // createAccount->add_flag("--transfer", transfer, + // (localized("Transfer voting power and right to unstake tokens to receiver"))); + // } add_standard_transaction_options(createAccount, "creator@active"); createAccount->callback([this] { - auth_type owner, active; + auth_type owner, active; - if( owner_key_str.find('@') != string::npos ) { - try { - owner = to_permission_level(owner_key_str); - } SYS_RETHROW_EXCEPTIONS( explained_exception, "Invalid owner permission level: ${permission}", ("permission", owner_key_str) ) - } else { - try { - owner = public_key_type(owner_key_str); - } SYS_RETHROW_EXCEPTIONS( public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str) ); - } + if (owner_key_str.find('@') != string::npos) { + try { + owner = to_permission_level(owner_key_str); + } SYS_RETHROW_EXCEPTIONS(explained_exception, "Invalid owner permission level: ${permission}", ("permission", owner_key_str)) + } else { + try { + owner = public_key_type(owner_key_str); + } SYS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str)); + } - if( active_key_str.empty() ) { - active = owner; - } else if( active_key_str.find('@') != string::npos ) { - try { - active = to_permission_level(active_key_str); - } SYS_RETHROW_EXCEPTIONS( explained_exception, "Invalid active permission level: ${permission}", ("permission", active_key_str) ) - } else { - try { - active = public_key_type(active_key_str); - } SYS_RETHROW_EXCEPTIONS( public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str) ); - } + if (active_key_str.empty()) { + active = owner; + } else if (active_key_str.find('@') != string::npos) { + try { + active = to_permission_level(active_key_str); + } SYS_RETHROW_EXCEPTIONS(explained_exception, "Invalid active permission level: ${permission}", ("permission", active_key_str)) + } else { + try { + active = public_key_type(active_key_str); + } SYS_RETHROW_EXCEPTIONS(public_key_type_exception, "Invalid active public key: ${public_key}", ("public_key", active_key_str)); + } - auto create = create_newaccount(name(creator), name(account_name), owner, active); - if (!simple) { - SYSC_ASSERT( buy_ram_eos.size() || buy_ram_bytes_in_kbytes || buy_ram_bytes, "ERROR: One of --buy-ram, --buy-ram-kbytes or --buy-ram-bytes should have non-zero value" ); - SYSC_ASSERT( !buy_ram_bytes_in_kbytes || !buy_ram_bytes, "ERROR: --buy-ram-kbytes and --buy-ram-bytes cannot be set at the same time" ); - action buyram = !buy_ram_eos.empty() ? create_buyram(name(creator), name(account_name), to_asset(buy_ram_eos)) - : create_buyrambytes(name(creator), name(account_name), (buy_ram_bytes_in_kbytes) ? (buy_ram_bytes_in_kbytes * 1024) : buy_ram_bytes); - auto net = to_asset(stake_net); - auto cpu = to_asset(stake_cpu); - if ( net.get_amount() != 0 || cpu.get_amount() != 0 ) { - action delegate = create_delegate( name(creator), name(account_name), net, cpu, transfer); - send_actions( { create, buyram, delegate } ); - } else { - send_actions( { create, buyram } ); - } + auto create = create_newaccount(name(creator), name(account_name), owner, active); + + // Previously, if !simple, we enforced RAM and staking requirements and then called buyram, buyrambytes, and/or delegatebw. + // We are now commenting this logic out so that no additional actions are sent. + /* + if (!simple) { + SYSC_ASSERT( buy_ram_eos.size() || buy_ram_bytes_in_kbytes || buy_ram_bytes, "ERROR: One of --buy-ram, --buy-ram-kbytes or --buy-ram-bytes should have non-zero value" ); + SYSC_ASSERT( !buy_ram_bytes_in_kbytes || !buy_ram_bytes, "ERROR: --buy-ram-kbytes and --buy-ram-bytes cannot be set at the same time" ); + + action buyram = !buy_ram_eos.empty() + ? create_buyram(name(creator), name(account_name), to_asset(buy_ram_eos)) + : create_buyrambytes(name(creator), name(account_name), + (buy_ram_bytes_in_kbytes) ? (buy_ram_bytes_in_kbytes * 1024) : buy_ram_bytes); + + auto net = to_asset(stake_net); + auto cpu = to_asset(stake_cpu); + + if (net.get_amount() != 0 || cpu.get_amount() != 0) { + action delegate = create_delegate(name(creator), name(account_name), net, cpu, transfer); + send_actions({ create, buyram, delegate }); } else { - send_actions( { create } ); + send_actions({ create, buyram }); } + } else { + send_actions({ create }); + } + */ + + // New logic: Always just send the create action. + send_actions({ create }); }); } }; + struct unregister_producer_subcommand { string producer_str; diff --git a/tests/abieos b/tests/abieos index 8d3aa1472e..902311b1c3 160000 --- a/tests/abieos +++ b/tests/abieos @@ -1 +1 @@ -Subproject commit 8d3aa1472e52e146cb86c6a82a7f1b97de6d4bc5 +Subproject commit 902311b1c3da1eca738cef453c33e0bb24b81b37