From 9f3383112a297cad5166112402c35d7fbf2209f8 Mon Sep 17 00:00:00 2001 From: mschoenebeck Date: Wed, 9 Aug 2023 16:10:51 -0500 Subject: [PATCH 1/3] limitauthchg patch - untested --- contracts/eosio.system/CMakeLists.txt | 1 + .../include/eosio.system/eosio.system.hpp | 46 +++++--- .../eosio.system/limit_auth_changes.hpp | 20 ++++ .../include/eosio.system/native.hpp | 100 ++++++++++++++---- .../eosio.system/src/limit_auth_changes.cpp | 56 ++++++++++ 5 files changed, 190 insertions(+), 33 deletions(-) create mode 100644 contracts/eosio.system/include/eosio.system/limit_auth_changes.hpp create mode 100644 contracts/eosio.system/src/limit_auth_changes.cpp diff --git a/contracts/eosio.system/CMakeLists.txt b/contracts/eosio.system/CMakeLists.txt index b059ef165..1547449b4 100644 --- a/contracts/eosio.system/CMakeLists.txt +++ b/contracts/eosio.system/CMakeLists.txt @@ -6,6 +6,7 @@ add_contract(eosio.system eosio.system ${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/voting.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/limit_auth_changes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resource.cpp ) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index dcd7b9697..543ffea49 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -663,11 +663,11 @@ namespace eosiosystem { * * @param from - the account to delegate bandwidth from, that is, the account holding * tokens to be staked, - * @param receiver - the account to delegate bandwith to, that is, the account to + * @param receiver - the account to delegate bandwidth to, that is, the account to * whose resources staked tokens are added * @param stake_net_quantity - tokens staked for NET bandwidth, * @param stake_cpu_quantity - tokens staked for CPU bandwidth, - * @param transfer - if true, ownership of staked tokens is transfered to `receiver`. + * @param transfer - if true, ownership of staked tokens is transferred to `receiver`. * * @post All producers `from` account has voted for will have their votes updated immediately. */ @@ -707,9 +707,9 @@ namespace eosiosystem { void withdraw( const name& owner, const asset& amount ); /** - * Buyrex action, buys REX in exchange for tokens taken out of user's REX fund by transfering + * Buyrex action, buys REX in exchange for tokens taken out of user's REX fund by transferring * core tokens from user REX fund and converts them to REX stake. By buying REX, user is - * lending tokens in order to be rented as CPU or NET resourses. + * lending tokens in order to be rented as CPU or NET resources. * Storage change is billed to 'from' account. * * @param from - owner account name, @@ -813,7 +813,7 @@ namespace eosiosystem { * * @param from - loan creator account, * @param loan_num - loan id, - * @param payment - tokens transfered from REX fund to loan fund. + * @param payment - tokens transferred from REX fund to loan fund. */ [[eosio::action]] void fundcpuloan( const name& from, uint64_t loan_num, const asset& payment ); @@ -824,7 +824,7 @@ namespace eosiosystem { * * @param from - loan creator account, * @param loan_num - loan id, - * @param payment - tokens transfered from REX fund to loan fund. + * @param payment - tokens transferred from REX fund to loan fund. */ [[eosio::action]] void fundnetloan( const name& from, uint64_t loan_num, const asset& payment ); @@ -834,7 +834,7 @@ namespace eosiosystem { * * @param from - loan creator account, * @param loan_num - loan id, - * @param amount - tokens transfered from CPU loan fund to REX fund. + * @param amount - tokens transferred from CPU loan fund to REX fund. */ [[eosio::action]] void defcpuloan( const name& from, uint64_t loan_num, const asset& amount ); @@ -844,7 +844,7 @@ namespace eosiosystem { * * @param from - loan creator account, * @param loan_num - loan id, - * @param amount - tokens transfered from NET loan fund to REX fund. + * @param amount - tokens transferred from NET loan fund to REX fund. */ [[eosio::action]] void defnetloan( const name& from, uint64_t loan_num, const asset& amount ); @@ -913,7 +913,7 @@ namespace eosiosystem { void closerex( const name& owner ); /** - * Undelegate bandwitdh action, decreases the total tokens delegated by `from` to `receiver` and/or + * Undelegate bandwidth action, decreases the total tokens delegated by `from` to `receiver` and/or * frees the memory associated with the delegation if there is nothing * left to delegate. * This will cause an immediate reduction in net/cpu bandwidth of the @@ -927,7 +927,7 @@ namespace eosiosystem { * * @param from - the account to undelegate bandwidth from, that is, * the account whose tokens will be unstaked, - * @param receiver - the account to undelegate bandwith to, that is, + * @param receiver - the account to undelegate bandwidth to, that is, * the account to whose benefit tokens have been staked, * @param unstake_net_quantity - tokens to be unstaked from NET bandwidth, * @param unstake_cpu_quantity - tokens to be unstaked from CPU bandwidth, @@ -950,7 +950,7 @@ namespace eosiosystem { * * @param payer - the ram buyer, * @param receiver - the ram receiver, - * @param quant - the quntity of tokens to buy ram with. + * @param quant - the quantity of tokens to buy ram with. */ [[eosio::action]] void buyram( const name& payer, const name& receiver, const asset& quant ); @@ -961,7 +961,7 @@ namespace eosiosystem { * * @param payer - the ram buyer, * @param receiver - the ram receiver, - * @param bytes - the quntity of ram to buy specified in bytes. + * @param bytes - the quantity of ram to buy specified in bytes. */ [[eosio::action]] void buyrambytes( const name& payer, const name& receiver, uint32_t bytes ); @@ -1078,7 +1078,7 @@ namespace eosiosystem { * update the proxy's weight. * Storage change is billed to `proxy`. * - * @param rpoxy - the account registering as voter proxy (or unregistering), + * @param proxy - the account registering as voter proxy (or unregistering), * @param isproxy - if true, proxy is registered; if false, proxy is unregistered. * * @pre Proxy must have something staked (existing row in voters table) @@ -1189,6 +1189,24 @@ namespace eosiosystem { void set_total(uint64_t total_cpu_us, uint64_t total_net_words, time_point_sec period_start); void issue_inflation(time_point_sec period_start); + /** + * limitauthchg opts into or out of restrictions on updateauth, deleteauth, linkauth, and unlinkauth. + * + * If either allow_perms or disallow_perms is non-empty, then opts into restrictions. If + * allow_perms is non-empty, then the authorized_by argument of the restricted actions must be in + * the vector, or the actions will abort. If disallow_perms is non-empty, then the authorized_by + * argument of the restricted actions must not be in the vector, or the actions will abort. + * + * If both allow_perms and disallow_perms are empty, then opts out of the restrictions. limitauthchg + * aborts if both allow_perms and disallow_perms are non-empty. + * + * @param account - account to change + * @param allow_perms - permissions which may use the restricted actions + * @param disallow_perms - permissions which may not use the restricted actions + */ + [[eosio::action]] + void limitauthchg( const name& account, const std::vector& allow_perms, const std::vector& disallow_perms ); + using init_action = eosio::action_wrapper<"init"_n, &system_contract::init>; using setacctram_action = eosio::action_wrapper<"setacctram"_n, &system_contract::setacctram>; using setacctnet_action = eosio::action_wrapper<"setacctnet"_n, &system_contract::setacctnet>; @@ -1244,7 +1262,7 @@ namespace eosiosystem { check(itr != rm.end(), "system contract must first be initialized"); return itr->quote.balance.symbol; } - + //defined in eosio.system.cpp static eosio_global_state get_default_parameters(); static eosio_global_state4 get_default_inflation_parameters(); diff --git a/contracts/eosio.system/include/eosio.system/limit_auth_changes.hpp b/contracts/eosio.system/include/eosio.system/limit_auth_changes.hpp new file mode 100644 index 000000000..1174bbad9 --- /dev/null +++ b/contracts/eosio.system/include/eosio.system/limit_auth_changes.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace eosiosystem { + using eosio::name; + + struct [[eosio::table("limitauthchg"),eosio::contract("eosio.system")]] limit_auth_change { + uint8_t version = 0; + name account; + std::vector allow_perms; + std::vector disallow_perms; + + uint64_t primary_key() const { return account.value; } + + EOSLIB_SERIALIZE(limit_auth_change, (version)(account)(allow_perms)(disallow_perms)) + }; + + typedef eosio::multi_index<"limitauthchg"_n, limit_auth_change> limit_auth_change_table; +} // namespace eosiosystem diff --git a/contracts/eosio.system/include/eosio.system/native.hpp b/contracts/eosio.system/include/eosio.system/native.hpp index b5cb33e2b..653a0d070 100644 --- a/contracts/eosio.system/include/eosio.system/native.hpp +++ b/contracts/eosio.system/include/eosio.system/native.hpp @@ -65,7 +65,7 @@ namespace eosiosystem { * Blockchain authority. * * An authority is defined by: - * - a vector of key_weights (a key_weight is a public key plus a wieght), + * - a vector of key_weights (a key_weight is a public key plus a weight), * - a vector of permission_level_weights, (a permission_level is an account name plus a permission name) * - a vector of wait_weights (a wait_weight is defined by a number of seconds to wait and a weight) * - a threshold value @@ -121,6 +121,8 @@ namespace eosiosystem { EOSLIB_SERIALIZE( abi_hash, (owner)(hash) ) }; + void check_auth_change(name contract, name account, const binary_extension& authorized_by); + // Method parameters commented out to prevent generation of code that parses input data. /** * The EOSIO core `native` contract that governs authorization and contracts' abi. @@ -157,61 +159,121 @@ namespace eosiosystem { ignore active); /** - * Update authorization action updates pemission for an account. + * Update authorization action updates permission for an account. + * + * This contract enforces additional rules: + * + * 1. If authorized_by is present and not "", then the contract does a + * require_auth2(account, authorized_by). + * 2. If the account has opted into limitauthchg, then authorized_by + * must be present and not "". + * 3. If the account has opted into limitauthchg, and allow_perms is + * not empty, then authorized_by must be in the array. + * 4. If the account has opted into limitauthchg, and disallow_perms is + * not empty, then authorized_by must not be in the array. * * @param account - the account for which the permission is updated - * @param pemission - the permission name which is updated - * @param parem - the parent of the permission which is updated - * @param aut - the json describing the permission authorization + * @param permission - the permission name which is updated + * @param parent - the parent of the permission which is updated + * @param auth - the json describing the permission authorization + * @param authorized_by - the permission which is authorizing this change */ [[eosio::action]] - void updateauth( ignore account, - ignore permission, - ignore parent, - ignore auth ) {} + void updateauth( name account, + name permission, + name parent, + authority auth, + binary_extension authorized_by ) { + check_auth_change(get_self(), account, authorized_by); + } /** * Delete authorization action deletes the authorization for an account's permission. * + * This contract enforces additional rules: + * + * 1. If authorized_by is present and not "", then the contract does a + * require_auth2(account, authorized_by). + * 2. If the account has opted into limitauthchg, then authorized_by + * must be present and not "". + * 3. If the account has opted into limitauthchg, and allow_perms is + * not empty, then authorized_by must be in the array. + * 4. If the account has opted into limitauthchg, and disallow_perms is + * not empty, then authorized_by must not be in the array. + * * @param account - the account for which the permission authorization is deleted, * @param permission - the permission name been deleted. + * @param authorized_by - the permission which is authorizing this change */ [[eosio::action]] - void deleteauth( ignore account, - ignore permission ) {} + void deleteauth( name account, + name permission, + binary_extension authorized_by ) { + check_auth_change(get_self(), account, authorized_by); + } /** * Link authorization action assigns a specific action from a contract to a permission you have created. Five system * actions can not be linked `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, and `canceldelay`. * This is useful because when doing authorization checks, the EOSIO based blockchain starts with the * action needed to be authorized (and the contract belonging to), and looks up which permission - * is needed to pass authorization validation. If a link is set, that permission is used for authoraization + * is needed to pass authorization validation. If a link is set, that permission is used for authorization * validation otherwise then active is the default, with the exception of `eosio.any`. * `eosio.any` is an implicit permission which exists on every account; you can link actions to `eosio.any` * and that will make it so linked actions are accessible to any permissions defined for the account. * + * This contract enforces additional rules: + * + * 1. If authorized_by is present and not "", then the contract does a + * require_auth2(account, authorized_by). + * 2. If the account has opted into limitauthchg, then authorized_by + * must be present and not "". + * 3. If the account has opted into limitauthchg, and allow_perms is + * not empty, then authorized_by must be in the array. + * 4. If the account has opted into limitauthchg, and disallow_perms is + * not empty, then authorized_by must not be in the array. + * * @param account - the permission's owner to be linked and the payer of the RAM needed to store this link, * @param code - the owner of the action to be linked, * @param type - the action to be linked, * @param requirement - the permission to be linked. + * @param authorized_by - the permission which is authorizing this change */ [[eosio::action]] - void linkauth( ignore account, - ignore code, - ignore type, - ignore requirement ) {} + void linkauth( name account, + name code, + name type, + name requirement, + binary_extension authorized_by ) { + check_auth_change(get_self(), account, authorized_by); + } /** * Unlink authorization action it's doing the reverse of linkauth action, by unlinking the given action. * + * This contract enforces additional rules: + * + * 1. If authorized_by is present and not "", then the contract does a + * require_auth2(account, authorized_by). + * 2. If the account has opted into limitauthchg, then authorized_by + * must be present and not "". + * 3. If the account has opted into limitauthchg, and allow_perms is + * not empty, then authorized_by must be in the array. + * 4. If the account has opted into limitauthchg, and disallow_perms is + * not empty, then authorized_by must not be in the array. + * * @param account - the owner of the permission to be unlinked and the receiver of the freed RAM, * @param code - the owner of the action to be unlinked, * @param type - the action to be unlinked. + * @param authorized_by - the permission which is authorizing this change */ [[eosio::action]] - void unlinkauth( ignore account, - ignore code, - ignore type ) {} + void unlinkauth( name account, + name code, + name type, + binary_extension authorized_by ) { + check_auth_change(get_self(), account, authorized_by); + } /** * Cancel delay action cancels a deferred transaction. diff --git a/contracts/eosio.system/src/limit_auth_changes.cpp b/contracts/eosio.system/src/limit_auth_changes.cpp new file mode 100644 index 000000000..debad97b4 --- /dev/null +++ b/contracts/eosio.system/src/limit_auth_changes.cpp @@ -0,0 +1,56 @@ +#include +#include + +namespace eosiosystem { + + void system_contract::limitauthchg(const name& account, const std::vector& allow_perms, + const std::vector& disallow_perms) { + limit_auth_change_table table(get_self(), get_self().value); + require_auth(account); + eosio::check(allow_perms.empty() || disallow_perms.empty(), "either allow_perms or disallow_perms must be empty"); + eosio::check(allow_perms.empty() || + std::find(allow_perms.begin(), allow_perms.end(), "owner"_n) != allow_perms.end(), + "allow_perms does not contain owner"); + eosio::check(disallow_perms.empty() || + std::find(disallow_perms.begin(), disallow_perms.end(), "owner"_n) == disallow_perms.end(), + "disallow_perms contains owner"); + auto it = table.find(account.value); + if(!allow_perms.empty() || !disallow_perms.empty()) { + if(it == table.end()) { + table.emplace(account, [&](auto& row){ + row.account = account; + row.allow_perms = allow_perms; + row.disallow_perms = disallow_perms; + }); + } else { + table.modify(it, account, [&](auto& row){ + row.allow_perms = allow_perms; + row.disallow_perms = disallow_perms; + }); + } + } else { + if(it != table.end()) + table.erase(it); + } + } + + void check_auth_change(name contract, name account, const binary_extension& authorized_by) { + name by(authorized_by.has_value() ? authorized_by.value().value : 0); + if(by.value) + eosio::require_auth({account, by}); + limit_auth_change_table table(contract, contract.value); + auto it = table.find(account.value); + if(it == table.end()) + return; + eosio::check(by.value, "authorized_by is required for this account"); + if(!it->allow_perms.empty()) + eosio::check( + std::find(it->allow_perms.begin(), it->allow_perms.end(), by) != it->allow_perms.end(), + "authorized_by does not appear in allow_perms"); + else + eosio::check( + std::find(it->disallow_perms.begin(), it->disallow_perms.end(), by) == it->disallow_perms.end(), + "authorized_by appears in disallow_perms"); + } + +} // namespace eosiosystem From 6fd918b3131333b56595f8f685339b3b0704649b Mon Sep 17 00:00:00 2001 From: mschoenebeck Date: Wed, 9 Aug 2023 16:37:24 -0500 Subject: [PATCH 2/3] fixed binary_extension --- .../eosio.system/include/eosio.system/native.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contracts/eosio.system/include/eosio.system/native.hpp b/contracts/eosio.system/include/eosio.system/native.hpp index 653a0d070..56097d10e 100644 --- a/contracts/eosio.system/include/eosio.system/native.hpp +++ b/contracts/eosio.system/include/eosio.system/native.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -121,7 +122,7 @@ namespace eosiosystem { EOSLIB_SERIALIZE( abi_hash, (owner)(hash) ) }; - void check_auth_change(name contract, name account, const binary_extension& authorized_by); + void check_auth_change(name contract, name account, const eosio::binary_extension& authorized_by); // Method parameters commented out to prevent generation of code that parses input data. /** @@ -183,7 +184,7 @@ namespace eosiosystem { name permission, name parent, authority auth, - binary_extension authorized_by ) { + eosio::binary_extension authorized_by ) { check_auth_change(get_self(), account, authorized_by); } @@ -208,7 +209,7 @@ namespace eosiosystem { [[eosio::action]] void deleteauth( name account, name permission, - binary_extension authorized_by ) { + eosio::binary_extension authorized_by ) { check_auth_change(get_self(), account, authorized_by); } @@ -244,7 +245,7 @@ namespace eosiosystem { name code, name type, name requirement, - binary_extension authorized_by ) { + eosio::binary_extension authorized_by ) { check_auth_change(get_self(), account, authorized_by); } @@ -271,7 +272,7 @@ namespace eosiosystem { void unlinkauth( name account, name code, name type, - binary_extension authorized_by ) { + eosio::binary_extension authorized_by ) { check_auth_change(get_self(), account, authorized_by); } From 41f81515c174ec49b9654362dfca87a5018d2d98 Mon Sep 17 00:00:00 2001 From: mschoenebeck Date: Wed, 20 Sep 2023 05:43:49 -0500 Subject: [PATCH 3/3] added wasmcfg action to system contract --- .../include/eosio.system/eosio.system.hpp | 8 +++ contracts/eosio.system/src/eosio.system.cpp | 70 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/contracts/eosio.system/include/eosio.system/eosio.system.hpp b/contracts/eosio.system/include/eosio.system/eosio.system.hpp index 543ffea49..a202d9430 100644 --- a/contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ b/contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -1095,6 +1095,14 @@ namespace eosiosystem { [[eosio::action]] void setparams( const eosio::blockchain_parameters& params ); + /** + * Sets the WebAssembly limits. Valid parameters are "low", + * "default" (equivalent to low), and "high". A value of "high" + * allows larger contracts to be deployed. + */ + [[eosio::action]] + void wasmcfg( const name& settings ); + /** * Claim rewards action, claims block producing and vote rewards. * @param owner - producer account claiming per-block and per-vote rewards. diff --git a/contracts/eosio.system/src/eosio.system.cpp b/contracts/eosio.system/src/eosio.system.cpp index 5cbae0a02..9dce07693 100644 --- a/contracts/eosio.system/src/eosio.system.cpp +++ b/contracts/eosio.system/src/eosio.system.cpp @@ -120,6 +120,76 @@ namespace eosiosystem { set_blockchain_parameters( params ); } + // The limits on contract WebAssembly modules + struct wasm_parameters + { + uint32_t max_mutable_global_bytes; + uint32_t max_table_elements; + uint32_t max_section_elements; + uint32_t max_linear_memory_init; + uint32_t max_func_local_bytes; + uint32_t max_nested_structures; + uint32_t max_symbol_bytes; + uint32_t max_module_bytes; + uint32_t max_code_bytes; + uint32_t max_pages; + uint32_t max_call_depth; + }; + + static constexpr wasm_parameters default_limits = { + .max_mutable_global_bytes = 1024, + .max_table_elements = 1024, + .max_section_elements = 8192, + .max_linear_memory_init = 64*1024, + .max_func_local_bytes = 8192, + .max_nested_structures = 1024, + .max_symbol_bytes = 8192, + .max_module_bytes = 20*1024*1024, + .max_code_bytes = 20*1024*1024, + .max_pages = 528, + .max_call_depth = 251 + }; + + static constexpr wasm_parameters high_limits = { + .max_mutable_global_bytes = 8192, + .max_table_elements = 8192, + .max_section_elements = 8192, + .max_linear_memory_init = 16*64*1024, + .max_func_local_bytes = 8192, + .max_nested_structures = 1024, + .max_symbol_bytes = 8192, + .max_module_bytes = 20*1024*1024, + .max_code_bytes = 20*1024*1024, + .max_pages = 528, + .max_call_depth = 1024 + }; + + extern "C" [[eosio::wasm_import]] void set_wasm_parameters_packed( const void*, size_t ); + + void set_wasm_parameters( const wasm_parameters& params ) + { + char buf[sizeof(uint32_t) + sizeof(params)] = {}; + memcpy(buf + sizeof(uint32_t), ¶ms, sizeof(params)); + set_wasm_parameters_packed( buf, sizeof(buf) ); + } + + void system_contract::wasmcfg( const name& settings ) + { + require_auth( get_self() ); + if( settings == "default"_n || settings == "low"_n ) + { + set_wasm_parameters( default_limits ); + } + else if( settings == "high"_n ) + { + set_wasm_parameters( high_limits ); + } + else + { + check(false, "Unknown configuration"); + } + } + void system_contract::setpriv( const name& account, uint8_t ispriv ) { require_auth( get_self() ); set_privileged( account, ispriv );