diff --git a/docs/02_cleos/02_how-to-guides/how-to-create-a-slim-account.md b/docs/02_cleos/02_how-to-guides/how-to-create-a-slim-account.md new file mode 100644 index 0000000000..9af66eb724 --- /dev/null +++ b/docs/02_cleos/02_how-to-guides/how-to-create-a-slim-account.md @@ -0,0 +1,49 @@ +## Overview + +This how-to guide provides instructions on how to create a new slim Antelope blockchain account using the `cleos` CLI tool. You can use accounts to deploy smart contracts and perform other related blockchain operations. Create one or multiple accounts as part of your development environment setup. + +The example in this how-to guide creates a new account named **ted**, authorized by the default system account **eosio**, using the `cleos` CLI tool. + +## Before you Begin + +Make sure you meet the following requirements: + +* Install the currently supported version of `cleos`. +[[info | Note]] +| The cleos tool is bundled with the Antelope software. [Installing Antelope](../../00_install/index.md) will also install the cleos tool. +* Learn about [Antelope Accounts and Permissions](/protocol-guides/04_accounts_and_permissions.md) +* Learn about Asymmetric Cryptography - [public key](/glossary.md#public-key) and [private key](/glossary.md#private-key) pairs. +* Create public/private keypair for the `owner` permissions of an account. + +## Command Reference + +See the following reference guide for `cleos` command line usage and related options: +* [`cleos create slimaccount`](../03_command-reference/create/slim_account.md) command and its parameters + +## Procedure + +The following step shows how to create a new account **ted** authorized by the default system account **eosio**. + +1. Run the following command to create the new account **ted**: + +```sh +cleos create slimaccount eosio ted EOS87TQktA5RVse2EguhztfQVEh6XXxBmgkU8b4Y5YnGvtYAoLGNN +``` +**Where**: +* `eosio` = the system account that authorizes the creation of a new account +* `ted` = the name of the new account conforming to [account naming conventions](/protocol-guides/04_accounts_and_permissions.md#2-accounts) +* `EOS87TQ...AoLGNN` = the owner public key or permission level for the new account (**required**) +[[info | Note]] +| To create a new account in the Antelope blockchain, an existing account, also referred to as a creator account, is required to authorize the creation of a new account. For a newly created Antelope blockchain, the default system account used to create a new account is **eosio**. + +**Example Output** + +```console +executed transaction: 80a54e6220ba90edfae2f35d1841f72660313337294685937ab4eedf422a26d8 152 bytes 139 us +# eosio <= eosio::newslimacc "0000000000ea305500000000000092ca01000000010003a887c1d7893cb40746c6f8fc6e0a964320d0316b85c6ebc8c905a... +warning: transaction executed locally, but may not be confirmed by the network yet ] +``` + +### Summary + +By following these instructions, you are able to create a new slim Antelope account in your blockchain environment. diff --git a/docs/02_cleos/03_command-reference/create/slim_account.md b/docs/02_cleos/03_command-reference/create/slim_account.md new file mode 100755 index 0000000000..0f95f6f90e --- /dev/null +++ b/docs/02_cleos/03_command-reference/create/slim_account.md @@ -0,0 +1,107 @@ +## Description +Create a new slim account on the blockchain (assumes system contract does not restrict RAM usage) + +## Usage + +```console +Usage: cleos create slimaccount [OPTIONS] creator name OwnerKey + +Positionals: + creator TEXT The name of the account creating the new account (required) + name TEXT The name of the new account (required) + OwnerKey TEXT The owner public key or permission level for the new account + +Options: + -h,--help Print this help message and exit + -x,--expiration set the time in seconds before a transaction expires, defaults to 30s + -f,--force-unique force the transaction to be unique. this will consume extra bandwidth and remove any protections against accidently issuing the same transaction multiple times + -s,--skip-sign Specify if unlocked wallet keys should be used to sign transaction + -j,--json print result as json + --json-file TEXT save result in json format into a file + -d,--dont-broadcast don't broadcast transaction to the network (just print to stdout) + --return-packed used in conjunction with --dont-broadcast to get the packed transaction + -r,--ref-block TEXT set the reference block num or block id used for TAPOS (Transaction as Proof-of-Stake) + --use-old-rpc use old RPC push_transaction, rather than new RPC send_transaction + -p,--permission TEXT ... An account and permission level to authorize, as in 'account@permission' (defaults to 'creator@active') + --max-cpu-usage-ms UINT set an upper limit on the milliseconds of cpu usage budget, for the execution of the transaction (defaults to 0 which means no limit) + --max-net-usage UINT set an upper limit on the net usage budget, in bytes, for the transaction (defaults to 0 which means no limit) + --delay-sec UINT set the delay_sec seconds, defaults to 0s +``` + +## Command +A set of Antelope keys is required to create an account. The Antelope keys can be generated by using `cleos create key`. + +```sh +cleos create slimaccount eosio alice EOS4toFS3YXEQCkuuw1aqDLrtHim86Gz9u3hBdcBw5KNPZcursVHq +``` + +## Output + +```json +{ + "transaction_id": "089e813085a99d854a3e6425fddd63d9fb6cfa1b11ebf807f134d0be1046bc2b", + "processed": { + "id": "089e813085a99d854a3e6425fddd63d9fb6cfa1b11ebf807f134d0be1046bc2b", + "block_num": 222, + "block_time": "2024-01-15T15:54:26.500", + "producer_block_id": null, + "receipt": { + "status": "executed", + "cpu_usage_us": 138, + "net_usage_words": 19 + }, + "elapsed": 138, + "net_usage": 152, + "scheduled": false, + "action_traces": [{ + "action_ordinal": 1, + "creator_action_ordinal": 0, + "closest_unnotified_ancestor_action_ordinal": 0, + "receipt": { + "receiver": "eosio", + "act_digest": "bfe408bd33f540cb6696d1a3a0a28ade8e72c5206b7de25f2b24589b41518725", + "global_sequence": 233, + "recv_sequence": 233, + "auth_sequence": [[ + "eosio", + 233 + ] + ], + "code_sequence": 0, + "abi_sequence": 0 + }, + "receiver": "eosio", + "act": { + "account": "eosio", + "name": "newslimacc", + "authorization": [{ + "actor": "eosio", + "permission": "active" + } + ], + "data": "0000000000ea30550000000000855c340100000001000200b35ad060d629717bd3dbec82731094dae9cd7e9980c39625ad58fa7f9b654b01000000", + "hex_data": "0000000000ea30550000000000855c340100000001000200b35ad060d629717bd3dbec82731094dae9cd7e9980c39625ad58fa7f9b654b01000000" + }, + "context_free": false, + "elapsed": 43, + "console": "", + "trx_id": "089e813085a99d854a3e6425fddd63d9fb6cfa1b11ebf807f134d0be1046bc2b", + "block_num": 222, + "block_time": "2024-01-15T15:54:26.500", + "producer_block_id": null, + "account_ram_deltas": [{ + "account": "alice", + "delta": 2210 + } + ], + "except": null, + "error_code": null, + "return_value_hex_data": "" + } + ], + "account_ram_delta": null, + "except": null, + "error_code": null + } +} +``` diff --git a/docs/02_cleos/03_command-reference/system/system-newslimacc.md b/docs/02_cleos/03_command-reference/system/system-newslimacc.md new file mode 100755 index 0000000000..36fb46c668 --- /dev/null +++ b/docs/02_cleos/03_command-reference/system/system-newslimacc.md @@ -0,0 +1,27 @@ +## Description + +Create a slim account, buy ram, stake for bandwidth for the account + +## Positional Arguments +- `creator` _TEXT_ - The name of the account creating the new account +- `name` _TEXT_ - The name of the new account +- `ownerKey` _TEXT_ - The owner public key for the new account +## Options +- `-h,--help` Print this help message and exit +- `--stake-net` _TEXT_ - The amount of EOS delegated for net bandwidth +- `--stake-cpu` _TEXT_ - The amount of EOS delegated for CPU bandwidth +- `--buy-ram-bytes` _UINT_ - The amount of RAM bytes to purchase for the new account in kilobytes KiB, default is 8 KiB +- `--buy-ram-EOS` _TEXT_ - The amount of RAM bytes to purchase for the new account in EOS +- `--transfer` - Transfer voting power and right to unstake EOS to receiver +- `-x,--expiration` _TEXT_ - set the time in seconds before a transaction expires, defaults to 30s +- `-f,--force-unique` - force the transaction to be unique. this will consume extra bandwidth and remove any protections against accidently issuing the same transaction multiple times +- `-s,--skip-sign` Specify if unlocked wallet keys should be used to sign transaction +- `-d,--dont-broadcast` - Don't broadcast transaction to the network (just print to stdout) +- `-r,--ref-block` _TEXT_ set the reference block num or block id used for TAPOS (Transaction as Proof-of-Stake) +- `-p,--permission` _TEXT_ - An account and permission level to authorize, as in 'account@permission' (defaults to 'account@active') +- `--max-cpu-usage-ms` _UINT_ - set an upper limit on the milliseconds of cpu usage budget, for the execution of the transaction (defaults to 0 which means no limit) +- `--max-net-usage` _UINT_ - set an upper limit on the net usage budget, in bytes, for the transaction (defaults to 0 which means no limit) +- `--delay-sec` _UINT_ set the delay_sec seconds, defaults to 0s +- `-j,--json` print result as json + +## Examples diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 75d42dbe35..f4f5f212ea 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -66,7 +66,8 @@ void apply_context::exec_one() digest_type act_digest; - const account_metadata_object* receiver_account = nullptr; + const account_object* receiver_account = nullptr; + const account_metadata_object* receiver_account_metadata = nullptr; auto handle_exception = [&](const auto& e) { @@ -80,9 +81,10 @@ void apply_context::exec_one() try { try { action_return_value.clear(); - receiver_account = &db.get( receiver ); - if( !(context_free && control.skip_trx_checks()) ) { - privileged = receiver_account->is_privileged(); + receiver_account = &db.get( receiver ); + receiver_account_metadata = db.find( receiver ); + if( receiver_account_metadata != nullptr && !(context_free && control.skip_trx_checks()) ) { + privileged = receiver_account_metadata->is_privileged(); auto native = control.find_apply_handler( receiver, act->account, act->name ); if( native ) { if( trx_context.enforce_whiteblacklist && control.is_speculative_block() ) { @@ -92,7 +94,7 @@ void apply_context::exec_one() (*native)( *this ); } - if( ( receiver_account->code_hash != digest_type() ) && + if( ( receiver_account_metadata->code_hash != digest_type() ) && ( !( act->account == config::system_account_name && act->name == "setcode"_n && receiver == config::system_account_name ) @@ -104,7 +106,7 @@ void apply_context::exec_one() control.check_action_list( act->account, act->name ); } try { - control.get_wasm_interface().apply( receiver_account->code_hash, receiver_account->vm_type, receiver_account->vm_version, *this ); + control.get_wasm_interface().apply( receiver_account_metadata->code_hash, receiver_account_metadata->vm_type, receiver_account_metadata->vm_version, *this ); } catch( const wasm_exit& ) {} } @@ -155,10 +157,10 @@ void apply_context::exec_one() handle_exception(wrapper); } - // Note: It should not be possible for receiver_account to be invalidated because: + // Note: It should not be possible for receiver_account_metadata to be invalidated because: // * a pointer to an object in a chainbase index is not invalidated if other objects in that index are modified, removed, or added; // * a pointer to an object in a chainbase index is not invalidated if the fields of that object are modified; - // * and, the *receiver_account object itself cannot be removed because accounts cannot be deleted in EOSIO. + // * and, the *receiver_account_metadata object itself cannot be removed because accounts cannot be deleted in EOSIO. action_trace& trace = trx_context.get_action_trace( action_ordinal ); trace.return_value = std::move(action_return_value); @@ -170,15 +172,15 @@ void apply_context::exec_one() r.global_sequence = next_global_sequence(); r.recv_sequence = next_recv_sequence( *receiver_account ); - const account_metadata_object* first_receiver_account = nullptr; + const account_metadata_object* first_receiver_account_metadata = nullptr; if( act->account == receiver ) { - first_receiver_account = receiver_account; + first_receiver_account_metadata = receiver_account_metadata; } else { - first_receiver_account = &db.get(act->account); + first_receiver_account_metadata = db.find(act->account); } - r.code_sequence = first_receiver_account->code_sequence; // could be modified by action execution above - r.abi_sequence = first_receiver_account->abi_sequence; // could be modified by action execution above + r.code_sequence = first_receiver_account_metadata != nullptr ? first_receiver_account_metadata->code_sequence : 0; // could be modified by action execution above + r.abi_sequence = first_receiver_account_metadata != nullptr ? first_receiver_account_metadata->abi_sequence: 0; // could be modified by action execution above for( const auto& auth : act->authorization ) { r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor ); @@ -1053,7 +1055,7 @@ uint64_t apply_context::next_global_sequence() { } } -uint64_t apply_context::next_recv_sequence( const account_metadata_object& receiver_account ) { +uint64_t apply_context::next_recv_sequence( const account_object& receiver_account ) { if ( trx_context.is_read_only() ) { // To avoid confusion of duplicated receive sequence number, hard code to be 0. return 0; @@ -1065,11 +1067,11 @@ uint64_t apply_context::next_recv_sequence( const account_metadata_object& recei } } uint64_t apply_context::next_auth_sequence( account_name actor ) { - const auto& amo = db.get( actor ); - db.modify( amo, [&](auto& am ){ - ++am.auth_sequence; + const auto& ao = db.get( actor ); + db.modify( ao, [&](auto& a ){ + ++a.auth_sequence; }); - return amo.auth_sequence; + return ao.auth_sequence; } void apply_context::add_ram_usage( account_name account, int64_t ram_delta ) { diff --git a/libraries/chain/authorization_manager.cpp b/libraries/chain/authorization_manager.cpp index 9cb4580a0c..0e552211de 100644 --- a/libraries/chain/authorization_manager.cpp +++ b/libraries/chain/authorization_manager.cpp @@ -17,7 +17,6 @@ namespace eosio { namespace chain { using authorization_index_set = index_set< permission_index, - permission_usage_index, permission_link_index >; @@ -28,8 +27,10 @@ namespace eosio { namespace chain { authorization_index_set::add_indices(_db); } - void authorization_manager::initialize_database() { - _db.create([](auto&){}); /// reserve perm 0 (used else where) + void authorization_manager::initialize_database(const fc::time_point& initial_timestamp) { + _db.create([&](auto& value){ + value.last_used = initial_timestamp; + }); /// reserve perm 0 (used else where) } namespace detail { @@ -48,10 +49,7 @@ namespace eosio { namespace chain { // lookup parent name const auto& parent = db.get(value.parent); res.parent = parent.name; - - // lookup the usage object - const auto& usage = db.get(value.usage_id); - res.last_used = usage.last_used; + res.last_used = value.last_used; return res; }; @@ -61,7 +59,7 @@ namespace eosio { namespace chain { value.owner = row.owner; value.last_updated = row.last_updated; value.auth = row.auth; - + value.last_used = row.last_used; value.parent = 0; if (value.id == 0) { EOS_ASSERT(row.parent == permission_name(), snapshot_exception, "Unexpected parent name on reserved permission 0"); @@ -72,6 +70,7 @@ namespace eosio { namespace chain { EOS_ASSERT(row.auth.waits.size() == 0, snapshot_exception, "Unexpected auth waits on reserved permission 0"); EOS_ASSERT(row.auth.threshold == 0, snapshot_exception, "Unexpected auth threshold on reserved permission 0"); EOS_ASSERT(row.last_updated == time_point(), snapshot_exception, "Unexpected auth last updated on reserved permission 0"); + // EOS_ASSERT(row.last_used == time_point(), snapshot_exception, "Unexpected auth last used on reserved permission 0"); value.parent = 0; } else if ( row.parent != permission_name()){ const auto& parent = db.get(boost::make_tuple(row.owner, row.parent)); @@ -79,16 +78,6 @@ namespace eosio { namespace chain { EOS_ASSERT(parent.id != 0, snapshot_exception, "Unexpected mapping to reserved permission 0"); value.parent = parent.id; } - - if (value.id != 0) { - // create the usage object - const auto& usage = db.create([&](auto& p) { - p.last_used = row.last_used; - }); - value.usage_id = usage.id; - } else { - value.usage_id = 0; - } } }; } @@ -97,11 +86,6 @@ namespace eosio { namespace chain { authorization_index_set::walk_indices([this, &snapshot]( auto utils ){ using section_t = typename decltype(utils)::index_t::value_type; - // skip the permission_usage_index as its inlined with permission_index - if (std::is_same::value) { - return; - } - snapshot->write_section([this]( auto& section ){ decltype(utils)::walk(_db, [this, §ion]( const auto &row ) { section.add_row(row, _db); @@ -114,11 +98,6 @@ namespace eosio { namespace chain { authorization_index_set::walk_indices([this, &snapshot]( auto utils ){ using section_t = typename decltype(utils)::index_t::value_type; - // skip the permission_usage_index as its inlined with permission_index - if (std::is_same::value) { - return; - } - snapshot->read_section([this]( auto& section ) { bool more = !section.empty(); while(more) { @@ -147,17 +126,13 @@ namespace eosio { namespace chain { creation_time = _control.pending_block_time(); } - const auto& perm_usage = _db.create([&](auto& p) { - p.last_used = creation_time; - }); - const auto& perm = _db.create([&](auto& p) { - p.usage_id = perm_usage.id; p.parent = parent; p.owner = account; p.name = name; p.last_updated = creation_time; p.auth = auth; + p.last_used = creation_time; if (auto dm_logger = _control.get_deep_mind_logger(is_trx_transient)) { dm_logger->on_create_permission(p); @@ -183,17 +158,13 @@ namespace eosio { namespace chain { creation_time = _control.pending_block_time(); } - const auto& perm_usage = _db.create([&](auto& p) { - p.last_used = creation_time; - }); - const auto& perm = _db.create([&](auto& p) { - p.usage_id = perm_usage.id; p.parent = parent; p.owner = account; p.name = name; p.last_updated = creation_time; p.auth = std::move(auth); + p.last_used = creation_time; if (auto dm_logger = _control.get_deep_mind_logger(is_trx_transient)) { dm_logger->on_create_permission(p); @@ -230,8 +201,6 @@ namespace eosio { namespace chain { EOS_ASSERT( range.first == range.second, action_validate_exception, "Cannot remove a permission which has children. Remove the children first."); - _db.get_mutable_index().remove_object( permission.usage_id._id ); - if (auto dm_logger = _control.get_deep_mind_logger(is_trx_transient)) { dm_logger->on_remove_permission(permission); } @@ -240,14 +209,14 @@ namespace eosio { namespace chain { } void authorization_manager::update_permission_usage( const permission_object& permission ) { - const auto& puo = _db.get( permission.usage_id ); - _db.modify( puo, [&](permission_usage_object& p) { + const auto& puo = _db.get( permission.id ); + _db.modify( puo, [&](permission_object& p) { p.last_used = _control.pending_block_time(); }); } fc::time_point authorization_manager::get_permission_last_used( const permission_object& permission )const { - return _db.get( permission.usage_id ).last_used; + return _db.get( permission.id ).last_used; } const permission_object* authorization_manager::find_permission( const permission_level& level )const @@ -303,8 +272,10 @@ namespace eosio { namespace chain { try { std::optional linked_permission = lookup_linked_permission(authorizer_account, scope, act_name); - if( !linked_permission ) - return config::active_name; + if( !linked_permission ){ + auto found_active_perm = _db.find( boost::make_tuple(authorizer_account,config::active_name) ); + return found_active_perm ? config::active_name: config::owner_name; + } if( *linked_permission == config::eosio_any_name ) return std::optional(); diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 37ff01f84b..431869f1fe 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -352,6 +352,7 @@ struct controller_impl { &BOOST_PP_CAT(apply_, BOOST_PP_CAT(contract, BOOST_PP_CAT(_,action) ) ) ) SET_APP_HANDLER( eosio, eosio, newaccount ); + SET_APP_HANDLER( eosio, eosio, newslimacc ); SET_APP_HANDLER( eosio, eosio, setcode ); SET_APP_HANDLER( eosio, eosio, setabi ); SET_APP_HANDLER( eosio, eosio, updateauth ); @@ -992,6 +993,64 @@ struct controller_impl { } } + if (std::is_same::value) { + using v6 = snapshot_account_object_v6; + + if (std::clamp(header.version, v6::minimum_version, v6::maximum_version) == header.version ) { + snapshot->read_section([&db = this->db](auto& section) { + bool more = !section.empty(); + while (more) { + v6 account_object_row; + more = section.read_row(account_object_row, db); + const auto *acct_itr = db.find( account_object_row.name ); + if(acct_itr == nullptr){ + db.create([&account_object_row](auto& value ){ + value.name = account_object_row.name; + value.creation_date = account_object_row.creation_date; + }); + db.create([&](auto& value) { + value.name = account_object_row.name; + value.abi = account_object_row.abi; + }); + } + } + }); + return; + } + } + + if (std::is_same::value) { + using v6 = snapshot_account_metadata_object_v6; + + if (std::clamp(header.version, v6::minimum_version, v6::maximum_version) == header.version ) { + snapshot->read_section([&db = this->db](auto& section) { + bool more = !section.empty(); + while (more) { + snapshot_account_metadata_object_v6 account_metadata_object_row; + more = section.read_row(account_metadata_object_row, db); + const auto *acct_itr = db.find( account_metadata_object_row.name ); + EOS_ASSERT(acct_itr != nullptr, snapshot_exception, "Unexpected snapshot_account_metadata_object1"); + db.modify( *acct_itr, [&]( account_object& value ){ + value.recv_sequence = account_metadata_object_row.recv_sequence; + value.auth_sequence = account_metadata_object_row.auth_sequence; + }); + const auto *acct_metadata_itr = db.find( account_metadata_object_row.name ); + EOS_ASSERT(acct_metadata_itr != nullptr, snapshot_exception, "Unexpected snapshot_account_metadata_object2"); + db.modify( *acct_metadata_itr, [&](auto& value) { + value.code_sequence = account_metadata_object_row.code_sequence; + value.abi_sequence = account_metadata_object_row.abi_sequence; + value.code_hash = account_metadata_object_row.code_hash; + value.last_code_update = account_metadata_object_row.last_code_update; + value.flags = account_metadata_object_row.flags; + value.vm_type = account_metadata_object_row.vm_type; + value.vm_version = account_metadata_object_row.vm_version; + }); + } + }); + return; + } + } + snapshot->read_section([this]( auto& section ) { bool more = !section.empty(); while(more) { @@ -1032,17 +1091,16 @@ struct controller_impl { db.create([&](auto& a) { a.name = name; a.creation_date = initial_timestamp; - + }); + db.create([&](auto & a) { + a.name = name; + a.set_privileged( is_privileged ); if( name == config::system_account_name ) { // The initial eosio ABI value affects consensus; see https://github.com/EOSIO/eos/issues/7794 // TODO: This doesn't charge RAM; a fix requires a consensus upgrade. a.abi.assign(eosio_abi_bin, sizeof(eosio_abi_bin)); } }); - db.create([&](auto & a) { - a.name = name; - a.set_privileged( is_privileged ); - }); const auto& owner_permission = authorization.create_permission(name, config::owner_name, 0, owner, false, initial_timestamp ); @@ -1097,7 +1155,7 @@ struct controller_impl { db.create([](auto&){}); - authorization.initialize_database(); + authorization.initialize_database(genesis.initial_timestamp); resource_limits.initialize_database(); authority system_auth(genesis.initial_key); diff --git a/libraries/chain/deep_mind.cpp b/libraries/chain/deep_mind.cpp index 7d740235cd..af99fe2b76 100644 --- a/libraries/chain/deep_mind.cpp +++ b/libraries/chain/deep_mind.cpp @@ -55,7 +55,7 @@ namespace eosio::chain { ("block_num", head_block_num) ("global_sequence_num", db.get().global_action_sequence) ); - const auto& idx = db.get_index(); + const auto& idx = db.get_index(); for (auto& row : idx.indices()) { if (row.abi.size() != 0) { fc_dlog(_logger, "ABIDUMP ABI ${contract} ${abi}", @@ -328,26 +328,26 @@ namespace eosio::chain { ("data", state) ); } - void deep_mind_handler::on_newaccount_resource_limits(const resource_limits::resource_limits_object& limits, const resource_limits::resource_usage_object& usage) + void deep_mind_handler::on_newaccount_resource_limits(const resource_limits::resource_object& limits) { fc_dlog(_logger, "RLIMIT_OP ACCOUNT_LIMITS INS ${data}", ("data", limits) ); - fc_dlog(_logger, "RLIMIT_OP ACCOUNT_USAGE INS ${data}", - ("data", usage) - ); } - void deep_mind_handler::on_update_account_usage(const resource_limits::resource_usage_object& usage) + void deep_mind_handler::on_update_account_usage(const resource_limits::resource_object& limits) { - fc_dlog(_logger, "RLIMIT_OP ACCOUNT_USAGE UPD ${data}", - ("data", usage) + fc_dlog(_logger, "RLIMIT_OP ACCOUNT_LIMITS UPD ${data}", + ("data", limits) ); } - void deep_mind_handler::on_set_account_limits(const resource_limits::resource_limits_object& limits) + void deep_mind_handler::on_set_account_limits(const resource_limits::resource_object& limits, const resource_limits::resource_pending_object& pending) { fc_dlog(_logger, "RLIMIT_OP ACCOUNT_LIMITS UPD ${data}", ("data", limits) ); + fc_dlog(_logger, "RLIMIT_OP ACCOUNT_PENDING UPD ${data}", + ("data", pending) + ); } void deep_mind_handler::on_ram_trace(std::string&& event_id, const char* family, const char* operation, const char* legacy_tag) { diff --git a/libraries/chain/eosio_contract.cpp b/libraries/chain/eosio_contract.cpp index 7170c03f59..3a7792bd97 100644 --- a/libraries/chain/eosio_contract.cpp +++ b/libraries/chain/eosio_contract.cpp @@ -62,7 +62,7 @@ void validate_authority_precondition( const apply_context& context, const author } /** - * This method is called assuming precondition_system_newaccount succeeds a + * This method is called assuming precondition_system_newaccount succeeds */ void apply_eosio_newaccount(apply_context& context) { EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "newaccount not allowed in read-only transaction" ); @@ -83,8 +83,8 @@ void apply_eosio_newaccount(apply_context& context) { EOS_ASSERT( name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long" ); // Check if the creator is privileged - const auto &creator = db.get(create.creator); - if( !creator.is_privileged() ) { + const auto* creator_metadata = db.find(create.creator); + if( creator_metadata == nullptr || !creator_metadata->is_privileged() ){ EOS_ASSERT( name_str.find( "eosio." ) != 0, action_validate_exception, "only privileged accounts can have names that start with 'eosio.'" ); } @@ -127,6 +127,59 @@ void apply_eosio_newaccount(apply_context& context) { } FC_CAPTURE_AND_RETHROW( (create) ) } +/** + * This method is called assuming precondition_system_newslimacc succeeds + */ +void apply_eosio_newslimacc(apply_context& context) { + EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "newslimacc not allowed in read-only transaction" ); + auto create = context.get_action().data_as(); + try { + context.require_authorization(create.creator); +// context.require_write_lock( config::eosio_auth_scope ); + auto& authorization = context.control.get_mutable_authorization_manager(); + + EOS_ASSERT( validate(create.owner), action_validate_exception, "Invalid owner authority"); + + auto& db = context.db; + + auto name_str = name(create.name).to_string(); + + EOS_ASSERT( !create.name.empty(), action_validate_exception, "account name cannot be empty" ); + EOS_ASSERT( name_str.size() <= 12, action_validate_exception, "account names can only be 12 chars long" ); + + // system account only can be created by newaccount + EOS_ASSERT( name_str.find( "eosio." ) != 0, action_validate_exception, + "only newaccount action can create account with name start with 'eosio.'" ); + + auto existing_account = db.find(create.name); + EOS_ASSERT(existing_account == nullptr, account_name_exists_exception, + "Cannot create account named ${name}, as that name is already taken", + ("name", create.name)); + + db.create([&](auto& a) { + a.name = create.name; + a.creation_date = context.control.pending_block_time(); + }); + for( const auto& auth : { create.owner } ){ + validate_authority_precondition( context, auth ); + } + + const auto& owner_permission = authorization.create_permission( create.name, config::owner_name, 0, + std::move(create.owner), context.trx_context.is_transient() ); + int64_t ram_delta = config::overhead_per_account_ram_bytes; + ram_delta -= config::billable_size_v; + ram_delta += config::billable_size_v; + ram_delta += owner_permission.auth.get_billable_size(); + context.control.get_mutable_resource_limits_manager().initialize_account(create.name, context.trx_context.is_transient()); + + if (auto dm_logger = context.control.get_deep_mind_logger(context.trx_context.is_transient())) { + dm_logger->on_ram_trace(RAM_EVENT_ID("${name}", ("name", create.name)), "account", "add", "newslimacc"); + } + + context.add_ram_usage(create.name, ram_delta); + +} FC_CAPTURE_AND_RETHROW( (create) ) } + void apply_eosio_setcode(apply_context& context) { EOS_ASSERT( !context.trx_context.is_read_only(), action_validate_exception, "setcode not allowed in read-only transaction" ); auto& db = context.db; @@ -144,9 +197,17 @@ void apply_eosio_setcode(apply_context& context) { code_hash = fc::sha256::hash( act.code.data(), (uint32_t)act.code.size() ); wasm_interface::validate(context.control, act.code); } + db.get(act.account); + const auto* account_metadata = db.find(act.account); + int64_t metadata_ram_delta = 0; + if(account_metadata == nullptr){ + metadata_ram_delta -= config::billable_size_v; + account_metadata = &db.create([&](auto& a) { + a.name = act.account; + }); + } - const auto& account = db.get(act.account); - bool existing_code = (account.code_hash != digest_type()); + bool existing_code = (account_metadata->code_hash != digest_type()); EOS_ASSERT( code_size > 0 || existing_code, set_exact_code, "contract is already cleared" ); @@ -154,13 +215,13 @@ void apply_eosio_setcode(apply_context& context) { int64_t new_size = code_size * config::setcode_ram_bytes_multiplier; if( existing_code ) { - const code_object& old_code_entry = db.get(boost::make_tuple(account.code_hash, account.vm_type, account.vm_version)); + const code_object& old_code_entry = db.get(boost::make_tuple(account_metadata->code_hash, account_metadata->vm_type, account_metadata->vm_version)); EOS_ASSERT( old_code_entry.code_hash != code_hash, set_exact_code, "contract is already running this version of code" ); old_size = (int64_t)old_code_entry.code.size() * config::setcode_ram_bytes_multiplier; if( old_code_entry.code_ref_count == 1 ) { db.remove(old_code_entry); - context.control.code_block_num_last_used(account.code_hash, account.vm_type, account.vm_version, context.control.head_block_num() + 1); + context.control.code_block_num_last_used(account_metadata->code_hash, account_metadata->vm_type, account_metadata->vm_version, context.control.head_block_num() + 1); } else { db.modify(old_code_entry, [](code_object& o) { --o.code_ref_count; @@ -187,7 +248,7 @@ void apply_eosio_setcode(apply_context& context) { } } - db.modify( account, [&]( auto& a ) { + db.modify( *account_metadata, [&]( auto& a ) { a.code_sequence += 1; a.code_hash = code_hash; a.vm_type = act.vmtype; @@ -207,7 +268,7 @@ void apply_eosio_setcode(apply_context& context) { dm_logger->on_ram_trace(RAM_EVENT_ID("${account}", ("account", act.account)), "code", operation, "setcode"); } - context.add_ram_usage( act.account, new_size - old_size ); + context.add_ram_usage( act.account, new_size - old_size + metadata_ram_delta ); } } @@ -218,20 +279,24 @@ void apply_eosio_setabi(apply_context& context) { context.require_authorization(act.account); - const auto& account = db.get(act.account); + db.get(act.account); + const auto* account_metadata = db.find(act.account); + int64_t metadata_ram_delta = 0; + if(account_metadata == nullptr){ + metadata_ram_delta -= config::billable_size_v; + account_metadata = &db.create([&](auto& a) { + a.name = act.account; + }); + } int64_t abi_size = act.abi.size(); - int64_t old_size = (int64_t)account.abi.size(); + int64_t old_size = (int64_t)account_metadata->abi.size(); int64_t new_size = abi_size; - db.modify( account, [&]( auto& a ) { - a.abi.assign(act.abi.data(), abi_size); - }); - - const auto& account_metadata = db.get(act.account); - db.modify( account_metadata, [&]( auto& a ) { + db.modify( *account_metadata, [&]( auto& a ) { a.abi_sequence += 1; + a.abi.assign(act.abi.data(), abi_size); }); if (new_size != old_size) { @@ -246,7 +311,7 @@ void apply_eosio_setabi(apply_context& context) { dm_logger->on_ram_trace(RAM_EVENT_ID("${account}", ("account", act.account)), "abi", operation, "setabi"); } - context.add_ram_usage( act.account, new_size - old_size ); + context.add_ram_usage( act.account, new_size - old_size + metadata_ram_delta ); } } diff --git a/libraries/chain/eosio_contract_abi.cpp b/libraries/chain/eosio_contract_abi.cpp index 24a93682c1..a2e3c2a9fa 100644 --- a/libraries/chain/eosio_contract_abi.cpp +++ b/libraries/chain/eosio_contract_abi.cpp @@ -144,6 +144,14 @@ abi_def eosio_contract_abi(const abi_def& eosio_system_abi) } }); + eos_abi.structs.emplace_back( struct_def { + "newslimacc", "", { + {"creator", "account_name"}, + {"name", "account_name"}, + {"owner", "authority"}, + } + }); + eos_abi.structs.emplace_back( struct_def { "setcode", "", { {"account", "account_name"}, @@ -215,6 +223,7 @@ abi_def eosio_contract_abi(const abi_def& eosio_system_abi) // TODO add ricardian contracts eos_abi.actions.push_back( action_def{name("newaccount"), "newaccount",""} ); + eos_abi.actions.push_back( action_def{name("newslimacc"), "newslimacc",""} ); eos_abi.actions.push_back( action_def{name("setcode"), "setcode",""} ); eos_abi.actions.push_back( action_def{name("setabi"), "setabi",""} ); eos_abi.actions.push_back( action_def{name("updateauth"), "updateauth",""} ); diff --git a/libraries/chain/include/eosio/chain/account_object.hpp b/libraries/chain/include/eosio/chain/account_object.hpp index d94149e33e..c7c73c069e 100644 --- a/libraries/chain/include/eosio/chain/account_object.hpp +++ b/libraries/chain/include/eosio/chain/account_object.hpp @@ -4,34 +4,45 @@ #include #include #include +#include #include "multi_index_includes.hpp" namespace eosio { namespace chain { - class account_object : public chainbase::object { - OBJECT_CTOR(account_object,(abi)) + struct snapshot_account_object_v6 { + static constexpr uint32_t minimum_version = 1; + static constexpr uint32_t maximum_version = 6; + static_assert(chain_snapshot_header::minimum_compatible_version <= maximum_version, "snapshot_account_object_v6 is no longer needed"); - id_type id; account_name name; //< name should not be changed within a chainbase modifier lambda block_timestamp_type creation_date; shared_blob abi; + }; + struct snapshot_account_metadata_object_v6 { + static constexpr uint32_t minimum_version = 1; + static constexpr uint32_t maximum_version = 6; + static_assert(chain_snapshot_header::minimum_compatible_version <= maximum_version, "snapshot_account_metadata_object_v6 is no longer needed"); - void set_abi( const eosio::chain::abi_def& a ) { - abi.resize_and_fill( fc::raw::pack_size( a ), [&a](char* data, std::size_t size) { - fc::datastream ds( data, size ); - fc::raw::pack( ds, a ); - }); - } - - eosio::chain::abi_def get_abi()const { - eosio::chain::abi_def a; - EOS_ASSERT( abi.size() != 0, abi_not_found_exception, "No ABI set on account ${n}", ("n",name) ); + account_name name; //< name should not be changed within a chainbase modifier lambda + uint64_t recv_sequence = 0; + uint64_t auth_sequence = 0; + uint64_t code_sequence = 0; + uint64_t abi_sequence = 0; + digest_type code_hash; + time_point last_code_update; + uint32_t flags = 0; + uint8_t vm_type = 0; + uint8_t vm_version = 0; + }; + class account_object : public chainbase::object { + OBJECT_CTOR(account_object) - fc::datastream ds( abi.data(), abi.size() ); - fc::raw::unpack( ds, a ); - return a; - } + id_type id; + account_name name; //< name should not be changed within a chainbase modifier lambda + block_timestamp_type creation_date; + uint64_t recv_sequence = 0; + uint64_t auth_sequence = 0; }; using account_id_type = account_object::id_type; @@ -43,10 +54,9 @@ namespace eosio { namespace chain { ordered_unique, member> > >; - class account_metadata_object : public chainbase::object { - OBJECT_CTOR(account_metadata_object); + OBJECT_CTOR(account_metadata_object,(abi)); enum class flags_fields : uint32_t { privileged = 1 @@ -54,8 +64,6 @@ namespace eosio { namespace chain { id_type id; account_name name; //< name should not be changed within a chainbase modifier lambda - uint64_t recv_sequence = 0; - uint64_t auth_sequence = 0; uint64_t code_sequence = 0; uint64_t abi_sequence = 0; digest_type code_hash; @@ -63,12 +71,29 @@ namespace eosio { namespace chain { uint32_t flags = 0; uint8_t vm_type = 0; uint8_t vm_version = 0; + shared_blob abi; bool is_privileged()const { return has_field( flags, flags_fields::privileged ); } void set_privileged( bool privileged ) { flags = set_field( flags, flags_fields::privileged, privileged ); } + + void set_abi( const eosio::chain::abi_def& a ) { + abi.resize_and_fill( fc::raw::pack_size( a ), [&a](char* data, std::size_t size) { + fc::datastream ds( data, size ); + fc::raw::pack( ds, a ); + }); + } + + eosio::chain::abi_def get_abi()const { + eosio::chain::abi_def a; + EOS_ASSERT( abi.size() != 0, abi_not_found_exception, "No ABI set on account ${n}", ("n",name) ); + + fc::datastream ds( abi.data(), abi.size() ); + fc::raw::unpack( ds, a ); + return a; + } }; struct by_name; @@ -98,13 +123,23 @@ namespace eosio { namespace chain { > >; + namespace config { + template<> + struct billable_size { + static const uint64_t overhead = overhead_per_row_per_index_ram_bytes * 2; ///< 2x indices id, name + static const uint64_t value = 54 + overhead; ///< fixed field + overhead + }; + } } } // eosio::chain CHAINBASE_SET_INDEX_TYPE(eosio::chain::account_object, eosio::chain::account_index) CHAINBASE_SET_INDEX_TYPE(eosio::chain::account_metadata_object, eosio::chain::account_metadata_index) CHAINBASE_SET_INDEX_TYPE(eosio::chain::account_ram_correction_object, eosio::chain::account_ram_correction_index) -FC_REFLECT(eosio::chain::account_object, (name)(creation_date)(abi)) -FC_REFLECT(eosio::chain::account_metadata_object, (name)(recv_sequence)(auth_sequence)(code_sequence)(abi_sequence) - (code_hash)(last_code_update)(flags)(vm_type)(vm_version)) +FC_REFLECT(eosio::chain::account_object, (name)(creation_date)(recv_sequence)(auth_sequence)) +FC_REFLECT(eosio::chain::account_metadata_object, (name)(code_sequence)(abi_sequence) + (code_hash)(last_code_update)(flags)(vm_type)(vm_version)(abi)) FC_REFLECT(eosio::chain::account_ram_correction_object, (name)(ram_correction)) +FC_REFLECT(eosio::chain::snapshot_account_object_v6, (name)(creation_date)(abi)) +FC_REFLECT(eosio::chain::snapshot_account_metadata_object_v6, (name)(recv_sequence)(auth_sequence)(code_sequence)(abi_sequence) + (code_hash)(last_code_update)(flags)(vm_type)(vm_version)) diff --git a/libraries/chain/include/eosio/chain/apply_context.hpp b/libraries/chain/include/eosio/chain/apply_context.hpp index da6e17cfa6..ae776f8b0e 100644 --- a/libraries/chain/include/eosio/chain/apply_context.hpp +++ b/libraries/chain/include/eosio/chain/apply_context.hpp @@ -586,7 +586,7 @@ class apply_context { vector get_active_producers() const; uint64_t next_global_sequence(); - uint64_t next_recv_sequence( const account_metadata_object& receiver_account ); + uint64_t next_recv_sequence( const account_object& receiver_account ); uint64_t next_auth_sequence( account_name actor ); void add_ram_usage( account_name account, int64_t ram_delta ); diff --git a/libraries/chain/include/eosio/chain/authorization_manager.hpp b/libraries/chain/include/eosio/chain/authorization_manager.hpp index 081b28deea..1b3731db85 100644 --- a/libraries/chain/include/eosio/chain/authorization_manager.hpp +++ b/libraries/chain/include/eosio/chain/authorization_manager.hpp @@ -23,7 +23,7 @@ namespace eosio { namespace chain { explicit authorization_manager(controller& c, chainbase::database& d); void add_indices(); - void initialize_database(); + void initialize_database(const fc::time_point& initial_timestamp); void add_to_snapshot( const snapshot_writer_ptr& snapshot ) const; void read_from_snapshot( const snapshot_reader_ptr& snapshot ); diff --git a/libraries/chain/include/eosio/chain/chain_snapshot.hpp b/libraries/chain/include/eosio/chain/chain_snapshot.hpp index 6df92ea3f8..d5a8458b57 100644 --- a/libraries/chain/include/eosio/chain/chain_snapshot.hpp +++ b/libraries/chain/include/eosio/chain/chain_snapshot.hpp @@ -23,10 +23,11 @@ struct chain_snapshot_header { * 5: Updated for v3.0.0 eos features: * - chain_config update * 6: Updated for v3.1.0 release + * 7: Updated for restructure account, permisison, resource objects */ static constexpr uint32_t minimum_compatible_version = 2; - static constexpr uint32_t current_version = 6; + static constexpr uint32_t current_version = 7; uint32_t version = current_version; diff --git a/libraries/chain/include/eosio/chain/contract_types.hpp b/libraries/chain/include/eosio/chain/contract_types.hpp index 54c0f8212b..02190045ed 100644 --- a/libraries/chain/include/eosio/chain/contract_types.hpp +++ b/libraries/chain/include/eosio/chain/contract_types.hpp @@ -22,6 +22,20 @@ struct newaccount { } }; +struct newslimacc { + account_name creator; + account_name name; + authority owner; + + static account_name get_account() { + return config::system_account_name; + } + + static action_name get_name() { + return "newslimacc"_n; + } +}; + struct setcode { account_name account; uint8_t vmtype = 0; @@ -155,6 +169,7 @@ struct onerror { } } /// namespace eosio::chain FC_REFLECT( eosio::chain::newaccount , (creator)(name)(owner)(active) ) +FC_REFLECT( eosio::chain::newslimacc , (creator)(name)(owner) ) FC_REFLECT( eosio::chain::setcode , (account)(vmtype)(vmversion)(code) ) FC_REFLECT( eosio::chain::setabi , (account)(abi) ) FC_REFLECT( eosio::chain::updateauth , (account)(permission)(parent)(auth) ) diff --git a/libraries/chain/include/eosio/chain/deep_mind.hpp b/libraries/chain/include/eosio/chain/deep_mind.hpp index ba630440a4..228ce856b4 100644 --- a/libraries/chain/include/eosio/chain/deep_mind.hpp +++ b/libraries/chain/include/eosio/chain/deep_mind.hpp @@ -20,8 +20,8 @@ struct ram_trace; namespace resource_limits { class resource_limits_config_object; class resource_limits_state_object; - struct resource_limits_object; - struct resource_usage_object; + struct resource_object; + struct resource_pending_object; } #define RAM_EVENT_ID( FORMAT, ... ) \ @@ -83,9 +83,9 @@ class deep_mind_handler void on_init_resource_limits(const resource_limits::resource_limits_config_object& config, const resource_limits::resource_limits_state_object& state); void on_update_resource_limits_config(const resource_limits::resource_limits_config_object& config); void on_update_resource_limits_state(const resource_limits::resource_limits_state_object& state); - void on_newaccount_resource_limits(const resource_limits::resource_limits_object& limits, const resource_limits::resource_usage_object& usage); - void on_update_account_usage(const resource_limits::resource_usage_object& usage); - void on_set_account_limits(const resource_limits::resource_limits_object& limits); + void on_newaccount_resource_limits(const resource_limits::resource_object& limits); + void on_update_account_usage(const resource_limits::resource_object& limits); + void on_set_account_limits(const resource_limits::resource_object& limits, const resource_limits::resource_pending_object& pending); // The trace is consumed by the next ram_event or ram_correction void on_ram_trace(std::string&& event_id, const char* family, const char* operation, const char* legacy_tag); void on_ram_event(account_name account, uint64_t new_usage, int64_t delta); diff --git a/libraries/chain/include/eosio/chain/eosio_contract.hpp b/libraries/chain/include/eosio/chain/eosio_contract.hpp index df881f5462..e03b52e1b3 100644 --- a/libraries/chain/include/eosio/chain/eosio_contract.hpp +++ b/libraries/chain/include/eosio/chain/eosio_contract.hpp @@ -12,6 +12,7 @@ namespace eosio { namespace chain { */ ///@{ void apply_eosio_newaccount(apply_context&); + void apply_eosio_newslimacc(apply_context&); void apply_eosio_updateauth(apply_context&); void apply_eosio_deleteauth(apply_context&); void apply_eosio_linkauth(apply_context&); diff --git a/libraries/chain/include/eosio/chain/permission_object.hpp b/libraries/chain/include/eosio/chain/permission_object.hpp index 5547dd2860..fb61596437 100644 --- a/libraries/chain/include/eosio/chain/permission_object.hpp +++ b/libraries/chain/include/eosio/chain/permission_object.hpp @@ -6,31 +6,15 @@ namespace eosio { namespace chain { - class permission_usage_object : public chainbase::object { - OBJECT_CTOR(permission_usage_object) - - id_type id; - time_point last_used; ///< when this permission was last used - }; - - struct by_account_permission; - using permission_usage_index = chainbase::shared_multi_index_container< - permission_usage_object, - indexed_by< - ordered_unique, member> - > - >; - - class permission_object : public chainbase::object { OBJECT_CTOR(permission_object, (auth)) id_type id; - permission_usage_object::id_type usage_id; id_type parent; ///< parent permission account_name owner; ///< the account this permission belongs to (should not be changed within a chainbase modifier lambda) permission_name name; ///< human-readable name for the permission (should not be changed within a chainbase modifier lambda) time_point last_updated; ///< the last time this authority was updated + time_point last_used; ///< when this permission was last used shared_authority auth; ///< authority required to execute this permission @@ -109,18 +93,21 @@ namespace eosio { namespace chain { >; namespace config { + // To make it backward compatible with previous versions of snapshot unit-tests template<> struct billable_size { // Also counts memory usage of the associated permission_usage_object static const uint64_t overhead = 5 * overhead_per_row_per_index_ram_bytes; ///< 5 indices 2x internal ID, parent, owner, name static const uint64_t value = (config::billable_size_v + 64) + overhead; ///< fixed field size + overhead }; + // template<> + // struct billable_size { + // static const uint64_t overhead = 4 * overhead_per_row_per_index_ram_bytes; ///< 4 indices internal ID, parent, owner, name + // static const uint64_t value = (config::billable_size_v + 48) + overhead; ///< fixed field size + overhead + // }; } } } // eosio::chain CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_object, eosio::chain::permission_index) -CHAINBASE_SET_INDEX_TYPE(eosio::chain::permission_usage_object, eosio::chain::permission_usage_index) - -FC_REFLECT(eosio::chain::permission_object, (usage_id)(parent)(owner)(name)(last_updated)(auth)) -FC_REFLECT(eosio::chain::snapshot_permission_object, (parent)(owner)(name)(last_updated)(last_used)(auth)) -FC_REFLECT(eosio::chain::permission_usage_object, (last_used)) +FC_REFLECT(eosio::chain::permission_object, (parent)(owner)(name)(last_updated)(last_used)(auth)) +FC_REFLECT(eosio::chain::snapshot_permission_object, (parent)(owner)(name)(last_updated)(last_used)(auth)) \ No newline at end of file diff --git a/libraries/chain/include/eosio/chain/resource_limits_private.hpp b/libraries/chain/include/eosio/chain/resource_limits_private.hpp index 3401d3423c..c9a89b467d 100644 --- a/libraries/chain/include/eosio/chain/resource_limits_private.hpp +++ b/libraries/chain/include/eosio/chain/resource_limits_private.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "multi_index_includes.hpp" @@ -189,58 +190,88 @@ namespace eosio { namespace chain { namespace resource_limits { } using usage_accumulator = impl::exponential_moving_average_accumulator<>; + struct snapshot_resource_limits_object_v6 { + static constexpr char section_name[] = "eosio::chain::resource_limits::resource_limits_object"; + static constexpr uint32_t minimum_version = 1; + static constexpr uint32_t maximum_version = 6; + static_assert(chain_snapshot_header::minimum_compatible_version <= maximum_version, "snapshot_resource_limits_object_v6 is no longer needed"); + + account_name owner; //< owner should not be changed within a chainbase modifier lambda + // bool pending = false; //< pending should not be changed within a chainbase modifier lambda + + int64_t net_weight = -1; + int64_t cpu_weight = -1; + int64_t ram_bytes = -1; + + }; + + struct snapshot_resource_usage_object_v6 { + static constexpr char section_name[] = "eosio::chain::resource_limits::resource_usage_object"; + static constexpr uint32_t minimum_version = 1; + static constexpr uint32_t maximum_version = 6; + static_assert(chain_snapshot_header::minimum_compatible_version <= maximum_version, "snapshot_resource_usage_object_v6 is no longer needed"); + + account_name owner; //< owner should not be changed within a chainbase modifier lambda + + usage_accumulator net_usage; + usage_accumulator cpu_usage; + + uint64_t ram_usage = 0; + }; /** * Every account that authorizes a transaction is billed for the full size of that transaction. This object * tracks the average usage of that account. */ - struct resource_limits_object : public chainbase::object { + struct resource_object : public chainbase::object { - OBJECT_CTOR(resource_limits_object) + OBJECT_CTOR(resource_object) id_type id; account_name owner; //< owner should not be changed within a chainbase modifier lambda - bool pending = false; //< pending should not be changed within a chainbase modifier lambda + // resource limits int64_t net_weight = -1; int64_t cpu_weight = -1; int64_t ram_bytes = -1; + // resource usage + usage_accumulator net_usage; + usage_accumulator cpu_usage; + uint64_t ram_usage = 0; }; struct by_owner; struct by_dirty; - using resource_limits_index = chainbase::shared_multi_index_container< - resource_limits_object, + using resource_index = chainbase::shared_multi_index_container< + resource_object, indexed_by< - ordered_unique, member>, - ordered_unique, - composite_key - > + ordered_unique, member>, + ordered_unique, member > > >; - struct resource_usage_object : public chainbase::object { - OBJECT_CTOR(resource_usage_object) - - id_type id; - account_name owner; //< owner should not be changed within a chainbase modifier lambda + /** + * Every account that authorizes a transaction is billed for the full size of that transaction. This object + * tracks the average usage of that account. + */ + class resource_pending_object : public chainbase::object { - usage_accumulator net_usage; - usage_accumulator cpu_usage; + OBJECT_CTOR(resource_pending_object) - uint64_t ram_usage = 0; + id_type id; + account_name owner; + int64_t net_weight = -1; + int64_t cpu_weight = -1; + int64_t ram_bytes = -1; }; - using resource_usage_index = chainbase::shared_multi_index_container< - resource_usage_object, + using resource_pending_index = chainbase::shared_multi_index_container< + resource_pending_object, indexed_by< - ordered_unique, member>, - ordered_unique, member > + ordered_unique, member>, + ordered_unique, member> > >; @@ -326,15 +357,17 @@ namespace eosio { namespace chain { namespace resource_limits { } } } /// eosio::chain::resource_limits -CHAINBASE_SET_INDEX_TYPE(eosio::chain::resource_limits::resource_limits_object, eosio::chain::resource_limits::resource_limits_index) -CHAINBASE_SET_INDEX_TYPE(eosio::chain::resource_limits::resource_usage_object, eosio::chain::resource_limits::resource_usage_index) +CHAINBASE_SET_INDEX_TYPE(eosio::chain::resource_limits::resource_object, eosio::chain::resource_limits::resource_index) +CHAINBASE_SET_INDEX_TYPE(eosio::chain::resource_limits::resource_pending_object, eosio::chain::resource_limits::resource_pending_index) CHAINBASE_SET_INDEX_TYPE(eosio::chain::resource_limits::resource_limits_config_object, eosio::chain::resource_limits::resource_limits_config_index) CHAINBASE_SET_INDEX_TYPE(eosio::chain::resource_limits::resource_limits_state_object, eosio::chain::resource_limits::resource_limits_state_index) FC_REFLECT(eosio::chain::resource_limits::usage_accumulator, (last_ordinal)(value_ex)(consumed)) // @ignore pending -FC_REFLECT(eosio::chain::resource_limits::resource_limits_object, (owner)(net_weight)(cpu_weight)(ram_bytes)) -FC_REFLECT(eosio::chain::resource_limits::resource_usage_object, (owner)(net_usage)(cpu_usage)(ram_usage)) +FC_REFLECT(eosio::chain::resource_limits::snapshot_resource_limits_object_v6, (owner)(net_weight)(cpu_weight)(ram_bytes)) +FC_REFLECT(eosio::chain::resource_limits::snapshot_resource_usage_object_v6, (owner)(net_usage)(cpu_usage)(ram_usage)) +FC_REFLECT(eosio::chain::resource_limits::resource_object, (owner)(net_weight)(cpu_weight)(ram_bytes)(net_usage)(cpu_usage)(ram_usage)) +FC_REFLECT(eosio::chain::resource_limits::resource_pending_object, (owner)(net_weight)(cpu_weight)(ram_bytes)) FC_REFLECT(eosio::chain::resource_limits::resource_limits_config_object, (cpu_limit_parameters)(net_limit_parameters)(account_cpu_usage_average_window)(account_net_usage_average_window)) FC_REFLECT(eosio::chain::resource_limits::resource_limits_state_object, (average_block_net_usage)(average_block_cpu_usage)(pending_net_usage)(pending_cpu_usage)(total_net_weight)(total_cpu_weight)(total_ram_bytes)(virtual_net_limit)(virtual_cpu_limit)) diff --git a/libraries/chain/include/eosio/chain/types.hpp b/libraries/chain/include/eosio/chain/types.hpp index 102b9f0dd6..c467d98dc0 100644 --- a/libraries/chain/include/eosio/chain/types.hpp +++ b/libraries/chain/include/eosio/chain/types.hpp @@ -139,7 +139,6 @@ namespace eosio::chain { account_object_type, account_metadata_object_type, permission_object_type, - permission_usage_object_type, permission_link_object_type, UNUSED_action_code_object_type, key_value_object_type, @@ -166,8 +165,8 @@ namespace eosio::chain { UNUSED_proxy_vote_object_type, UNUSED_scope_sequence_object_type, table_id_object_type, - resource_limits_object_type, - resource_usage_object_type, + resource_object_type, + resource_pending_object_type, resource_limits_state_object_type, resource_limits_config_object_type, account_history_object_type, ///< Defined by history_plugin diff --git a/libraries/chain/resource_limits.cpp b/libraries/chain/resource_limits.cpp index b11eea1dba..af049d5fbe 100644 --- a/libraries/chain/resource_limits.cpp +++ b/libraries/chain/resource_limits.cpp @@ -6,13 +6,14 @@ #include #include #include +#include #include namespace eosio { namespace chain { namespace resource_limits { using resource_index_set = index_set< - resource_limits_index, - resource_usage_index, + resource_index, + resource_pending_index, resource_limits_state_index, resource_limits_config_index >; @@ -82,7 +83,57 @@ void resource_limits_manager::add_to_snapshot( const snapshot_writer_ptr& snapsh } void resource_limits_manager::read_from_snapshot( const snapshot_reader_ptr& snapshot ) { - resource_index_set::walk_indices([this, &snapshot]( auto utils ){ + chain_snapshot_header header; + snapshot->read_section([this, &header]( auto §ion ){ + section.read_row(header, _db); + header.validate(); + }); + + resource_index_set::walk_indices([this, &snapshot, &header]( auto utils ){ + using value_t = typename decltype(utils)::index_t::value_type; + + // read resource_limits_object and resource_usage_object from old snapshot + if (std::is_same::value || std::is_same::value) { + using v6 = snapshot_resource_limits_object_v6; + if (std::clamp(header.version, v6::minimum_version, v6::maximum_version) == header.version ) { + // skip the resource_pending_object as its inlined with resource_object section + if (std::is_same::value){ + return; + } + snapshot->read_section(snapshot_resource_limits_object_v6::section_name, [this]( auto& section ) { + bool more = !section.empty(); + while (more) { + snapshot_resource_limits_object_v6 snapshot_resource_limits; + more = section.read_row(snapshot_resource_limits, _db); + const auto* actual_limits = _db.find( snapshot_resource_limits.owner ); + EOS_ASSERT(actual_limits == nullptr, snapshot_exception, "Unexpected snapshot_resource_limits_object_v6"); + _db.create([&](auto& value) { + value.owner = snapshot_resource_limits.owner; + value.net_weight = snapshot_resource_limits.net_weight; + value.cpu_weight = snapshot_resource_limits.cpu_weight; + value.ram_bytes = snapshot_resource_limits.ram_bytes; + }); + } + }); + + snapshot->read_section(snapshot_resource_usage_object_v6::section_name, [this]( auto& section ) { + bool more = !section.empty(); + while (more) { + snapshot_resource_usage_object_v6 snapshot_resource_usage; + more = section.read_row(snapshot_resource_usage, _db); + const auto *actual_limits = _db.find( snapshot_resource_usage.owner ); + EOS_ASSERT(actual_limits != nullptr, snapshot_exception, "Unexpected snapshot_resource_usage_object_v6"); + _db.modify( *actual_limits, [&]( resource_object& value ){ + value.net_usage = snapshot_resource_usage.net_usage; + value.cpu_usage = snapshot_resource_usage.cpu_usage; + value.ram_usage = snapshot_resource_usage.ram_usage; + }); + } + }); + return; + } + } + snapshot->read_section([this]( auto& section ) { bool more = !section.empty(); while(more) { @@ -95,15 +146,11 @@ void resource_limits_manager::read_from_snapshot( const snapshot_reader_ptr& sna } void resource_limits_manager::initialize_account(const account_name& account, bool is_trx_transient) { - const auto& limits = _db.create([&]( resource_limits_object& bl ) { + const auto& resource = _db.create([&]( resource_object& bl ) { bl.owner = account; }); - - const auto& usage = _db.create([&]( resource_usage_object& bu ) { - bu.owner = account; - }); if (auto dm_logger = _get_deep_mind_logger(is_trx_transient)) { - dm_logger->on_newaccount_resource_limits(limits, usage); + dm_logger->on_newaccount_resource_limits(resource); } } @@ -128,7 +175,7 @@ void resource_limits_manager::set_block_parameters(const elastic_limit_parameter void resource_limits_manager::update_account_usage(const flat_set& accounts, uint32_t time_slot ) { const auto& config = _db.get(); for( const auto& a : accounts ) { - const auto& usage = _db.get( a ); + const auto& usage = _db.get( a ); _db.modify( usage, [&]( auto& bu ){ bu.net_usage.add( 0, time_slot, config.account_net_usage_average_window ); bu.cpu_usage.add( 0, time_slot, config.account_cpu_usage_average_window ); @@ -142,7 +189,7 @@ void resource_limits_manager::add_transaction_usage(const flat_set for( const auto& a : accounts ) { - const auto& usage = _db.get( a ); + const auto& usage = _db.get( a ); int64_t unused; int64_t net_weight; int64_t cpu_weight; @@ -153,7 +200,7 @@ void resource_limits_manager::add_transaction_usage(const flat_set bu.cpu_usage.add( cpu_usage, time_slot, config.account_cpu_usage_average_window ); if (auto dm_logger = _get_deep_mind_logger(is_trx_transient)) { - dm_logger->on_update_account_usage(bu); + dm_logger->on_update_account_usage(usage); } }); @@ -213,7 +260,7 @@ void resource_limits_manager::add_pending_ram_usage( const account_name account, return; } - const auto& usage = _db.get( account ); + const auto& usage = _db.get( account ); EOS_ASSERT( ram_delta <= 0 || UINT64_MAX - usage.ram_usage >= (uint64_t)ram_delta, transaction_exception, "Ram usage delta would overflow UINT64_MAX"); @@ -232,7 +279,7 @@ void resource_limits_manager::add_pending_ram_usage( const account_name account, void resource_limits_manager::verify_account_ram_usage( const account_name account )const { int64_t ram_bytes; int64_t net_weight; int64_t cpu_weight; get_account_limits( account, ram_bytes, net_weight, cpu_weight ); - const auto& usage = _db.get( account ); + const auto& usage = _db.get( account ); if( ram_bytes >= 0 ) { EOS_ASSERT( usage.ram_usage <= static_cast(ram_bytes), ram_usage_exceeded, @@ -242,27 +289,26 @@ void resource_limits_manager::verify_account_ram_usage( const account_name accou } int64_t resource_limits_manager::get_account_ram_usage( const account_name& name )const { - return _db.get( name ).ram_usage; + 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, bool is_trx_transient) { - //const auto& usage = _db.get( account ); + //const auto& usage = _db.get( account ); /* * Since we need to delay these until the next resource limiting boundary, these are created in a "pending" * state or adjusted in an existing "pending" state. The chain controller will collapse "pending" state into * the actual state at the next appropriate boundary. */ - auto find_or_create_pending_limits = [&]() -> const resource_limits_object& { - const auto* pending_limits = _db.find( boost::make_tuple(true, account) ); + auto find_or_create_pending_limits = [&]() -> const resource_pending_object& { + const auto* pending_limits = _db.find( account ); if (pending_limits == nullptr) { - const auto& limits = _db.get( boost::make_tuple(false, account)); - return _db.create([&](resource_limits_object& pending_limits){ - pending_limits.owner = limits.owner; - pending_limits.ram_bytes = limits.ram_bytes; - pending_limits.net_weight = limits.net_weight; - pending_limits.cpu_weight = limits.cpu_weight; - pending_limits.pending = true; + const auto& actual_limits = _db.get( account); + return _db.create([&](resource_pending_object& pending_limits){ + pending_limits.owner = actual_limits.owner; + pending_limits.ram_bytes = actual_limits.ram_bytes; + pending_limits.net_weight = actual_limits.net_weight; + pending_limits.cpu_weight = actual_limits.cpu_weight; }); } else { return *pending_limits; @@ -287,13 +333,14 @@ bool resource_limits_manager::set_account_limits( const account_name& account, i */ } - _db.modify( limits, [&]( resource_limits_object& pending_limits ){ + _db.modify( limits, [&]( resource_pending_object& pending_limits ){ pending_limits.ram_bytes = ram_bytes; pending_limits.net_weight = net_weight; pending_limits.cpu_weight = cpu_weight; if (auto dm_logger = _get_deep_mind_logger(is_trx_transient)) { - dm_logger->on_set_account_limits(pending_limits); + const auto& actual_limits = _db.get( account); + dm_logger->on_set_account_limits(actual_limits, pending_limits); } }); @@ -301,13 +348,13 @@ bool resource_limits_manager::set_account_limits( const account_name& account, i } void resource_limits_manager::get_account_limits( const account_name& account, int64_t& ram_bytes, int64_t& net_weight, int64_t& cpu_weight ) const { - const auto* pending_buo = _db.find( boost::make_tuple(true, account) ); + const auto* pending_buo = _db.find( account ); if (pending_buo) { ram_bytes = pending_buo->ram_bytes; net_weight = pending_buo->net_weight; cpu_weight = pending_buo->cpu_weight; } else { - const auto& buo = _db.get( boost::make_tuple( false, account ) ); + const auto& buo = _db.get( account ); ram_bytes = buo.ram_bytes; net_weight = buo.net_weight; cpu_weight = buo.cpu_weight; @@ -315,7 +362,7 @@ void resource_limits_manager::get_account_limits( const account_name& account, i } bool resource_limits_manager::is_unlimited_cpu( const account_name& account ) const { - const auto* buo = _db.find( boost::make_tuple(false, account) ); + const auto* buo = _db.find( account ); if (buo) { return buo->cpu_weight == -1; } @@ -323,8 +370,8 @@ bool resource_limits_manager::is_unlimited_cpu( const account_name& account ) co } void resource_limits_manager::process_account_limit_updates() { - auto& multi_index = _db.get_mutable_index(); - auto& by_owner_index = multi_index.indices().get(); + auto& multi_index = _db.get_mutable_index(); + auto& pending_idx = multi_index.indices().get(); // convenience local lambda to reduce clutter auto update_state_and_value = [](uint64_t &total, int64_t &value, int64_t pending_value, const char* debug_which) -> void { @@ -343,14 +390,10 @@ void resource_limits_manager::process_account_limit_updates() { const auto& state = _db.get(); _db.modify(state, [&](resource_limits_state_object& rso){ - while(!by_owner_index.empty()) { - const auto& itr = by_owner_index.lower_bound(boost::make_tuple(true)); - if (itr == by_owner_index.end() || itr->pending!= true) { - break; - } - - const auto& actual_entry = _db.get(boost::make_tuple(false, itr->owner)); - _db.modify(actual_entry, [&](resource_limits_object& rlo){ + while(pending_idx.begin() != pending_idx.end()) { + const auto& itr = pending_idx.begin(); + const auto& actual_entry = _db.get(itr->owner); + _db.modify(actual_entry, [&](resource_object& rlo){ update_state_and_value(rso.total_ram_bytes, rlo.ram_bytes, itr->ram_bytes, "ram_bytes"); update_state_and_value(rso.total_cpu_weight, rlo.cpu_weight, itr->cpu_weight, "cpu_weight"); update_state_and_value(rso.total_net_weight, rlo.net_weight, itr->net_weight, "net_weight"); @@ -431,7 +474,7 @@ std::pair resource_limits_manager::get_account_cpu_limit_ex( const account_name& name, uint32_t greylist_limit, const std::optional& current_time) const { const auto& state = _db.get(); - const auto& usage = _db.get(name); + const auto& usage = _db.get(name); const auto& config = _db.get(); int64_t cpu_weight, x, y; @@ -493,7 +536,7 @@ std::pair resource_limits_manager::get_account_net_limit_ex( const account_name& name, uint32_t greylist_limit, const std::optional& current_time) const { const auto& config = _db.get(); const auto& state = _db.get(); - const auto& usage = _db.get(name); + const auto& usage = _db.get(name); int64_t net_weight, x, y; get_account_limits( name, x, net_weight, y ); diff --git a/libraries/chain/webassembly/privileged.cpp b/libraries/chain/webassembly/privileged.cpp index f9a8456745..8965298a6d 100644 --- a/libraries/chain/webassembly/privileged.cpp +++ b/libraries/chain/webassembly/privileged.cpp @@ -213,7 +213,8 @@ namespace eosio { namespace chain { namespace webassembly { } bool interface::is_privileged( account_name n ) const { - return context.db.get( n ).is_privileged(); + auto const*account_metadata_itr = context.db.find( n ); + return account_metadata_itr != nullptr && account_metadata_itr->is_privileged(); } void interface::set_privileged( account_name n, bool is_priv ) { diff --git a/libraries/chain/webassembly/runtimes/eos-vm.cpp b/libraries/chain/webassembly/runtimes/eos-vm.cpp index 3a41448c19..e6fc1f8995 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm.cpp @@ -218,7 +218,8 @@ class eos_vm_profiling_module : public wasm_instantiated_module_interface { if(auto it = _prof.find(account); it != _prof.end()) { return it->second.get(); } else { - auto code_sequence = context.control.db().get(account).code_sequence; + auto const *account_metadata_itr = context.control.db().find(account); + auto code_sequence = account_metadata_itr != nullptr ? account_metadata_itr->code_sequence : 0; std::string basename = account.to_string() + "." + std::to_string(code_sequence); auto prof = std::make_unique(basename + ".profile", *_instantiated_module); auto [pos,_] = _prof.insert(std::pair{ account, std::move(prof)}); diff --git a/libraries/state_history/abi.cpp b/libraries/state_history/abi.cpp index d778d0c7fc..8f04f9a5f1 100644 --- a/libraries/state_history/abi.cpp +++ b/libraries/state_history/abi.cpp @@ -481,6 +481,16 @@ extern const char* const state_history_plugin_abi = R"({ { "type": "authority", "name": "auth" } ] }, + { + "name": "permission_v1", "fields": [ + { "type": "name", "name": "owner" }, + { "type": "name", "name": "name" }, + { "type": "name", "name": "parent" }, + { "type": "time_point", "name": "last_updated" }, + { "type": "time_point", "name": "last_used" }, + { "type": "authority", "name": "auth" } + ] + }, { "name": "permission_link_v0", "fields": [ { "type": "name", "name": "account" }, @@ -578,7 +588,7 @@ extern const char* const state_history_plugin_abi = R"({ { "name": "generated_transaction", "types": ["generated_transaction_v0"] }, { "name": "activated_protocol_feature", "types": ["activated_protocol_feature_v0"] }, { "name": "protocol_state", "types": ["protocol_state_v0"] }, - { "name": "permission", "types": ["permission_v0"] }, + { "name": "permission", "types": ["permission_v0", "permission_v1"] }, { "name": "permission_link", "types": ["permission_link_v0"] }, { "name": "resource_limits", "types": ["resource_limits_v0"] }, { "name": "usage_accumulator", "types": ["usage_accumulator_v0"] }, diff --git a/libraries/state_history/create_deltas.cpp b/libraries/state_history/create_deltas.cpp index 3a7fd142db..b920fe0011 100644 --- a/libraries/state_history/create_deltas.cpp +++ b/libraries/state_history/create_deltas.cpp @@ -13,14 +13,6 @@ bool include_delta(const chain::table_id_object& old, const chain::table_id_obje return old.payer != curr.payer; } -bool include_delta(const chain::resource_limits::resource_limits_object& old, - const chain::resource_limits::resource_limits_object& curr) { - return // - old.net_weight != curr.net_weight || // - old.cpu_weight != curr.cpu_weight || // - old.ram_bytes != curr.ram_bytes; -} - bool include_delta(const chain::resource_limits::resource_limits_state_object& old, const chain::resource_limits::resource_limits_state_object& curr) { return // @@ -36,6 +28,11 @@ bool include_delta(const chain::resource_limits::resource_limits_state_object& o old.virtual_net_limit != curr.virtual_net_limit || // old.virtual_cpu_limit != curr.virtual_cpu_limit; } +bool include_delta(const chain::account_object& old, const chain::account_object& curr) { + return // + old.name != curr.name || // + old.creation_date != curr.creation_date; +} bool include_delta(const chain::account_metadata_object& old, const chain::account_metadata_object& curr) { return // @@ -151,7 +148,6 @@ void pack_deltas(boost::iostreams::filtering_ostreambuf& obuf, const chainbase:: chain::index256_index*, chain::index_double_index*, chain::index_long_double_index*, chain::global_property_multi_index*, chain::generated_transaction_multi_index*, chain::protocol_state_multi_index*, chain::permission_index*, chain::permission_link_index*, - chain::resource_limits::resource_limits_index*, chain::resource_limits::resource_usage_index*, chain::resource_limits::resource_limits_state_index*, chain::resource_limits::resource_limits_config_index*>()); @@ -176,8 +172,8 @@ void pack_deltas(boost::iostreams::filtering_ostreambuf& obuf, const chainbase:: process_table(ds, "permission", db.get_index(), pack_row); process_table(ds, "permission_link", db.get_index(), pack_row); - process_table(ds, "resource_limits", db.get_index(), pack_row); - process_table(ds, "resource_usage", db.get_index(), pack_row); + process_table(ds, "resource", db.get_index(), pack_row); + process_table(ds, "resource_pending_object", db.get_index(), pack_row); process_table(ds, "resource_limits_state", db.get_index(), pack_row); process_table(ds, "resource_limits_config", db.get_index(), diff --git a/libraries/state_history/include/eosio/state_history/serialization.hpp b/libraries/state_history/include/eosio/state_history/serialization.hpp index af4a71e125..d3fc4f629a 100644 --- a/libraries/state_history/include/eosio/state_history/serialization.hpp +++ b/libraries/state_history/include/eosio/state_history/serialization.hpp @@ -175,7 +175,6 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper_stat fc::raw::pack(ds, fc::unsigned_int(0)); fc::raw::pack(ds, as_type(obj.obj.name.to_uint64_t())); fc::raw::pack(ds, as_type(obj.obj.creation_date)); - fc::raw::pack(ds, as_type(obj.obj.abi)); return ds; } @@ -192,6 +191,7 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper_stat fc::raw::pack(ds, as_type(obj.obj.vm_version)); fc::raw::pack(ds, as_type(obj.obj.code_hash)); } + fc::raw::pack(ds, as_type(obj.obj.abi)); return ds; } @@ -433,7 +433,7 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper datastream& operator<<(datastream& ds, const history_serial_wrapper& obj) { - fc::raw::pack(ds, fc::unsigned_int(0)); + fc::raw::pack(ds, fc::unsigned_int(1)); fc::raw::pack(ds, as_type(obj.obj.owner.to_uint64_t())); fc::raw::pack(ds, as_type(obj.obj.name.to_uint64_t())); if (obj.obj.parent._id) { @@ -452,6 +452,7 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper(0)); } fc::raw::pack(ds, as_type(obj.obj.last_updated)); + fc::raw::pack(ds, as_type(obj.obj.last_used)); fc::raw::pack(ds, make_history_serial_wrapper(obj.db, as_type(obj.obj.auth))); return ds; } @@ -466,18 +467,6 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper_stat return ds; } -template -datastream& operator<<(datastream& ds, const history_serial_wrapper_stateless& obj) { - EOS_ASSERT(!obj.obj.pending, eosio::chain::plugin_exception, - "accepted_block sent while resource_limits_object in pending state"); - fc::raw::pack(ds, fc::unsigned_int(0)); - fc::raw::pack(ds, as_type(obj.obj.owner.to_uint64_t())); - fc::raw::pack(ds, as_type(obj.obj.net_weight)); - fc::raw::pack(ds, as_type(obj.obj.cpu_weight)); - fc::raw::pack(ds, as_type(obj.obj.ram_bytes)); - return ds; -} - template datastream& operator<<(datastream& ds, const history_serial_wrapper_stateless& obj) { fc::raw::pack(ds, fc::unsigned_int(0)); @@ -488,15 +477,28 @@ datastream& operator<<(datastream& ds, const history_serial_wrapper_stat } template -datastream& operator<<(datastream& ds, const history_serial_wrapper& obj) { +datastream& operator<<(datastream& ds, const history_serial_wrapper_stateless& obj) { fc::raw::pack(ds, fc::unsigned_int(0)); fc::raw::pack(ds, as_type(obj.obj.owner.to_uint64_t())); + fc::raw::pack(ds, as_type(obj.obj.net_weight)); + fc::raw::pack(ds, as_type(obj.obj.cpu_weight)); + fc::raw::pack(ds, as_type(obj.obj.ram_bytes)); fc::raw::pack(ds, make_history_serial_wrapper(as_type(obj.obj.net_usage))); fc::raw::pack(ds, make_history_serial_wrapper(as_type(obj.obj.cpu_usage))); fc::raw::pack(ds, as_type(obj.obj.ram_usage)); return ds; } +template +datastream& operator<<(datastream& ds, const history_serial_wrapper& obj) { + fc::raw::pack(ds, fc::unsigned_int(0)); + fc::raw::pack(ds, as_type(obj.obj.owner.to_uint64_t())); + fc::raw::pack(ds, as_type(obj.obj.net_weight)); + fc::raw::pack(ds, as_type(obj.obj.cpu_weight)); + fc::raw::pack(ds, as_type(obj.obj.ram_bytes)); + return ds; +} + template datastream& operator<<(datastream& ds, const history_serial_wrapper& obj) { fc::raw::pack(ds, fc::unsigned_int(0)); diff --git a/libraries/testing/contracts/eosio.bios/eosio.bios.hpp b/libraries/testing/contracts/eosio.bios/eosio.bios.hpp index 37c387ab6d..129e248ebe 100644 --- a/libraries/testing/contracts/eosio.bios/eosio.bios.hpp +++ b/libraries/testing/contracts/eosio.bios/eosio.bios.hpp @@ -164,6 +164,25 @@ namespace eosiobios { name name, ignore owner, ignore active){} + + /** + * New slim account action + * + * @details Called after a new account is created. This code enforces resource-limits rules + * for new slim accounts as well as new slim account naming conventions. + * + * 1. accounts cannot contain '.' symbols which forces all acccounts to be 12 + * characters long without '.' until a future account auction process is implemented + * which prevents name squatting. + * + * 2. new accounts must stake a minimal number of tokens (as set in system parameters) + * therefore, this method will execute an inline buyram from receiver for newacnt in + * an amount equal to the current new account creation fee. + */ + [[eosio::action]] + void newslimacc( name creator, + name name, + ignore owner){} /** * Update authorization action. * @@ -377,6 +396,7 @@ namespace eosiobios { typedef eosio::multi_index< "abihash"_n, abi_hash > abi_hash_table; using newaccount_action = action_wrapper<"newaccount"_n, &bios::newaccount>; + using newslimacc_action = action_wrapper<"newslimacc"_n, &bios::newslimacc>; using updateauth_action = action_wrapper<"updateauth"_n, &bios::updateauth>; using deleteauth_action = action_wrapper<"deleteauth"_n, &bios::deleteauth>; using linkauth_action = action_wrapper<"linkauth"_n, &bios::linkauth>; diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index ef559ad985..b300b820ba 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -268,6 +268,13 @@ namespace eosio { namespace testing { bool include_code = true ); + transaction_trace_ptr create_slim_account( account_name name, + account_name creator = config::system_account_name, + permission_name creator_perm = config::active_name, + bool multisig = false, + bool include_code = true + ); + transaction_trace_ptr push_reqauth( account_name from, const vector& auths, const vector& keys ); transaction_trace_ptr push_reqauth(account_name from, string role, bool multi_sig = false); // use when just want any old non-context free action @@ -306,9 +313,9 @@ namespace eosio { namespace testing { return get_private_key( keyname, role ).get_public_key(); } - void set_code( account_name name, const char* wast, const private_key_type* signer = nullptr ); - void set_code( account_name name, const vector wasm, const private_key_type* signer = nullptr ); - void set_abi( account_name name, const std::string& abi_json, const private_key_type* signer = nullptr ); + void set_code( account_name name, const char* wast, const private_key_type* signer = nullptr, permission_name perm = config::active_name); + void set_code( account_name name, const vector wasm, const private_key_type* signer = nullptr, permission_name perm = config::active_name ); + void set_abi( account_name name, const std::string& abi_json, const private_key_type* signer = nullptr, permission_name perm = config::active_name ); bool is_code_cached( account_name name ) const; @@ -343,8 +350,12 @@ namespace eosio { namespace testing { auto get_resolver() { return [this]( const account_name& name ) -> std::optional { try { - const auto& accnt = control->db().get( name ); - if( abi_def abi; abi_serializer::to_abi( accnt.abi, abi )) { + const auto* accnt_metadata = control->db().find( name ); + shared_blob account_abi; + if( accnt_metadata != nullptr ){ + account_abi = accnt_metadata->abi; + } + if( abi_def abi; abi_serializer::to_abi( account_abi, abi )) { return abi_serializer( std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time ) ); } return std::optional(); diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 10140d16a8..4b9a117f28 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -597,6 +597,45 @@ namespace eosio { namespace testing { return push_transaction( trx ); } + transaction_trace_ptr base_tester::create_slim_account( account_name a, account_name creator, permission_name creator_perm, bool multisig, bool include_code ) { + signed_transaction trx; + set_transaction_headers(trx); + + authority owner_auth; + if( multisig ) { + // multisig between account's owner key and creators active permission + owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}}); + } else { + owner_auth = authority( get_public_key( a, "owner" ) ); + } + + auto sort_permissions = []( authority& auth ) { + std::sort( auth.accounts.begin(), auth.accounts.end(), + []( const permission_level_weight& lhs, const permission_level_weight& rhs ) { + return lhs.permission < rhs.permission; + } + ); + }; + + if( include_code ) { + FC_ASSERT( owner_auth.threshold <= std::numeric_limits::max(), "threshold is too high" ); + owner_auth.accounts.push_back( permission_level_weight{ {a, config::eosio_code_name}, + static_cast(owner_auth.threshold) } ); + sort_permissions(owner_auth); + } + + trx.actions.emplace_back( vector{{creator,creator_perm}}, + newslimacc{ + .creator = creator, + .name = a, + .owner = owner_auth, + }); + + set_transaction_headers(trx); + trx.sign( get_private_key( creator, creator_perm.to_string() ), control->get_chain_id() ); + return push_transaction( trx ); + } + transaction_trace_ptr base_tester::push_transaction( packed_transaction& trx, fc::time_point deadline, uint32_t billed_cpu_time_us @@ -704,7 +743,6 @@ namespace eosio { namespace testing { uint32_t expiration, uint32_t delay_sec ) - { try { signed_transaction trx; trx.actions.emplace_back( get_action( code, acttype, auths, data ) ); @@ -718,8 +756,8 @@ namespace eosio { namespace testing { action base_tester::get_action( account_name code, action_name acttype, vector auths, const variant_object& data )const { try { - const auto& acnt = control->get_account(code); - auto abi = acnt.get_abi(); + const auto& accnt_metadata = control->db().get( code ); + auto abi = accnt_metadata.get_abi(); chain::abi_serializer abis(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); string action_type_name = abis.get_action_type(acttype); @@ -945,14 +983,14 @@ namespace eosio { namespace testing { } - void base_tester::set_code( account_name account, const char* wast, const private_key_type* signer ) try { - set_code(account, wast_to_wasm(wast), signer); + void base_tester::set_code( account_name account, const char* wast, const private_key_type* signer, permission_name perm ) try { + set_code(account, wast_to_wasm(wast), signer, perm); } FC_CAPTURE_AND_RETHROW( (account) ) - void base_tester::set_code( account_name account, const vector wasm, const private_key_type* signer ) try { + void base_tester::set_code( account_name account, const vector wasm, const private_key_type* signer, permission_name perm ) try { signed_transaction trx; - trx.actions.emplace_back( vector{{account,config::active_name}}, + trx.actions.emplace_back( vector{{account,perm}}, setcode{ .account = account, .vmtype = 0, @@ -964,16 +1002,16 @@ namespace eosio { namespace testing { if( signer ) { trx.sign( *signer, control->get_chain_id() ); } else { - trx.sign( get_private_key( account, "active" ), control->get_chain_id() ); + trx.sign( get_private_key( account, perm.to_string() ), control->get_chain_id() ); } push_transaction( trx ); } FC_CAPTURE_AND_RETHROW( (account) ) - void base_tester::set_abi( account_name account, const std::string& abi_json, const private_key_type* signer ) { + void base_tester::set_abi( account_name account, const std::string& abi_json, const private_key_type* signer, permission_name perm ) { auto abi = fc::json::from_string(abi_json).template as(); signed_transaction trx; - trx.actions.emplace_back( vector{{account,config::active_name}}, + trx.actions.emplace_back( vector{{account,perm}}, setabi{ .account = account, .abi = fc::raw::pack(abi) @@ -983,16 +1021,16 @@ namespace eosio { namespace testing { if( signer ) { trx.sign( *signer, control->get_chain_id() ); } else { - trx.sign( get_private_key( account, "active" ), control->get_chain_id() ); + trx.sign( get_private_key( account, perm.to_string() ), control->get_chain_id() ); } push_transaction( trx ); } bool base_tester::is_code_cached( eosio::chain::account_name name ) const { const auto& db = control->db(); - const account_metadata_object* receiver_account = &db.template get( name ); - if ( receiver_account->code_hash == digest_type() ) return false; - return control->get_wasm_interface().is_code_cached( receiver_account->code_hash, receiver_account->vm_type, receiver_account->vm_version ); + const account_metadata_object* receiver_account_metadata = db.template find( name ); + if ( receiver_account_metadata == nullptr || receiver_account_metadata->code_hash == digest_type() ) return false; + return control->get_wasm_interface().is_code_cached( receiver_account_metadata->code_hash, receiver_account_metadata->vm_type, receiver_account_metadata->vm_version ); } diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index f3ebc7d614..883f90bad0 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1536,10 +1536,10 @@ string convert_to_string(const float128_t& source, const string& key_type, const abi_def get_abi( const controller& db, const name& account ) { const auto &d = db.db(); - const account_object *code_accnt = d.find(account); - EOS_ASSERT(code_accnt != nullptr, chain::account_query_exception, "Fail to retrieve account for ${account}", ("account", account) ); + const auto *code_accnt_metadata = d.find(account); + EOS_ASSERT(code_accnt_metadata != nullptr, chain::account_query_exception, "Fail to retrieve metadata account for ${account}", ("account", account) ); abi_def abi; - abi_serializer::to_abi(code_accnt->abi, abi); + abi_serializer::to_abi(code_accnt_metadata->abi, abi); return abi; } @@ -2291,9 +2291,9 @@ read_only::get_abi_results read_only::get_abi( const get_abi_params& params, con get_abi_results result; result.account_name = params.account_name; const auto& d = db.db(); - const auto& accnt = d.get( params.account_name ); + const auto& accnt_metadata = d.get( params.account_name ); - if( abi_def abi; abi_serializer::to_abi(accnt.abi, abi) ) { + if( abi_def abi; abi_serializer::to_abi(accnt_metadata.abi, abi) ) { result.abi = std::move(abi); } @@ -2306,19 +2306,17 @@ read_only::get_code_results read_only::get_code( const get_code_params& params, get_code_results result; result.account_name = params.account_name; const auto& d = db.db(); - const auto& accnt_obj = d.get( params.account_name ); - const auto& accnt_metadata_obj = d.get( params.account_name ); + const auto& accnt_metadata = d.get( params.account_name ); EOS_ASSERT( params.code_as_wasm, unsupported_feature, "Returning WAST from get_code is no longer supported" ); - if( accnt_metadata_obj.code_hash != digest_type() ) { - const auto& code_obj = d.get(accnt_metadata_obj.code_hash); + if( accnt_metadata.code_hash != digest_type() ) { + const auto& code_obj = d.get(accnt_metadata.code_hash); result.wasm = string(code_obj.code.begin(), code_obj.code.end()); result.code_hash = code_obj.code_hash; - } - - if( abi_def abi; abi_serializer::to_abi(accnt_obj.abi, abi) ) { - result.abi = std::move(abi); + if( abi_def abi; abi_serializer::to_abi(accnt_metadata.abi, abi) ) { + result.abi = std::move(abi); + } } return result; @@ -2330,10 +2328,10 @@ read_only::get_code_hash_results read_only::get_code_hash( const get_code_hash_p get_code_hash_results result; result.account_name = params.account_name; const auto& d = db.db(); - const auto& accnt = d.get( params.account_name ); + const auto& accnt_metadata = d.get( params.account_name ); - if( accnt.code_hash != digest_type() ) - result.code_hash = accnt.code_hash; + if( accnt_metadata.code_hash != digest_type() ) + result.code_hash = accnt_metadata.code_hash; return result; } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account code hash") @@ -2345,13 +2343,12 @@ read_only::get_raw_code_and_abi_results read_only::get_raw_code_and_abi( const g result.account_name = params.account_name; const auto& d = db.db(); - const auto& accnt_obj = d.get(params.account_name); - const auto& accnt_metadata_obj = d.get(params.account_name); - if( accnt_metadata_obj.code_hash != digest_type() ) { - const auto& code_obj = d.get(accnt_metadata_obj.code_hash); + const auto& accnt_metadata = d.get(params.account_name); + if( accnt_metadata.code_hash != digest_type() ) { + const auto& code_obj = d.get(accnt_metadata.code_hash); result.wasm = blob{{code_obj.code.begin(), code_obj.code.end()}}; } - result.abi = blob{{accnt_obj.abi.begin(), accnt_obj.abi.end()}}; + result.abi = blob{{accnt_metadata.abi.begin(), accnt_metadata.abi.end()}}; return result; } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account code/abi") @@ -2363,13 +2360,12 @@ read_only::get_raw_abi_results read_only::get_raw_abi( const get_raw_abi_params& result.account_name = params.account_name; const auto& d = db.db(); - const auto& accnt_obj = d.get(params.account_name); - const auto& accnt_metadata_obj = d.get(params.account_name); - result.abi_hash = fc::sha256::hash( accnt_obj.abi.data(), accnt_obj.abi.size() ); - if( accnt_metadata_obj.code_hash != digest_type() ) - result.code_hash = accnt_metadata_obj.code_hash; + const auto& accnt_metadata = d.get(params.account_name); + result.abi_hash = fc::sha256::hash( accnt_metadata.abi.data(), accnt_metadata.abi.size() ); + if( accnt_metadata.code_hash != digest_type() ) + result.code_hash = accnt_metadata.code_hash; if( !params.abi_hash || *params.abi_hash != result.abi_hash ) - result.abi = blob{{accnt_obj.abi.begin(), accnt_obj.abi.end()}}; + result.abi = blob{{accnt_metadata.abi.begin(), accnt_metadata.abi.end()}}; return result; } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account abi") @@ -2389,10 +2385,11 @@ read_only::get_account_return_t read_only::get_account( const get_account_params 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 ); - - result.privileged = accnt_metadata_obj.is_privileged(); - result.last_code_update = accnt_metadata_obj.last_code_update; + const auto *accnt_metadata_itr = db.db().find( result.account_name ); + if(accnt_metadata_itr != nullptr){ + result.privileged = accnt_metadata_itr->is_privileged(); + result.last_code_update = accnt_metadata_itr->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; @@ -2459,7 +2456,7 @@ read_only::get_account_return_t read_only::get_account( const get_account_params // add eosio.any linked authorizations result.eosio_any_linked_actions = get_linked_actions(chain::config::eosio_any_name); - const auto& code_account = db.db().get( config::system_account_name ); + const auto* code_account_metadata = db.db().find( config::system_account_name ); struct http_params_t { std::optional> total_resources; std::optional> self_delegated_bandwidth; @@ -2469,8 +2466,11 @@ read_only::get_account_return_t read_only::get_account( const get_account_params }; http_params_t http_params; - - if( abi_def abi; abi_serializer::to_abi(code_account.abi, abi) ) { + shared_blob account_abi; + if( code_account_metadata != nullptr ){ + account_abi = code_account_metadata->abi; + } + if( abi_def abi; abi_serializer::to_abi(account_abi, abi) ) { const auto token_code = "eosio.token"_n; diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index 2a45273775..4c546541d4 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -53,10 +53,10 @@ namespace eosio { inline auto make_resolver(const controller& control, fc::microseconds abi_serializer_max_time, throw_on_yield yield_throw ) { return [&control, abi_serializer_max_time, yield_throw](const account_name& name) -> std::optional { if (name.good()) { - const auto* accnt = control.db().template find( name ); - if( accnt != nullptr ) { + const auto* accnt_metadata = control.db().template find( name ); + if( accnt_metadata != nullptr ) { try { - if( abi_def abi; abi_serializer::to_abi( accnt->abi, abi ) ) { + if( abi_def abi; abi_serializer::to_abi( accnt_metadata->abi, abi ) ) { return abi_serializer( std::move( abi ), abi_serializer::create_yield_function( abi_serializer_max_time ) ); } } catch( ... ) { diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index 67167cc9a7..551e053cf4 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -753,6 +753,17 @@ chain::action create_newaccount(const name& creator, const name& newaccount, aut }; } +chain::action create_newslimacc(const name& creator, const name& newaccount, authority owner) { + return action { + get_account_permissions(tx_permission, {creator,config::owner_name}), + eosio::chain::newslimacc{ + .creator = creator, + .name = newaccount, + .owner = owner + } + }; +} + chain::action create_action(const vector& authorization, const account_name& code, const action_name& act, const fc::variant& args) { return chain::action{authorization, code, act, variant_to_bin(code, act, args)}; } @@ -1287,6 +1298,83 @@ struct create_account_subcommand { } }; +struct create_slim_account_subcommand { + string creator; + string account_name; + string owner_key_str; + string stake_net; + string stake_cpu; + uint32_t buy_ram_bytes_in_kbytes = 0; + uint32_t buy_ram_bytes = 0; + string buy_ram_eos; + bool transfer = false; + bool simple = false; + + create_slim_account_subcommand(CLI::App* actionRoot, bool s) : simple(s) { + auto createSlimAccount = actionRoot->add_subcommand( + (simple ? "slimaccount" : "newslimacc"), + (simple ? localized("Create a new account on the blockchain (assumes system contract does not restrict RAM usage)") + : localized("Create a new account on the blockchain with initial resources") ) + ); + createSlimAccount->add_option("creator", creator, localized("The name of the account creating the new account"))->required(); + createSlimAccount->add_option("name", account_name, localized("The name of the new account"))->required(); + createSlimAccount->add_option("OwnerKey", owner_key_str, localized("The owner public key, permission level, or authority for the new account")); + + if (!simple) { + createSlimAccount->add_option("--stake-net", stake_net, + (localized("The amount of tokens delegated for net bandwidth")))->required(); + createSlimAccount->add_option("--stake-cpu", stake_cpu, + (localized("The amount of tokens delegated for CPU bandwidth")))->required(); + createSlimAccount->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)"))); + createSlimAccount->add_option("--buy-ram-bytes", buy_ram_bytes, + (localized("The amount of RAM bytes to purchase for the new account in bytes"))); + createSlimAccount->add_option("--buy-ram", buy_ram_eos, + (localized("The amount of RAM bytes to purchase for the new account in tokens"))); + createSlimAccount->add_flag("--transfer", transfer, + (localized("Transfer voting power and right to unstake tokens to receiver"))); + } + + add_standard_transaction_options_plus_signing(createSlimAccount, "creator@active"); + + createSlimAccount->callback([this] { + authority owner; + EOSC_ASSERT( !owner_key_str.empty(), "Owner public key must not empty" ); + if ( owner_key_str.find('{') != string::npos ) { + try{ + owner = parse_json_authority_or_key(owner_key_str); + } EOS_RETHROW_EXCEPTIONS( explained_exception, "Invalid owner authority: ${authority}", ("authority", owner_key_str) ) + }else if( owner_key_str.find('@') != string::npos ) { + try { + owner = authority(to_permission_level(owner_key_str)); + } EOS_RETHROW_EXCEPTIONS( explained_exception, "Invalid owner permission level: ${permission}", ("permission", owner_key_str) ) + } else { + try { + owner = authority(public_key_type(owner_key_str)); + } EOS_RETHROW_EXCEPTIONS( public_key_type_exception, "Invalid owner public key: ${public_key}", ("public_key", owner_key_str) ); + } + + auto create = create_newslimacc(name(creator), name(account_name), owner); + if (!simple) { + EOSC_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" ); + EOSC_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 }, signing_keys_opt.get_keys()); + } else { + send_actions( { create, buyram }, signing_keys_opt.get_keys()); + } + } else { + send_actions( { create }, signing_keys_opt.get_keys()); + } + }); + } +}; + struct unregister_producer_subcommand { string producer_str; @@ -2860,6 +2948,9 @@ int main( int argc, char** argv ) { // create account auto createAccount = create_account_subcommand( create, true /*simple*/ ); + // create account + auto createSlimAccount = create_slim_account_subcommand( create, true /*simple*/ ); + // convert subcommand auto convert = app.add_subcommand("convert", localized("Pack and unpack transactions")); // TODO also add converting action args based on abi from here ? convert->require_subcommand(); @@ -4447,6 +4538,7 @@ int main( int argc, char** argv ) { system->require_subcommand(); auto createAccountSystem = create_account_subcommand( system, false /*simple*/ ); + auto createSlimAccountSystem = create_slim_account_subcommand( system, false /*simple*/ ); auto registerProducer = register_producer_subcommand(system); auto unregisterProducer = unregister_producer_subcommand(system); diff --git a/tests/chain_plugin_tests.cpp b/tests/chain_plugin_tests.cpp index bd04915173..05ab23662f 100644 --- a/tests/chain_plugin_tests.cpp +++ b/tests/chain_plugin_tests.cpp @@ -48,8 +48,8 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, validating_tester ) try { auto resolver = [&,this]( const account_name& name ) -> std::optional { try { - const auto& accnt = this->control->db().get( name ); - if (abi_def abi; abi_serializer::to_abi(accnt.abi, abi)) { + const auto& accnt_metadata = this->control->db().get( name ); + if (abi_def abi; abi_serializer::to_abi(accnt_metadata.abi, abi)) { return abi_serializer(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } return std::optional(); diff --git a/tests/test_chain_plugin.cpp b/tests/test_chain_plugin.cpp index 4d365e8a02..7f6509b964 100644 --- a/tests/test_chain_plugin.cpp +++ b/tests/test_chain_plugin.cpp @@ -108,6 +108,38 @@ class chain_plugin_tester : public validating_tester { return push_transaction( trx ); } + transaction_trace_ptr create_slim_account_with_resources( account_name a, account_name creator, uint32_t ram_bytes = 8000 ) { + signed_transaction trx; + set_transaction_headers(trx); + + trx.actions.emplace_back( vector{{creator,config::owner_name}}, + newslimacc{ + .creator = creator, + .name = a, + .owner = authority( get_public_key( a, "owner" ) ) + }); + + trx.actions.emplace_back( get_action( config::system_account_name, "buyrambytes"_n, vector{{creator,config::active_name}}, + mvo() + ("payer", creator) + ("receiver", a) + ("bytes", ram_bytes) ) + ); + trx.actions.emplace_back( get_action( config::system_account_name, "delegatebw"_n, vector{{creator,config::active_name}}, + mvo() + ("from", creator) + ("receiver", a) + ("stake_net_quantity", core_from_string("10.0000") ) + ("stake_cpu_quantity", core_from_string("10.0000") ) + ("transfer", 0 ) + ) + ); + + set_transaction_headers(trx); + trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); + return push_transaction( trx ); + } + transaction_trace_ptr create_account_with_resources( account_name a, account_name creator, asset ramfunds, bool multisig, asset net = core_from_string("10.0000"), asset cpu = core_from_string("10.0000") ) { signed_transaction trx; @@ -196,9 +228,9 @@ class chain_plugin_tester : public validating_tester { set_abi( "eosio.token"_n, test_contracts::eosio_token_abi() ); { - const auto& accnt = control->db().get( "eosio.token"_n ); + const auto& accnt_metadata = control->db().get( "eosio.token"_n ); abi_def abi; - BOOST_CHECK_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_CHECK_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); token_abi_ser.set_abi(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } @@ -215,9 +247,9 @@ class chain_plugin_tester : public validating_tester { ("core", symbol(CORE_SYMBOL).to_string())); { - const auto& accnt = control->db().get( config::system_account_name ); + const auto& accnt_metadata = control->db().get( config::system_account_name ); abi_def abi; - BOOST_CHECK_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_CHECK_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); abi_ser.set_abi(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } @@ -501,4 +533,21 @@ BOOST_FIXTURE_TEST_CASE(account_results_rex_info_test, chain_plugin_tester) { tr } FC_LOG_AND_RETHROW() } + +BOOST_FIXTURE_TEST_CASE(slim_account_results_total_resources_test, chain_plugin_tester) { try { + + produce_blocks(10); + setup_system_accounts(); + produce_blocks(); + create_slim_account_with_resources("slim11111111"_n, config::system_account_name); + //stake more than 15% of total EOS supply to activate chain + transfer( name("eosio"), name("slim11111111"), core_from_string("650000000.0000"), name("eosio") ); + + read_only::get_account_results results = get_account_info(name("slim11111111")); + BOOST_CHECK(results.total_resources.get_type() != fc::variant::type_id::null_type); + BOOST_CHECK_EQUAL(core_from_string("10.0000"), results.total_resources["net_weight"].as()); + BOOST_CHECK_EQUAL(core_from_string("10.0000"), results.total_resources["cpu_weight"].as()); + BOOST_CHECK_EQUAL(results.total_resources["ram_bytes"].as_int64() > 0, true); + +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/abi_tests.cpp b/unittests/abi_tests.cpp index d3a936fc46..7674e7fc47 100644 --- a/unittests/abi_tests.cpp +++ b/unittests/abi_tests.cpp @@ -1115,6 +1115,72 @@ BOOST_AUTO_TEST_CASE(newaccount_test) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(newslimacc_test) +{ try { + + abi_serializer abis(eosio_contract_abi(abi_def()), abi_serializer::create_yield_function( max_serialization_time )); + + BOOST_CHECK(true); + const char* test_data = R"=====( + { + "creator" : "newacct.crtr", + "name" : "newacct.name", + "owner" : { + "threshold" : 2147483145, + "keys" : [ {"key" : "EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im", "weight" : 57005}, + {"key" : "EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf", "weight" : 57605} ], + "accounts" : [ {"permission" : {"actor" : "prm.acct1", "permission" : "prm.prm1"}, "weight" : 53005 }, + {"permission" : {"actor" : "prm.acct2", "permission" : "prm.prm2"}, "weight" : 53405 }], + "waits" : [] + } } + )====="; + + auto var = fc::json::from_string(test_data); + + auto newacct = var.as(); + BOOST_TEST(name("newacct.crtr") == newacct.creator); + BOOST_TEST(name("newacct.name") == newacct.name); + + BOOST_TEST(2147483145u == newacct.owner.threshold); + + BOOST_TEST_REQUIRE(2u == newacct.owner.keys.size()); + BOOST_TEST("EOS65rXebLhtk2aTTzP4e9x1AQZs7c5NNXJp89W8R3HyaA6Zyd4im" == newacct.owner.keys[0].key.to_string({})); + BOOST_TEST(57005u == newacct.owner.keys[0].weight); + BOOST_TEST("EOS5eVr9TVnqwnUBNwf9kwMTbrHvX5aPyyEG97dz2b2TNeqWRzbJf" == newacct.owner.keys[1].key.to_string({})); + BOOST_TEST(57605u == newacct.owner.keys[1].weight); + + BOOST_TEST_REQUIRE(2u == newacct.owner.accounts.size()); + BOOST_TEST(name("prm.acct1") == newacct.owner.accounts[0].permission.actor); + BOOST_TEST(name("prm.prm1") == newacct.owner.accounts[0].permission.permission); + BOOST_TEST(53005u == newacct.owner.accounts[0].weight); + BOOST_TEST(name("prm.acct2") == newacct.owner.accounts[1].permission.actor); + BOOST_TEST(name("prm.prm2") == newacct.owner.accounts[1].permission.permission); + BOOST_TEST(53405u == newacct.owner.accounts[1].weight); + + auto var2 = verify_byte_round_trip_conversion( abis, "newslimacc", var ); + auto newaccount2 = var2.as(); + BOOST_TEST(newacct.creator == newaccount2.creator); + BOOST_TEST(newacct.name == newaccount2.name); + + BOOST_TEST(newacct.owner.threshold == newaccount2.owner.threshold); + + BOOST_TEST_REQUIRE(newacct.owner.keys.size() == newaccount2.owner.keys.size()); + BOOST_TEST(newacct.owner.keys[0].key == newaccount2.owner.keys[0].key); + BOOST_TEST(newacct.owner.keys[0].weight == newaccount2.owner.keys[0].weight); + BOOST_TEST(newacct.owner.keys[1].key == newaccount2.owner.keys[1].key); + BOOST_TEST(newacct.owner.keys[1].weight == newaccount2.owner.keys[1].weight); + + BOOST_TEST_REQUIRE(newacct.owner.accounts.size() == newaccount2.owner.accounts.size()); + BOOST_TEST(newacct.owner.accounts[0].permission.actor == newaccount2.owner.accounts[0].permission.actor); + BOOST_TEST(newacct.owner.accounts[0].permission.permission == newaccount2.owner.accounts[0].permission.permission); + BOOST_TEST(newacct.owner.accounts[0].weight == newaccount2.owner.accounts[0].weight); + BOOST_TEST(newacct.owner.accounts[1].permission.actor == newaccount2.owner.accounts[1].permission.actor); + BOOST_TEST(newacct.owner.accounts[1].permission.permission == newaccount2.owner.accounts[1].permission.permission); + BOOST_TEST(newacct.owner.accounts[1].weight == newaccount2.owner.accounts[1].weight); + + verify_type_round_trip_conversion( abis, "newslimacc", var); + +} FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE(setcode_test) { try { diff --git a/unittests/auth_tests.cpp b/unittests/auth_tests.cpp index 02fa619866..d8223d35a6 100644 --- a/unittests/auth_tests.cpp +++ b/unittests/auth_tests.cpp @@ -223,6 +223,146 @@ try { } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(update_auths_with_slim_account) { +try { + validating_tester chain; + chain.create_slim_account(name("alice")); + chain.create_slim_account(name("bob")); + + // Deleting active should fail + BOOST_CHECK_THROW(chain.delete_authority(name("alice"), name("active")), permission_query_exception); + // Deleting owner should fail + BOOST_CHECK_THROW(chain.delete_authority(name("alice"), name("owner")), action_validate_exception); + + // Change owner permission + const auto new_owner_priv_key = chain.get_private_key(name("alice"), "new_owner"); + const auto new_owner_pub_key = new_owner_priv_key.get_public_key(); + chain.set_authority(name("alice"), name("owner"), authority(new_owner_pub_key), {}); + chain.produce_blocks(); + + // Ensure the permission is updated + permission_object::id_type owner_id; + { + auto obj = chain.find(boost::make_tuple(name("alice"), name("owner"))); + BOOST_TEST(obj != nullptr); + BOOST_TEST(obj->owner == name("alice")); + BOOST_TEST(obj->name == name("owner")); + BOOST_TEST(obj->parent == 0); + owner_id = obj->id; + auto auth = obj->auth.to_authority(); + BOOST_TEST(auth.threshold == 1u); + BOOST_TEST(auth.keys.size() == 1u); + BOOST_TEST(auth.accounts.size() == 0u); + BOOST_TEST(auth.keys[0].key.to_string({}) == new_owner_pub_key.to_string({})); + BOOST_TEST(auth.keys[0].key == new_owner_pub_key); + BOOST_TEST(auth.keys[0].weight == 1); + } + + // set active permission, remember that the owner key has been changed + const auto new_active_priv_key = chain.get_private_key(name("alice"), "new_active"); + const auto new_active_pub_key = new_active_priv_key.get_public_key(); + chain.set_authority(name("alice"), name("active"), authority(new_active_pub_key), name("owner"), + { permission_level{name("alice"), name("owner")} }, { chain.get_private_key(name("alice"), "new_owner") }); + chain.produce_blocks(); + + { + auto obj = chain.find(boost::make_tuple(name("alice"), name("active"))); + BOOST_TEST(obj != nullptr); + BOOST_TEST(obj->owner == name("alice")); + BOOST_TEST(obj->name == name("active")); + BOOST_TEST(obj->parent == owner_id); + auto auth = obj->auth.to_authority(); + BOOST_TEST(auth.threshold == 1u); + BOOST_TEST(auth.keys.size() == 1u); + BOOST_TEST(auth.accounts.size() == 0u); + BOOST_TEST(auth.keys[0].key == new_active_pub_key); + BOOST_TEST(auth.keys[0].weight == 1u); + } + + auto spending_priv_key = chain.get_private_key(name("alice"), "spending"); + auto spending_pub_key = spending_priv_key.get_public_key(); + auto trading_priv_key = chain.get_private_key(name("alice"), "trading"); + auto trading_pub_key = trading_priv_key.get_public_key(); + + // add bob active permission + const auto bob_active_priv_key = chain.get_private_key(name("bob"), "active"); + const auto bob_active_pub_key = bob_active_priv_key.get_public_key(); + chain.set_authority(name("bob"), name("active"), authority(bob_active_pub_key), name("owner"), + { permission_level{name("bob"), name("owner")} }, { chain.get_private_key(name("bob"), "owner") }); + // Bob attempts to create new spending auth for Alice + BOOST_CHECK_THROW( chain.set_authority( name("alice"), name("spending"), authority(spending_pub_key), name("active"), + { permission_level{name("bob"), name("active")} }, + { chain.get_private_key(name("bob"), "active") } ), + irrelevant_auth_exception ); + + // Create new spending auth + chain.set_authority(name("alice"), name("spending"), authority(spending_pub_key), name("active"), + { permission_level{name("alice"), name("active")} }, { new_active_priv_key }); + chain.produce_blocks(); + { + auto obj = chain.find(boost::make_tuple(name("alice"), name("spending"))); + BOOST_TEST(obj != nullptr); + BOOST_TEST(obj->owner == name("alice")); + BOOST_TEST(obj->name == name("spending")); + BOOST_TEST(chain.get(obj->parent).owner == name("alice")); + BOOST_TEST(chain.get(obj->parent).name == name("active")); + } + + // Update spending auth parent to be its own, should fail + BOOST_CHECK_THROW(chain.set_authority(name("alice"), name("spending"), authority{spending_pub_key}, name("spending"), + { permission_level{name("alice"), name("spending")} }, { spending_priv_key }), action_validate_exception); + // Update spending auth parent to be owner, should fail + BOOST_CHECK_THROW(chain.set_authority(name("alice"), name("spending"), authority{spending_pub_key}, name("owner"), + { permission_level{name("alice"), name("spending")} }, { spending_priv_key }), action_validate_exception); + + // Remove spending auth + chain.delete_authority(name("alice"), name("spending"), { permission_level{name("alice"), name("active")} }, { new_active_priv_key }); + { + auto obj = chain.find(boost::make_tuple(name("alice"), name("spending"))); + BOOST_TEST(obj == nullptr); + } + chain.produce_blocks(); + + // Create new trading auth + chain.set_authority(name("alice"), name("trading"), authority{trading_pub_key}, name("active"), + { permission_level{name("alice"), name("active")} }, { new_active_priv_key }); + // Recreate spending auth again, however this time, it's under trading instead of owner + chain.set_authority(name("alice"), name("spending"), authority{spending_pub_key}, name("trading"), + { permission_level{name("alice"), name("trading")} }, { trading_priv_key }); + chain.produce_blocks(); + + // Verify correctness of trading and spending + { + const auto* trading = chain.find(boost::make_tuple(name("alice"), name("trading"))); + const auto* spending = chain.find(boost::make_tuple(name("alice"), name("spending"))); + BOOST_TEST(trading != nullptr); + BOOST_TEST(spending != nullptr); + BOOST_TEST(trading->owner == name("alice")); + BOOST_TEST(spending->owner == name("alice")); + BOOST_TEST(trading->name == name("trading")); + BOOST_TEST(spending->name == name("spending")); + BOOST_TEST(spending->parent == trading->id); + BOOST_TEST(chain.get(trading->parent).owner == name("alice")); + BOOST_TEST(chain.get(trading->parent).name == name("active")); + + } + + // Delete trading, should fail since it has children (spending) + BOOST_CHECK_THROW(chain.delete_authority(name("alice"), name("trading"), + { permission_level{name("alice"), name("active")} }, { new_active_priv_key }), action_validate_exception); + // Update trading parent to be spending, should fail since changing parent authority is not supported + BOOST_CHECK_THROW(chain.set_authority(name("alice"), name("trading"), authority{trading_pub_key}, name("spending"), + { permission_level{name("alice"), name("trading")} }, { trading_priv_key }), action_validate_exception); + + // Delete spending auth + chain.delete_authority(name("alice"), name("spending"), { permission_level{name("alice"), name("active")} }, { new_active_priv_key }); + BOOST_TEST((chain.find(boost::make_tuple(name("alice"), name("spending")))) == nullptr); + // Delete trading auth, now it should succeed since it doesn't have any children anymore + chain.delete_authority(name("alice"), name("trading"), { permission_level{name("alice"), name("active")} }, { new_active_priv_key }); + BOOST_TEST((chain.find(boost::make_tuple(name("alice"), name("trading")))) == nullptr); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE(update_auth_unknown_private_key) { try { validating_tester chain; @@ -366,6 +506,57 @@ try { } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(create_slim_account) { +try { + validating_tester chain; + chain.create_slim_account(name("joe")); + chain.produce_block(); + + // Verify account created properly + const auto& joe_owner_authority = chain.get(boost::make_tuple(name("joe"), name("owner"))); + BOOST_TEST(joe_owner_authority.auth.threshold == 1u); + BOOST_TEST(joe_owner_authority.auth.accounts.size() == 1u); + BOOST_TEST(joe_owner_authority.auth.keys.size() == 1u); + BOOST_TEST(joe_owner_authority.auth.keys[0].key.to_string({}) == chain.get_public_key(name("joe"), "owner").to_string({})); + BOOST_TEST(joe_owner_authority.auth.keys[0].weight == 1u); + + const auto& joe_active_authority = chain.find(boost::make_tuple(name("joe"), name("active"))); + BOOST_TEST(joe_active_authority == nullptr); + + // Creating new slim account from a slim account creator + chain.create_slim_account(name("alice"), name("joe"), config::owner_name); + + // Verify account created properly + const auto& alice_owner_authority = chain.find(boost::make_tuple(name("alice"), name("owner"))); + BOOST_TEST(alice_owner_authority != nullptr); + const auto& alice_active_authority = chain.find(boost::make_tuple(name("alice"), name("active"))); + BOOST_TEST(alice_active_authority == nullptr); + + // create new slim account by non-existent creator permission + BOOST_CHECK_THROW( chain.create_slim_account(name("bob"), name("joe"), config::active_name), transaction_exception ); + + // Create duplicate name + BOOST_CHECK_EXCEPTION(chain.create_account(name("joe")), action_validate_exception, + fc_exception_message_is("Cannot create account named joe, as that name is already taken")); + // Create duplicate name + BOOST_CHECK_EXCEPTION(chain.create_slim_account(name("joe")), action_validate_exception, + fc_exception_message_is("Cannot create account named joe, as that name is already taken")); + + // Creating account with name more than 12 chars + BOOST_CHECK_EXCEPTION(chain.create_slim_account(name("aaaaaaaaaaaaa"), name("joe"), config::owner_name), action_validate_exception, + fc_exception_message_is("account names can only be 12 chars long")); + + + // Creating account with eosio. prefix, should fail + BOOST_CHECK_EXCEPTION(chain.create_slim_account(name("eosio.test1")), action_validate_exception, + fc_exception_message_is("only newaccount action can create account with name start with 'eosio.'")); + + // Creating account with eosio. prefix with non-privileged account, should fail + BOOST_CHECK_EXCEPTION(chain.create_slim_account(name("eosio.test2"), name("joe"), config::owner_name), action_validate_exception, + fc_exception_message_is("only newaccount action can create account with name start with 'eosio.'")); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE( any_auth ) { try { validating_tester chain; chain.create_accounts( {name("alice"), name("bob")} ); @@ -419,7 +610,7 @@ try { const chainbase::database &db = chain.control->db(); - using resource_usage_object = eosio::chain::resource_limits::resource_usage_object; + using resource_object = eosio::chain::resource_limits::resource_object; using by_owner = eosio::chain::resource_limits::by_owner; auto create_acc = [&](account_name a) { @@ -449,9 +640,9 @@ try { create_acc(acc2); - const auto &usage = db.get(acc1); + const auto &usage = db.get(acc1); - const auto &usage2 = db.get(acc1a); + const auto &usage2 = db.get(acc1a); BOOST_TEST(usage.cpu_usage.average() > 0U); BOOST_TEST(usage.net_usage.average() > 0U); diff --git a/unittests/bootseq_tests.cpp b/unittests/bootseq_tests.cpp index e5d2a5a344..0255c8a71e 100644 --- a/unittests/bootseq_tests.cpp +++ b/unittests/bootseq_tests.cpp @@ -67,9 +67,9 @@ class bootseq_tester : public validating_tester { ("core", symbol(CORE_SYMBOL).to_string()) ); } - const auto& accnt = control->db().get( config::system_account_name ); + const auto& accnt_metadata = control->db().get( config::system_account_name ); abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); abi_ser.set_abi(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } @@ -163,9 +163,9 @@ class bootseq_tester : public validating_tester { set_code(account, wasm, signer); set_abi(account, abi, signer); if (account == config::system_account_name) { - const auto& accnt = control->db().get( account ); + const auto& accnt_metadata = control->db().get( account ); abi_def abi_definition; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi_definition), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi_definition), true); abi_ser.set_abi(std::move(abi_definition), abi_serializer::create_yield_function( abi_serializer_max_time )); } produce_blocks(); diff --git a/unittests/contracts/eosio.system/eosio.system.cpp b/unittests/contracts/eosio.system/eosio.system.cpp index 4e63c66fce..367f2c2692 100644 --- a/unittests/contracts/eosio.system/eosio.system.cpp +++ b/unittests/contracts/eosio.system/eosio.system.cpp @@ -259,6 +259,44 @@ void native::newaccount( name creator, set_resource_limits( newact, 0, 0, 0 ); } +void native::newslimacc( name creator, + name newact, + ignore owner ) { + + if( creator != _self ) { + uint64_t tmp = newact.value >> 4; + bool has_dot = false; + + for( uint32_t i = 0; i < 12; ++i ) { + has_dot |= !(tmp & 0x1f); + tmp >>= 5; + } + if( has_dot ) { // or is less than 12 characters + auto suffix = newact.suffix(); + if( suffix == newact ) { + name_bid_table bids(_self, _self.value); + auto current = bids.find( newact.value ); + check( current != bids.end(), "no active bid for name" ); + check( current->high_bidder == creator, "only highest bidder can claim" ); + check( current->high_bid < 0, "auction for name is not closed yet" ); + bids.erase( current ); + } else { + check( creator == suffix, "only suffix may create this account" ); + } + } + } + + user_resources_table userres( _self, newact.value); + + userres.emplace( newact, [&]( auto& res ) { + res.owner = newact; + res.net_weight = asset( 0, system_contract::get_core_symbol() ); + res.cpu_weight = asset( 0, system_contract::get_core_symbol() ); + }); + + set_resource_limits( newact, 0, 0, 0 ); +} + void native::setabi( name acnt, const std::vector& abi ) { eosio::multi_index< "abihash"_n, abi_hash > table(_self, _self.value); auto itr = table.find( acnt.value ); @@ -303,7 +341,7 @@ void system_contract::init( unsigned_int version, symbol core ) { EOSIO_DISPATCH( eosiosystem::system_contract, // native.hpp (newaccount definition is actually in eosio.system.cpp) -(newaccount)(updateauth)(deleteauth)(linkauth)(unlinkauth)(canceldelay)(onerror)(setabi) +(newaccount)(newslimacc)(updateauth)(deleteauth)(linkauth)(unlinkauth)(canceldelay)(onerror)(setabi) // eosio.system.cpp (init)(setram)(setramrate)(setparams)(setpriv)(setalimits)(rmvproducer)(updtrevision)(bidname)(bidrefund) // rex.cpp diff --git a/unittests/contracts/eosio.system/native.hpp b/unittests/contracts/eosio.system/native.hpp index 27596c7523..87e4f5b787 100644 --- a/unittests/contracts/eosio.system/native.hpp +++ b/unittests/contracts/eosio.system/native.hpp @@ -100,6 +100,10 @@ class [[eosio::contract("eosio.system")]] native : public eosio::contract { ignore owner, ignore active); + [[eosio::action]] + void newslimacc( name creator, + name name, + ignore owner); [[eosio::action]] void updateauth( ignore account, diff --git a/unittests/dry_run_trx_tests.cpp b/unittests/dry_run_trx_tests.cpp index 6ca9409bb3..1d80024a14 100644 --- a/unittests/dry_run_trx_tests.cpp +++ b/unittests/dry_run_trx_tests.cpp @@ -155,9 +155,9 @@ BOOST_FIXTURE_TEST_CASE(setabi_test, dry_run_trx_tester) { try { send_action(act, false); // should not throw send_action(act, true); // should not throw - const auto* accnt = control->db().template find( "setabitest"_n ); - BOOST_REQUIRE(accnt); - BOOST_TEST(accnt->abi.size() == 0u); // no abi actually set + const auto* accnt_metadata = control->db().template find( "setabitest"_n ); + BOOST_REQUIRE(accnt_metadata); + BOOST_TEST(accnt_metadata->abi.size() == 0u); // no abi actually set } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE(updateauth_test, dry_run_trx_tester) { try { @@ -300,33 +300,33 @@ BOOST_FIXTURE_TEST_CASE(sequence_numbers_test, dry_run_trx_tester) { try { set_up_test_contract(); const auto& p = control->get_dynamic_global_properties(); - auto receiver_account = control->db().find("noauthtable"_n); - auto amo = control->db().find("alice"_n); + auto receiver_account = control->db().find("noauthtable"_n); + auto ao = control->db().find("alice"_n); // verify sequence numbers in state increment for non-read-only transactions auto prev_global_action_sequence = p.global_action_sequence; auto prev_recv_sequence = receiver_account->recv_sequence; - auto prev_auth_sequence = amo->auth_sequence; + auto prev_auth_sequence = ao->auth_sequence; auto res = send_db_api_transaction("insert"_n, insert_data); BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); BOOST_CHECK_EQUAL( prev_global_action_sequence + 1, p.global_action_sequence ); BOOST_CHECK_EQUAL( prev_recv_sequence + 1, receiver_account->recv_sequence ); - BOOST_CHECK_EQUAL( prev_auth_sequence + 1, amo->auth_sequence ); + BOOST_CHECK_EQUAL( prev_auth_sequence + 1, ao->auth_sequence ); produce_block(); // verify sequence numbers in state do not change for dry-run transactions prev_global_action_sequence = p.global_action_sequence; prev_recv_sequence = receiver_account->recv_sequence; - prev_auth_sequence = amo->auth_sequence; + prev_auth_sequence = ao->auth_sequence; send_db_api_transaction("getage"_n, getage_data, vector{{"alice"_n, config::active_name}}, transaction_metadata::trx_type::dry_run); BOOST_CHECK_EQUAL( prev_global_action_sequence, p.global_action_sequence ); BOOST_CHECK_EQUAL( prev_recv_sequence, receiver_account->recv_sequence ); - BOOST_CHECK_EQUAL( prev_auth_sequence, amo->auth_sequence ); + BOOST_CHECK_EQUAL( prev_auth_sequence, ao->auth_sequence ); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/eosio.token_tests.cpp b/unittests/eosio.token_tests.cpp index e785a76953..b24dbae12f 100644 --- a/unittests/eosio.token_tests.cpp +++ b/unittests/eosio.token_tests.cpp @@ -31,9 +31,9 @@ class eosio_token_tester : public tester { produce_blocks(); - const auto& accnt = control->db().get( "eosio.token"_n ); + const auto& accnt_metadata = control->db().get( "eosio.token"_n ); abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); abi_ser.set_abi(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } diff --git a/unittests/eosio_system_tester.hpp b/unittests/eosio_system_tester.hpp index a477d6d41b..3493deaa4e 100644 --- a/unittests/eosio_system_tester.hpp +++ b/unittests/eosio_system_tester.hpp @@ -37,9 +37,9 @@ class eosio_system_tester : public validating_tester { set_abi( "eosio.token"_n, test_contracts::eosio_token_abi() ); { - const auto& accnt = control->db().get( "eosio.token"_n ); + const auto& accnt_metadata = control->db().get( "eosio.token"_n ); abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); token_abi_ser.set_abi(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } @@ -56,9 +56,9 @@ class eosio_system_tester : public validating_tester { ("core", symbol(CORE_SYMBOL).to_string())); { - const auto& accnt = control->db().get( config::system_account_name ); + const auto& accnt_metadata = control->db().get( config::system_account_name ); abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); abi_ser.set_abi(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } @@ -418,9 +418,9 @@ class eosio_system_tester : public validating_tester { set_abi( "eosio.msig"_n, test_contracts::eosio_msig_abi() ); produce_blocks(); - const auto& accnt = control->db().get( "eosio.msig"_n ); + const auto& accnt_metadata = control->db().get( "eosio.msig"_n ); abi_def msig_abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, msig_abi), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, msig_abi), true); msig_abi_ser.set_abi(std::move(msig_abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } return msig_abi_ser; diff --git a/unittests/ram_tests.cpp b/unittests/ram_tests.cpp index a3f878421f..9f91d4ebf1 100644 --- a/unittests/ram_tests.cpp +++ b/unittests/ram_tests.cpp @@ -95,8 +95,8 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, eosio_system::eosio_system_tester) { try { tester->push_action( "testram11111"_n, "setentry"_n, "testram11111"_n, mvo() ("payer", "testram11111") ("from", 1) - ("to", 10) - ("size", 1790 /*1920*/)), + ("to", 8) + ("size", 1800 /*1920*/)), ram_usage_exceeded, fc_exception_message_starts_with("account testram11111 has insufficient ram")); wlog("ram_tests 2 %%%%%%"); @@ -155,7 +155,7 @@ BOOST_FIXTURE_TEST_CASE(ram_tests, eosio_system::eosio_system_tester) { try { ("payer", "testram11111") ("from", 12) ("to", 12) - ("size", 1780)), + ("size", 1980)), ram_usage_exceeded, fc_exception_message_starts_with("account testram11111 has insufficient ram")); produce_blocks(1); diff --git a/unittests/read_only_trx_tests.cpp b/unittests/read_only_trx_tests.cpp index 8280574cca..9226e4d014 100644 --- a/unittests/read_only_trx_tests.cpp +++ b/unittests/read_only_trx_tests.cpp @@ -301,33 +301,33 @@ BOOST_FIXTURE_TEST_CASE(sequence_numbers_test, read_only_trx_tester) { try { set_up_test_contract(); const auto& p = control->get_dynamic_global_properties(); - auto receiver_account = control->db().find("noauthtable"_n); - auto amo = control->db().find("alice"_n); + auto receiver_account = control->db().find("noauthtable"_n); + auto ao = control->db().find("alice"_n); // verify sequence numbers in state increment for non-read-only transactions auto prev_global_action_sequence = p.global_action_sequence; auto prev_recv_sequence = receiver_account->recv_sequence; - auto prev_auth_sequence = amo->auth_sequence; + auto prev_auth_sequence = ao->auth_sequence; auto res = send_db_api_transaction("insert"_n, insert_data); BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed); BOOST_CHECK_EQUAL( prev_global_action_sequence + 1, p.global_action_sequence ); BOOST_CHECK_EQUAL( prev_recv_sequence + 1, receiver_account->recv_sequence ); - BOOST_CHECK_EQUAL( prev_auth_sequence + 1, amo->auth_sequence ); + BOOST_CHECK_EQUAL( prev_auth_sequence + 1, ao->auth_sequence ); produce_block(); // verify sequence numbers in state do not change for read-only transactions prev_global_action_sequence = p.global_action_sequence; prev_recv_sequence = receiver_account->recv_sequence; - prev_auth_sequence = amo->auth_sequence; + prev_auth_sequence = ao->auth_sequence; send_db_api_transaction("getage"_n, getage_data, {}, transaction_metadata::trx_type::read_only); BOOST_CHECK_EQUAL( prev_global_action_sequence, p.global_action_sequence ); BOOST_CHECK_EQUAL( prev_recv_sequence, receiver_account->recv_sequence ); - BOOST_CHECK_EQUAL( prev_auth_sequence, amo->auth_sequence ); + BOOST_CHECK_EQUAL( prev_auth_sequence, ao->auth_sequence ); } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/slim_account_tests.cpp b/unittests/slim_account_tests.cpp new file mode 100644 index 0000000000..e7ee405966 --- /dev/null +++ b/unittests/slim_account_tests.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* config_parse_error */ +#include + +#include "fork_test_utilities.hpp" +#include "test_cfd_transaction.hpp" + +using namespace eosio; +using namespace eosio::chain; +using namespace eosio::testing; + +BOOST_AUTO_TEST_SUITE(slim_account_tests) + +BOOST_AUTO_TEST_CASE(create_slim_account) +try +{ + tester chain(setup_policy::full); + chain.create_slim_account({"slimacc"_n}); + const auto& slim_accnt = chain.control->db().get( "slimacc"_n ); + BOOST_TEST(slim_accnt.name == "slimacc"_n); + + auto account_metadata_itr = chain.control->db().find( "slimacc"_n ); + BOOST_TEST(account_metadata_itr == nullptr); + auto active_permission_itr = chain.control->db().find(boost::make_tuple(name("slimacc"), name("active"))); + BOOST_TEST(active_permission_itr == nullptr); + auto owner_permission_itr = chain.control->db().find(boost::make_tuple(name("slimacc"), name("owner"))); + BOOST_TEST(owner_permission_itr != nullptr); + BOOST_TEST(owner_permission_itr->owner == "slimacc"_n); +} +FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_CASE(set_contract_with_slim_account) +try +{ + tester chain(setup_policy::full); + chain.create_slim_account({"slimacc"_n}); + chain.produce_blocks(); + chain.set_code("slimacc"_n, test_contracts::eosio_token_wasm(), nullptr, config::owner_name); + chain.set_abi("slimacc"_n, test_contracts::eosio_token_abi(), nullptr, config::owner_name); + + const auto& slim_accnt_metadata = chain.control->db().get( "slimacc"_n ); + BOOST_TEST(slim_accnt_metadata.abi.size() != size_t(0)); + + auto account_metadata_itr = chain.control->db().find( "slimacc"_n ); + BOOST_TEST(account_metadata_itr != nullptr); + BOOST_TEST(account_metadata_itr->code_hash != digest_type()); +} +FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index 29d66047a0..a6e5619600 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -168,8 +168,8 @@ BOOST_FIXTURE_TEST_CASE( abi_from_variant, validating_tester ) try { auto resolver = [&,this]( const account_name& name ) -> std::optional { try { - const auto& accnt = this->control->db().get( name ); - if (abi_def abi; abi_serializer::to_abi(accnt.abi, abi)) { + const auto& accnt_metadata = this->control->db().get( name ); + if (abi_def abi; abi_serializer::to_abi(accnt_metadata.abi, abi)) { return abi_serializer(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); } return std::optional(); @@ -1030,9 +1030,9 @@ BOOST_FIXTURE_TEST_CASE(noop, validating_tester) try { set_code("noop"_n, test_contracts::noop_wasm()); set_abi("noop"_n, test_contracts::noop_abi()); - const auto& accnt = control->db().get("noop"_n); + const auto& accnt_metadata = control->db().get("noop"_n); abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); abi_serializer abi_ser(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); { @@ -1093,9 +1093,9 @@ BOOST_FIXTURE_TEST_CASE(noop, validating_tester) try { BOOST_FIXTURE_TEST_CASE(eosio_abi, validating_tester) try { produce_blocks(2); - const auto& accnt = control->db().get(config::system_account_name); + const auto& accnt_metadata = control->db().get(config::system_account_name); abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt_metadata.abi, abi), true); abi_serializer abi_ser(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time )); signed_transaction trx;