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