From a3d0f8eebee4f1787bdb5d18ef2770de8b7c6d26 Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Thu, 22 Jul 2021 12:10:32 -0400 Subject: [PATCH 01/11] initial fix --- libraries/eosiolib/CMakeLists.txt | 4 ++++ libraries/eosiolib/eosiolib.cpp | 3 --- tools/include/compiler_options.hpp.in | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/eosiolib/CMakeLists.txt b/libraries/eosiolib/CMakeLists.txt index 345506346d..1201cc45a9 100644 --- a/libraries/eosiolib/CMakeLists.txt +++ b/libraries/eosiolib/CMakeLists.txt @@ -18,6 +18,9 @@ add_library(eosio_cmem memory.cpp ${HEADERS}) +add_library(eosio_contract_name + contract_name.cpp + ${HEADERS}) set_target_properties(eosio_malloc PROPERTIES LINKER_LANGUAGE C) @@ -35,6 +38,7 @@ add_custom_command( TARGET eosio POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) add_custom_command( TARGET eosio_dsm POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) add_custom_command( TARGET eosio_cmem POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) +add_custom_command( TARGET eosio_contract_name POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) if (ENABLE_NATIVE_COMPILER) add_native_library(native_eosio diff --git a/libraries/eosiolib/eosiolib.cpp b/libraries/eosiolib/eosiolib.cpp index b7e980c2ba..fc51fdb64c 100644 --- a/libraries/eosiolib/eosiolib.cpp +++ b/libraries/eosiolib/eosiolib.cpp @@ -5,9 +5,6 @@ #include -extern "C" volatile uint64_t eosio_contract_name = 0; -extern "C" volatile void eosio_set_contract_name(uint64_t n) { eosio_contract_name = n; } // LLVM creates the call to this at the beginning of apply - namespace eosio { extern "C" { __attribute__((eosio_wasm_import)) diff --git a/tools/include/compiler_options.hpp.in b/tools/include/compiler_options.hpp.in index a335f887a3..fdc247b86b 100644 --- a/tools/include/compiler_options.hpp.in +++ b/tools/include/compiler_options.hpp.in @@ -507,6 +507,7 @@ static void GetLdDefaults(std::vector& ldopts) { ldopts.emplace_back("-lc++"); ldopts.emplace_back("-lc"); ldopts.emplace_back("-leosio"); + ldopts.emplace_back("-leosio_contract_name"); if (use_old_malloc_opt) ldopts.emplace_back("-leosio_malloc"); else From b6dd156d0bd5d19dd2c9c20b5f39406c9c48e302 Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Thu, 22 Jul 2021 13:08:25 -0400 Subject: [PATCH 02/11] add extertal c code --- libraries/eosiolib/contract_name.cpp | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 libraries/eosiolib/contract_name.cpp diff --git a/libraries/eosiolib/contract_name.cpp b/libraries/eosiolib/contract_name.cpp new file mode 100644 index 0000000000..b5fa83a0e0 --- /dev/null +++ b/libraries/eosiolib/contract_name.cpp @@ -0,0 +1,6 @@ +#include + +extern "C" { + volatile uint64_t eosio_contract_name = 0; + volatile void eosio_set_contract_name(uint64_t n) { eosio_contract_name = n; } // LLVM creates the call to this at the beginning of apply +} From 7eed04c7d4cfa799fd1a3549f02182b1ceb1c031 Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Mon, 9 Aug 2021 09:56:13 -0400 Subject: [PATCH 03/11] use contracts withoug KV --- .../eosio.contracts/CMakeLists.txt | 23 + .../eosio.contracts/eosio.bios/CMakeLists.txt | 13 + .../include/eosio.bios/eosio.bios.hpp | 282 ++++ .../ricardian/eosio.bios.contracts.md.in | 189 +++ .../eosio.bios/src/eosio.bios.cpp | 57 + .../eosio.contracts/eosio.msig/CMakeLists.txt | 13 + .../include/eosio.msig/eosio.msig.hpp | 162 ++ .../ricardian/eosio.msig.contracts.md.in | 73 + .../eosio.msig/src/eosio.msig.cpp | 207 +++ .../eosio.system/CMakeLists.txt | 33 + .../include/eosio.system/eosio.system.hpp | 1314 +++++++++++++++++ .../include/eosio.system/exchange_state.hpp | 48 + .../include/eosio.system/native.hpp | 260 ++++ .../include/eosio.system/rex.results.hpp | 59 + .../ricardian/eosio.system.clauses.md | 63 + .../ricardian/eosio.system.contracts.md.in | 703 +++++++++ .../eosio.system/src/delegate_bandwidth.cpp | 413 ++++++ .../eosio.system/src/eosio.system.cpp | 400 +++++ .../eosio.system/src/exchange_state.cpp | 110 ++ .../eosio.system/src/name_bidding.cpp | 80 + .../eosio.system/src/native.cpp | 11 + .../eosio.system/src/producer_pay.cpp | 191 +++ .../eosio.contracts/eosio.system/src/rex.cpp | 1218 +++++++++++++++ .../eosio.system/src/rex.results.cpp | 11 + .../eosio.system/src/voting.cpp | 415 ++++++ .../eosio.token/CMakeLists.txt | 13 + .../include/eosio.token/eosio.token.hpp | 146 ++ .../ricardian/eosio.token.contracts.md.in | 95 ++ .../eosio.token/src/eosio.token.cpp | 159 ++ .../eosio.contracts/eosio.wrap/CMakeLists.txt | 13 + .../include/eosio.wrap/eosio.wrap.hpp | 38 + .../ricardian/eosio.wrap.contracts.md.in | 13 + .../eosio.wrap/src/eosio.wrap.cpp | 16 + .../eosio.contracts/icons/account.png | Bin 0 -> 3795 bytes .../eosio.contracts/icons/account.svg | 1 + .../eosio.contracts/icons/admin.png | Bin 0 -> 2937 bytes .../eosio.contracts/icons/admin.svg | 1 + .../eosio.contracts/icons/multisig.png | Bin 0 -> 1411 bytes .../eosio.contracts/icons/multisig.svg | 1 + .../eosio.contracts/icons/resource.png | Bin 0 -> 1913 bytes .../eosio.contracts/icons/resource.svg | 1 + .../eosio.contracts/icons/rex.png | Bin 0 -> 2770 bytes .../eosio.contracts/icons/rex.svg | 1 + .../eosio.contracts/icons/token.png | Bin 0 -> 4268 bytes .../eosio.contracts/icons/token.svg | 1 + .../eosio.contracts/icons/transfer.png | Bin 0 -> 3856 bytes .../eosio.contracts/icons/transfer.svg | 1 + .../eosio.contracts/icons/voting.png | Bin 0 -> 3238 bytes .../eosio.contracts/icons/voting.svg | 1 + 49 files changed, 6849 insertions(+) create mode 100644 tests/unit/test_contracts/eosio.contracts/CMakeLists.txt create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in create mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/account.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/account.svg create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/admin.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/admin.svg create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/multisig.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/multisig.svg create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/resource.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/resource.svg create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/rex.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/rex.svg create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/token.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/token.svg create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/transfer.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/transfer.svg create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/voting.png create mode 100644 tests/unit/test_contracts/eosio.contracts/icons/voting.svg diff --git a/tests/unit/test_contracts/eosio.contracts/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/CMakeLists.txt new file mode 100644 index 0000000000..69d438e146 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required( VERSION 3.5 ) + +project(contracts) + +set(EOSIO_WASM_OLD_BEHAVIOR "Off") +find_package(eosio.cdt) + +set(ICON_BASE_URL "http://127.0.0.1/ricardian_assets/eosio.contracts/icons") + +set(ACCOUNT_ICON_URI "account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f") +set(ADMIN_ICON_URI "admin.png#9bf1cec664863bd6aaac0f814b235f8799fb02c850e9aa5da34e8a004bd6518e") +set(MULTISIG_ICON_URI "multisig.png#4fb41d3cf02d0dd2d35a29308e93c2d826ec770d6bb520db668f530764be7153") +set(RESOURCE_ICON_URI "resource.png#3830f1ce8cb07f7757dbcf383b1ec1b11914ac34a1f9d8b065f07600fa9dac19") +set(REX_ICON_URI "rex.png#d229837fa62a464b9c71e06060aa86179adf0b3f4e3b8c4f9702f4f4b0c340a8") +set(TOKEN_ICON_URI "token.png#207ff68b0406eaa56618b08bda81d6a0954543f36adc328ab3065f31a5c5d654") +set(TRANSFER_ICON_URI "transfer.png#5dfad0df72772ee1ccc155e670c1d124f5c5122f1d5027565df38b418042d1dd") +set(VOTING_ICON_URI "voting.png#db28cd3db6e62d4509af3644ce7d377329482a14bb4bfaca2aa5f1400d8e8a84") + +add_subdirectory(eosio.bios) +add_subdirectory(eosio.msig) +add_subdirectory(eosio.system) +add_subdirectory(eosio.token) +add_subdirectory(eosio.wrap) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt new file mode 100644 index 0000000000..3709f70c18 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt @@ -0,0 +1,13 @@ +add_contract(eosio.bios eosio.bios ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.bios.cpp) + +target_include_directories(eosio.bios + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set_target_properties(eosio.bios + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.bios.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.bios.contracts.md @ONLY ) + +target_compile_options( eosio.bios PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp new file mode 100644 index 0000000000..643e562c8f --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp @@ -0,0 +1,282 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace eosiobios { + + using eosio::action_wrapper; + using eosio::check; + using eosio::checksum256; + using eosio::ignore; + using eosio::name; + using eosio::permission_level; + using eosio::public_key; + + struct permission_level_weight { + permission_level permission; + uint16_t weight; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( permission_level_weight, (permission)(weight) ) + }; + + struct key_weight { + eosio::public_key key; + uint16_t weight; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( key_weight, (key)(weight) ) + }; + + struct wait_weight { + uint32_t wait_sec; + uint16_t weight; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( wait_weight, (wait_sec)(weight) ) + }; + + struct authority { + uint32_t threshold = 0; + std::vector keys; + std::vector accounts; + std::vector waits; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( authority, (threshold)(keys)(accounts)(waits) ) + }; + + struct block_header { + uint32_t timestamp; + name producer; + uint16_t confirmed = 0; + checksum256 previous; + checksum256 transaction_mroot; + checksum256 action_mroot; + uint32_t schedule_version = 0; + std::optional new_producers; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE(block_header, (timestamp)(producer)(confirmed)(previous)(transaction_mroot)(action_mroot) + (schedule_version)(new_producers)) + }; + + /** + * The `eosio.bios` is the first sample of system contract provided by `block.one` through the EOSIO platform. It is a minimalist system contract because it only supplies the actions that are absolutely critical to bootstrap a chain and nothing more. This allows for a chain agnostic approach to bootstrapping a chain. + * + * Just like in the `eosio.system` sample contract implementation, there are a few actions which are not implemented at the contract level (`newaccount`, `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, `canceldelay`, `onerror`, `setabi`, `setcode`), they are just declared in the contract so they will show in the contract's ABI and users will be able to push those actions to the chain via the account holding the `eosio.system` contract, but the implementation is at the EOSIO core level. They are referred to as EOSIO native actions. + */ + class [[eosio::contract("eosio.bios")]] bios : public eosio::contract { + public: + using contract::contract; + /** + * New account action, called after a new account is created. This code enforces resource-limits rules + * for new accounts as well as new 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 newaccount( name creator, + name name, + ignore owner, + ignore active){} + /** + * Update authorization action updates pemission for an account. + * + * @param account - the account for which the permission is updated, + * @param pemission - the permission name which is updated, + * @param parem - the parent of the permission which is updated, + * @param aut - the json describing the permission authorization. + */ + [[eosio::action]] + void updateauth( ignore account, + ignore permission, + ignore parent, + ignore auth ) {} + + /** + * Delete authorization action deletes the authorization for an account's permission. + * + * @param account - the account for which the permission authorization is deleted, + * @param permission - the permission name been deleted. + */ + [[eosio::action]] + void deleteauth( ignore account, + ignore permission ) {} + + /** + * Link authorization action assigns a specific action from a contract to a permission you have created. Five system + * actions can not be linked `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, and `canceldelay`. + * This is useful because when doing authorization checks, the EOSIO based blockchain starts with the + * action needed to be authorized (and the contract belonging to), and looks up which permission + * is needed to pass authorization validation. If a link is set, that permission is used for authoraization + * validation otherwise then active is the default, with the exception of `eosio.any`. + * `eosio.any` is an implicit permission which exists on every account; you can link actions to `eosio.any` + * and that will make it so linked actions are accessible to any permissions defined for the account. + * + * @param account - the permission's owner to be linked and the payer of the RAM needed to store this link, + * @param code - the owner of the action to be linked, + * @param type - the action to be linked, + * @param requirement - the permission to be linked. + */ + [[eosio::action]] + void linkauth( ignore account, + ignore code, + ignore type, + ignore requirement ) {} + + /** + * Unlink authorization action it's doing the reverse of linkauth action, by unlinking the given action. + * + * @param account - the owner of the permission to be unlinked and the receiver of the freed RAM, + * @param code - the owner of the action to be unlinked, + * @param type - the action to be unlinked. + */ + [[eosio::action]] + void unlinkauth( ignore account, + ignore code, + ignore type ) {} + + /** + * Cancel delay action cancels a deferred transaction. + * + * @param canceling_auth - the permission that authorizes this action, + * @param trx_id - the deferred transaction id to be cancelled. + */ + [[eosio::action]] + void canceldelay( ignore canceling_auth, ignore trx_id ) {} + + /** + * Set code action sets the contract code for an account. + * + * @param account - the account for which to set the contract code. + * @param vmtype - reserved, set it to zero. + * @param vmversion - reserved, set it to zero. + * @param code - the code content to be set, in the form of a blob binary.. + */ + [[eosio::action]] + void setcode( name account, uint8_t vmtype, uint8_t vmversion, const std::vector& code ) {} + + /** + * Set abi action sets the abi for contract identified by `account` name. Creates an entry in the abi_hash_table + * index, with `account` name as key, if it is not already present and sets its value with the abi hash. + * Otherwise it is updating the current abi hash value for the existing `account` key. + * + * @param account - the name of the account to set the abi for + * @param abi - the abi hash represented as a vector of characters + */ + [[eosio::action]] + void setabi( name account, const std::vector& abi ); + + /** + * On error action, notification of this action is delivered to the sender of a deferred transaction + * when an objective error occurs while executing the deferred transaction. + * This action is not meant to be called directly. + * + * @param sender_id - the id for the deferred transaction chosen by the sender, + * @param sent_trx - the deferred transaction that failed. + */ + [[eosio::action]] + void onerror( ignore sender_id, ignore> sent_trx ); + + /** + * Set privilege action allows to set privilege status for an account (turn it on/off). + * @param account - the account to set the privileged status for. + * @param is_priv - 0 for false, > 0 for true. + */ + [[eosio::action]] + void setpriv( name account, uint8_t is_priv ); + + /** + * Sets the resource limits of an account + * + * @param account - name of the account whose resource limit to be set + * @param ram_bytes - ram limit in absolute bytes + * @param net_weight - fractionally proportionate net limit of available resources based on (weight / total_weight_of_all_accounts) + * @param cpu_weight - fractionally proportionate cpu limit of available resources based on (weight / total_weight_of_all_accounts) + */ + [[eosio::action]] + void setalimits( name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ); + + /** + * Set producers action, sets a new list of active producers, by proposing a schedule change, once the block that + * contains the proposal becomes irreversible, the schedule is promoted to "pending" + * automatically. Once the block that promotes the schedule is irreversible, the schedule will + * become "active". + * + * @param schedule - New list of active producers to set + */ + [[eosio::action]] + void setprods( const std::vector& schedule ); + + /** + * Set params action, sets the blockchain parameters. By tuning these parameters, various degrees of customization can be achieved. + * + * @param params - New blockchain parameters to set + */ + [[eosio::action]] + void setparams( const eosio::blockchain_parameters& params ); + + /** + * Require authorization action, checks if the account name `from` passed in as param has authorization to access + * current action, that is, if it is listed in the action’s allowed permissions vector. + * + * @param from - the account name to authorize + */ + [[eosio::action]] + void reqauth( name from ); + + /** + * Activate action, activates a protocol feature + * + * @param feature_digest - hash of the protocol feature to activate. + */ + [[eosio::action]] + void activate( const eosio::checksum256& feature_digest ); + + /** + * Require activated action, asserts that a protocol feature has been activated + * + * @param feature_digest - hash of the protocol feature to check for activation. + */ + [[eosio::action]] + void reqactivated( const eosio::checksum256& feature_digest ); + + struct [[eosio::table]] abi_hash { + name owner; + checksum256 hash; + uint64_t primary_key()const { return owner.value; } + + EOSLIB_SERIALIZE( abi_hash, (owner)(hash) ) + }; + + typedef eosio::multi_index< "abihash"_n, abi_hash > abi_hash_table; + + using newaccount_action = action_wrapper<"newaccount"_n, &bios::newaccount>; + 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>; + using unlinkauth_action = action_wrapper<"unlinkauth"_n, &bios::unlinkauth>; + using canceldelay_action = action_wrapper<"canceldelay"_n, &bios::canceldelay>; + using setcode_action = action_wrapper<"setcode"_n, &bios::setcode>; + using setabi_action = action_wrapper<"setabi"_n, &bios::setabi>; + using setpriv_action = action_wrapper<"setpriv"_n, &bios::setpriv>; + using setalimits_action = action_wrapper<"setalimits"_n, &bios::setalimits>; + using setprods_action = action_wrapper<"setprods"_n, &bios::setprods>; + using setparams_action = action_wrapper<"setparams"_n, &bios::setparams>; + using reqauth_action = action_wrapper<"reqauth"_n, &bios::reqauth>; + using activate_action = action_wrapper<"activate"_n, &bios::activate>; + using reqactivated_action = action_wrapper<"reqactivated"_n, &bios::reqactivated>; + }; +} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in new file mode 100644 index 0000000000..a08b66e433 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in @@ -0,0 +1,189 @@ +

activate

+ +--- +spec_version: "0.2.0" +title: Activate Protocol Feature +summary: 'Activate protocol feature {{nowrap feature_digest}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} activates the protocol feature with a digest of {{feature_digest}}. + +

canceldelay

+ +--- +spec_version: "0.2.0" +title: Cancel Delayed Transaction +summary: '{{nowrap canceling_auth.actor}} cancels a delayed transaction' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{canceling_auth.actor}} cancels the delayed transaction with id {{trx_id}}. + +

deleteauth

+ +--- +spec_version: "0.2.0" +title: Delete Account Permission +summary: 'Delete the {{nowrap permission}} permission of {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Delete the {{permission}} permission of {{account}}. + +

linkauth

+ +--- +spec_version: "0.2.0" +title: Link Action to Permission +summary: '{{nowrap account}} sets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract to {{nowrap requirement}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{account}} sets the minimum required permission for the {{#if type}}{{type}} action of the{{/if}} {{code}} contract to {{requirement}}. + +{{#if type}}{{else}}Any links explicitly associated to specific actions of {{code}} will take precedence.{{/if}} + +

newaccount

+ +--- +spec_version: "0.2.0" +title: Create New Account +summary: '{{nowrap creator}} creates a new account with the name {{nowrap name}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{creator}} creates a new account with the name {{name}} and the following permissions: + +owner permission with authority: +{{to_json owner}} + +active permission with authority: +{{to_json active}} + +

reqactivated

+ +--- +spec_version: "0.2.0" +title: Assert Protocol Feature Activation +summary: 'Assert that protocol feature {{nowrap feature_digest}} has been activated' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +Assert that the protocol feature with a digest of {{feature_digest}} has been activated. + +

reqauth

+ +--- +spec_version: "0.2.0" +title: Assert Authorization +summary: 'Assert that authorization by {{nowrap from}} is provided' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Assert that authorization by {{from}} is provided. + +

setabi

+ +--- +spec_version: "0.2.0" +title: Deploy Contract ABI +summary: 'Deploy contract ABI on account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Deploy the ABI file associated with the contract on account {{account}}. + +

setalimits

+ +--- +spec_version: "0.2.0" +title: Adjust Resource Limits of Account +summary: 'Adjust resource limits of account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} updates {{account}}’s resource limits to have a RAM quota of {{ram_bytes}} bytes, a NET bandwidth quota of {{net_weight}} and a CPU bandwidth quota of {{cpu_weight}}. + +

setcode

+ +--- +spec_version: "0.2.0" +title: Deploy Contract Code +summary: 'Deploy contract code on account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Deploy compiled contract code to the account {{account}}. + +

setparams

+ +--- +spec_version: "0.2.0" +title: Set System Parameters +summary: 'Set system parameters' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} sets system parameters to: +{{to_json params}} + +

setpriv

+ +--- +spec_version: "0.2.0" +title: Make an Account Privileged or Unprivileged +summary: '{{#if is_priv}}Make {{nowrap account}} privileged{{else}}Remove privileged status of {{nowrap account}}{{/if}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{#if is_priv}} +{{$action.account}} makes {{account}} privileged. +{{else}} +{{$action.account}} removes privileged status of {{account}}. +{{/if}} + +

setprods

+ +--- +spec_version: "0.2.0" +title: Set Block Producers +summary: 'Set block producer schedule' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} proposes a block producer schedule of: +{{#each schedule}} + 1. {{this.producer_name}} +{{/each}} + +The block signing authorities of each of the producers in the above schedule are listed below: +{{#each schedule}} +### {{this.producer_name}} +{{to_json this.authority}} +{{/each}} + +

unlinkauth

+ +--- +spec_version: "0.2.0" +title: Unlink Action from Permission +summary: '{{nowrap account}} unsets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{account}} removes the association between the {{#if type}}{{type}} action of the{{/if}} {{code}} contract and its minimum required permission. + +{{#if type}}{{else}}This will not remove any links explicitly associated to specific actions of {{code}}.{{/if}} + +

updateauth

+ +--- +spec_version: "0.2.0" +title: Modify Account Permission +summary: 'Add or update the {{nowrap permission}} permission of {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Modify, and create if necessary, the {{permission}} permission of {{account}} to have a parent permission of {{parent}} and the following authority: +{{to_json auth}} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp new file mode 100644 index 0000000000..69d6758f34 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp @@ -0,0 +1,57 @@ +#include + +namespace eosiobios { + +void bios::setabi( name account, const std::vector& abi ) { + abi_hash_table table(get_self(), get_self().value); + auto itr = table.find( account.value ); + if( itr == table.end() ) { + table.emplace( account, [&]( auto& row ) { + row.owner = account; + row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); + }); + } else { + table.modify( itr, eosio::same_payer, [&]( auto& row ) { + row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); + }); + } +} + +void bios::onerror( ignore, ignore> ) { + check( false, "the onerror action cannot be called directly" ); +} + +void bios::setpriv( name account, uint8_t is_priv ) { + require_auth( get_self() ); + set_privileged( account, is_priv ); +} + +void bios::setalimits( name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ) { + require_auth( get_self() ); + set_resource_limits( account, ram_bytes, net_weight, cpu_weight ); +} + +void bios::setprods( const std::vector& schedule ) { + require_auth( get_self() ); + set_proposed_producers( schedule ); +} + +void bios::setparams( const eosio::blockchain_parameters& params ) { + require_auth( get_self() ); + set_blockchain_parameters( params ); +} + +void bios::reqauth( name from ) { + require_auth( from ); +} + +void bios::activate( const eosio::checksum256& feature_digest ) { + require_auth( get_self() ); + preactivate_feature( feature_digest ); +} + +void bios::reqactivated( const eosio::checksum256& feature_digest ) { + check( is_feature_activated( feature_digest ), "protocol feature is not activated" ); +} + +} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt new file mode 100644 index 0000000000..0947ea9d0e --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt @@ -0,0 +1,13 @@ +add_contract(eosio.msig eosio.msig ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.msig.cpp) + +target_include_directories(eosio.msig + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set_target_properties(eosio.msig + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.msig.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.msig.contracts.md @ONLY ) + +target_compile_options( eosio.msig PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp new file mode 100644 index 0000000000..19f2600cc1 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp @@ -0,0 +1,162 @@ +#pragma once + +#include +#include +#include +#include + +namespace eosio { + + /** + * The `eosio.msig` system contract allows for creation of proposed transactions which require authorization from a list of accounts, approval of the proposed transactions by those accounts required to approve it, and finally, it also allows the execution of the approved transactions on the blockchain. + * + * In short, the workflow to propose, review, approve and then executed a transaction it can be described by the following: + * - first you create a transaction json file, + * - then you submit this proposal to the `eosio.msig` contract, and you also insert the account permissions required to approve this proposal into the command that submits the proposal to the blockchain, + * - the proposal then gets stored on the blockchain by the `eosio.msig` contract, and is accessible for review and approval to those accounts required to approve it, + * - after each of the appointed accounts required to approve the proposed transactions reviews and approves it, you can execute the proposed transaction. The `eosio.msig` contract will execute it automatically, but not before validating that the transaction has not expired, it is not cancelled, and it has been signed by all the permissions in the initial proposal's required permission list. + */ + class [[eosio::contract("eosio.msig")]] multisig : public contract { + public: + using contract::contract; + + /** + * Propose action, creates a proposal containing one transaction. + * Allows an account `proposer` to make a proposal `proposal_name` which has `requested` + * permission levels expected to approve the proposal, and if approved by all expected + * permission levels then `trx` transaction can we executed by this proposal. + * The `proposer` account is authorized and the `trx` transaction is verified if it was + * authorized by the provided keys and permissions, and if the proposal name doesn’t + * already exist; if all validations pass the `proposal_name` and `trx` trasanction are + * saved in the proposals table and the `requested` permission levels to the + * approvals table (for the `proposer` context). Storage changes are billed to `proposer`. + * + * @param proposer - The account proposing a transaction + * @param proposal_name - The name of the proposal (should be unique for proposer) + * @param requested - Permission levels expected to approve the proposal + * @param trx - Proposed transaction + */ + [[eosio::action]] + void propose(ignore proposer, ignore proposal_name, + ignore> requested, ignore trx); + /** + * Approve action approves an existing proposal. Allows an account, the owner of `level` permission, to approve a proposal `proposal_name` + * proposed by `proposer`. If the proposal's requested approval list contains the `level` + * permission then the `level` permission is moved from internal `requested_approvals` list to + * internal `provided_approvals` list of the proposal, thus persisting the approval for + * the `proposal_name` proposal. Storage changes are billed to `proposer`. + * + * @param proposer - The account proposing a transaction + * @param proposal_name - The name of the proposal (should be unique for proposer) + * @param level - Permission level approving the transaction + * @param proposal_hash - Transaction's checksum + */ + [[eosio::action]] + void approve( name proposer, name proposal_name, permission_level level, + const eosio::binary_extension& proposal_hash ); + /** + * Unapprove action revokes an existing proposal. This action is the reverse of the `approve` action: if all validations pass + * the `level` permission is erased from internal `provided_approvals` and added to the internal + * `requested_approvals` list, and thus un-approve or revoke the proposal. + * + * @param proposer - The account proposing a transaction + * @param proposal_name - The name of the proposal (should be an existing proposal) + * @param level - Permission level revoking approval for proposal + */ + [[eosio::action]] + void unapprove( name proposer, name proposal_name, permission_level level ); + /** + * Cancel action cancels an existing proposal. + * + * @param proposer - The account proposing a transaction + * @param proposal_name - The name of the proposal (should be an existing proposal) + * @param canceler - The account cancelling the proposal (only the proposer can cancel an unexpired transaction, and the canceler has to be different than the proposer) + * + * Allows the `canceler` account to cancel the `proposal_name` proposal, created by a `proposer`, + * only after time has expired on the proposed transaction. It removes corresponding entries from + * internal proptable and from approval (or old approvals) tables as well. + */ + [[eosio::action]] + void cancel( name proposer, name proposal_name, name canceler ); + /** + * Exec action allows an `executer` account to execute a proposal. + * + * Preconditions: + * - `executer` has authorization, + * - `proposal_name` is found in the proposals table, + * - all requested approvals are received, + * - proposed transaction is not expired, + * - and approval accounts are not found in invalidations table. + * + * If all preconditions are met the transaction is executed as a deferred transaction, + * and the proposal is erased from the proposals table. + * + * @param proposer - The account proposing a transaction + * @param proposal_name - The name of the proposal (should be an existing proposal) + * @param executer - The account executing the transaction + */ + [[eosio::action]] + void exec( name proposer, name proposal_name, name executer ); + /** + * Invalidate action allows an `account` to invalidate itself, that is, its name is added to + * the invalidations table and this table will be cross referenced when exec is performed. + * + * @param account - The account invalidating the transaction + */ + [[eosio::action]] + void invalidate( name account ); + + using propose_action = eosio::action_wrapper<"propose"_n, &multisig::propose>; + using approve_action = eosio::action_wrapper<"approve"_n, &multisig::approve>; + using unapprove_action = eosio::action_wrapper<"unapprove"_n, &multisig::unapprove>; + using cancel_action = eosio::action_wrapper<"cancel"_n, &multisig::cancel>; + using exec_action = eosio::action_wrapper<"exec"_n, &multisig::exec>; + using invalidate_action = eosio::action_wrapper<"invalidate"_n, &multisig::invalidate>; + + private: + struct [[eosio::table]] proposal { + name proposal_name; + std::vector packed_transaction; + + uint64_t primary_key()const { return proposal_name.value; } + }; + + typedef eosio::multi_index< "proposal"_n, proposal > proposals; + + struct [[eosio::table]] old_approvals_info { + name proposal_name; + std::vector requested_approvals; + std::vector provided_approvals; + + uint64_t primary_key()const { return proposal_name.value; } + }; + typedef eosio::multi_index< "approvals"_n, old_approvals_info > old_approvals; + + struct approval { + permission_level level; + time_point time; + }; + + struct [[eosio::table]] approvals_info { + uint8_t version = 1; + name proposal_name; + //requested approval doesn't need to cointain time, but we want requested approval + //to be of exact the same size ad provided approval, in this case approve/unapprove + //doesn't change serialized data size. So, we use the same type. + std::vector requested_approvals; + std::vector provided_approvals; + + uint64_t primary_key()const { return proposal_name.value; } + }; + typedef eosio::multi_index< "approvals2"_n, approvals_info > approvals; + + struct [[eosio::table]] invalidation { + name account; + time_point last_invalidation_time; + + uint64_t primary_key() const { return account.value; } + }; + + typedef eosio::multi_index< "invals"_n, invalidation > invalidations; + }; +} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in new file mode 100644 index 0000000000..b9f3f35f85 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in @@ -0,0 +1,73 @@ +

approve

+ +--- +spec_version: "0.2.0" +title: Approve Proposed Transaction +summary: '{{nowrap level.actor}} approves the {{nowrap proposal_name}} proposal' +icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ +--- + +{{level.actor}} approves the {{proposal_name}} proposal proposed by {{proposer}} with the {{level.permission}} permission of {{level.actor}}. + +

cancel

+ +--- +spec_version: "0.2.0" +title: Cancel Proposed Transaction +summary: '{{nowrap canceler}} cancels the {{nowrap proposal_name}} proposal' +icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ +--- + +{{canceler}} cancels the {{proposal_name}} proposal submitted by {{proposer}}. + +

exec

+ +--- +spec_version: "0.2.0" +title: Execute Proposed Transaction +summary: '{{nowrap executer}} executes the {{nowrap proposal_name}} proposal' +icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ +--- + +{{executer}} executes the {{proposal_name}} proposal submitted by {{proposer}} if the minimum required approvals for the proposal have been secured. + +

invalidate

+ +--- +spec_version: "0.2.0" +title: Invalidate All Approvals +summary: '{{nowrap account}} invalidates approvals on outstanding proposals' +icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ +--- + +{{account}} invalidates all approvals on proposals which have not yet executed. + +

propose

+ +--- +spec_version: "0.2.0" +title: Propose Transaction +summary: '{{nowrap proposer}} creates the {{nowrap proposal_name}}' +icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ +--- + +{{proposer}} creates the {{proposal_name}} proposal for the following transaction: +{{to_json trx}} + +The proposal requests approvals from the following accounts at the specified permission levels: +{{#each requested}} + + {{this.permission}} permission of {{this.actor}} +{{/each}} + +If the proposed transaction is not executed prior to {{trx.expiration}}, the proposal will automatically expire. + +

unapprove

+ +--- +spec_version: "0.2.0" +title: Unapprove Proposed Transaction +summary: '{{nowrap level.actor}} revokes the approval previously provided to {{nowrap proposal_name}} proposal' +icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ +--- + +{{level.actor}} revokes the approval previously provided at their {{level.permission}} permission level from the {{proposal_name}} proposal proposed by {{proposer}}. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp new file mode 100644 index 0000000000..1769219290 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp @@ -0,0 +1,207 @@ +#include +#include +#include + +#include + +namespace eosio { + +void multisig::propose( ignore proposer, + ignore proposal_name, + ignore> requested, + ignore trx ) +{ + name _proposer; + name _proposal_name; + std::vector _requested; + transaction_header _trx_header; + + _ds >> _proposer >> _proposal_name >> _requested; + + const char* trx_pos = _ds.pos(); + size_t size = _ds.remaining(); + _ds >> _trx_header; + + require_auth( _proposer ); + check( _trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); + //check( trx_header.actions.size() > 0, "transaction must have at least one action" ); + + proposals proptable( get_self(), _proposer.value ); + check( proptable.find( _proposal_name.value ) == proptable.end(), "proposal with the same name exists" ); + + auto packed_requested = pack(_requested); + auto res = check_transaction_authorization( + trx_pos, size, + (const char*)0, 0, + packed_requested.data(), packed_requested.size() + ); + + check( res > 0, "transaction authorization failed" ); + + std::vector pkd_trans; + pkd_trans.resize(size); + memcpy((char*)pkd_trans.data(), trx_pos, size); + proptable.emplace( _proposer, [&]( auto& prop ) { + prop.proposal_name = _proposal_name; + prop.packed_transaction = pkd_trans; + }); + + approvals apptable( get_self(), _proposer.value ); + apptable.emplace( _proposer, [&]( auto& a ) { + a.proposal_name = _proposal_name; + a.requested_approvals.reserve( _requested.size() ); + for ( auto& level : _requested ) { + a.requested_approvals.push_back( approval{ level, time_point{ microseconds{0} } } ); + } + }); +} + +void multisig::approve( name proposer, name proposal_name, permission_level level, + const eosio::binary_extension& proposal_hash ) +{ + require_auth( level ); + + if( proposal_hash ) { + proposals proptable( get_self(), proposer.value ); + auto& prop = proptable.get( proposal_name.value, "proposal not found" ); + assert_sha256( prop.packed_transaction.data(), prop.packed_transaction.size(), *proposal_hash ); + } + + approvals apptable( get_self(), proposer.value ); + auto apps_it = apptable.find( proposal_name.value ); + if ( apps_it != apptable.end() ) { + auto itr = std::find_if( apps_it->requested_approvals.begin(), apps_it->requested_approvals.end(), [&](const approval& a) { return a.level == level; } ); + check( itr != apps_it->requested_approvals.end(), "approval is not on the list of requested approvals" ); + + apptable.modify( apps_it, proposer, [&]( auto& a ) { + a.provided_approvals.push_back( approval{ level, current_time_point() } ); + a.requested_approvals.erase( itr ); + }); + } else { + old_approvals old_apptable( get_self(), proposer.value ); + auto& apps = old_apptable.get( proposal_name.value, "proposal not found" ); + + auto itr = std::find( apps.requested_approvals.begin(), apps.requested_approvals.end(), level ); + check( itr != apps.requested_approvals.end(), "approval is not on the list of requested approvals" ); + + old_apptable.modify( apps, proposer, [&]( auto& a ) { + a.provided_approvals.push_back( level ); + a.requested_approvals.erase( itr ); + }); + } +} + +void multisig::unapprove( name proposer, name proposal_name, permission_level level ) { + require_auth( level ); + + approvals apptable( get_self(), proposer.value ); + auto apps_it = apptable.find( proposal_name.value ); + if ( apps_it != apptable.end() ) { + auto itr = std::find_if( apps_it->provided_approvals.begin(), apps_it->provided_approvals.end(), [&](const approval& a) { return a.level == level; } ); + check( itr != apps_it->provided_approvals.end(), "no approval previously granted" ); + apptable.modify( apps_it, proposer, [&]( auto& a ) { + a.requested_approvals.push_back( approval{ level, current_time_point() } ); + a.provided_approvals.erase( itr ); + }); + } else { + old_approvals old_apptable( get_self(), proposer.value ); + auto& apps = old_apptable.get( proposal_name.value, "proposal not found" ); + auto itr = std::find( apps.provided_approvals.begin(), apps.provided_approvals.end(), level ); + check( itr != apps.provided_approvals.end(), "no approval previously granted" ); + old_apptable.modify( apps, proposer, [&]( auto& a ) { + a.requested_approvals.push_back( level ); + a.provided_approvals.erase( itr ); + }); + } +} + +void multisig::cancel( name proposer, name proposal_name, name canceler ) { + require_auth( canceler ); + + proposals proptable( get_self(), proposer.value ); + auto& prop = proptable.get( proposal_name.value, "proposal not found" ); + + if( canceler != proposer ) { + check( unpack( prop.packed_transaction ).expiration < eosio::time_point_sec(current_time_point()), "cannot cancel until expiration" ); + } + proptable.erase(prop); + + //remove from new table + approvals apptable( get_self(), proposer.value ); + auto apps_it = apptable.find( proposal_name.value ); + if ( apps_it != apptable.end() ) { + apptable.erase(apps_it); + } else { + old_approvals old_apptable( get_self(), proposer.value ); + auto apps_it = old_apptable.find( proposal_name.value ); + check( apps_it != old_apptable.end(), "proposal not found" ); + old_apptable.erase(apps_it); + } +} + +void multisig::exec( name proposer, name proposal_name, name executer ) { + require_auth( executer ); + + proposals proptable( get_self(), proposer.value ); + auto& prop = proptable.get( proposal_name.value, "proposal not found" ); + transaction_header trx_header; + datastream ds( prop.packed_transaction.data(), prop.packed_transaction.size() ); + ds >> trx_header; + check( trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); + + approvals apptable( get_self(), proposer.value ); + auto apps_it = apptable.find( proposal_name.value ); + std::vector approvals; + invalidations inv_table( get_self(), get_self().value ); + if ( apps_it != apptable.end() ) { + approvals.reserve( apps_it->provided_approvals.size() ); + for ( auto& p : apps_it->provided_approvals ) { + auto it = inv_table.find( p.level.actor.value ); + if ( it == inv_table.end() || it->last_invalidation_time < p.time ) { + approvals.push_back(p.level); + } + } + apptable.erase(apps_it); + } else { + old_approvals old_apptable( get_self(), proposer.value ); + auto& apps = old_apptable.get( proposal_name.value, "proposal not found" ); + for ( auto& level : apps.provided_approvals ) { + auto it = inv_table.find( level.actor.value ); + if ( it == inv_table.end() ) { + approvals.push_back( level ); + } + } + old_apptable.erase(apps); + } + auto packed_provided_approvals = pack(approvals); + auto res = check_transaction_authorization( + prop.packed_transaction.data(), prop.packed_transaction.size(), + (const char*)0, 0, + packed_provided_approvals.data(), packed_provided_approvals.size() + ); + + check( res > 0, "transaction authorization failed" ); + + send_deferred( (uint128_t(proposer.value) << 64) | proposal_name.value, executer, + prop.packed_transaction.data(), prop.packed_transaction.size() ); + + proptable.erase(prop); +} + +void multisig::invalidate( name account ) { + require_auth( account ); + invalidations inv_table( get_self(), get_self().value ); + auto it = inv_table.find( account.value ); + if ( it == inv_table.end() ) { + inv_table.emplace( account, [&](auto& i) { + i.account = account; + i.last_invalidation_time = current_time_point(); + }); + } else { + inv_table.modify( it, account, [&](auto& i) { + i.last_invalidation_time = current_time_point(); + }); + } +} + +} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt new file mode 100644 index 0000000000..a1642fa291 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt @@ -0,0 +1,33 @@ +add_contract(eosio.system eosio.system + ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.system.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/delegate_bandwidth.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/name_bidding.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/voting.cpp +) + +target_include_directories(eosio.system + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../eosio.token/include) + +set_target_properties(eosio.system + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + +add_contract(rex.results rex.results ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.results.cpp) + +target_include_directories(rex.results + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set_target_properties(rex.results + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.rex") + +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.system.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.system.contracts.md @ONLY ) + +target_compile_options( eosio.system PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp new file mode 100644 index 0000000000..e4205a0c63 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp @@ -0,0 +1,1314 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX +#undef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX +#endif +// CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX macro determines whether ramfee and namebid proceeds are +// channeled to REX pool. In order to stop these proceeds from being channeled, the macro must +// be set to 0. +#define CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX 1 + +namespace eosiosystem { + + using eosio::asset; + using eosio::block_timestamp; + using eosio::check; + using eosio::const_mem_fun; + using eosio::datastream; + using eosio::indexed_by; + using eosio::name; + using eosio::same_payer; + using eosio::symbol; + using eosio::symbol_code; + using eosio::time_point; + using eosio::time_point_sec; + using eosio::unsigned_int; + + template + static inline auto has_field( F flags, E field ) + -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && + std::is_enum_v && std::is_same_v< F, std::underlying_type_t >, bool> + { + return ( (flags & static_cast(field)) != 0 ); + } + + template + static inline auto set_field( F flags, E field, bool value = true ) + -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && + std::is_enum_v && std::is_same_v< F, std::underlying_type_t >, F > + { + if( value ) + return ( flags | static_cast(field) ); + else + return ( flags & ~static_cast(field) ); + } + + static constexpr uint32_t seconds_per_year = 52 * 7 * 24 * 3600; + static constexpr uint32_t seconds_per_day = 24 * 3600; + static constexpr uint32_t seconds_per_hour = 3600; + static constexpr int64_t useconds_per_year = int64_t(seconds_per_year) * 1000'000ll; + static constexpr int64_t useconds_per_day = int64_t(seconds_per_day) * 1000'000ll; + static constexpr int64_t useconds_per_hour = int64_t(seconds_per_hour) * 1000'000ll; + static constexpr uint32_t blocks_per_day = 2 * seconds_per_day; // half seconds per day + + static constexpr int64_t min_activated_stake = 150'000'000'0000; + static constexpr int64_t ram_gift_bytes = 1400; + static constexpr int64_t min_pervote_daily_pay = 100'0000; + static constexpr uint32_t refund_delay_sec = 3 * seconds_per_day; + + static constexpr int64_t inflation_precision = 100; // 2 decimals + static constexpr int64_t default_annual_rate = 500; // 5% annual rate + static constexpr int64_t pay_factor_precision = 10000; + static constexpr int64_t default_inflation_pay_factor = 50000; // producers pay share = 10000 / 50000 = 20% of the inflation + static constexpr int64_t default_votepay_factor = 40000; // per-block pay share = 10000 / 40000 = 25% of the producer pay + + // A name bid, which consists of: + // - a `newname` name that the bid is for + // - a `high_bidder` account name that is the one with the highest bid so far + // - the `high_bid` which is amount of highest bid + // - and `last_bid_time` which is the time of the highest bid + struct [[eosio::table, eosio::contract("eosio.system")]] name_bid { + name newname; + name high_bidder; + int64_t high_bid = 0; ///< negative high_bid == closed auction waiting to be claimed + time_point last_bid_time; + + uint64_t primary_key()const { return newname.value; } + uint64_t by_high_bid()const { return static_cast(-high_bid); } + }; + + // A bid refund, which is defined by: + // - the `bidder` account name owning the refund + // - the `amount` to be refunded + struct [[eosio::table, eosio::contract("eosio.system")]] bid_refund { + name bidder; + asset amount; + + uint64_t primary_key()const { return bidder.value; } + }; + typedef eosio::multi_index< "namebids"_n, name_bid, + indexed_by<"highbid"_n, const_mem_fun > + > name_bid_table; + + typedef eosio::multi_index< "bidrefunds"_n, bid_refund > bid_refund_table; + + // Defines new global state parameters. + struct [[eosio::table("global"), eosio::contract("eosio.system")]] eosio_global_state : eosio::blockchain_parameters { + uint64_t free_ram()const { return max_ram_size - total_ram_bytes_reserved; } + + uint64_t max_ram_size = 64ll*1024 * 1024 * 1024; + uint64_t total_ram_bytes_reserved = 0; + int64_t total_ram_stake = 0; + + block_timestamp last_producer_schedule_update; + time_point last_pervote_bucket_fill; + int64_t pervote_bucket = 0; + int64_t perblock_bucket = 0; + uint32_t total_unpaid_blocks = 0; /// all blocks which have been produced but not paid + int64_t total_activated_stake = 0; + time_point thresh_activated_stake_time; + uint16_t last_producer_schedule_size = 0; + double total_producer_vote_weight = 0; /// the sum of all producer votes + block_timestamp last_name_close; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE_DERIVED( eosio_global_state, eosio::blockchain_parameters, + (max_ram_size)(total_ram_bytes_reserved)(total_ram_stake) + (last_producer_schedule_update)(last_pervote_bucket_fill) + (pervote_bucket)(perblock_bucket)(total_unpaid_blocks)(total_activated_stake)(thresh_activated_stake_time) + (last_producer_schedule_size)(total_producer_vote_weight)(last_name_close) ) + }; + + // Defines new global state parameters added after version 1.0 + struct [[eosio::table("global2"), eosio::contract("eosio.system")]] eosio_global_state2 { + eosio_global_state2(){} + + uint16_t new_ram_per_block = 0; + block_timestamp last_ram_increase; + block_timestamp last_block_num; /* deprecated */ + double total_producer_votepay_share = 0; + uint8_t revision = 0; ///< used to track version updates in the future. + + EOSLIB_SERIALIZE( eosio_global_state2, (new_ram_per_block)(last_ram_increase)(last_block_num) + (total_producer_votepay_share)(revision) ) + }; + + // Defines new global state parameters added after version 1.3.0 + struct [[eosio::table("global3"), eosio::contract("eosio.system")]] eosio_global_state3 { + eosio_global_state3() { } + time_point last_vpay_state_update; + double total_vpay_share_change_rate = 0; + + EOSLIB_SERIALIZE( eosio_global_state3, (last_vpay_state_update)(total_vpay_share_change_rate) ) + }; + + // Defines new global state parameters to store inflation rate and distribution + struct [[eosio::table("global4"), eosio::contract("eosio.system")]] eosio_global_state4 { + eosio_global_state4() { } + double continuous_rate; + int64_t inflation_pay_factor; + int64_t votepay_factor; + + EOSLIB_SERIALIZE( eosio_global_state4, (continuous_rate)(inflation_pay_factor)(votepay_factor) ) + }; + + inline eosio::block_signing_authority convert_to_block_signing_authority( const eosio::public_key& producer_key ) { + return eosio::block_signing_authority_v0{ .threshold = 1, .keys = {{producer_key, 1}} }; + } + + // Defines `producer_info` structure to be stored in `producer_info` table, added after version 1.0 + struct [[eosio::table, eosio::contract("eosio.system")]] producer_info { + name owner; + double total_votes = 0; + eosio::public_key producer_key; /// a packed public key object + bool is_active = true; + std::string url; + uint32_t unpaid_blocks = 0; + time_point last_claim_time; + uint16_t location = 0; + eosio::binary_extension producer_authority; // added in version 1.9.0 + + uint64_t primary_key()const { return owner.value; } + double by_votes()const { return is_active ? -total_votes : total_votes; } + bool active()const { return is_active; } + void deactivate() { producer_key = public_key(); producer_authority.reset(); is_active = false; } + + eosio::block_signing_authority get_producer_authority()const { + if( producer_authority.has_value() ) { + bool zero_threshold = std::visit( [](auto&& auth ) -> bool { + return (auth.threshold == 0); + }, *producer_authority ); + // zero_threshold could be true despite the validation done in regproducer2 because the v1.9.0 eosio.system + // contract has a bug which may have modified the producer table such that the producer_authority field + // contains a default constructed eosio::block_signing_authority (which has a 0 threshold and so is invalid). + if( !zero_threshold ) return *producer_authority; + } + return convert_to_block_signing_authority( producer_key ); + } + + // The unregprod and claimrewards actions modify unrelated fields of the producers table and under the default + // serialization behavior they would increase the size of the serialized table if the producer_authority field + // was not already present. This is acceptable (though not necessarily desired) because those two actions require + // the authority of the producer who pays for the table rows. + // However, the rmvproducer action and the onblock transaction would also modify the producer table in a similar + // way and increasing its serialized size is not acceptable in that context. + // So, a custom serialization is defined to handle the binary_extension producer_authority + // field in the desired way. (Note: v1.9.0 did not have this custom serialization behavior.) + + template + friend DataStream& operator << ( DataStream& ds, const producer_info& t ) { + ds << t.owner + << t.total_votes + << t.producer_key + << t.is_active + << t.url + << t.unpaid_blocks + << t.last_claim_time + << t.location; + + if( !t.producer_authority.has_value() ) return ds; + + return ds << t.producer_authority; + } + + template + friend DataStream& operator >> ( DataStream& ds, producer_info& t ) { + return ds >> t.owner + >> t.total_votes + >> t.producer_key + >> t.is_active + >> t.url + >> t.unpaid_blocks + >> t.last_claim_time + >> t.location + >> t.producer_authority; + } + }; + + // Defines new producer info structure to be stored in new producer info table, added after version 1.3.0 + struct [[eosio::table, eosio::contract("eosio.system")]] producer_info2 { + name owner; + double votepay_share = 0; + time_point last_votepay_share_update; + + uint64_t primary_key()const { return owner.value; } + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( producer_info2, (owner)(votepay_share)(last_votepay_share_update) ) + }; + + // Voter info. Voter info stores information about the voter: + // - `owner` the voter + // - `proxy` the proxy set by the voter, if any + // - `producers` the producers approved by this voter if no proxy set + // - `staked` the amount staked + struct [[eosio::table, eosio::contract("eosio.system")]] voter_info { + name owner; /// the voter + name proxy; /// the proxy set by the voter, if any + std::vector producers; /// the producers approved by this voter if no proxy set + int64_t staked = 0; + + // Every time a vote is cast we must first "undo" the last vote weight, before casting the + // new vote weight. Vote weight is calculated as: + // stated.amount * 2 ^ ( weeks_since_launch/weeks_per_year) + double last_vote_weight = 0; /// the vote weight cast the last time the vote was updated + + // Total vote weight delegated to this voter. + double proxied_vote_weight= 0; /// the total vote weight delegated to this voter as a proxy + bool is_proxy = 0; /// whether the voter is a proxy for others + + + uint32_t flags1 = 0; + uint32_t reserved2 = 0; + eosio::asset reserved3; + + uint64_t primary_key()const { return owner.value; } + + enum class flags1_fields : uint32_t { + ram_managed = 1, + net_managed = 2, + cpu_managed = 4 + }; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(producers)(staked)(last_vote_weight)(proxied_vote_weight)(is_proxy)(flags1)(reserved2)(reserved3) ) + }; + + + typedef eosio::multi_index< "voters"_n, voter_info > voters_table; + + + typedef eosio::multi_index< "producers"_n, producer_info, + indexed_by<"prototalvote"_n, const_mem_fun > + > producers_table; + + typedef eosio::multi_index< "producers2"_n, producer_info2 > producers_table2; + + + typedef eosio::singleton< "global"_n, eosio_global_state > global_state_singleton; + + typedef eosio::singleton< "global2"_n, eosio_global_state2 > global_state2_singleton; + + typedef eosio::singleton< "global3"_n, eosio_global_state3 > global_state3_singleton; + + typedef eosio::singleton< "global4"_n, eosio_global_state4 > global_state4_singleton; + + struct [[eosio::table, eosio::contract("eosio.system")]] user_resources { + name owner; + asset net_weight; + asset cpu_weight; + int64_t ram_bytes = 0; + + bool is_empty()const { return net_weight.amount == 0 && cpu_weight.amount == 0 && ram_bytes == 0; } + uint64_t primary_key()const { return owner.value; } + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( user_resources, (owner)(net_weight)(cpu_weight)(ram_bytes) ) + }; + + // Every user 'from' has a scope/table that uses every receipient 'to' as the primary key. + struct [[eosio::table, eosio::contract("eosio.system")]] delegated_bandwidth { + name from; + name to; + asset net_weight; + asset cpu_weight; + + bool is_empty()const { return net_weight.amount == 0 && cpu_weight.amount == 0; } + uint64_t primary_key()const { return to.value; } + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( delegated_bandwidth, (from)(to)(net_weight)(cpu_weight) ) + + }; + + struct [[eosio::table, eosio::contract("eosio.system")]] refund_request { + name owner; + time_point_sec request_time; + eosio::asset net_amount; + eosio::asset cpu_amount; + + bool is_empty()const { return net_amount.amount == 0 && cpu_amount.amount == 0; } + uint64_t primary_key()const { return owner.value; } + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( refund_request, (owner)(request_time)(net_amount)(cpu_amount) ) + }; + + + typedef eosio::multi_index< "userres"_n, user_resources > user_resources_table; + typedef eosio::multi_index< "delband"_n, delegated_bandwidth > del_bandwidth_table; + typedef eosio::multi_index< "refunds"_n, refund_request > refunds_table; + + // `rex_pool` structure underlying the rex pool table. A rex pool table entry is defined by: + // - `version` defaulted to zero, + // - `total_lent` total amount of CORE_SYMBOL in open rex_loans + // - `total_unlent` total amount of CORE_SYMBOL available to be lent (connector), + // - `total_rent` fees received in exchange for lent (connector), + // - `total_lendable` total amount of CORE_SYMBOL that have been lent (total_unlent + total_lent), + // - `total_rex` total number of REX shares allocated to contributors to total_lendable, + // - `namebid_proceeds` the amount of CORE_SYMBOL to be transferred from namebids to REX pool, + // - `loan_num` increments with each new loan + struct [[eosio::table,eosio::contract("eosio.system")]] rex_pool { + uint8_t version = 0; + asset total_lent; + asset total_unlent; + asset total_rent; + asset total_lendable; + asset total_rex; + asset namebid_proceeds; + uint64_t loan_num = 0; + + uint64_t primary_key()const { return 0; } + }; + + typedef eosio::multi_index< "rexpool"_n, rex_pool > rex_pool_table; + + // `rex_return_pool` structure underlying the rex return pool table. A rex return pool table entry is defined by: + // - `version` defaulted to zero, + // - `last_dist_time` the last time proceeds from renting, ram fees, and name bids were added to the rex pool, + // - `pending_bucket_time` timestamp of the pending 12-hour return bucket, + // - `oldest_bucket_time` cached timestamp of the oldest 12-hour return bucket, + // - `pending_bucket_proceeds` proceeds in the pending 12-hour return bucket, + // - `current_rate_of_increase` the current rate per dist_interval at which proceeds are added to the rex pool, + // - `proceeds` the maximum amount of proceeds that can be added to the rex pool at any given time + struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_pool { + uint8_t version = 0; + time_point_sec last_dist_time; + time_point_sec pending_bucket_time = time_point_sec::maximum(); + time_point_sec oldest_bucket_time = time_point_sec::min(); + int64_t pending_bucket_proceeds = 0; + int64_t current_rate_of_increase = 0; + int64_t proceeds = 0; + + static constexpr uint32_t total_intervals = 30 * 144; // 30 days + static constexpr uint32_t dist_interval = 10 * 60; // 10 minutes + static constexpr uint8_t hours_per_bucket = 12; + static_assert( total_intervals * dist_interval == 30 * seconds_per_day ); + + uint64_t primary_key()const { return 0; } + }; + + typedef eosio::multi_index< "rexretpool"_n, rex_return_pool > rex_return_pool_table; + + // `rex_return_buckets` structure underlying the rex return buckets table. A rex return buckets table is defined by: + // - `version` defaulted to zero, + // - `return_buckets` buckets of proceeds accumulated in 12-hour intervals + struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_buckets { + uint8_t version = 0; + std::map return_buckets; + + uint64_t primary_key()const { return 0; } + }; + + typedef eosio::multi_index< "retbuckets"_n, rex_return_buckets > rex_return_buckets_table; + + // `rex_fund` structure underlying the rex fund table. A rex fund table entry is defined by: + // - `version` defaulted to zero, + // - `owner` the owner of the rex fund, + // - `balance` the balance of the fund. + struct [[eosio::table,eosio::contract("eosio.system")]] rex_fund { + uint8_t version = 0; + name owner; + asset balance; + + uint64_t primary_key()const { return owner.value; } + }; + + typedef eosio::multi_index< "rexfund"_n, rex_fund > rex_fund_table; + + // `rex_balance` structure underlying the rex balance table. A rex balance table entry is defined by: + // - `version` defaulted to zero, + // - `owner` the owner of the rex fund, + // - `vote_stake` the amount of CORE_SYMBOL currently included in owner's vote, + // - `rex_balance` the amount of REX owned by owner, + // - `matured_rex` matured REX available for selling + struct [[eosio::table,eosio::contract("eosio.system")]] rex_balance { + uint8_t version = 0; + name owner; + asset vote_stake; + asset rex_balance; + int64_t matured_rex = 0; + std::deque> rex_maturities; /// REX daily maturity buckets + + uint64_t primary_key()const { return owner.value; } + }; + + typedef eosio::multi_index< "rexbal"_n, rex_balance > rex_balance_table; + + // `rex_loan` structure underlying the `rex_cpu_loan_table` and `rex_net_loan_table`. A rex net/cpu loan table entry is defined by: + // - `version` defaulted to zero, + // - `from` account creating and paying for loan, + // - `receiver` account receiving rented resources, + // - `payment` SYS tokens paid for the loan, + // - `balance` is the amount of SYS tokens available to be used for loan auto-renewal, + // - `total_staked` total amount staked, + // - `loan_num` loan number/id, + // - `expiration` the expiration time when loan will be either closed or renewed + // If payment <= balance, the loan is renewed, and closed otherwise. + struct [[eosio::table,eosio::contract("eosio.system")]] rex_loan { + uint8_t version = 0; + name from; + name receiver; + asset payment; + asset balance; + asset total_staked; + uint64_t loan_num; + eosio::time_point expiration; + + uint64_t primary_key()const { return loan_num; } + uint64_t by_expr()const { return expiration.elapsed.count(); } + uint64_t by_owner()const { return from.value; } + }; + + typedef eosio::multi_index< "cpuloan"_n, rex_loan, + indexed_by<"byexpr"_n, const_mem_fun>, + indexed_by<"byowner"_n, const_mem_fun> + > rex_cpu_loan_table; + + typedef eosio::multi_index< "netloan"_n, rex_loan, + indexed_by<"byexpr"_n, const_mem_fun>, + indexed_by<"byowner"_n, const_mem_fun> + > rex_net_loan_table; + + struct [[eosio::table,eosio::contract("eosio.system")]] rex_order { + uint8_t version = 0; + name owner; + asset rex_requested; + asset proceeds; + asset stake_change; + eosio::time_point order_time; + bool is_open = true; + + void close() { is_open = false; } + uint64_t primary_key()const { return owner.value; } + uint64_t by_time()const { return is_open ? order_time.elapsed.count() : std::numeric_limits::max(); } + }; + + typedef eosio::multi_index< "rexqueue"_n, rex_order, + indexed_by<"bytime"_n, const_mem_fun>> rex_order_table; + + struct rex_order_outcome { + bool success; + asset proceeds; + asset stake_change; + }; + + /** + * The `eosio.system` smart contract is provided by `block.one` as a sample system contract, and it defines the structures and actions needed for blockchain's core functionality. + * + * Just like in the `eosio.bios` sample contract implementation, there are a few actions which are not implemented at the contract level (`newaccount`, `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, `canceldelay`, `onerror`, `setabi`, `setcode`), they are just declared in the contract so they will show in the contract's ABI and users will be able to push those actions to the chain via the account holding the `eosio.system` contract, but the implementation is at the EOSIO core level. They are referred to as EOSIO native actions. + * + * - Users can stake tokens for CPU and Network bandwidth, and then vote for producers or + * delegate their vote to a proxy. + * - Producers register in order to be voted for, and can claim per-block and per-vote rewards. + * - Users can buy and sell RAM at a market-determined price. + * - Users can bid on premium names. + * - A resource exchange system (REX) allows token holders to lend their tokens, + * and users to rent CPU and Network resources in return for a market-determined fee. + */ + class [[eosio::contract("eosio.system")]] system_contract : public native { + + private: + voters_table _voters; + producers_table _producers; + producers_table2 _producers2; + global_state_singleton _global; + global_state2_singleton _global2; + global_state3_singleton _global3; + global_state4_singleton _global4; + eosio_global_state _gstate; + eosio_global_state2 _gstate2; + eosio_global_state3 _gstate3; + eosio_global_state4 _gstate4; + rammarket _rammarket; + rex_pool_table _rexpool; + rex_return_pool_table _rexretpool; + rex_return_buckets_table _rexretbuckets; + rex_fund_table _rexfunds; + rex_balance_table _rexbalance; + rex_order_table _rexorders; + + public: + static constexpr eosio::name active_permission{"active"_n}; + static constexpr eosio::name token_account{"eosio.token"_n}; + static constexpr eosio::name ram_account{"eosio.ram"_n}; + static constexpr eosio::name ramfee_account{"eosio.ramfee"_n}; + static constexpr eosio::name stake_account{"eosio.stake"_n}; + static constexpr eosio::name bpay_account{"eosio.bpay"_n}; + static constexpr eosio::name vpay_account{"eosio.vpay"_n}; + static constexpr eosio::name names_account{"eosio.names"_n}; + static constexpr eosio::name saving_account{"eosio.saving"_n}; + static constexpr eosio::name rex_account{"eosio.rex"_n}; + static constexpr eosio::name null_account{"eosio.null"_n}; + static constexpr symbol ramcore_symbol = symbol(symbol_code("RAMCORE"), 4); + static constexpr symbol ram_symbol = symbol(symbol_code("RAM"), 0); + static constexpr symbol rex_symbol = symbol(symbol_code("REX"), 4); + + system_contract( name s, name code, datastream ds ); + ~system_contract(); + + // Returns the core symbol by system account name + // @param system_account - the system account to get the core symbol for. + static symbol get_core_symbol( name system_account = "eosio"_n ) { + rammarket rm(system_account, system_account.value); + const static auto sym = get_core_symbol( rm ); + return sym; + } + + // Actions: + /** + * The Init action initializes the system contract for a version and a symbol. + * Only succeeds when: + * - version is 0 and + * - symbol is found and + * - system token supply is greater than 0, + * - and system contract wasn’t already been initialized. + * + * @param version - the version, has to be 0, + * @param core - the system symbol. + */ + [[eosio::action]] + void init( unsigned_int version, const symbol& core ); + + /** + * On block action. This special action is triggered when a block is applied by the given producer + * and cannot be generated from any other source. It is used to pay producers and calculate + * missed blocks of other producers. Producer pay is deposited into the producer's stake + * balance and can be withdrawn over time. If blocknum is the start of a new round this may + * update the active producer config from the producer votes. + * + * @param header - the block header produced. + */ + [[eosio::action]] + void onblock( ignore header ); + + /** + * Set account limits action sets the resource limits of an account + * + * @param account - name of the account whose resource limit to be set, + * @param ram_bytes - ram limit in absolute bytes, + * @param net_weight - fractionally proportionate net limit of available resources based on (weight / total_weight_of_all_accounts), + * @param cpu_weight - fractionally proportionate cpu limit of available resources based on (weight / total_weight_of_all_accounts). + */ + [[eosio::action]] + void setalimits( const name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ); + + /** + * Set account RAM limits action, which sets the RAM limits of an account + * + * @param account - name of the account whose resource limit to be set, + * @param ram_bytes - ram limit in absolute bytes. + */ + [[eosio::action]] + void setacctram( const name& account, const std::optional& ram_bytes ); + + /** + * Set account NET limits action, which sets the NET limits of an account + * + * @param account - name of the account whose resource limit to be set, + * @param net_weight - fractionally proportionate net limit of available resources based on (weight / total_weight_of_all_accounts). + */ + [[eosio::action]] + void setacctnet( const name& account, const std::optional& net_weight ); + + /** + * Set account CPU limits action, which sets the CPU limits of an account + * + * @param account - name of the account whose resource limit to be set, + * @param cpu_weight - fractionally proportionate cpu limit of available resources based on (weight / total_weight_of_all_accounts). + */ + [[eosio::action]] + void setacctcpu( const name& account, const std::optional& cpu_weight ); + + + /** + * The activate action, activates a protocol feature + * + * @param feature_digest - hash of the protocol feature to activate. + */ + [[eosio::action]] + void activate( const eosio::checksum256& feature_digest ); + + // functions defined in delegate_bandwidth.cpp + + /** + * Delegate bandwidth and/or cpu action. Stakes SYS from the balance of `from` for the benefit of `receiver`. + * + * @param from - the account to delegate bandwidth from, that is, the account holding + * tokens to be staked, + * @param receiver - the account to delegate bandwith to, that is, the account to + * whose resources staked tokens are added + * @param stake_net_quantity - tokens staked for NET bandwidth, + * @param stake_cpu_quantity - tokens staked for CPU bandwidth, + * @param transfer - if true, ownership of staked tokens is transfered to `receiver`. + * + * @post All producers `from` account has voted for will have their votes updated immediately. + */ + [[eosio::action]] + void delegatebw( const name& from, const name& receiver, + const asset& stake_net_quantity, const asset& stake_cpu_quantity, bool transfer ); + + /** + * Setrex action, sets total_rent balance of REX pool to the passed value. + * @param balance - amount to set the REX pool balance. + */ + [[eosio::action]] + void setrex( const asset& balance ); + + /** + * Deposit to REX fund action. Deposits core tokens to user REX fund. + * All proceeds and expenses related to REX are added to or taken out of this fund. + * An inline transfer from 'owner' liquid balance is executed. + * All REX-related costs and proceeds are deducted from and added to 'owner' REX fund, + * with one exception being buying REX using staked tokens. + * Storage change is billed to 'owner'. + * + * @param owner - REX fund owner account, + * @param amount - amount of tokens to be deposited. + */ + [[eosio::action]] + void deposit( const name& owner, const asset& amount ); + + /** + * Withdraw from REX fund action, withdraws core tokens from user REX fund. + * An inline token transfer to user balance is executed. + * + * @param owner - REX fund owner account, + * @param amount - amount of tokens to be withdrawn. + */ + [[eosio::action]] + void withdraw( const name& owner, const asset& amount ); + + /** + * Buyrex action, buys REX in exchange for tokens taken out of user's REX fund by transfering + * core tokens from user REX fund and converts them to REX stake. By buying REX, user is + * lending tokens in order to be rented as CPU or NET resourses. + * Storage change is billed to 'from' account. + * + * @param from - owner account name, + * @param amount - amount of tokens taken out of 'from' REX fund. + * + * @pre A voting requirement must be satisfied before action can be executed. + * @pre User must vote for at least 21 producers or delegate vote to proxy before buying REX. + * + * @post User votes are updated following this action. + * @post Tokens used in purchase are added to user's voting power. + * @post Bought REX cannot be sold before 4 days counting from end of day of purchase. + */ + [[eosio::action]] + void buyrex( const name& from, const asset& amount ); + + /** + * Unstaketorex action, uses staked core tokens to buy REX. + * Storage change is billed to 'owner' account. + * + * @param owner - owner of staked tokens, + * @param receiver - account name that tokens have previously been staked to, + * @param from_net - amount of tokens to be unstaked from NET bandwidth and used for REX purchase, + * @param from_cpu - amount of tokens to be unstaked from CPU bandwidth and used for REX purchase. + * + * @pre A voting requirement must be satisfied before action can be executed. + * @pre User must vote for at least 21 producers or delegate vote to proxy before buying REX. + * + * @post User votes are updated following this action. + * @post Tokens used in purchase are added to user's voting power. + * @post Bought REX cannot be sold before 4 days counting from end of day of purchase. + */ + [[eosio::action]] + void unstaketorex( const name& owner, const name& receiver, const asset& from_net, const asset& from_cpu ); + + /** + * Sellrex action, sells REX in exchange for core tokens by converting REX stake back into core tokens + * at current exchange rate. If order cannot be processed, it gets queued until there is enough + * in REX pool to fill order, and will be processed within 30 days at most. If successful, user + * votes are updated, that is, proceeds are deducted from user's voting power. In case sell order + * is queued, storage change is billed to 'from' account. + * + * @param from - owner account of REX, + * @param rex - amount of REX to be sold. + */ + [[eosio::action]] + void sellrex( const name& from, const asset& rex ); + + /** + * Cnclrexorder action, cancels unfilled REX sell order by owner if one exists. + * + * @param owner - owner account name. + * + * @pre Order cannot be cancelled once it's been filled. + */ + [[eosio::action]] + void cnclrexorder( const name& owner ); + + /** + * Rentcpu action, uses payment to rent as many SYS tokens as possible as determined by market price and + * stake them for CPU for the benefit of receiver, after 30 days the rented core delegation of CPU + * will expire. At expiration, if balance is greater than or equal to `loan_payment`, `loan_payment` + * is taken out of loan balance and used to renew the loan. Otherwise, the loan is closed and user + * is refunded any remaining balance. + * Owner can fund or refund a loan at any time before its expiration. + * All loan expenses and refunds come out of or are added to owner's REX fund. + * + * @param from - account creating and paying for CPU loan, 'from' account can add tokens to loan + * balance using action `fundcpuloan` and withdraw from loan balance using `defcpuloan` + * @param receiver - account receiving rented CPU resources, + * @param loan_payment - tokens paid for the loan, it has to be greater than zero, + * amount of rented resources is calculated from `loan_payment`, + * @param loan_fund - additional tokens can be zero, and is added to loan balance. + * Loan balance represents a reserve that is used at expiration for automatic loan renewal. + */ + [[eosio::action]] + void rentcpu( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ); + + /** + * Rentnet action, uses payment to rent as many SYS tokens as possible as determined by market price and + * stake them for NET for the benefit of receiver, after 30 days the rented core delegation of NET + * will expire. At expiration, if balance is greater than or equal to `loan_payment`, `loan_payment` + * is taken out of loan balance and used to renew the loan. Otherwise, the loan is closed and user + * is refunded any remaining balance. + * Owner can fund or refund a loan at any time before its expiration. + * All loan expenses and refunds come out of or are added to owner's REX fund. + * + * @param from - account creating and paying for NET loan, 'from' account can add tokens to loan + * balance using action `fundnetloan` and withdraw from loan balance using `defnetloan`, + * @param receiver - account receiving rented NET resources, + * @param loan_payment - tokens paid for the loan, it has to be greater than zero, + * amount of rented resources is calculated from `loan_payment`, + * @param loan_fund - additional tokens can be zero, and is added to loan balance. + * Loan balance represents a reserve that is used at expiration for automatic loan renewal. + */ + [[eosio::action]] + void rentnet( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ); + + /** + * Fundcpuloan action, transfers tokens from REX fund to the fund of a specific CPU loan in order to + * be used for loan renewal at expiry. + * + * @param from - loan creator account, + * @param loan_num - loan id, + * @param payment - tokens transfered from REX fund to loan fund. + */ + [[eosio::action]] + void fundcpuloan( const name& from, uint64_t loan_num, const asset& payment ); + + /** + * Fundnetloan action, transfers tokens from REX fund to the fund of a specific NET loan in order to + * be used for loan renewal at expiry. + * + * @param from - loan creator account, + * @param loan_num - loan id, + * @param payment - tokens transfered from REX fund to loan fund. + */ + [[eosio::action]] + void fundnetloan( const name& from, uint64_t loan_num, const asset& payment ); + + /** + * Defcpuloan action, withdraws tokens from the fund of a specific CPU loan and adds them to REX fund. + * + * @param from - loan creator account, + * @param loan_num - loan id, + * @param amount - tokens transfered from CPU loan fund to REX fund. + */ + [[eosio::action]] + void defcpuloan( const name& from, uint64_t loan_num, const asset& amount ); + + /** + * Defnetloan action, withdraws tokens from the fund of a specific NET loan and adds them to REX fund. + * + * @param from - loan creator account, + * @param loan_num - loan id, + * @param amount - tokens transfered from NET loan fund to REX fund. + */ + [[eosio::action]] + void defnetloan( const name& from, uint64_t loan_num, const asset& amount ); + + /** + * Updaterex action, updates REX owner vote weight to current value of held REX tokens. + * + * @param owner - REX owner account. + */ + [[eosio::action]] + void updaterex( const name& owner ); + + /** + * Rexexec action, processes max CPU loans, max NET loans, and max queued sellrex orders. + * Action does not execute anything related to a specific user. + * + * @param user - any account can execute this action, + * @param max - number of each of CPU loans, NET loans, and sell orders to be processed. + */ + [[eosio::action]] + void rexexec( const name& user, uint16_t max ); + + /** + * Consolidate action, consolidates REX maturity buckets into one bucket that can be sold after 4 days + * starting from the end of the day. + * + * @param owner - REX owner account name. + */ + [[eosio::action]] + void consolidate( const name& owner ); + + /** + * Mvtosavings action, moves a specified amount of REX into savings bucket. REX savings bucket + * never matures. In order for it to be sold, it has to be moved explicitly + * out of that bucket. Then the moved amount will have the regular maturity + * period of 4 days starting from the end of the day. + * + * @param owner - REX owner account name. + * @param rex - amount of REX to be moved. + */ + [[eosio::action]] + void mvtosavings( const name& owner, const asset& rex ); + + /** + * Mvfrsavings action, moves a specified amount of REX out of savings bucket. The moved amount + * will have the regular REX maturity period of 4 days. + * + * @param owner - REX owner account name. + * @param rex - amount of REX to be moved. + */ + [[eosio::action]] + void mvfrsavings( const name& owner, const asset& rex ); + + /** + * Closerex action, deletes owner records from REX tables and frees used RAM. Owner must not have + * an outstanding REX balance. + * + * @param owner - user account name. + * + * @pre If owner has a non-zero REX balance, the action fails; otherwise, + * owner REX balance entry is deleted. + * @pre If owner has no outstanding loans and a zero REX fund balance, + * REX fund entry is deleted. + */ + [[eosio::action]] + void closerex( const name& owner ); + + /** + * Undelegate bandwitdh action, decreases the total tokens delegated by `from` to `receiver` and/or + * frees the memory associated with the delegation if there is nothing + * left to delegate. + * This will cause an immediate reduction in net/cpu bandwidth of the + * receiver. + * A transaction is scheduled to send the tokens back to `from` after + * the staking period has passed. If existing transaction is scheduled, it + * will be canceled and a new transaction issued that has the combined + * undelegated amount. + * The `from` account loses voting power as a result of this call and + * all producer tallies are updated. + * + * @param from - the account to undelegate bandwidth from, that is, + * the account whose tokens will be unstaked, + * @param receiver - the account to undelegate bandwith to, that is, + * the account to whose benefit tokens have been staked, + * @param unstake_net_quantity - tokens to be unstaked from NET bandwidth, + * @param unstake_cpu_quantity - tokens to be unstaked from CPU bandwidth, + * + * @post Unstaked tokens are transferred to `from` liquid balance via a + * deferred transaction with a delay of 3 days. + * @post If called during the delay period of a previous `undelegatebw` + * action, pending action is canceled and timer is reset. + * @post All producers `from` account has voted for will have their votes updated immediately. + * @post Bandwidth and storage for the deferred transaction are billed to `from`. + */ + [[eosio::action]] + void undelegatebw( const name& from, const name& receiver, + const asset& unstake_net_quantity, const asset& unstake_cpu_quantity ); + + /** + * Buy ram action, increases receiver's ram quota based upon current price and quantity of + * tokens provided. An inline transfer from receiver to system contract of + * tokens will be executed. + * + * @param payer - the ram buyer, + * @param receiver - the ram receiver, + * @param quant - the quntity of tokens to buy ram with. + */ + [[eosio::action]] + void buyram( const name& payer, const name& receiver, const asset& quant ); + + /** + * Buy a specific amount of ram bytes action. Increases receiver's ram in quantity of bytes provided. + * An inline transfer from receiver to system contract of tokens will be executed. + * + * @param payer - the ram buyer, + * @param receiver - the ram receiver, + * @param bytes - the quntity of ram to buy specified in bytes. + */ + [[eosio::action]] + void buyrambytes( const name& payer, const name& receiver, uint32_t bytes ); + + /** + * Sell ram action, reduces quota by bytes and then performs an inline transfer of tokens + * to receiver based upon the average purchase price of the original quota. + * + * @param account - the ram seller account, + * @param bytes - the amount of ram to sell in bytes. + */ + [[eosio::action]] + void sellram( const name& account, int64_t bytes ); + + /** + * Refund action, this action is called after the delegation-period to claim all pending + * unstaked tokens belonging to owner. + * + * @param owner - the owner of the tokens claimed. + */ + [[eosio::action]] + void refund( const name& owner ); + + // functions defined in voting.cpp + + /** + * Register producer action, indicates that a particular account wishes to become a producer, + * this action will create a `producer_config` and a `producer_info` object for `producer` scope + * in producers tables. + * + * @param producer - account registering to be a producer candidate, + * @param producer_key - the public key of the block producer, this is the key used by block producer to sign blocks, + * @param url - the url of the block producer, normally the url of the block producer presentation website, + * @param location - is the country code as defined in the ISO 3166, https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes + * + * @pre Producer to register is an account + * @pre Authority of producer to register + */ + [[eosio::action]] + void regproducer( const name& producer, const public_key& producer_key, const std::string& url, uint16_t location ); + + /** + * Register producer action, indicates that a particular account wishes to become a producer, + * this action will create a `producer_config` and a `producer_info` object for `producer` scope + * in producers tables. + * + * @param producer - account registering to be a producer candidate, + * @param producer_authority - the weighted threshold multisig block signing authority of the block producer used to sign blocks, + * @param url - the url of the block producer, normally the url of the block producer presentation website, + * @param location - is the country code as defined in the ISO 3166, https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes + * + * @pre Producer to register is an account + * @pre Authority of producer to register + */ + [[eosio::action]] + void regproducer2( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ); + + /** + * Unregister producer action, deactivates the block producer with account name `producer`. + * + * Deactivate the block producer with account name `producer`. + * @param producer - the block producer account to unregister. + */ + [[eosio::action]] + void unregprod( const name& producer ); + + /** + * Set ram action sets the ram supply. + * @param max_ram_size - the amount of ram supply to set. + */ + [[eosio::action]] + void setram( uint64_t max_ram_size ); + + /** + * Set ram rate action, sets the rate of increase of RAM in bytes per block. It is capped by the uint16_t to + * a maximum rate of 3 TB per year. If update_ram_supply hasn't been called for the most recent block, + * then new ram will be allocated at the old rate up to the present block before switching the rate. + * + * @param bytes_per_block - the amount of bytes per block increase to set. + */ + [[eosio::action]] + void setramrate( uint16_t bytes_per_block ); + + /** + * Vote producer action, votes for a set of producers. This action updates the list of `producers` voted for, + * for `voter` account. If voting for a `proxy`, the producer votes will not change until the + * proxy updates their own vote. Voter can vote for a proxy __or__ a list of at most 30 producers. + * Storage change is billed to `voter`. + * + * @param voter - the account to change the voted producers for, + * @param proxy - the proxy to change the voted producers for, + * @param producers - the list of producers to vote for, a maximum of 30 producers is allowed. + * + * @pre Producers must be sorted from lowest to highest and must be registered and active + * @pre If proxy is set then no producers can be voted for + * @pre If proxy is set then proxy account must exist and be registered as a proxy + * @pre Every listed producer or proxy must have been previously registered + * @pre Voter must authorize this action + * @pre Voter must have previously staked some EOS for voting + * @pre Voter->staked must be up to date + * + * @post Every producer previously voted for will have vote reduced by previous vote weight + * @post Every producer newly voted for will have vote increased by new vote amount + * @post Prior proxy will proxied_vote_weight decremented by previous vote weight + * @post New proxy will proxied_vote_weight incremented by new vote weight + */ + [[eosio::action]] + void voteproducer( const name& voter, const name& proxy, const std::vector& producers ); + + /** + * Register proxy action, sets `proxy` account as proxy. + * An account marked as a proxy can vote with the weight of other accounts which + * have selected it as a proxy. Other accounts must refresh their voteproducer to + * update the proxy's weight. + * Storage change is billed to `proxy`. + * + * @param rpoxy - the account registering as voter proxy (or unregistering), + * @param isproxy - if true, proxy is registered; if false, proxy is unregistered. + * + * @pre Proxy must have something staked (existing row in voters table) + * @pre New state must be different than current state + */ + [[eosio::action]] + void regproxy( const name& proxy, bool isproxy ); + + /** + * Set the blockchain parameters. By tunning these parameters a degree of + * customization can be achieved. + * @param params - New blockchain parameters to set. + */ + [[eosio::action]] + void setparams( const eosio::blockchain_parameters& params ); + + /** + * Claim rewards action, claims block producing and vote rewards. + * @param owner - producer account claiming per-block and per-vote rewards. + */ + [[eosio::action]] + void claimrewards( const name& owner ); + + /** + * Set privilege status for an account. Allows to set privilege status for an account (turn it on/off). + * @param account - the account to set the privileged status for. + * @param is_priv - 0 for false, > 0 for true. + */ + [[eosio::action]] + void setpriv( const name& account, uint8_t is_priv ); + + /** + * Remove producer action, deactivates a producer by name, if not found asserts. + * @param producer - the producer account to deactivate. + */ + [[eosio::action]] + void rmvproducer( const name& producer ); + + /** + * Update revision action, updates the current revision. + * @param revision - it has to be incremented by 1 compared with current revision. + * + * @pre Current revision can not be higher than 254, and has to be smaller + * than or equal 1 (“set upper bound to greatest revision supported in the code”). + */ + [[eosio::action]] + void updtrevision( uint8_t revision ); + + /** + * Bid name action, allows an account `bidder` to place a bid for a name `newname`. + * @param bidder - the account placing the bid, + * @param newname - the name the bid is placed for, + * @param bid - the amount of system tokens payed for the bid. + * + * @pre Bids can be placed only on top-level suffix, + * @pre Non empty name, + * @pre Names longer than 12 chars are not allowed, + * @pre Names equal with 12 chars can be created without placing a bid, + * @pre Bid has to be bigger than zero, + * @pre Bid's symbol must be system token, + * @pre Bidder account has to be different than current highest bidder, + * @pre Bid must increase current bid by 10%, + * @pre Auction must still be opened. + */ + [[eosio::action]] + void bidname( const name& bidder, const name& newname, const asset& bid ); + + /** + * Bid refund action, allows the account `bidder` to get back the amount it bid so far on a `newname` name. + * + * @param bidder - the account that gets refunded, + * @param newname - the name for which the bid was placed and now it gets refunded for. + */ + [[eosio::action]] + void bidrefund( const name& bidder, const name& newname ); + + /** + * Change the annual inflation rate of the core token supply and specify how + * the new issued tokens will be distributed based on the following structure. + * + * @param annual_rate - Annual inflation rate of the core token supply. + * (eg. For 5% Annual inflation => annual_rate=500 + * For 1.5% Annual inflation => annual_rate=150 + * @param inflation_pay_factor - Inverse of the fraction of the inflation used to reward block producers. + * The remaining inflation will be sent to the `eosio.saving` account. + * (eg. For 20% of inflation going to block producer rewards => inflation_pay_factor = 50000 + * For 100% of inflation going to block producer rewards => inflation_pay_factor = 10000). + * @param votepay_factor - Inverse of the fraction of the block producer rewards to be distributed proportional to blocks produced. + * The remaining rewards will be distributed proportional to votes received. + * (eg. For 25% of block producer rewards going towards block pay => votepay_factor = 40000 + * For 75% of block producer rewards going towards block pay => votepay_factor = 13333). + */ + [[eosio::action]] + void setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor ); + + using init_action = eosio::action_wrapper<"init"_n, &system_contract::init>; + using setacctram_action = eosio::action_wrapper<"setacctram"_n, &system_contract::setacctram>; + using setacctnet_action = eosio::action_wrapper<"setacctnet"_n, &system_contract::setacctnet>; + using setacctcpu_action = eosio::action_wrapper<"setacctcpu"_n, &system_contract::setacctcpu>; + using activate_action = eosio::action_wrapper<"activate"_n, &system_contract::activate>; + using delegatebw_action = eosio::action_wrapper<"delegatebw"_n, &system_contract::delegatebw>; + using deposit_action = eosio::action_wrapper<"deposit"_n, &system_contract::deposit>; + using withdraw_action = eosio::action_wrapper<"withdraw"_n, &system_contract::withdraw>; + using buyrex_action = eosio::action_wrapper<"buyrex"_n, &system_contract::buyrex>; + using unstaketorex_action = eosio::action_wrapper<"unstaketorex"_n, &system_contract::unstaketorex>; + using sellrex_action = eosio::action_wrapper<"sellrex"_n, &system_contract::sellrex>; + using cnclrexorder_action = eosio::action_wrapper<"cnclrexorder"_n, &system_contract::cnclrexorder>; + using rentcpu_action = eosio::action_wrapper<"rentcpu"_n, &system_contract::rentcpu>; + using rentnet_action = eosio::action_wrapper<"rentnet"_n, &system_contract::rentnet>; + using fundcpuloan_action = eosio::action_wrapper<"fundcpuloan"_n, &system_contract::fundcpuloan>; + using fundnetloan_action = eosio::action_wrapper<"fundnetloan"_n, &system_contract::fundnetloan>; + using defcpuloan_action = eosio::action_wrapper<"defcpuloan"_n, &system_contract::defcpuloan>; + using defnetloan_action = eosio::action_wrapper<"defnetloan"_n, &system_contract::defnetloan>; + using updaterex_action = eosio::action_wrapper<"updaterex"_n, &system_contract::updaterex>; + using rexexec_action = eosio::action_wrapper<"rexexec"_n, &system_contract::rexexec>; + using setrex_action = eosio::action_wrapper<"setrex"_n, &system_contract::setrex>; + using mvtosavings_action = eosio::action_wrapper<"mvtosavings"_n, &system_contract::mvtosavings>; + using mvfrsavings_action = eosio::action_wrapper<"mvfrsavings"_n, &system_contract::mvfrsavings>; + using consolidate_action = eosio::action_wrapper<"consolidate"_n, &system_contract::consolidate>; + using closerex_action = eosio::action_wrapper<"closerex"_n, &system_contract::closerex>; + using undelegatebw_action = eosio::action_wrapper<"undelegatebw"_n, &system_contract::undelegatebw>; + using buyram_action = eosio::action_wrapper<"buyram"_n, &system_contract::buyram>; + using buyrambytes_action = eosio::action_wrapper<"buyrambytes"_n, &system_contract::buyrambytes>; + using sellram_action = eosio::action_wrapper<"sellram"_n, &system_contract::sellram>; + using refund_action = eosio::action_wrapper<"refund"_n, &system_contract::refund>; + using regproducer_action = eosio::action_wrapper<"regproducer"_n, &system_contract::regproducer>; + using regproducer2_action = eosio::action_wrapper<"regproducer2"_n, &system_contract::regproducer2>; + using unregprod_action = eosio::action_wrapper<"unregprod"_n, &system_contract::unregprod>; + using setram_action = eosio::action_wrapper<"setram"_n, &system_contract::setram>; + using setramrate_action = eosio::action_wrapper<"setramrate"_n, &system_contract::setramrate>; + using voteproducer_action = eosio::action_wrapper<"voteproducer"_n, &system_contract::voteproducer>; + using regproxy_action = eosio::action_wrapper<"regproxy"_n, &system_contract::regproxy>; + using claimrewards_action = eosio::action_wrapper<"claimrewards"_n, &system_contract::claimrewards>; + using rmvproducer_action = eosio::action_wrapper<"rmvproducer"_n, &system_contract::rmvproducer>; + using updtrevision_action = eosio::action_wrapper<"updtrevision"_n, &system_contract::updtrevision>; + using bidname_action = eosio::action_wrapper<"bidname"_n, &system_contract::bidname>; + using bidrefund_action = eosio::action_wrapper<"bidrefund"_n, &system_contract::bidrefund>; + using setpriv_action = eosio::action_wrapper<"setpriv"_n, &system_contract::setpriv>; + using setalimits_action = eosio::action_wrapper<"setalimits"_n, &system_contract::setalimits>; + using setparams_action = eosio::action_wrapper<"setparams"_n, &system_contract::setparams>; + using setinflation_action = eosio::action_wrapper<"setinflation"_n, &system_contract::setinflation>; + + private: + // Implementation details: + + static symbol get_core_symbol( const rammarket& rm ) { + auto itr = rm.find(ramcore_symbol.raw()); + check(itr != rm.end(), "system contract must first be initialized"); + return itr->quote.balance.symbol; + } + + //defined in eosio.system.cpp + static eosio_global_state get_default_parameters(); + static eosio_global_state4 get_default_inflation_parameters(); + symbol core_symbol()const; + void update_ram_supply(); + + // defined in rex.cpp + void runrex( uint16_t max ); + void update_rex_pool(); + void update_resource_limits( const name& from, const name& receiver, int64_t delta_net, int64_t delta_cpu ); + void check_voting_requirement( const name& owner, + const char* error_msg = "must vote for at least 21 producers or for a proxy before buying REX" )const; + rex_order_outcome fill_rex_order( const rex_balance_table::const_iterator& bitr, const asset& rex ); + asset update_rex_account( const name& owner, const asset& proceeds, const asset& unstake_quant, bool force_vote_update = false ); + void channel_to_rex( const name& from, const asset& amount ); + void channel_namebid_to_rex( const int64_t highest_bid ); + template + int64_t rent_rex( T& table, const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ); + template + void fund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& payment ); + template + void defund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& amount ); + void transfer_from_fund( const name& owner, const asset& amount ); + void transfer_to_fund( const name& owner, const asset& amount ); + bool rex_loans_available()const; + bool rex_system_initialized()const { return _rexpool.begin() != _rexpool.end(); } + bool rex_available()const { return rex_system_initialized() && _rexpool.begin()->total_rex.amount > 0; } + static time_point_sec get_rex_maturity(); + asset add_to_rex_balance( const name& owner, const asset& payment, const asset& rex_received ); + asset add_to_rex_pool( const asset& payment ); + void add_to_rex_return_pool( const asset& fee ); + void process_rex_maturities( const rex_balance_table::const_iterator& bitr ); + void consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, + const asset& rex_in_sell_order ); + int64_t read_rex_savings( const rex_balance_table::const_iterator& bitr ); + void put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex ); + void update_rex_stake( const name& voter ); + + void add_loan_to_rex_pool( const asset& payment, int64_t rented_tokens, bool new_loan ); + void remove_loan_from_rex_pool( const rex_loan& loan ); + template + int64_t update_renewed_loan( Index& idx, const Iterator& itr, int64_t rented_tokens ); + + // defined in delegate_bandwidth.cpp + void changebw( name from, const name& receiver, + const asset& stake_net_quantity, const asset& stake_cpu_quantity, bool transfer ); + void update_voting_power( const name& voter, const asset& total_update ); + + // defined in voting.cpp + void register_producer( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ); + void update_elected_producers( const block_timestamp& timestamp ); + void update_votes( const name& voter, const name& proxy, const std::vector& producers, bool voting ); + void propagate_weight_change( const voter_info& voter ); + double update_producer_votepay_share( const producers_table2::const_iterator& prod_itr, + const time_point& ct, + double shares_rate, bool reset_to_zero = false ); + double update_total_votepay_share( const time_point& ct, + double additional_shares_delta = 0.0, double shares_rate_delta = 0.0 ); + + template + class registration { + public: + template + struct for_each { + template + static constexpr void call( system_contract* this_contract, Args&&... args ) + { + std::invoke( P, this_contract, args... ); + for_each::call( this_contract, std::forward(args)... ); + } + }; + template + struct for_each

{ + template + static constexpr void call( system_contract* this_contract, Args&&... args ) + { + std::invoke( P, this_contract, std::forward(args)... ); + } + }; + + template + constexpr void operator() ( Args&&... args ) + { + for_each::call( this_contract, std::forward(args)... ); + } + + system_contract* this_contract; + }; + + registration<&system_contract::update_rex_stake> vote_stake_updater{ this }; + }; + +} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp new file mode 100644 index 0000000000..c339b97060 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +namespace eosiosystem { + + using eosio::asset; + using eosio::symbol; + + /** + * Uses Bancor math to create a 50/50 relay between two asset types. + * + * The state of the bancor exchange is entirely contained within this struct. + * There are no external side effects associated with using this API. + */ + struct [[eosio::table, eosio::contract("eosio.system")]] exchange_state { + asset supply; + + struct connector { + asset balance; + double weight = .5; + + EOSLIB_SERIALIZE( connector, (balance)(weight) ) + }; + + connector base; + connector quote; + + uint64_t primary_key()const { return supply.symbol.raw(); } + + asset convert_to_exchange( connector& reserve, const asset& payment ); + asset convert_from_exchange( connector& reserve, const asset& tokens ); + asset convert( const asset& from, const symbol& to ); + asset direct_convert( const asset& from, const symbol& to ); + + static int64_t get_bancor_output( int64_t inp_reserve, + int64_t out_reserve, + int64_t inp ); + static int64_t get_bancor_input( int64_t out_reserve, + int64_t inp_reserve, + int64_t out ); + + EOSLIB_SERIALIZE( exchange_state, (supply)(base)(quote) ) + }; + + typedef eosio::multi_index< "rammarket"_n, exchange_state > rammarket; +} /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp new file mode 100644 index 0000000000..a0931cde26 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp @@ -0,0 +1,260 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace eosiosystem { + + using eosio::checksum256; + using eosio::ignore; + using eosio::name; + using eosio::permission_level; + using eosio::public_key; + + /** + * A weighted permission. + * + * Defines a weighted permission, that is a permission which has a weight associated. + * A permission is defined by an account name plus a permission name. + */ + struct permission_level_weight { + permission_level permission; + uint16_t weight; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( permission_level_weight, (permission)(weight) ) + }; + + /** + * Weighted key. + * + * A weighted key is defined by a public key and an associated weight. + */ + struct key_weight { + eosio::public_key key; + uint16_t weight; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( key_weight, (key)(weight) ) + }; + + /** + * Wait weight. + * + * A wait weight is defined by a number of seconds to wait for and a weight. + */ + struct wait_weight { + uint32_t wait_sec; + uint16_t weight; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( wait_weight, (wait_sec)(weight) ) + }; + + /** + * Blockchain authority. + * + * An authority is defined by: + * - a vector of key_weights (a key_weight is a public key plus a wieght), + * - a vector of permission_level_weights, (a permission_level is an account name plus a permission name) + * - a vector of wait_weights (a wait_weight is defined by a number of seconds to wait and a weight) + * - a threshold value + */ + struct authority { + uint32_t threshold = 0; + std::vector keys; + std::vector accounts; + std::vector waits; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE( authority, (threshold)(keys)(accounts)(waits) ) + }; + + /** + * Blockchain block header. + * + * A block header is defined by: + * - a timestamp, + * - the producer that created it, + * - a confirmed flag default as zero, + * - a link to previous block, + * - a link to the transaction merkel root, + * - a link to action root, + * - a schedule version, + * - and a producers' schedule. + */ + struct block_header { + uint32_t timestamp; + name producer; + uint16_t confirmed = 0; + checksum256 previous; + checksum256 transaction_mroot; + checksum256 action_mroot; + uint32_t schedule_version = 0; + std::optional new_producers; + + // explicit serialization macro is not necessary, used here only to improve compilation time + EOSLIB_SERIALIZE(block_header, (timestamp)(producer)(confirmed)(previous)(transaction_mroot)(action_mroot) + (schedule_version)(new_producers)) + }; + + /** + * abi_hash is the structure underlying the abihash table and consists of: + * - `owner`: the account owner of the contract's abi + * - `hash`: is the sha256 hash of the abi/binary + */ + struct [[eosio::table("abihash"), eosio::contract("eosio.system")]] abi_hash { + name owner; + checksum256 hash; + uint64_t primary_key()const { return owner.value; } + + EOSLIB_SERIALIZE( abi_hash, (owner)(hash) ) + }; + + // Method parameters commented out to prevent generation of code that parses input data. + /** + * The EOSIO core `native` contract that governs authorization and contracts' abi. + */ + class [[eosio::contract("eosio.system")]] native : public eosio::contract { + public: + + using eosio::contract::contract; + + /** + * These actions map one-on-one with the ones defined in core layer of EOSIO, that's where their implementation + * actually is done. + * They are present here only so they can show up in the abi file and thus user can send them + * to this contract, but they have no specific implementation at this contract level, + * they will execute the implementation at the core layer and nothing else. + */ + /** + * New account action is called after a new account is created. This code enforces resource-limits rules + * for new accounts as well as new 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 newaccount( const name& creator, + const name& name, + ignore owner, + ignore active); + + /** + * Update authorization action updates pemission for an account. + * + * @param account - the account for which the permission is updated + * @param pemission - the permission name which is updated + * @param parem - the parent of the permission which is updated + * @param aut - the json describing the permission authorization + */ + [[eosio::action]] + void updateauth( ignore account, + ignore permission, + ignore parent, + ignore auth ) {} + + /** + * Delete authorization action deletes the authorization for an account's permission. + * + * @param account - the account for which the permission authorization is deleted, + * @param permission - the permission name been deleted. + */ + [[eosio::action]] + void deleteauth( ignore account, + ignore permission ) {} + + /** + * Link authorization action assigns a specific action from a contract to a permission you have created. Five system + * actions can not be linked `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, and `canceldelay`. + * This is useful because when doing authorization checks, the EOSIO based blockchain starts with the + * action needed to be authorized (and the contract belonging to), and looks up which permission + * is needed to pass authorization validation. If a link is set, that permission is used for authoraization + * validation otherwise then active is the default, with the exception of `eosio.any`. + * `eosio.any` is an implicit permission which exists on every account; you can link actions to `eosio.any` + * and that will make it so linked actions are accessible to any permissions defined for the account. + * + * @param account - the permission's owner to be linked and the payer of the RAM needed to store this link, + * @param code - the owner of the action to be linked, + * @param type - the action to be linked, + * @param requirement - the permission to be linked. + */ + [[eosio::action]] + void linkauth( ignore account, + ignore code, + ignore type, + ignore requirement ) {} + + /** + * Unlink authorization action it's doing the reverse of linkauth action, by unlinking the given action. + * + * @param account - the owner of the permission to be unlinked and the receiver of the freed RAM, + * @param code - the owner of the action to be unlinked, + * @param type - the action to be unlinked. + */ + [[eosio::action]] + void unlinkauth( ignore account, + ignore code, + ignore type ) {} + + /** + * Cancel delay action cancels a deferred transaction. + * + * @param canceling_auth - the permission that authorizes this action, + * @param trx_id - the deferred transaction id to be cancelled. + */ + [[eosio::action]] + void canceldelay( ignore canceling_auth, ignore trx_id ) {} + + /** + * On error action, notification of this action is delivered to the sender of a deferred transaction + * when an objective error occurs while executing the deferred transaction. + * This action is not meant to be called directly. + * + * @param sender_id - the id for the deferred transaction chosen by the sender, + * @param sent_trx - the deferred transaction that failed. + */ + [[eosio::action]] + void onerror( ignore sender_id, ignore> sent_trx ); + + /** + * Set abi action sets the contract abi for an account. + * + * @param account - the account for which to set the contract abi. + * @param abi - the abi content to be set, in the form of a blob binary. + */ + [[eosio::action]] + void setabi( const name& account, const std::vector& abi ); + + /** + * Set code action sets the contract code for an account. + * + * @param account - the account for which to set the contract code. + * @param vmtype - reserved, set it to zero. + * @param vmversion - reserved, set it to zero. + * @param code - the code content to be set, in the form of a blob binary.. + */ + [[eosio::action]] + void setcode( const name& account, uint8_t vmtype, uint8_t vmversion, const std::vector& code ) {} + + using newaccount_action = eosio::action_wrapper<"newaccount"_n, &native::newaccount>; + using updateauth_action = eosio::action_wrapper<"updateauth"_n, &native::updateauth>; + using deleteauth_action = eosio::action_wrapper<"deleteauth"_n, &native::deleteauth>; + using linkauth_action = eosio::action_wrapper<"linkauth"_n, &native::linkauth>; + using unlinkauth_action = eosio::action_wrapper<"unlinkauth"_n, &native::unlinkauth>; + using canceldelay_action = eosio::action_wrapper<"canceldelay"_n, &native::canceldelay>; + using setcode_action = eosio::action_wrapper<"setcode"_n, &native::setcode>; + using setabi_action = eosio::action_wrapper<"setabi"_n, &native::setabi>; + }; +} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp new file mode 100644 index 0000000000..378d6add31 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +using eosio::action_wrapper; +using eosio::asset; +using eosio::name; + +/** + * The actions `buyresult`, `sellresult`, `rentresult`, and `orderresult` of `rex.results` are all no-ops. + * They are added as inline convenience actions to `rentnet`, `rentcpu`, `buyrex`, `unstaketorex`, and `sellrex`. + * An inline convenience action does not have any effect, however, + * its data includes the result of the parent action and appears in its trace. + */ +class [[eosio::contract("rex.results")]] rex_results : eosio::contract { + public: + + using eosio::contract::contract; + + /** + * Buyresult action. + * + * @param rex_received - amount of tokens used in buy order + */ + [[eosio::action]] + void buyresult( const asset& rex_received ); + + /** + * Sellresult action. + * + * @param proceeds - amount of tokens used in sell order + */ + [[eosio::action]] + void sellresult( const asset& proceeds ); + + /** + * Orderresult action. + * + * @param owner - the owner of the order + * @param proceeds - amount of tokens used in order + */ + [[eosio::action]] + void orderresult( const name& owner, const asset& proceeds ); + + /** + * Rentresult action. + * + * @param rented_tokens - amount of rented tokens + */ + [[eosio::action]] + void rentresult( const asset& rented_tokens ); + + using buyresult_action = action_wrapper<"buyresult"_n, &rex_results::buyresult>; + using sellresult_action = action_wrapper<"sellresult"_n, &rex_results::sellresult>; + using orderresult_action = action_wrapper<"orderresult"_n, &rex_results::orderresult>; + using rentresult_action = action_wrapper<"rentresult"_n, &rex_results::rentresult>; +}; diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md b/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md new file mode 100644 index 0000000000..ef5bfc9877 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md @@ -0,0 +1,63 @@ +

UserAgreement

+ +User agreement for the chain can go here. + +

BlockProducerAgreement

+ +I, {{producer}}, hereby nominate myself for consideration as an elected block producer. + +If {{producer}} is selected to produce blocks by the system contract, I will sign blocks with my registered block signing keys and I hereby attest that I will keep these keys secret and secure. + +If {{producer}} is unable to perform obligations under this contract I will resign my position using the unregprod action. + +I acknowledge that a block is 'objectively valid' if it conforms to the deterministic blockchain rules in force at the time of its creation, and is 'objectively invalid' if it fails to conform to those rules. + +{{producer}} hereby agrees to only use my registered block signing keys to sign messages under the following scenarios: + +* proposing an objectively valid block at the time appointed by the block scheduling algorithm; +* pre-confirming a block produced by another producer in the schedule when I find said block objectively valid; +* and, confirming a block for which {{producer}} has received pre-confirmation messages from more than two-thirds of the active block producers. + +I hereby accept liability for any and all provable damages that result from my: + +* signing two different block proposals with the same timestamp; +* signing two different block proposals with the same block number; +* signing any block proposal which builds off of an objectively invalid block; +* signing a pre-confirmation for an objectively invalid block; +* or, signing a confirmation for a block for which I do not possess pre-confirmation messages from more than two-thirds of the active block producers. + +I hereby agree that double-signing for a timestamp or block number in concert with two or more other block producers shall automatically be deemed malicious and cause {{producer}} to be subject to: + +* a fine equal to the past year of compensation received, +* immediate disqualification from being a producer, +* and/or other damages. + +An exception may be made if {{producer}} can demonstrate that the double-signing occurred due to a bug in the reference software; the burden of proof is on {{producer}}. + +I hereby agree not to interfere with the producer election process. I agree to process all producer election transactions that occur in blocks I create, to sign all objectively valid blocks I create that contain election transactions, and to sign all pre-confirmations and confirmations necessary to facilitate transfer of control to the next set of producers as determined by the system contract. + +I hereby acknowledge that more than two-thirds of the active block producers may vote to disqualify {{producer}} in the event {{producer}} is unable to produce blocks or is unable to be reached, according to criteria agreed to among block producers. + +If {{producer}} qualifies for and chooses to collect compensation due to votes received, {{producer}} will provide a public endpoint allowing at least 100 peers to maintain synchronization with the blockchain and/or submit transactions to be included. {{producer}} shall maintain at least one validating node with full state and signature checking and shall report any objectively invalid blocks produced by the active block producers. Reporting shall be via a method to be agreed to among block producers, said method and reports to be made public. + +The community agrees to allow {{producer}} to authenticate peers as necessary to prevent abuse and denial of service attacks; however, {{producer}} agrees not to discriminate against non-abusive peers. + +I agree to process transactions on a FIFO (first in, first out) best-effort basis and to honestly bill transactions for measured execution time. + +I {{producer}} agree not to manipulate the contents of blocks in order to derive profit from: the order in which transactions are included, or the hash of the block that is produced. + +I, {{producer}}, hereby agree to disclose and attest under penalty of perjury all ultimate beneficial owners of my business entity who own more than 10% and all direct shareholders. + +I, {{producer}}, hereby agree to cooperate with other block producers to carry out our respective and mutual obligations under this agreement, including but not limited to maintaining network stability and a valid blockchain. + +I, {{producer}}, agree to maintain a website hosted at {{url}} which contains up-to-date information on all disclosures required by this contract. + +I, {{producer}}, agree to set the location value of {{location}} such that {{producer}} is scheduled with minimal latency between my previous and next peer. + +I, {{producer}}, agree to maintain time synchronization within 10 ms of global atomic clock time, using a method agreed to among block producers. + +I, {{producer}}, agree not to produce blocks before my scheduled time unless I have received all blocks produced by the prior block producer. + +I, {{producer}}, agree not to publish blocks with timestamps more than 500ms in the future unless the prior block is more than 75% full by either NET or CPU bandwidth metrics. + +I, {{producer}}, agree not to set the RAM supply to more RAM than my nodes contain and to resign if I am unable to provide the RAM approved by more than two-thirds of active block producers, as shown in the system parameters. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in new file mode 100644 index 0000000000..9ab33cd888 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in @@ -0,0 +1,703 @@ +

activate

+ +--- +spec_version: "0.2.0" +title: Activate Protocol Feature +summary: 'Activate protocol feature {{nowrap feature_digest}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} activates the protocol feature with a digest of {{feature_digest}}. + +

bidname

+ +--- +spec_version: "0.2.0" +title: Bid On a Premium Account Name +summary: '{{nowrap bidder}} bids on the premium account name {{nowrap newname}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{bidder}} bids {{bid}} on an auction to own the premium account name {{newname}}. + +{{bidder}} transfers {{bid}} to the system to cover the cost of the bid, which will be returned to {{bidder}} only if {{bidder}} is later outbid in the auction for {{newname}} by another account. + +If the auction for {{newname}} closes with {{bidder}} remaining as the highest bidder, {{bidder}} will be authorized to create the account with name {{newname}}. + +## Bid refund behavior + +If {{bidder}}’s bid on {{newname}} is later outbid by another account, {{bidder}} will be able to claim back the transferred amount of {{bid}}. The system will attempt to automatically do this on behalf of {{bidder}}, but the automatic refund may occasionally fail which will then require {{bidder}} to manually claim the refund with the bidrefund action. + +## Auction close criteria + +The system should automatically close the auction for {{newname}} if it satisfies the condition that over a period of two minutes the following two properties continuously hold: + +- no one has bid on {{newname}} within the last 24 hours; +- and, the value of the latest bid on {{newname}} is greater than the value of the bids on each of the other open auctions. + +Be aware that the condition to close the auction described above are sufficient but not necessary. The auction for {{newname}} cannot close unless both of the properties are simultaneously satisfied, but it may be closed without requiring the properties to hold for a period of 2 minutes. + +

bidrefund

+ +--- +spec_version: "0.2.0" +title: Claim Refund on Name Bid +summary: 'Claim refund on {{nowrap newname}} bid' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{bidder}} claims refund on {{newname}} bid after being outbid by someone else. + +

buyram

+ +--- +spec_version: "0.2.0" +title: Buy RAM +summary: '{{nowrap payer}} buys RAM on behalf of {{nowrap receiver}} by paying {{nowrap quant}}' +icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ +--- + +{{payer}} buys RAM on behalf of {{receiver}} by paying {{quant}}. This transaction will incur a 0.5% fee out of {{quant}} and the amount of RAM delivered will depend on market rates. + +

buyrambytes

+ +--- +spec_version: "0.2.0" +title: Buy RAM +summary: '{{nowrap payer}} buys {{nowrap bytes}} bytes of RAM on behalf of {{nowrap receiver}}' +icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ +--- + +{{payer}} buys approximately {{bytes}} bytes of RAM on behalf of {{receiver}} by paying market rates for RAM. This transaction will incur a 0.5% fee and the cost will depend on market rates. + +

buyrex

+ +--- +spec_version: "0.2.0" +title: Buy REX Tokens +summary: '{{nowrap from}} buys REX tokens in exchange for {{nowrap amount}} and their vote stake increases by {{nowrap amount}}' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{amount}} is taken out of {{from}}’s REX fund and used to purchase REX tokens at the current market exchange rate. In order for the action to succeed, {{from}} must have voted for a proxy or at least 21 block producers. {{amount}} is added to {{from}}’s vote stake. + +A sell order of the purchased amount can only be initiated after waiting for the maturity period of 4 to 5 days to pass. Even then, depending on the market conditions, the initiated sell order may not be executed immediately. + +

canceldelay

+ +--- +spec_version: "0.2.0" +title: Cancel Delayed Transaction +summary: '{{nowrap canceling_auth.actor}} cancels a delayed transaction' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{canceling_auth.actor}} cancels the delayed transaction with id {{trx_id}}. + +

claimrewards

+ +--- +spec_version: "0.2.0" +title: Claim Block Producer Rewards +summary: '{{nowrap owner}} claims block and vote rewards' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{owner}} claims block and vote rewards from the system. + +

closerex

+ +--- +spec_version: "0.2.0" +title: Cleanup Unused REX Data +summary: 'Delete REX related DB entries and free associated RAM' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +Delete REX related DB entries and free associated RAM for {{owner}}. + +To fully delete all REX related DB entries, {{owner}} must ensure that their REX balance and REX fund amounts are both zero and they have no outstanding loans. + +

cnclrexorder

+ +--- +spec_version: "0.2.0" +title: Cancel Scheduled REX Sell Order +summary: '{{nowrap owner}} cancels a scheduled sell order if not yet filled' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{owner}} cancels their open sell order. + +

consolidate

+ +--- +spec_version: "0.2.0" +title: Consolidate REX Maturity Buckets Into One +summary: 'Consolidate REX maturity buckets into one' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +Consolidate REX maturity buckets into one bucket that {{owner}} will not be able to sell until 4 to 5 days later. + +

defcpuloan

+ +--- +spec_version: "0.2.0" +title: Withdraw from the Fund of a Specific CPU Loan +summary: '{{nowrap from}} transfers {{nowrap amount}} from the fund of CPU loan number {{nowrap loan_num}} back to REX fund' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from}} transfers {{amount}} from the fund of CPU loan number {{loan_num}} back to REX fund. + +

defnetloan

+ +--- +spec_version: "0.2.0" +title: Withdraw from the Fund of a Specific NET Loan +summary: '{{nowrap from}} transfers {{nowrap amount}} from the fund of NET loan number {{nowrap loan_num}} back to REX fund' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from}} transfers {{amount}} from the fund of NET loan number {{loan_num}} back to REX fund. + +

delegatebw

+ +--- +spec_version: "0.2.0" +title: Stake Tokens for NET and/or CPU +summary: 'Stake tokens for NET and/or CPU and optionally transfer ownership' +icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ +--- + +{{#if transfer}} {{from}} stakes on behalf of {{receiver}} {{stake_net_quantity}} for NET bandwidth and {{stake_cpu_quantity}} for CPU bandwidth. + +Staked tokens will also be transferred to {{receiver}}. The sum of these two quantities will be deducted from {{from}}’s liquid balance and add to the vote weight of {{receiver}}. +{{else}} +{{from}} stakes to self and delegates to {{receiver}} {{stake_net_quantity}} for NET bandwidth and {{stake_cpu_quantity}} for CPU bandwidth. + +The sum of these two quantities add to the vote weight of {{from}}. +{{/if}} + +

deleteauth

+ +--- +spec_version: "0.2.0" +title: Delete Account Permission +summary: 'Delete the {{nowrap permission}} permission of {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Delete the {{permission}} permission of {{account}}. + +

deposit

+ +--- +spec_version: "0.2.0" +title: Deposit Into REX Fund +summary: 'Add to {{nowrap owner}}’s REX fund by transferring {{nowrap amount}} from {{nowrap owner}}’s liquid balance' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +Transfer {{amount}} from {{owner}}’s liquid balance to {{owner}}’s REX fund. All proceeds and expenses related to REX are added to or taken out of this fund. + +

fundcpuloan

+ +--- +spec_version: "0.2.0" +title: Deposit into the Fund of a Specific CPU Loan +summary: '{{nowrap from}} funds a CPU loan' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from}} transfers {{payment}} from REX fund to the fund of CPU loan number {{loan_num}} in order to be used in loan renewal at expiry. {{from}} can withdraw the total balance of the loan fund at any time. + +

fundnetloan

+ +--- +spec_version: "0.2.0" +title: Deposit into the Fund of a Specific NET Loan +summary: '{{nowrap from}} funds a NET loan' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from}} transfers {{payment}} from REX fund to the fund of NET loan number {{loan_num}} in order to be used in loan renewal at expiry. {{from}} can withdraw the total balance of the loan fund at any time. + +

init

+ +--- +spec_version: "0.2.0" +title: Initialize System Contract +summary: 'Initialize system contract' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +Initialize system contract. The core token symbol will be set to {{core}}. + +

linkauth

+ +--- +spec_version: "0.2.0" +title: Link Action to Permission +summary: '{{nowrap account}} sets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract to {{nowrap requirement}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{account}} sets the minimum required permission for the {{#if type}}{{type}} action of the{{/if}} {{code}} contract to {{requirement}}. + +{{#if type}}{{else}}Any links explicitly associated to specific actions of {{code}} will take precedence.{{/if}} + +

newaccount

+ +--- +spec_version: "0.2.0" +title: Create New Account +summary: '{{nowrap creator}} creates a new account with the name {{nowrap name}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{creator}} creates a new account with the name {{name}} and the following permissions: + +owner permission with authority: +{{to_json owner}} + +active permission with authority: +{{to_json active}} + +

mvfrsavings

+ +--- +spec_version: "0.2.0" +title: Unlock REX Tokens +summary: '{{nowrap owner}} unlocks REX Tokens' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{owner}} unlocks {{rex}} by moving it out of the REX savings bucket. The unlocked REX tokens cannot be sold until 4 to 5 days later. + +

mvtosavings

+ +--- +spec_version: "0.2.0" +title: Lock REX Tokens +summary: '{{nowrap owner}} locks REX Tokens' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{owner}} locks {{rex}} by moving it into the REX savings bucket. The locked REX tokens cannot be sold directly and will have to be unlocked explicitly before selling. + +

refund

+ +--- +spec_version: "0.2.0" +title: Claim Unstaked Tokens +summary: 'Return previously unstaked tokens to {{nowrap owner}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Return previously unstaked tokens to {{owner}} after the unstaking period has elapsed. + +

regproducer

+ +--- +spec_version: "0.2.0" +title: Register as a Block Producer Candidate +summary: 'Register {{nowrap producer}} account as a block producer candidate' +icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ +--- + +Register {{producer}} account as a block producer candidate. + +URL: {{url}} +Location code: {{location}} +Block signing key: {{producer_key}} + +## Block Producer Agreement +{{$clauses.BlockProducerAgreement}} + +

regproducer2

+ +--- +spec_version: "0.2.0" +title: Register as a Block Producer Candidate +summary: 'Register {{nowrap producer}} account as a block producer candidate' +icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ +--- + +Register {{producer}} account as a block producer candidate. + +URL: {{url}} +Location code: {{location}} +Block signing authority: +{{to_json producer_authority}} + +## Block Producer Agreement +{{$clauses.BlockProducerAgreement}} + +

regproxy

+ +--- +spec_version: "0.2.0" +title: Register/unregister as a Proxy +summary: 'Register/unregister {{nowrap proxy}} as a proxy account' +icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ +--- + +{{#if isproxy}} +{{proxy}} registers as a proxy that can vote on behalf of accounts that appoint it as their proxy. +{{else}} +{{proxy}} unregisters as a proxy that can vote on behalf of accounts that appoint it as their proxy. +{{/if}} + +

rentcpu

+ +--- +spec_version: "0.2.0" +title: Rent CPU Bandwidth for 30 Days +summary: '{{nowrap from}} pays {{nowrap loan_payment}} to rent CPU bandwidth for {{nowrap receiver}}' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from}} pays {{loan_payment}} to rent CPU bandwidth on behalf of {{receiver}} for a period of 30 days. + +{{loan_payment}} is taken out of {{from}}’s REX fund. The market price determines the number of tokens to be staked to {{receiver}}’s CPU resources. In addition, {{from}} provides {{loan_fund}}, which is also taken out of {{from}}’s REX fund, to be used for automatic renewal of the loan. + +At expiration, if the loan has less funds than {{loan_payment}}, it is closed and lent tokens that have been staked are taken out of {{receiver}}’s CPU bandwidth. Otherwise, it is renewed at the market price at the time of renewal, that is, the number of staked tokens is recalculated and {{receiver}}’s CPU bandwidth is updated accordingly. {{from}} can fund or defund a loan at any time before expiration. When the loan is closed, {{from}} is refunded any tokens remaining in the loan fund. + +

rentnet

+ +--- +spec_version: "0.2.0" +title: Rent NET Bandwidth for 30 Days +summary: '{{nowrap from}} pays {{nowrap loan_payment}} to rent NET bandwidth for {{nowrap receiver}}' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from}} pays {{loan_payment}} to rent NET bandwidth on behalf of {{receiver}} for a period of 30 days. + +{{loan_payment}} is taken out of {{from}}’s REX fund. The market price determines the number of tokens to be staked to {{receiver}}’s NET resources for 30 days. In addition, {{from}} provides {{loan_fund}}, which is also taken out of {{from}}’s REX fund, to be used for automatic renewal of the loan. + +At expiration, if the loan has less funds than {{loan_payment}}, it is closed and lent tokens that have been staked are taken out of {{receiver}}’s NET bandwidth. Otherwise, it is renewed at the market price at the time of renewal, that is, the number of staked tokens is recalculated and {{receiver}}’s NET bandwidth is updated accordingly. {{from}} can fund or defund a loan at any time before expiration. When the loan is closed, {{from}} is refunded any tokens remaining in the loan fund. + +

rexexec

+ +--- +spec_version: "0.2.0" +title: Perform REX Maintenance +summary: 'Process sell orders and expired loans' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +Performs REX maintenance by processing a maximum of {{max}} REX sell orders and expired loans. Any account can execute this action. + +

rmvproducer

+ +--- +spec_version: "0.2.0" +title: Forcibly Unregister a Block Producer Candidate +summary: '{{nowrap producer}} is unregistered as a block producer candidate' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} unregisters {{producer}} as a block producer candidate. {{producer}} account will retain its votes and those votes can change based on voter stake changes or votes removed from {{producer}}. However new voters will not be able to vote for {{producer}} while it remains unregistered. + +

sellram

+ +--- +spec_version: "0.2.0" +title: Sell RAM From Account +summary: 'Sell unused RAM from {{nowrap account}}' +icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ +--- + +Sell {{bytes}} bytes of unused RAM from account {{account}} at market price. This transaction will incur a 0.5% fee on the proceeds which depend on market rates. + +

sellrex

+ +--- +spec_version: "0.2.0" +title: Sell REX Tokens in Exchange for EOS +summary: '{{nowrap from}} sells {{nowrap rex}} tokens' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from}} initiates a sell order to sell {{rex}} tokens at the market exchange rate during the time at which the order is ultimately executed. If {{from}} already has an open sell order in the sell queue, {{rex}} will be added to the amount of the sell order without change the position of the sell order within the queue. Once the sell order is executed, proceeds are added to {{from}}’s REX fund, the value of sold REX tokens is deducted from {{from}}’s vote stake, and votes are updated accordingly. + +Depending on the market conditions, it may not be possible to fill the entire sell order immediately. In such a case, the sell order is added to the back of a sell queue. A sell order at the front of the sell queue will automatically be executed when the market conditions allow for the entire order to be filled. Regardless of the market conditions, the system is designed to execute this sell order within 30 days. {{from}} can cancel the order at any time before it is filled using the cnclrexorder action. + +

setabi

+ +--- +spec_version: "0.2.0" +title: Deploy Contract ABI +summary: 'Deploy contract ABI on account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Deploy the ABI file associated with the contract on account {{account}}. + +

setacctcpu

+ +--- +spec_version: "0.2.0" +title: Explicitly Manage the CPU Quota of Account +summary: 'Explicitly manage the CPU bandwidth quota of account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{#if_has_value cpu_weight}} +Explicitly manage the CPU bandwidth quota of account {{account}} by pinning it to a weight of {{cpu_weight}}. + +{{account}} can stake and unstake, however, it will not change their CPU bandwidth quota as long as it remains pinned. +{{else}} +Unpin the CPU bandwidth quota of account {{account}}. The CPU bandwidth quota of {{account}} will be driven by the current tokens staked for CPU bandwidth by {{account}}. +{{/if_has_value}} + +

setacctnet

+ +--- +spec_version: "0.2.0" +title: Explicitly Manage the NET Quota of Account +summary: 'Explicitly manage the NET bandwidth quota of account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{#if_has_value net_weight}} +Explicitly manage the network bandwidth quota of account {{account}} by pinning it to a weight of {{net_weight}}. + +{{account}} can stake and unstake, however, it will not change their NET bandwidth quota as long as it remains pinned. +{{else}} +Unpin the NET bandwidth quota of account {{account}}. The NET bandwidth quota of {{account}} will be driven by the current tokens staked for NET bandwidth by {{account}}. +{{/if_has_value}} + +

setacctram

+ +--- +spec_version: "0.2.0" +title: Explicitly Manage the RAM Quota of Account +summary: 'Explicitly manage the RAM quota of account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{#if_has_value ram_bytes}} +Explicitly manage the RAM quota of account {{account}} by pinning it to {{ram_bytes}} bytes. + +{{account}} can buy and sell RAM, however, it will not change their RAM quota as long as it remains pinned. +{{else}} +Unpin the RAM quota of account {{account}}. The RAM quota of {{account}} will be driven by the current RAM holdings of {{account}}. +{{/if_has_value}} + +

setalimits

+ +--- +spec_version: "0.2.0" +title: Adjust Resource Limits of Account +summary: 'Adjust resource limits of account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} updates {{account}}’s resource limits to have a RAM quota of {{ram_bytes}} bytes, a NET bandwidth quota of {{net_weight}} and a CPU bandwidth quota of {{cpu_weight}}. + +

setcode

+ +--- +spec_version: "0.2.0" +title: Deploy Contract Code +summary: 'Deploy contract code on account {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Deploy compiled contract code to the account {{account}}. + +

setparams

+ +--- +spec_version: "0.2.0" +title: Set System Parameters +summary: 'Set System Parameters' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} sets system parameters to: +{{to_json params}} + +

setpriv

+ +--- +spec_version: "0.2.0" +title: Make an Account Privileged or Unprivileged +summary: '{{#if is_priv}}Make {{nowrap account}} privileged{{else}}Remove privileged status of {{nowrap account}}{{/if}}' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{#if is_priv}} +{{$action.account}} makes {{account}} privileged. +{{else}} +{{$action.account}} removes privileged status of {{account}}. +{{/if}} + +

setram

+ +--- +spec_version: "0.2.0" +title: Configure the Available RAM +summary: 'Configure the available RAM' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} configures the available RAM to {{max_ram_size}} bytes. + +

setramrate

+ +--- +spec_version: "0.2.0" +title: Set the Rate of Increase of RAM +summary: 'Set the rate of increase of RAM per block' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} sets the rate of increase of RAM to {{bytes_per_block}} bytes/block. + +

setrex

+ +--- +spec_version: "0.2.0" +title: Adjust REX Pool Virtual Balance +summary: 'Adjust REX Pool Virtual Balance' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} adjusts REX loan rate by setting REX pool virtual balance to {{balance}}. No token transfer or issue is executed in this action. + +

setinflation

+ +--- +spec_version: "0.2.0" +title: Set Inflation Parameters +summary: 'Set inflation parameters' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} sets the inflation parameters as follows: + +* Annual inflation rate (in units of a hundredth of a percent): {{annual_rate}} +* Fraction of inflation used to reward block producers: 10000/{{inflation_pay_factor}} +* Fraction of block producer rewards to be distributed proportional to blocks produced: 10000/{{votepay_factor}} + +

undelegatebw

+ +--- +spec_version: "0.2.0" +title: Unstake Tokens for NET and/or CPU +summary: 'Unstake tokens for NET and/or CPU from {{nowrap receiver}}' +icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ +--- + +{{from}} unstakes from {{receiver}} {{unstake_net_quantity}} for NET bandwidth and {{unstake_cpu_quantity}} for CPU bandwidth. + +The sum of these two quantities will be removed from the vote weight of {{receiver}} and will be made available to {{from}} after an uninterrupted 3 day period without further unstaking by {{from}}. After the uninterrupted 3 day period passes, the system will attempt to automatically return the funds to {{from}}’s regular token balance. However, this automatic refund may occasionally fail which will then require {{from}} to manually claim the funds with the refund action. + +

unlinkauth

+ +--- +spec_version: "0.2.0" +title: Unlink Action from Permission +summary: '{{nowrap account}} unsets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +{{account}} removes the association between the {{#if type}}{{type}} action of the{{/if}} {{code}} contract and its minimum required permission. + +{{#if type}}{{else}}This will not remove any links explicitly associated to specific actions of {{code}}.{{/if}} + +

unregprod

+ +--- +spec_version: "0.2.0" +title: Unregister as a Block Producer Candidate +summary: '{{nowrap producer}} unregisters as a block producer candidate' +icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ +--- + +{{producer}} unregisters as a block producer candidate. {{producer}} account will retain its votes and those votes can change based on voter stake changes or votes removed from {{producer}}. However new voters will not be able to vote for {{producer}} while it remains unregistered. + +

unstaketorex

+ +--- +spec_version: "0.2.0" +title: Buy REX Tokens Using Staked Tokens +summary: '{{nowrap owner}} buys REX tokens in exchange for tokens currently staked to NET and/or CPU' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +{{from_net}} and {{from_cpu}} are withdrawn from {{receiver}}’s NET and CPU bandwidths respectively. These funds are used to purchase REX tokens at the current market exchange rate. In order for the action to succeed, {{owner}} must have voted for a proxy or at least 21 block producers. + +A sell order of the purchased amount can only be initiated after waiting for the maturity period of 4 to 5 days to pass. Even then, depending on the market conditions, the initiated sell order may not be executed immediately. + +

updateauth

+ +--- +spec_version: "0.2.0" +title: Modify Account Permission +summary: 'Add or update the {{nowrap permission}} permission of {{nowrap account}}' +icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ +--- + +Modify, and create if necessary, the {{permission}} permission of {{account}} to have a parent permission of {{parent}} and the following authority: +{{to_json auth}} + +

updaterex

+ +--- +spec_version: "0.2.0" +title: Update REX Owner Vote Weight +summary: 'Update vote weight to current value of held REX tokens' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +Update vote weight of {{owner}} account to current value of held REX tokens. + +

updtrevision

+ +--- +spec_version: "0.2.0" +title: Update System Contract Revision Number +summary: 'Update system contract revision number' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{$action.account}} advances the system contract revision number to {{revision}}. + +

voteproducer

+ +--- +spec_version: "0.2.0" +title: Vote for Block Producers +summary: '{{nowrap voter}} votes for {{#if proxy}}the proxy {{nowrap proxy}}{{else}}up to 30 block producer candidates{{/if}}' +icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ +--- + +{{#if proxy}} +{{voter}} votes for the proxy {{proxy}}. +At the time of voting the full weight of voter’s staked (CPU + NET) tokens will be cast towards each of the producers voted by {{proxy}}. +{{else}} +{{voter}} votes for the following block producer candidates: + +{{#each producers}} + + {{this}} +{{/each}} + +At the time of voting the full weight of voter’s staked (CPU + NET) tokens will be cast towards each of the above producers. +{{/if}} + +

withdraw

+ +--- +spec_version: "0.2.0" +title: Withdraw from REX Fund +summary: 'Withdraw {{nowrap amount}} from {{nowrap owner}}’s REX fund by transferring to {{owner}}’s liquid balance' +icon: @ICON_BASE_URL@/@REX_ICON_URI@ +--- + +Withdraws {{amount}} from {{owner}}’s REX fund and transfer them to {{owner}}’s liquid balance. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp new file mode 100644 index 0000000000..a64506c010 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp @@ -0,0 +1,413 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace eosiosystem { + + using eosio::asset; + using eosio::const_mem_fun; + using eosio::current_time_point; + using eosio::indexed_by; + using eosio::permission_level; + using eosio::seconds; + using eosio::time_point_sec; + using eosio::token; + + /** + * This action will buy an exact amount of ram and bill the payer the current market price. + */ + void system_contract::buyrambytes( const name& payer, const name& receiver, uint32_t bytes ) { + auto itr = _rammarket.find(ramcore_symbol.raw()); + const int64_t ram_reserve = itr->base.balance.amount; + const int64_t eos_reserve = itr->quote.balance.amount; + const int64_t cost = exchange_state::get_bancor_input( ram_reserve, eos_reserve, bytes ); + const int64_t cost_plus_fee = cost / double(0.995); + buyram( payer, receiver, asset{ cost_plus_fee, core_symbol() } ); + } + + + /** + * When buying ram the payer irreversiblly transfers quant to system contract and only + * the receiver may reclaim the tokens via the sellram action. The receiver pays for the + * storage of all database records associated with this action. + * + * RAM is a scarce resource whose supply is defined by global properties max_ram_size. RAM is + * priced using the bancor algorithm such that price-per-byte with a constant reserve ratio of 100:1. + */ + void system_contract::buyram( const name& payer, const name& receiver, const asset& quant ) + { + require_auth( payer ); + update_ram_supply(); + + check( quant.symbol == core_symbol(), "must buy ram with core token" ); + check( quant.amount > 0, "must purchase a positive amount" ); + + auto fee = quant; + fee.amount = ( fee.amount + 199 ) / 200; /// .5% fee (round up) + // fee.amount cannot be 0 since that is only possible if quant.amount is 0 which is not allowed by the assert above. + // If quant.amount == 1, then fee.amount == 1, + // otherwise if quant.amount > 1, then 0 < fee.amount < quant.amount. + auto quant_after_fee = quant; + quant_after_fee.amount -= fee.amount; + // quant_after_fee.amount should be > 0 if quant.amount > 1. + // If quant.amount == 1, then quant_after_fee.amount == 0 and the next inline transfer will fail causing the buyram action to fail. + { + token::transfer_action transfer_act{ token_account, { {payer, active_permission}, {ram_account, active_permission} } }; + transfer_act.send( payer, ram_account, quant_after_fee, "buy ram" ); + } + if ( fee.amount > 0 ) { + token::transfer_action transfer_act{ token_account, { {payer, active_permission} } }; + transfer_act.send( payer, ramfee_account, fee, "ram fee" ); + channel_to_rex( ramfee_account, fee ); + } + + int64_t bytes_out; + + const auto& market = _rammarket.get(ramcore_symbol.raw(), "ram market does not exist"); + _rammarket.modify( market, same_payer, [&]( auto& es ) { + bytes_out = es.direct_convert( quant_after_fee, ram_symbol ).amount; + }); + + check( bytes_out > 0, "must reserve a positive amount" ); + + _gstate.total_ram_bytes_reserved += uint64_t(bytes_out); + _gstate.total_ram_stake += quant_after_fee.amount; + + user_resources_table userres( get_self(), receiver.value ); + auto res_itr = userres.find( receiver.value ); + if( res_itr == userres.end() ) { + res_itr = userres.emplace( receiver, [&]( auto& res ) { + res.owner = receiver; + res.net_weight = asset( 0, core_symbol() ); + res.cpu_weight = asset( 0, core_symbol() ); + res.ram_bytes = bytes_out; + }); + } else { + userres.modify( res_itr, receiver, [&]( auto& res ) { + res.ram_bytes += bytes_out; + }); + } + + auto voter_itr = _voters.find( res_itr->owner.value ); + if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) { + int64_t ram_bytes, net, cpu; + get_resource_limits( res_itr->owner, ram_bytes, net, cpu ); + set_resource_limits( res_itr->owner, res_itr->ram_bytes + ram_gift_bytes, net, cpu ); + } + } + + /** + * The system contract now buys and sells RAM allocations at prevailing market prices. + * This may result in traders buying RAM today in anticipation of potential shortages + * tomorrow. Overall this will result in the market balancing the supply and demand + * for RAM over time. + */ + void system_contract::sellram( const name& account, int64_t bytes ) { + require_auth( account ); + update_ram_supply(); + + check( bytes > 0, "cannot sell negative byte" ); + + user_resources_table userres( get_self(), account.value ); + auto res_itr = userres.find( account.value ); + check( res_itr != userres.end(), "no resource row" ); + check( res_itr->ram_bytes >= bytes, "insufficient quota" ); + + asset tokens_out; + auto itr = _rammarket.find(ramcore_symbol.raw()); + _rammarket.modify( itr, same_payer, [&]( auto& es ) { + /// the cast to int64_t of bytes is safe because we certify bytes is <= quota which is limited by prior purchases + tokens_out = es.direct_convert( asset(bytes, ram_symbol), core_symbol()); + }); + + check( tokens_out.amount > 1, "token amount received from selling ram is too low" ); + + _gstate.total_ram_bytes_reserved -= static_cast(bytes); // bytes > 0 is asserted above + _gstate.total_ram_stake -= tokens_out.amount; + + //// this shouldn't happen, but just in case it does we should prevent it + check( _gstate.total_ram_stake >= 0, "error, attempt to unstake more tokens than previously staked" ); + + userres.modify( res_itr, account, [&]( auto& res ) { + res.ram_bytes -= bytes; + }); + + auto voter_itr = _voters.find( res_itr->owner.value ); + if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) { + int64_t ram_bytes, net, cpu; + get_resource_limits( res_itr->owner, ram_bytes, net, cpu ); + set_resource_limits( res_itr->owner, res_itr->ram_bytes + ram_gift_bytes, net, cpu ); + } + + { + token::transfer_action transfer_act{ token_account, { {ram_account, active_permission}, {account, active_permission} } }; + transfer_act.send( ram_account, account, asset(tokens_out), "sell ram" ); + } + auto fee = ( tokens_out.amount + 199 ) / 200; /// .5% fee (round up) + // since tokens_out.amount was asserted to be at least 2 earlier, fee.amount < tokens_out.amount + if ( fee > 0 ) { + token::transfer_action transfer_act{ token_account, { {account, active_permission} } }; + transfer_act.send( account, ramfee_account, asset(fee, core_symbol()), "sell ram fee" ); + channel_to_rex( ramfee_account, asset(fee, core_symbol() )); + } + } + + void validate_b1_vesting( int64_t stake ) { + const int64_t base_time = 1527811200; /// 2018-06-01 + const int64_t max_claimable = 100'000'000'0000ll; + const int64_t claimable = int64_t(max_claimable * double(current_time_point().sec_since_epoch() - base_time) / (10*seconds_per_year) ); + + check( max_claimable - claimable <= stake, "b1 can only claim their tokens over 10 years" ); + } + + void system_contract::changebw( name from, const name& receiver, + const asset& stake_net_delta, const asset& stake_cpu_delta, bool transfer ) + { + require_auth( from ); + check( stake_net_delta.amount != 0 || stake_cpu_delta.amount != 0, "should stake non-zero amount" ); + check( std::abs( (stake_net_delta + stake_cpu_delta).amount ) + >= std::max( std::abs( stake_net_delta.amount ), std::abs( stake_cpu_delta.amount ) ), + "net and cpu deltas cannot be opposite signs" ); + + name source_stake_from = from; + if ( transfer ) { + from = receiver; + } + + // update stake delegated from "from" to "receiver" + { + del_bandwidth_table del_tbl( get_self(), from.value ); + auto itr = del_tbl.find( receiver.value ); + if( itr == del_tbl.end() ) { + itr = del_tbl.emplace( from, [&]( auto& dbo ){ + dbo.from = from; + dbo.to = receiver; + dbo.net_weight = stake_net_delta; + dbo.cpu_weight = stake_cpu_delta; + }); + } + else { + del_tbl.modify( itr, same_payer, [&]( auto& dbo ){ + dbo.net_weight += stake_net_delta; + dbo.cpu_weight += stake_cpu_delta; + }); + } + check( 0 <= itr->net_weight.amount, "insufficient staked net bandwidth" ); + check( 0 <= itr->cpu_weight.amount, "insufficient staked cpu bandwidth" ); + if ( itr->is_empty() ) { + del_tbl.erase( itr ); + } + } // itr can be invalid, should go out of scope + + // update totals of "receiver" + { + user_resources_table totals_tbl( get_self(), receiver.value ); + auto tot_itr = totals_tbl.find( receiver.value ); + if( tot_itr == totals_tbl.end() ) { + tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) { + tot.owner = receiver; + tot.net_weight = stake_net_delta; + tot.cpu_weight = stake_cpu_delta; + }); + } else { + totals_tbl.modify( tot_itr, from == receiver ? from : same_payer, [&]( auto& tot ) { + tot.net_weight += stake_net_delta; + tot.cpu_weight += stake_cpu_delta; + }); + } + check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); + check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); + + { + bool ram_managed = false; + bool net_managed = false; + bool cpu_managed = false; + + auto voter_itr = _voters.find( receiver.value ); + if( voter_itr != _voters.end() ) { + ram_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ); + net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed ); + cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed ); + } + + if( !(net_managed && cpu_managed) ) { + int64_t ram_bytes, net, cpu; + get_resource_limits( receiver, ram_bytes, net, cpu ); + + set_resource_limits( receiver, + ram_managed ? ram_bytes : std::max( tot_itr->ram_bytes + ram_gift_bytes, ram_bytes ), + net_managed ? net : tot_itr->net_weight.amount, + cpu_managed ? cpu : tot_itr->cpu_weight.amount ); + } + } + + if ( tot_itr->is_empty() ) { + totals_tbl.erase( tot_itr ); + } + } // tot_itr can be invalid, should go out of scope + + // create refund or update from existing refund + if ( stake_account != source_stake_from ) { //for eosio both transfer and refund make no sense + refunds_table refunds_tbl( get_self(), from.value ); + auto req = refunds_tbl.find( from.value ); + + //create/update/delete refund + auto net_balance = stake_net_delta; + auto cpu_balance = stake_cpu_delta; + bool need_deferred_trx = false; + + + // net and cpu are same sign by assertions in delegatebw and undelegatebw + // redundant assertion also at start of changebw to protect against misuse of changebw + bool is_undelegating = (net_balance.amount + cpu_balance.amount ) < 0; + bool is_delegating_to_self = (!transfer && from == receiver); + + if( is_delegating_to_self || is_undelegating ) { + if ( req != refunds_tbl.end() ) { //need to update refund + refunds_tbl.modify( req, same_payer, [&]( refund_request& r ) { + if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) { + r.request_time = current_time_point(); + } + r.net_amount -= net_balance; + if ( r.net_amount.amount < 0 ) { + net_balance = -r.net_amount; + r.net_amount.amount = 0; + } else { + net_balance.amount = 0; + } + r.cpu_amount -= cpu_balance; + if ( r.cpu_amount.amount < 0 ){ + cpu_balance = -r.cpu_amount; + r.cpu_amount.amount = 0; + } else { + cpu_balance.amount = 0; + } + }); + + check( 0 <= req->net_amount.amount, "negative net refund amount" ); //should never happen + check( 0 <= req->cpu_amount.amount, "negative cpu refund amount" ); //should never happen + + if ( req->is_empty() ) { + refunds_tbl.erase( req ); + need_deferred_trx = false; + } else { + need_deferred_trx = true; + } + } else if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) { //need to create refund + refunds_tbl.emplace( from, [&]( refund_request& r ) { + r.owner = from; + if ( net_balance.amount < 0 ) { + r.net_amount = -net_balance; + net_balance.amount = 0; + } else { + r.net_amount = asset( 0, core_symbol() ); + } + if ( cpu_balance.amount < 0 ) { + r.cpu_amount = -cpu_balance; + cpu_balance.amount = 0; + } else { + r.cpu_amount = asset( 0, core_symbol() ); + } + r.request_time = current_time_point(); + }); + need_deferred_trx = true; + } // else stake increase requested with no existing row in refunds_tbl -> nothing to do with refunds_tbl + } /// end if is_delegating_to_self || is_undelegating + + if ( need_deferred_trx ) { + eosio::transaction out; + out.actions.emplace_back( permission_level{from, active_permission}, + get_self(), "refund"_n, + from + ); + out.delay_sec = refund_delay_sec; + eosio::cancel_deferred( from.value ); // TODO: Remove this line when replacing deferred trxs is fixed + out.send( from.value, from, true ); + } else { + eosio::cancel_deferred( from.value ); + } + + auto transfer_amount = net_balance + cpu_balance; + if ( 0 < transfer_amount.amount ) { + token::transfer_action transfer_act{ token_account, { {source_stake_from, active_permission} } }; + transfer_act.send( source_stake_from, stake_account, asset(transfer_amount), "stake bandwidth" ); + } + } + + vote_stake_updater( from ); + update_voting_power( from, stake_net_delta + stake_cpu_delta ); + } + + void system_contract::update_voting_power( const name& voter, const asset& total_update ) + { + auto voter_itr = _voters.find( voter.value ); + if( voter_itr == _voters.end() ) { + voter_itr = _voters.emplace( voter, [&]( auto& v ) { + v.owner = voter; + v.staked = total_update.amount; + }); + } else { + _voters.modify( voter_itr, same_payer, [&]( auto& v ) { + v.staked += total_update.amount; + }); + } + + check( 0 <= voter_itr->staked, "stake for voting cannot be negative" ); + + if( voter == "b1"_n ) { + validate_b1_vesting( voter_itr->staked ); + } + + if( voter_itr->producers.size() || voter_itr->proxy ) { + update_votes( voter, voter_itr->proxy, voter_itr->producers, false ); + } + } + + void system_contract::delegatebw( const name& from, const name& receiver, + const asset& stake_net_quantity, + const asset& stake_cpu_quantity, bool transfer ) + { + asset zero_asset( 0, core_symbol() ); + check( stake_cpu_quantity >= zero_asset, "must stake a positive amount" ); + check( stake_net_quantity >= zero_asset, "must stake a positive amount" ); + check( stake_net_quantity.amount + stake_cpu_quantity.amount > 0, "must stake a positive amount" ); + check( !transfer || from != receiver, "cannot use transfer flag if delegating to self" ); + + changebw( from, receiver, stake_net_quantity, stake_cpu_quantity, transfer); + } // delegatebw + + void system_contract::undelegatebw( const name& from, const name& receiver, + const asset& unstake_net_quantity, const asset& unstake_cpu_quantity ) + { + asset zero_asset( 0, core_symbol() ); + check( unstake_cpu_quantity >= zero_asset, "must unstake a positive amount" ); + check( unstake_net_quantity >= zero_asset, "must unstake a positive amount" ); + check( unstake_cpu_quantity.amount + unstake_net_quantity.amount > 0, "must unstake a positive amount" ); + check( _gstate.thresh_activated_stake_time != time_point(), + "cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" ); + + changebw( from, receiver, -unstake_net_quantity, -unstake_cpu_quantity, false); + } // undelegatebw + + + void system_contract::refund( const name& owner ) { + require_auth( owner ); + + refunds_table refunds_tbl( get_self(), owner.value ); + auto req = refunds_tbl.find( owner.value ); + check( req != refunds_tbl.end(), "refund request not found" ); + check( req->request_time + seconds(refund_delay_sec) <= current_time_point(), + "refund is not available yet" ); + token::transfer_action transfer_act{ token_account, { {stake_account, active_permission}, {req->owner, active_permission} } }; + transfer_act.send( stake_account, req->owner, req->net_amount + req->cpu_amount, "unstake" ); + refunds_tbl.erase( req ); + } + + +} //namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp new file mode 100644 index 0000000000..202210c7d8 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp @@ -0,0 +1,400 @@ +#include +#include + +#include +#include + +#include + +namespace eosiosystem { + + using eosio::current_time_point; + using eosio::token; + + double get_continuous_rate(int64_t annual_rate) { + return std::log1p(double(annual_rate)/double(100*inflation_precision)); + } + + system_contract::system_contract( name s, name code, datastream ds ) + :native(s,code,ds), + _voters(get_self(), get_self().value), + _producers(get_self(), get_self().value), + _producers2(get_self(), get_self().value), + _global(get_self(), get_self().value), + _global2(get_self(), get_self().value), + _global3(get_self(), get_self().value), + _global4(get_self(), get_self().value), + _rammarket(get_self(), get_self().value), + _rexpool(get_self(), get_self().value), + _rexretpool(get_self(), get_self().value), + _rexretbuckets(get_self(), get_self().value), + _rexfunds(get_self(), get_self().value), + _rexbalance(get_self(), get_self().value), + _rexorders(get_self(), get_self().value) + { + _gstate = _global.exists() ? _global.get() : get_default_parameters(); + _gstate2 = _global2.exists() ? _global2.get() : eosio_global_state2{}; + _gstate3 = _global3.exists() ? _global3.get() : eosio_global_state3{}; + _gstate4 = _global4.exists() ? _global4.get() : get_default_inflation_parameters(); + } + + eosio_global_state system_contract::get_default_parameters() { + eosio_global_state dp; + get_blockchain_parameters(dp); + return dp; + } + + eosio_global_state4 system_contract::get_default_inflation_parameters() { + eosio_global_state4 gs4; + gs4.continuous_rate = get_continuous_rate(default_annual_rate); + gs4.inflation_pay_factor = default_inflation_pay_factor; + gs4.votepay_factor = default_votepay_factor; + return gs4; + } + + symbol system_contract::core_symbol()const { + const static auto sym = get_core_symbol( _rammarket ); + return sym; + } + + system_contract::~system_contract() { + _global.set( _gstate, get_self() ); + _global2.set( _gstate2, get_self() ); + _global3.set( _gstate3, get_self() ); + _global4.set( _gstate4, get_self() ); + } + + void system_contract::setram( uint64_t max_ram_size ) { + require_auth( get_self() ); + + check( _gstate.max_ram_size < max_ram_size, "ram may only be increased" ); /// decreasing ram might result market maker issues + check( max_ram_size < 1024ll*1024*1024*1024*1024, "ram size is unrealistic" ); + check( max_ram_size > _gstate.total_ram_bytes_reserved, "attempt to set max below reserved" ); + + auto delta = int64_t(max_ram_size) - int64_t(_gstate.max_ram_size); + auto itr = _rammarket.find(ramcore_symbol.raw()); + + /** + * Increase the amount of ram for sale based upon the change in max ram size. + */ + _rammarket.modify( itr, same_payer, [&]( auto& m ) { + m.base.balance.amount += delta; + }); + + _gstate.max_ram_size = max_ram_size; + } + + void system_contract::update_ram_supply() { + auto cbt = eosio::current_block_time(); + + if( cbt <= _gstate2.last_ram_increase ) return; + + auto itr = _rammarket.find(ramcore_symbol.raw()); + auto new_ram = (cbt.slot - _gstate2.last_ram_increase.slot)*_gstate2.new_ram_per_block; + _gstate.max_ram_size += new_ram; + + /** + * Increase the amount of ram for sale based upon the change in max ram size. + */ + _rammarket.modify( itr, same_payer, [&]( auto& m ) { + m.base.balance.amount += new_ram; + }); + _gstate2.last_ram_increase = cbt; + } + + void system_contract::setramrate( uint16_t bytes_per_block ) { + require_auth( get_self() ); + + update_ram_supply(); + _gstate2.new_ram_per_block = bytes_per_block; + } + + void system_contract::setparams( const eosio::blockchain_parameters& params ) { + require_auth( get_self() ); + (eosio::blockchain_parameters&)(_gstate) = params; + check( 3 <= _gstate.max_authority_depth, "max_authority_depth should be at least 3" ); + set_blockchain_parameters( params ); + } + + void system_contract::setpriv( const name& account, uint8_t ispriv ) { + require_auth( get_self() ); + set_privileged( account, ispriv ); + } + + void system_contract::setalimits( const name& account, int64_t ram, int64_t net, int64_t cpu ) { + require_auth( get_self() ); + + user_resources_table userres( get_self(), account.value ); + auto ritr = userres.find( account.value ); + check( ritr == userres.end(), "only supports unlimited accounts" ); + + auto vitr = _voters.find( account.value ); + if( vitr != _voters.end() ) { + bool ram_managed = has_field( vitr->flags1, voter_info::flags1_fields::ram_managed ); + bool net_managed = has_field( vitr->flags1, voter_info::flags1_fields::net_managed ); + bool cpu_managed = has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed ); + check( !(ram_managed || net_managed || cpu_managed), "cannot use setalimits on an account with managed resources" ); + } + + set_resource_limits( account, ram, net, cpu ); + } + + void system_contract::setacctram( const name& account, const std::optional& ram_bytes ) { + require_auth( get_self() ); + + int64_t current_ram, current_net, current_cpu; + get_resource_limits( account, current_ram, current_net, current_cpu ); + + int64_t ram = 0; + + if( !ram_bytes ) { + auto vitr = _voters.find( account.value ); + check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::ram_managed ), + "RAM of account is already unmanaged" ); + + user_resources_table userres( get_self(), account.value ); + auto ritr = userres.find( account.value ); + + ram = ram_gift_bytes; + if( ritr != userres.end() ) { + ram += ritr->ram_bytes; + } + + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, false ); + }); + } else { + check( *ram_bytes >= 0, "not allowed to set RAM limit to unlimited" ); + + auto vitr = _voters.find( account.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true ); + }); + } else { + _voters.emplace( account, [&]( auto& v ) { + v.owner = account; + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true ); + }); + } + + ram = *ram_bytes; + } + + set_resource_limits( account, ram, current_net, current_cpu ); + } + + void system_contract::setacctnet( const name& account, const std::optional& net_weight ) { + require_auth( get_self() ); + + int64_t current_ram, current_net, current_cpu; + get_resource_limits( account, current_ram, current_net, current_cpu ); + + int64_t net = 0; + + if( !net_weight ) { + auto vitr = _voters.find( account.value ); + check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::net_managed ), + "Network bandwidth of account is already unmanaged" ); + + user_resources_table userres( get_self(), account.value ); + auto ritr = userres.find( account.value ); + + if( ritr != userres.end() ) { + net = ritr->net_weight.amount; + } + + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, false ); + }); + } else { + check( *net_weight >= -1, "invalid value for net_weight" ); + + auto vitr = _voters.find( account.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true ); + }); + } else { + _voters.emplace( account, [&]( auto& v ) { + v.owner = account; + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true ); + }); + } + + net = *net_weight; + } + + set_resource_limits( account, current_ram, net, current_cpu ); + } + + void system_contract::setacctcpu( const name& account, const std::optional& cpu_weight ) { + require_auth( get_self() ); + + int64_t current_ram, current_net, current_cpu; + get_resource_limits( account, current_ram, current_net, current_cpu ); + + int64_t cpu = 0; + + if( !cpu_weight ) { + auto vitr = _voters.find( account.value ); + check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed ), + "CPU bandwidth of account is already unmanaged" ); + + user_resources_table userres( get_self(), account.value ); + auto ritr = userres.find( account.value ); + + if( ritr != userres.end() ) { + cpu = ritr->cpu_weight.amount; + } + + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, false ); + }); + } else { + check( *cpu_weight >= -1, "invalid value for cpu_weight" ); + + auto vitr = _voters.find( account.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& v ) { + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true ); + }); + } else { + _voters.emplace( account, [&]( auto& v ) { + v.owner = account; + v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true ); + }); + } + + cpu = *cpu_weight; + } + + set_resource_limits( account, current_ram, current_net, cpu ); + } + + void system_contract::activate( const eosio::checksum256& feature_digest ) { + require_auth( get_self() ); + preactivate_feature( feature_digest ); + } + + void system_contract::rmvproducer( const name& producer ) { + require_auth( get_self() ); + auto prod = _producers.find( producer.value ); + check( prod != _producers.end(), "producer not found" ); + _producers.modify( prod, same_payer, [&](auto& p) { + p.deactivate(); + }); + } + + void system_contract::updtrevision( uint8_t revision ) { + require_auth( get_self() ); + check( _gstate2.revision < 255, "can not increment revision" ); // prevent wrap around + check( revision == _gstate2.revision + 1, "can only increment revision by one" ); + check( revision <= 1, // set upper bound to greatest revision supported in the code + "specified revision is not yet supported by the code" ); + _gstate2.revision = revision; + } + + void system_contract::setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor ) { + require_auth(get_self()); + check(annual_rate >= 0, "annual_rate can't be negative"); + if ( inflation_pay_factor < pay_factor_precision ) { + check( false, "inflation_pay_factor must not be less than " + std::to_string(pay_factor_precision) ); + } + if ( votepay_factor < pay_factor_precision ) { + check( false, "votepay_factor must not be less than " + std::to_string(pay_factor_precision) ); + } + _gstate4.continuous_rate = get_continuous_rate(annual_rate); + _gstate4.inflation_pay_factor = inflation_pay_factor; + _gstate4.votepay_factor = votepay_factor; + _global4.set( _gstate4, get_self() ); + } + + /** + * Called after a new account is created. This code enforces resource-limits rules + * for new accounts as well as new account naming conventions. + * + * Account names containing '.' symbols must have a suffix equal to the name of the creator. + * This allows users who buy a premium name (shorter than 12 characters with no dots) to be the only ones + * who can create accounts with the creator's name as a suffix. + * + */ + void native::newaccount( const name& creator, + const name& newact, + ignore owner, + ignore active ) { + + if( creator != get_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(get_self(), get_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( get_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( const name& acnt, const std::vector& abi ) { + eosio::multi_index< "abihash"_n, abi_hash > table(get_self(), get_self().value); + auto itr = table.find( acnt.value ); + if( itr == table.end() ) { + table.emplace( acnt, [&]( auto& row ) { + row.owner = acnt; + row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); + }); + } else { + table.modify( itr, same_payer, [&]( auto& row ) { + row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); + }); + } + } + + void system_contract::init( unsigned_int version, const symbol& core ) { + require_auth( get_self() ); + check( version.value == 0, "unsupported version for init action" ); + + auto itr = _rammarket.find(ramcore_symbol.raw()); + check( itr == _rammarket.end(), "system contract has already been initialized" ); + + auto system_token_supply = eosio::token::get_supply(token_account, core.code() ); + check( system_token_supply.symbol == core, "specified core symbol does not exist (precision mismatch)" ); + + check( system_token_supply.amount > 0, "system token supply must be greater than 0" ); + _rammarket.emplace( get_self(), [&]( auto& m ) { + m.supply.amount = 100000000000000ll; + m.supply.symbol = ramcore_symbol; + m.base.balance.amount = int64_t(_gstate.free_ram()); + m.base.balance.symbol = ram_symbol; + m.quote.balance.amount = system_token_supply.amount / 1000; + m.quote.balance.symbol = core; + }); + + token::open_action open_act{ token_account, { {get_self(), active_permission} } }; + open_act.send( rex_account, core, get_self() ); + } + +} /// eosio.system diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp new file mode 100644 index 0000000000..8f9734bcb0 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp @@ -0,0 +1,110 @@ +#include + +#include + +#include + +namespace eosiosystem { + + using eosio::check; + + asset exchange_state::convert_to_exchange( connector& reserve, const asset& payment ) + { + const double S0 = supply.amount; + const double R0 = reserve.balance.amount; + const double dR = payment.amount; + const double F = reserve.weight; + + double dS = S0 * ( std::pow(1. + dR / R0, F) - 1. ); + if ( dS < 0 ) dS = 0; // rounding errors + reserve.balance += payment; + supply.amount += int64_t(dS); + return asset( int64_t(dS), supply.symbol ); + } + + asset exchange_state::convert_from_exchange( connector& reserve, const asset& tokens ) + { + const double R0 = reserve.balance.amount; + const double S0 = supply.amount; + const double dS = -tokens.amount; // dS < 0, tokens are subtracted from supply + const double Fi = double(1) / reserve.weight; + + double dR = R0 * ( std::pow(1. + dS / S0, Fi) - 1. ); // dR < 0 since dS < 0 + if ( dR > 0 ) dR = 0; // rounding errors + reserve.balance.amount -= int64_t(-dR); + supply -= tokens; + return asset( int64_t(-dR), reserve.balance.symbol ); + } + + asset exchange_state::convert( const asset& from, const symbol& to ) + { + const auto& sell_symbol = from.symbol; + const auto& base_symbol = base.balance.symbol; + const auto& quote_symbol = quote.balance.symbol; + check( sell_symbol != to, "cannot convert to the same symbol" ); + + asset out( 0, to ); + if ( sell_symbol == base_symbol && to == quote_symbol ) { + const asset tmp = convert_to_exchange( base, from ); + out = convert_from_exchange( quote, tmp ); + } else if ( sell_symbol == quote_symbol && to == base_symbol ) { + const asset tmp = convert_to_exchange( quote, from ); + out = convert_from_exchange( base, tmp ); + } else { + check( false, "invalid conversion" ); + } + return out; + } + + asset exchange_state::direct_convert( const asset& from, const symbol& to ) + { + const auto& sell_symbol = from.symbol; + const auto& base_symbol = base.balance.symbol; + const auto& quote_symbol = quote.balance.symbol; + check( sell_symbol != to, "cannot convert to the same symbol" ); + + asset out( 0, to ); + if ( sell_symbol == base_symbol && to == quote_symbol ) { + out.amount = get_bancor_output( base.balance.amount, quote.balance.amount, from.amount ); + base.balance += from; + quote.balance -= out; + } else if ( sell_symbol == quote_symbol && to == base_symbol ) { + out.amount = get_bancor_output( quote.balance.amount, base.balance.amount, from.amount ); + quote.balance += from; + base.balance -= out; + } else { + check( false, "invalid conversion" ); + } + return out; + } + + int64_t exchange_state::get_bancor_output( int64_t inp_reserve, + int64_t out_reserve, + int64_t inp ) + { + const double ib = inp_reserve; + const double ob = out_reserve; + const double in = inp; + + int64_t out = int64_t( (in * ob) / (ib + in) ); + + if ( out < 0 ) out = 0; + + return out; + } + + int64_t exchange_state::get_bancor_input( int64_t out_reserve, + int64_t inp_reserve, + int64_t out ) + { + const double ob = out_reserve; + const double ib = inp_reserve; + + int64_t inp = (ib * out) / (ob - out); + + if ( inp < 0 ) inp = 0; + + return inp; + } + +} /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp new file mode 100644 index 0000000000..4c01f353a1 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include + +namespace eosiosystem { + + using eosio::current_time_point; + using eosio::token; + + void system_contract::bidname( const name& bidder, const name& newname, const asset& bid ) { + require_auth( bidder ); + check( newname.suffix() == newname, "you can only bid on top-level suffix" ); + + check( (bool)newname, "the empty name is not a valid account name to bid on" ); + check( (newname.value & 0xFull) == 0, "13 character names are not valid account names to bid on" ); + check( (newname.value & 0x1F0ull) == 0, "accounts with 12 character names and no dots can be created without bidding required" ); + check( !is_account( newname ), "account already exists" ); + check( bid.symbol == core_symbol(), "asset must be system token" ); + check( bid.amount > 0, "insufficient bid" ); + token::transfer_action transfer_act{ token_account, { {bidder, active_permission} } }; + transfer_act.send( bidder, names_account, bid, std::string("bid name ")+ newname.to_string() ); + name_bid_table bids(get_self(), get_self().value); + print( name{bidder}, " bid ", bid, " on ", name{newname}, "\n" ); + auto current = bids.find( newname.value ); + if( current == bids.end() ) { + bids.emplace( bidder, [&]( auto& b ) { + b.newname = newname; + b.high_bidder = bidder; + b.high_bid = bid.amount; + b.last_bid_time = current_time_point(); + }); + } else { + check( current->high_bid > 0, "this auction has already closed" ); + check( bid.amount - current->high_bid > (current->high_bid / 10), "must increase bid by 10%" ); + check( current->high_bidder != bidder, "account is already highest bidder" ); + + bid_refund_table refunds_table(get_self(), newname.value); + + auto it = refunds_table.find( current->high_bidder.value ); + if ( it != refunds_table.end() ) { + refunds_table.modify( it, same_payer, [&](auto& r) { + r.amount += asset( current->high_bid, core_symbol() ); + }); + } else { + refunds_table.emplace( bidder, [&](auto& r) { + r.bidder = current->high_bidder; + r.amount = asset( current->high_bid, core_symbol() ); + }); + } + + eosio::transaction t; + t.actions.emplace_back( permission_level{current->high_bidder, active_permission}, + get_self(), "bidrefund"_n, + std::make_tuple( current->high_bidder, newname ) + ); + t.delay_sec = 0; + uint128_t deferred_id = (uint128_t(newname.value) << 64) | current->high_bidder.value; + eosio::cancel_deferred( deferred_id ); + t.send( deferred_id, bidder ); + + bids.modify( current, bidder, [&]( auto& b ) { + b.high_bidder = bidder; + b.high_bid = bid.amount; + b.last_bid_time = current_time_point(); + }); + } + } + + void system_contract::bidrefund( const name& bidder, const name& newname ) { + bid_refund_table refunds_table(get_self(), newname.value); + auto it = refunds_table.find( bidder.value ); + check( it != refunds_table.end(), "refund not found" ); + + token::transfer_action transfer_act{ token_account, { {names_account, active_permission}, {bidder, active_permission} } }; + transfer_act.send( names_account, bidder, asset(it->amount), std::string("refund bid on name ")+(name{newname}).to_string() ); + refunds_table.erase( it ); + } + +} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp new file mode 100644 index 0000000000..df98daabf2 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp @@ -0,0 +1,11 @@ +#include + +#include + +namespace eosiosystem { + + void native::onerror( ignore, ignore> ) { + eosio::check( false, "the onerror action cannot be called directly" ); + } + +} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp new file mode 100644 index 0000000000..4db2f77403 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp @@ -0,0 +1,191 @@ +#include +#include + +namespace eosiosystem { + + using eosio::current_time_point; + using eosio::microseconds; + using eosio::token; + + void system_contract::onblock( ignore ) { + using namespace eosio; + + require_auth(get_self()); + + block_timestamp timestamp; + name producer; + _ds >> timestamp >> producer; + + // _gstate2.last_block_num is not used anywhere in the system contract code anymore. + // Although this field is deprecated, we will continue updating it for now until the last_block_num field + // is eventually completely removed, at which point this line can be removed. + _gstate2.last_block_num = timestamp; + + /** until activation, no new rewards are paid */ + if( _gstate.thresh_activated_stake_time == time_point() ) + return; + + if( _gstate.last_pervote_bucket_fill == time_point() ) /// start the presses + _gstate.last_pervote_bucket_fill = current_time_point(); + + + /** + * At startup the initial producer may not be one that is registered / elected + * and therefore there may be no producer object for them. + */ + auto prod = _producers.find( producer.value ); + if ( prod != _producers.end() ) { + _gstate.total_unpaid_blocks++; + _producers.modify( prod, same_payer, [&](auto& p ) { + p.unpaid_blocks++; + }); + } + + /// only update block producers once every minute, block_timestamp is in half seconds + if( timestamp.slot - _gstate.last_producer_schedule_update.slot > 120 ) { + update_elected_producers( timestamp ); + + if( (timestamp.slot - _gstate.last_name_close.slot) > blocks_per_day ) { + name_bid_table bids(get_self(), get_self().value); + auto idx = bids.get_index<"highbid"_n>(); + auto highest = idx.lower_bound( std::numeric_limits::max()/2 ); + if( highest != idx.end() && + highest->high_bid > 0 && + (current_time_point() - highest->last_bid_time) > microseconds(useconds_per_day) && + _gstate.thresh_activated_stake_time > time_point() && + (current_time_point() - _gstate.thresh_activated_stake_time) > microseconds(14 * useconds_per_day) + ) { + _gstate.last_name_close = timestamp; + channel_namebid_to_rex( highest->high_bid ); + idx.modify( highest, same_payer, [&]( auto& b ){ + b.high_bid = -b.high_bid; + }); + } + } + } + } + + void system_contract::claimrewards( const name& owner ) { + require_auth( owner ); + + const auto& prod = _producers.get( owner.value ); + check( prod.active(), "producer does not have an active key" ); + + check( _gstate.thresh_activated_stake_time != time_point(), + "cannot claim rewards until the chain is activated (at least 15% of all tokens participate in voting)" ); + + const auto ct = current_time_point(); + + check( ct - prod.last_claim_time > microseconds(useconds_per_day), "already claimed rewards within past day" ); + + const asset token_supply = token::get_supply(token_account, core_symbol().code() ); + const auto usecs_since_last_fill = (ct - _gstate.last_pervote_bucket_fill).count(); + + if( usecs_since_last_fill > 0 && _gstate.last_pervote_bucket_fill > time_point() ) { + double additional_inflation = (_gstate4.continuous_rate * double(token_supply.amount) * double(usecs_since_last_fill)) / double(useconds_per_year); + check( additional_inflation <= double(std::numeric_limits::max() - ((1ll << 10) - 1)), + "overflow in calculating new tokens to be issued; inflation rate is too high" ); + int64_t new_tokens = (additional_inflation < 0.0) ? 0 : static_cast(additional_inflation); + + int64_t to_producers = (new_tokens * uint128_t(pay_factor_precision)) / _gstate4.inflation_pay_factor; + int64_t to_savings = new_tokens - to_producers; + int64_t to_per_block_pay = (to_producers * uint128_t(pay_factor_precision)) / _gstate4.votepay_factor; + int64_t to_per_vote_pay = to_producers - to_per_block_pay; + + if( new_tokens > 0 ) { + { + token::issue_action issue_act{ token_account, { {get_self(), active_permission} } }; + issue_act.send( get_self(), asset(new_tokens, core_symbol()), "issue tokens for producer pay and savings" ); + } + { + token::transfer_action transfer_act{ token_account, { {get_self(), active_permission} } }; + if( to_savings > 0 ) { + transfer_act.send( get_self(), saving_account, asset(to_savings, core_symbol()), "unallocated inflation" ); + } + if( to_per_block_pay > 0 ) { + transfer_act.send( get_self(), bpay_account, asset(to_per_block_pay, core_symbol()), "fund per-block bucket" ); + } + if( to_per_vote_pay > 0 ) { + transfer_act.send( get_self(), vpay_account, asset(to_per_vote_pay, core_symbol()), "fund per-vote bucket" ); + } + } + } + + _gstate.pervote_bucket += to_per_vote_pay; + _gstate.perblock_bucket += to_per_block_pay; + _gstate.last_pervote_bucket_fill = ct; + } + + auto prod2 = _producers2.find( owner.value ); + + /// New metric to be used in pervote pay calculation. Instead of vote weight ratio, we combine vote weight and + /// time duration the vote weight has been held into one metric. + const auto last_claim_plus_3days = prod.last_claim_time + microseconds(3 * useconds_per_day); + + bool crossed_threshold = (last_claim_plus_3days <= ct); + bool updated_after_threshold = true; + if ( prod2 != _producers2.end() ) { + updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update); + } else { + prod2 = _producers2.emplace( owner, [&]( producer_info2& info ) { + info.owner = owner; + info.last_votepay_share_update = ct; + }); + } + + // Note: updated_after_threshold implies cross_threshold (except if claiming rewards when the producers2 table row did not exist). + // The exception leads to updated_after_threshold to be treated as true regardless of whether the threshold was crossed. + // This is okay because in this case the producer will not get paid anything either way. + // In fact it is desired behavior because the producers votes need to be counted in the global total_producer_votepay_share for the first time. + + int64_t producer_per_block_pay = 0; + if( _gstate.total_unpaid_blocks > 0 ) { + producer_per_block_pay = (_gstate.perblock_bucket * prod.unpaid_blocks) / _gstate.total_unpaid_blocks; + } + + double new_votepay_share = update_producer_votepay_share( prod2, + ct, + updated_after_threshold ? 0.0 : prod.total_votes, + true // reset votepay_share to zero after updating + ); + + int64_t producer_per_vote_pay = 0; + if( _gstate2.revision > 0 ) { + double total_votepay_share = update_total_votepay_share( ct ); + if( total_votepay_share > 0 && !crossed_threshold ) { + producer_per_vote_pay = int64_t((new_votepay_share * _gstate.pervote_bucket) / total_votepay_share); + if( producer_per_vote_pay > _gstate.pervote_bucket ) + producer_per_vote_pay = _gstate.pervote_bucket; + } + } else { + if( _gstate.total_producer_vote_weight > 0 ) { + producer_per_vote_pay = int64_t((_gstate.pervote_bucket * prod.total_votes) / _gstate.total_producer_vote_weight); + } + } + + if( producer_per_vote_pay < min_pervote_daily_pay ) { + producer_per_vote_pay = 0; + } + + _gstate.pervote_bucket -= producer_per_vote_pay; + _gstate.perblock_bucket -= producer_per_block_pay; + _gstate.total_unpaid_blocks -= prod.unpaid_blocks; + + update_total_votepay_share( ct, -new_votepay_share, (updated_after_threshold ? prod.total_votes : 0.0) ); + + _producers.modify( prod, same_payer, [&](auto& p) { + p.last_claim_time = ct; + p.unpaid_blocks = 0; + }); + + if ( producer_per_block_pay > 0 ) { + token::transfer_action transfer_act{ token_account, { {bpay_account, active_permission}, {owner, active_permission} } }; + transfer_act.send( bpay_account, owner, asset(producer_per_block_pay, core_symbol()), "producer block pay" ); + } + if ( producer_per_vote_pay > 0 ) { + token::transfer_action transfer_act{ token_account, { {vpay_account, active_permission}, {owner, active_permission} } }; + transfer_act.send( vpay_account, owner, asset(producer_per_vote_pay, core_symbol()), "producer vote pay" ); + } + } + +} //namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp new file mode 100644 index 0000000000..8e9d881f36 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp @@ -0,0 +1,1218 @@ +#include +#include +#include + +namespace eosiosystem { + + using eosio::current_time_point; + using eosio::token; + using eosio::seconds; + + void system_contract::deposit( const name& owner, const asset& amount ) + { + require_auth( owner ); + + check( amount.symbol == core_symbol(), "must deposit core token" ); + check( 0 < amount.amount, "must deposit a positive amount" ); + // inline transfer from owner's token balance + { + token::transfer_action transfer_act{ token_account, { owner, active_permission } }; + transfer_act.send( owner, rex_account, amount, "deposit to REX fund" ); + } + transfer_to_fund( owner, amount ); + } + + void system_contract::withdraw( const name& owner, const asset& amount ) + { + require_auth( owner ); + + check( amount.symbol == core_symbol(), "must withdraw core token" ); + check( 0 < amount.amount, "must withdraw a positive amount" ); + update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); + transfer_from_fund( owner, amount ); + // inline transfer to owner's token balance + { + token::transfer_action transfer_act{ token_account, { rex_account, active_permission } }; + transfer_act.send( rex_account, owner, amount, "withdraw from REX fund" ); + } + } + + void system_contract::buyrex( const name& from, const asset& amount ) + { + require_auth( from ); + + check( amount.symbol == core_symbol(), "asset must be core token" ); + check( 0 < amount.amount, "must use positive amount" ); + check_voting_requirement( from ); + transfer_from_fund( from, amount ); + const asset rex_received = add_to_rex_pool( amount ); + const asset delta_rex_stake = add_to_rex_balance( from, amount, rex_received ); + runrex(2); + update_rex_account( from, asset( 0, core_symbol() ), delta_rex_stake ); + // dummy action added so that amount of REX tokens purchased shows up in action trace + rex_results::buyresult_action buyrex_act( rex_account, std::vector{ } ); + buyrex_act.send( rex_received ); + } + + void system_contract::unstaketorex( const name& owner, const name& receiver, const asset& from_net, const asset& from_cpu ) + { + require_auth( owner ); + + check( from_net.symbol == core_symbol() && from_cpu.symbol == core_symbol(), "asset must be core token" ); + check( (0 <= from_net.amount) && (0 <= from_cpu.amount) && (0 < from_net.amount || 0 < from_cpu.amount), + "must unstake a positive amount to buy rex" ); + check_voting_requirement( owner ); + + { + del_bandwidth_table dbw_table( get_self(), owner.value ); + auto del_itr = dbw_table.require_find( receiver.value, "delegated bandwidth record does not exist" ); + check( from_net.amount <= del_itr->net_weight.amount, "amount exceeds tokens staked for net"); + check( from_cpu.amount <= del_itr->cpu_weight.amount, "amount exceeds tokens staked for cpu"); + dbw_table.modify( del_itr, same_payer, [&]( delegated_bandwidth& dbw ) { + dbw.net_weight.amount -= from_net.amount; + dbw.cpu_weight.amount -= from_cpu.amount; + }); + if ( del_itr->is_empty() ) { + dbw_table.erase( del_itr ); + } + } + + update_resource_limits( name(0), receiver, -from_net.amount, -from_cpu.amount ); + + const asset payment = from_net + from_cpu; + // inline transfer from stake_account to rex_account + { + token::transfer_action transfer_act{ token_account, { stake_account, active_permission } }; + transfer_act.send( stake_account, rex_account, payment, "buy REX with staked tokens" ); + } + const asset rex_received = add_to_rex_pool( payment ); + add_to_rex_balance( owner, payment, rex_received ); + runrex(2); + update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ), true ); + // dummy action added so that amount of REX tokens purchased shows up in action trace + rex_results::buyresult_action buyrex_act( rex_account, std::vector{ } ); + buyrex_act.send( rex_received ); + } + + void system_contract::sellrex( const name& from, const asset& rex ) + { + require_auth( from ); + + runrex(2); + + auto bitr = _rexbalance.require_find( from.value, "user must first buyrex" ); + check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, + "asset must be a positive amount of (REX, 4)" ); + process_rex_maturities( bitr ); + check( rex.amount <= bitr->matured_rex, "insufficient available rex" ); + + const auto current_order = fill_rex_order( bitr, rex ); + if ( current_order.success && current_order.proceeds.amount == 0 ) { + check( false, "proceeds are negligible" ); + } + asset pending_sell_order = update_rex_account( from, current_order.proceeds, current_order.stake_change ); + if ( !current_order.success ) { + if ( from == "b1"_n ) { + check( false, "b1 sellrex orders should not be queued" ); + } + /** + * REX order couldn't be filled and is added to queue. + * If account already has an open order, requested rex is added to existing order. + */ + auto oitr = _rexorders.find( from.value ); + if ( oitr == _rexorders.end() ) { + oitr = _rexorders.emplace( from, [&]( auto& order ) { + order.owner = from; + order.rex_requested = rex; + order.is_open = true; + order.proceeds = asset( 0, core_symbol() ); + order.stake_change = asset( 0, core_symbol() ); + order.order_time = current_time_point(); + }); + } else { + _rexorders.modify( oitr, same_payer, [&]( auto& order ) { + order.rex_requested.amount += rex.amount; + }); + } + pending_sell_order.amount = oitr->rex_requested.amount; + } + check( pending_sell_order.amount <= bitr->matured_rex, "insufficient funds for current and scheduled orders" ); + // dummy action added so that sell order proceeds show up in action trace + if ( current_order.success ) { + rex_results::sellresult_action sellrex_act( rex_account, std::vector{ } ); + sellrex_act.send( current_order.proceeds ); + } + } + + void system_contract::cnclrexorder( const name& owner ) + { + require_auth( owner ); + + auto itr = _rexorders.require_find( owner.value, "no sellrex order is scheduled" ); + check( itr->is_open, "sellrex order has been filled and cannot be canceled" ); + _rexorders.erase( itr ); + } + + void system_contract::rentcpu( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ) + { + require_auth( from ); + + rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); + int64_t rented_tokens = rent_rex( cpu_loans, from, receiver, loan_payment, loan_fund ); + update_resource_limits( from, receiver, 0, rented_tokens ); + } + + void system_contract::rentnet( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ) + { + require_auth( from ); + + rex_net_loan_table net_loans( get_self(), get_self().value ); + int64_t rented_tokens = rent_rex( net_loans, from, receiver, loan_payment, loan_fund ); + update_resource_limits( from, receiver, rented_tokens, 0 ); + } + + void system_contract::fundcpuloan( const name& from, uint64_t loan_num, const asset& payment ) + { + require_auth( from ); + + rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); + fund_rex_loan( cpu_loans, from, loan_num, payment ); + } + + void system_contract::fundnetloan( const name& from, uint64_t loan_num, const asset& payment ) + { + require_auth( from ); + + rex_net_loan_table net_loans( get_self(), get_self().value ); + fund_rex_loan( net_loans, from, loan_num, payment ); + } + + void system_contract::defcpuloan( const name& from, uint64_t loan_num, const asset& amount ) + { + require_auth( from ); + + rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); + defund_rex_loan( cpu_loans, from, loan_num, amount ); + } + + void system_contract::defnetloan( const name& from, uint64_t loan_num, const asset& amount ) + { + require_auth( from ); + + rex_net_loan_table net_loans( get_self(), get_self().value ); + defund_rex_loan( net_loans, from, loan_num, amount ); + } + + void system_contract::updaterex( const name& owner ) + { + require_auth( owner ); + + runrex(2); + + auto itr = _rexbalance.require_find( owner.value, "account has no REX balance" ); + const asset init_stake = itr->vote_stake; + + auto rexp_itr = _rexpool.begin(); + const int64_t total_rex = rexp_itr->total_rex.amount; + const int64_t total_lendable = rexp_itr->total_lendable.amount; + const int64_t rex_balance = itr->rex_balance.amount; + + asset current_stake( 0, core_symbol() ); + if ( total_rex > 0 ) { + current_stake.amount = ( uint128_t(rex_balance) * total_lendable ) / total_rex; + } + _rexbalance.modify( itr, same_payer, [&]( auto& rb ) { + rb.vote_stake = current_stake; + }); + + update_rex_account( owner, asset( 0, core_symbol() ), current_stake - init_stake, true ); + process_rex_maturities( itr ); + } + + void system_contract::setrex( const asset& balance ) + { + require_auth( "eosio"_n ); + + check( balance.amount > 0, "balance must be set to have a positive amount" ); + check( balance.symbol == core_symbol(), "balance symbol must be core symbol" ); + check( rex_system_initialized(), "rex system is not initialized" ); + _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& pool ) { + pool.total_rent = balance; + }); + } + + void system_contract::rexexec( const name& user, uint16_t max ) + { + require_auth( user ); + + runrex( max ); + } + + void system_contract::consolidate( const name& owner ) + { + require_auth( owner ); + + runrex(2); + + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); + asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); + consolidate_rex_balance( bitr, rex_in_sell_order ); + } + + void system_contract::mvtosavings( const name& owner, const asset& rex ) + { + require_auth( owner ); + + runrex(2); + + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); + check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); + const asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); + const int64_t rex_in_savings = read_rex_savings( bitr ); + check( rex.amount + rex_in_sell_order.amount + rex_in_savings <= bitr->rex_balance.amount, + "insufficient REX balance" ); + process_rex_maturities( bitr ); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + int64_t moved_rex = 0; + while ( !rb.rex_maturities.empty() && moved_rex < rex.amount) { + const int64_t drex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second ); + rb.rex_maturities.back().second -= drex; + moved_rex += drex; + if ( rb.rex_maturities.back().second == 0 ) { + rb.rex_maturities.pop_back(); + } + } + if ( moved_rex < rex.amount ) { + const int64_t drex = rex.amount - moved_rex; + rb.matured_rex -= drex; + moved_rex += drex; + check( rex_in_sell_order.amount <= rb.matured_rex, "logic error in mvtosavings" ); + } + check( moved_rex == rex.amount, "programmer error in mvtosavings" ); + }); + put_rex_savings( bitr, rex_in_savings + rex.amount ); + } + + void system_contract::mvfrsavings( const name& owner, const asset& rex ) + { + require_auth( owner ); + + runrex(2); + + auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); + check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); + const int64_t rex_in_savings = read_rex_savings( bitr ); + check( rex.amount <= rex_in_savings, "insufficient REX in savings" ); + process_rex_maturities( bitr ); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + const time_point_sec maturity = get_rex_maturity(); + if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { + rb.rex_maturities.back().second += rex.amount; + } else { + rb.rex_maturities.emplace_back( maturity, rex.amount ); + } + }); + put_rex_savings( bitr, rex_in_savings - rex.amount ); + update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); + } + + void system_contract::closerex( const name& owner ) + { + require_auth( owner ); + + if ( rex_system_initialized() ) + runrex(2); + + update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); + + /// check for any outstanding loans or rex fund + { + rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); + auto cpu_idx = cpu_loans.get_index<"byowner"_n>(); + bool no_outstanding_cpu_loans = ( cpu_idx.find( owner.value ) == cpu_idx.end() ); + + rex_net_loan_table net_loans( get_self(), get_self().value ); + auto net_idx = net_loans.get_index<"byowner"_n>(); + bool no_outstanding_net_loans = ( net_idx.find( owner.value ) == net_idx.end() ); + + auto fund_itr = _rexfunds.find( owner.value ); + bool no_outstanding_rex_fund = ( fund_itr != _rexfunds.end() ) && ( fund_itr->balance.amount == 0 ); + + if ( no_outstanding_cpu_loans && no_outstanding_net_loans && no_outstanding_rex_fund ) { + _rexfunds.erase( fund_itr ); + } + } + + /// check for remaining rex balance + { + auto rex_itr = _rexbalance.find( owner.value ); + if ( rex_itr != _rexbalance.end() ) { + check( rex_itr->rex_balance.amount == 0, "account has remaining REX balance, must sell first"); + _rexbalance.erase( rex_itr ); + } + } + } + + /** + * @brief Updates account NET and CPU resource limits + * + * @param from - account charged for RAM if there is a need + * @param receiver - account whose resource limits are updated + * @param delta_net - change in NET bandwidth limit + * @param delta_cpu - change in CPU bandwidth limit + */ + void system_contract::update_resource_limits( const name& from, const name& receiver, int64_t delta_net, int64_t delta_cpu ) + { + if ( delta_cpu == 0 && delta_net == 0 ) { // nothing to update + return; + } + + user_resources_table totals_tbl( get_self(), receiver.value ); + auto tot_itr = totals_tbl.find( receiver.value ); + if ( tot_itr == totals_tbl.end() ) { + check( 0 <= delta_net && 0 <= delta_cpu, "logic error, should not occur"); + tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) { + tot.owner = receiver; + tot.net_weight = asset( delta_net, core_symbol() ); + tot.cpu_weight = asset( delta_cpu, core_symbol() ); + }); + } else { + totals_tbl.modify( tot_itr, same_payer, [&]( auto& tot ) { + tot.net_weight.amount += delta_net; + tot.cpu_weight.amount += delta_cpu; + }); + } + check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); + check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); + + { + bool net_managed = false; + bool cpu_managed = false; + + auto voter_itr = _voters.find( receiver.value ); + if( voter_itr != _voters.end() ) { + net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed ); + cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed ); + } + + if( !(net_managed && cpu_managed) ) { + int64_t ram_bytes = 0, net = 0, cpu = 0; + get_resource_limits( receiver, ram_bytes, net, cpu ); + + set_resource_limits( receiver, + ram_bytes, + net_managed ? net : tot_itr->net_weight.amount, + cpu_managed ? cpu : tot_itr->cpu_weight.amount ); + } + } + + if ( tot_itr->is_empty() ) { + totals_tbl.erase( tot_itr ); + } + } + + /** + * @brief Checks if account satisfies voting requirement (voting for a proxy or 21 producers) + * for buying REX + * + * @param owner - account buying or already holding REX tokens + * @err_msg - error message + */ + void system_contract::check_voting_requirement( const name& owner, const char* error_msg )const + { + auto vitr = _voters.find( owner.value ); + check( vitr != _voters.end() && ( vitr->proxy || 21 <= vitr->producers.size() ), error_msg ); + } + + /** + * @brief Checks if CPU and Network loans are available + * + * Loans are available if 1) REX pool lendable balance is nonempty, and 2) there are no + * unfilled sellrex orders. + */ + bool system_contract::rex_loans_available()const + { + if ( !rex_available() ) { + return false; + } else { + if ( _rexorders.begin() == _rexorders.end() ) { + return true; // no outstanding sellrex orders + } else { + auto idx = _rexorders.get_index<"bytime"_n>(); + return !idx.begin()->is_open; // no outstanding unfilled sellrex orders + } + } + } + + /** + * @brief Updates rex_pool balances upon creating a new loan or renewing an existing one + * + * @param payment - loan fee paid + * @param rented_tokens - amount of tokens to be staked to loan receiver + * @param new_loan - flag indicating whether the loan is new or being renewed + */ + void system_contract::add_loan_to_rex_pool( const asset& payment, int64_t rented_tokens, bool new_loan ) + { + add_to_rex_return_pool( payment ); + _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rt ) { + // add payment to total_rent + rt.total_rent.amount += payment.amount; + // move rented_tokens from total_unlent to total_lent + rt.total_unlent.amount -= rented_tokens; + rt.total_lent.amount += rented_tokens; + // increment loan_num if a new loan is being created + if ( new_loan ) { + rt.loan_num++; + } + }); + } + + /** + * @brief Updates rex_pool balances upon closing an expired loan + * + * @param loan - loan to be closed + */ + void system_contract::remove_loan_from_rex_pool( const rex_loan& loan ) + { + const auto& pool = _rexpool.begin(); + const int64_t delta_total_rent = exchange_state::get_bancor_output( pool->total_unlent.amount, + pool->total_rent.amount, + loan.total_staked.amount ); + _rexpool.modify( pool, same_payer, [&]( auto& rt ) { + // deduct calculated delta_total_rent from total_rent + rt.total_rent.amount -= delta_total_rent; + // move rented tokens from total_lent to total_unlent + rt.total_unlent.amount += loan.total_staked.amount; + rt.total_lent.amount -= loan.total_staked.amount; + rt.total_lendable.amount = rt.total_unlent.amount + rt.total_lent.amount; + }); + } + + /** + * @brief Updates the fields of an existing loan that is being renewed + */ + template + int64_t system_contract::update_renewed_loan( Index& idx, const Iterator& itr, int64_t rented_tokens ) + { + int64_t delta_stake = rented_tokens - itr->total_staked.amount; + idx.modify ( itr, same_payer, [&]( auto& loan ) { + loan.total_staked.amount = rented_tokens; + loan.expiration += eosio::days(30); + loan.balance.amount -= loan.payment.amount; + }); + return delta_stake; + } + + /** + * @brief Performs maintenance operations on expired NET and CPU loans and sellrex orders + * + * @param max - maximum number of each of the three categories to be processed + */ + void system_contract::runrex( uint16_t max ) + { + check( rex_system_initialized(), "rex system not initialized yet" ); + + update_rex_pool(); + + const auto& pool = _rexpool.begin(); + + auto process_expired_loan = [&]( auto& idx, const auto& itr ) -> std::pair { + /// update rex_pool in order to delete existing loan + remove_loan_from_rex_pool( *itr ); + bool delete_loan = false; + int64_t delta_stake = 0; + /// calculate rented tokens at current price + int64_t rented_tokens = exchange_state::get_bancor_output( pool->total_rent.amount, + pool->total_unlent.amount, + itr->payment.amount ); + /// conditions for loan renewal + bool renew_loan = itr->payment <= itr->balance /// loan has sufficient balance + && itr->payment.amount < rented_tokens /// loan has favorable return + && rex_loans_available(); /// no pending sell orders + if ( renew_loan ) { + /// update rex_pool in order to account for renewed loan + add_loan_to_rex_pool( itr->payment, rented_tokens, false ); + /// update renewed loan fields + delta_stake = update_renewed_loan( idx, itr, rented_tokens ); + } else { + delete_loan = true; + delta_stake = -( itr->total_staked.amount ); + /// refund "from" account if the closed loan balance is positive + if ( itr->balance.amount > 0 ) { + transfer_to_fund( itr->from, itr->balance ); + } + } + + return { delete_loan, delta_stake }; + }; + + /// transfer from eosio.names to eosio.rex + if ( pool->namebid_proceeds.amount > 0 ) { + channel_to_rex( names_account, pool->namebid_proceeds ); + _rexpool.modify( pool, same_payer, [&]( auto& rt ) { + rt.namebid_proceeds.amount = 0; + }); + } + + /// process cpu loans + { + rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); + auto cpu_idx = cpu_loans.get_index<"byexpr"_n>(); + for ( uint16_t i = 0; i < max; ++i ) { + auto itr = cpu_idx.begin(); + if ( itr == cpu_idx.end() || itr->expiration > current_time_point() ) break; + + auto result = process_expired_loan( cpu_idx, itr ); + if ( result.second != 0 ) + update_resource_limits( itr->from, itr->receiver, 0, result.second ); + + if ( result.first ) + cpu_idx.erase( itr ); + } + } + + /// process net loans + { + rex_net_loan_table net_loans( get_self(), get_self().value ); + auto net_idx = net_loans.get_index<"byexpr"_n>(); + for ( uint16_t i = 0; i < max; ++i ) { + auto itr = net_idx.begin(); + if ( itr == net_idx.end() || itr->expiration > current_time_point() ) break; + + auto result = process_expired_loan( net_idx, itr ); + if ( result.second != 0 ) + update_resource_limits( itr->from, itr->receiver, result.second, 0 ); + + if ( result.first ) + net_idx.erase( itr ); + } + } + + /// process sellrex orders + if ( _rexorders.begin() != _rexorders.end() ) { + auto idx = _rexorders.get_index<"bytime"_n>(); + auto oitr = idx.begin(); + for ( uint16_t i = 0; i < max; ++i ) { + if ( oitr == idx.end() || !oitr->is_open ) break; + auto next = oitr; + ++next; + auto bitr = _rexbalance.find( oitr->owner.value ); + if ( bitr != _rexbalance.end() ) { // should always be true + auto result = fill_rex_order( bitr, oitr->rex_requested ); + if ( result.success ) { + const name order_owner = oitr->owner; + idx.modify( oitr, same_payer, [&]( auto& order ) { + order.proceeds.amount = result.proceeds.amount; + order.stake_change.amount = result.stake_change.amount; + order.close(); + }); + /// send dummy action to show owner and proceeds of filled sellrex order + rex_results::orderresult_action order_act( rex_account, std::vector{ } ); + order_act.send( order_owner, result.proceeds ); + } + } + oitr = next; + } + } + + } + + /** + * @brief Adds returns from the REX return pool to the REX pool + */ + void system_contract::update_rex_pool() + { + auto get_elapsed_intervals = [&]( const time_point_sec& t1, const time_point_sec& t0 ) -> uint32_t { + return ( t1.sec_since_epoch() - t0.sec_since_epoch() ) / rex_return_pool::dist_interval; + }; + + const time_point_sec ct = current_time_point(); + const uint32_t cts = ct.sec_since_epoch(); + const time_point_sec effective_time{cts - cts % rex_return_pool::dist_interval}; + + const auto ret_pool_elem = _rexretpool.begin(); + const auto ret_buckets_elem = _rexretbuckets.begin(); + + if ( ret_pool_elem == _rexretpool.end() || effective_time <= ret_pool_elem->last_dist_time ) { + return; + } + + const int64_t current_rate = ret_pool_elem->current_rate_of_increase; + const uint32_t elapsed_intervals = get_elapsed_intervals( effective_time, ret_pool_elem->last_dist_time ); + int64_t change_estimate = current_rate * elapsed_intervals; + + { + const bool new_return_bucket = ret_pool_elem->pending_bucket_time <= effective_time; + int64_t new_bucket_rate = 0; + time_point_sec new_bucket_time = time_point_sec::min(); + _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) { + if ( new_return_bucket ) { + int64_t remainder = rp.pending_bucket_proceeds % rex_return_pool::total_intervals; + new_bucket_rate = ( rp.pending_bucket_proceeds - remainder ) / rex_return_pool::total_intervals; + new_bucket_time = rp.pending_bucket_time; + rp.current_rate_of_increase += new_bucket_rate; + change_estimate += remainder + new_bucket_rate * get_elapsed_intervals( effective_time, rp.pending_bucket_time ); + rp.pending_bucket_proceeds = 0; + rp.pending_bucket_time = time_point_sec::maximum(); + if ( new_bucket_time < rp.oldest_bucket_time ) { + rp.oldest_bucket_time = new_bucket_time; + } + } + rp.proceeds -= change_estimate; + rp.last_dist_time = effective_time; + }); + + if ( new_return_bucket ) { + _rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) { + rb.return_buckets[new_bucket_time] = new_bucket_rate; + }); + } + } + + const time_point_sec time_threshold = effective_time - seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval); + if ( ret_pool_elem->oldest_bucket_time <= time_threshold ) { + int64_t expired_rate = 0; + int64_t surplus = 0; + _rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) { + auto& return_buckets = rb.return_buckets; + auto iter = return_buckets.begin(); + while ( iter != return_buckets.end() && iter->first <= time_threshold ) { + auto next = iter; + ++next; + const uint32_t overtime = get_elapsed_intervals( effective_time, + iter->first + seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval) ); + surplus += iter->second * overtime; + expired_rate += iter->second; + return_buckets.erase(iter); + iter = next; + } + }); + + _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) { + if ( !ret_buckets_elem->return_buckets.empty() ) { + rp.oldest_bucket_time = ret_buckets_elem->return_buckets.begin()->first; + } else { + rp.oldest_bucket_time = time_point_sec::min(); + } + if ( expired_rate > 0) { + rp.current_rate_of_increase -= expired_rate; + } + if ( surplus > 0 ) { + change_estimate -= surplus; + rp.proceeds += surplus; + } + }); + } + + if ( change_estimate > 0 && ret_pool_elem->proceeds < 0 ) { + _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) { + change_estimate += rp.proceeds; + rp.proceeds = 0; + }); + } + + if ( change_estimate > 0 ) { + _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& pool ) { + pool.total_unlent.amount += change_estimate; + pool.total_lendable = pool.total_unlent + pool.total_lent; + }); + } + } + + template + int64_t system_contract::rent_rex( T& table, const name& from, const name& receiver, const asset& payment, const asset& fund ) + { + runrex(2); + + check( rex_loans_available(), "rex loans are currently not available" ); + check( payment.symbol == core_symbol() && fund.symbol == core_symbol(), "must use core token" ); + check( 0 < payment.amount && 0 <= fund.amount, "must use positive asset amount" ); + + transfer_from_fund( from, payment + fund ); + + const auto& pool = _rexpool.begin(); /// already checked that _rexpool.begin() != _rexpool.end() in rex_loans_available() + + int64_t rented_tokens = exchange_state::get_bancor_output( pool->total_rent.amount, + pool->total_unlent.amount, + payment.amount ); + check( payment.amount < rented_tokens, "loan price does not favor renting" ); + add_loan_to_rex_pool( payment, rented_tokens, true ); + + table.emplace( from, [&]( auto& c ) { + c.from = from; + c.receiver = receiver; + c.payment = payment; + c.balance = fund; + c.total_staked = asset( rented_tokens, core_symbol() ); + c.expiration = current_time_point() + eosio::days(30); + c.loan_num = pool->loan_num; + }); + + rex_results::rentresult_action rentresult_act{ rex_account, std::vector{ } }; + rentresult_act.send( asset{ rented_tokens, core_symbol() } ); + return rented_tokens; + } + + /** + * @brief Processes a sellrex order and returns object containing the results + * + * Processes an incoming or already scheduled sellrex order. If REX pool has enough core + * tokens not frozen in loans, order is filled. In this case, REX pool totals, user rex_balance + * and user vote_stake are updated. However, this function does not update user voting power. The + * function returns success flag, order proceeds, and vote stake delta. These are used later in a + * different function to complete order processing, i.e. transfer proceeds to user REX fund and + * update user vote weight. + * + * @param bitr - iterator pointing to rex_balance database record + * @param rex - amount of rex to be sold + * + * @return rex_order_outcome - a struct containing success flag, order proceeds, and resultant + * vote stake change + */ + rex_order_outcome system_contract::fill_rex_order( const rex_balance_table::const_iterator& bitr, const asset& rex ) + { + auto rexitr = _rexpool.begin(); + const int64_t S0 = rexitr->total_lendable.amount; + const int64_t R0 = rexitr->total_rex.amount; + const int64_t p = (uint128_t(rex.amount) * S0) / R0; + const int64_t R1 = R0 - rex.amount; + const int64_t S1 = S0 - p; + asset proceeds( p, core_symbol() ); + asset stake_change( 0, core_symbol() ); + bool success = false; + + const int64_t unlent_lower_bound = rexitr->total_lent.amount / 10; + const int64_t available_unlent = rexitr->total_unlent.amount - unlent_lower_bound; // available_unlent <= 0 is possible + if ( proceeds.amount <= available_unlent ) { + const int64_t init_vote_stake_amount = bitr->vote_stake.amount; + const int64_t current_stake_value = ( uint128_t(bitr->rex_balance.amount) * S0 ) / R0; + _rexpool.modify( rexitr, same_payer, [&]( auto& rt ) { + rt.total_rex.amount = R1; + rt.total_lendable.amount = S1; + rt.total_unlent.amount = rt.total_lendable.amount - rt.total_lent.amount; + }); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + rb.vote_stake.amount = current_stake_value - proceeds.amount; + rb.rex_balance.amount -= rex.amount; + rb.matured_rex -= rex.amount; + }); + stake_change.amount = bitr->vote_stake.amount - init_vote_stake_amount; + success = true; + } else { + proceeds.amount = 0; + } + + return { success, proceeds, stake_change }; + } + + template + void system_contract::fund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& payment ) + { + check( payment.symbol == core_symbol(), "must use core token" ); + transfer_from_fund( from, payment ); + auto itr = table.require_find( loan_num, "loan not found" ); + check( itr->from == from, "user must be loan creator" ); + check( itr->expiration > current_time_point(), "loan has already expired" ); + table.modify( itr, same_payer, [&]( auto& loan ) { + loan.balance.amount += payment.amount; + }); + } + + template + void system_contract::defund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& amount ) + { + check( amount.symbol == core_symbol(), "must use core token" ); + auto itr = table.require_find( loan_num, "loan not found" ); + check( itr->from == from, "user must be loan creator" ); + check( itr->expiration > current_time_point(), "loan has already expired" ); + check( itr->balance >= amount, "insufficent loan balance" ); + table.modify( itr, same_payer, [&]( auto& loan ) { + loan.balance.amount -= amount.amount; + }); + transfer_to_fund( from, amount ); + } + + /** + * @brief Transfers tokens from owner REX fund + * + * @pre - owner REX fund has sufficient balance + * + * @param owner - owner account name + * @param amount - tokens to be transfered out of REX fund + */ + void system_contract::transfer_from_fund( const name& owner, const asset& amount ) + { + check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount from REX fund" ); + auto itr = _rexfunds.require_find( owner.value, "must deposit to REX fund first" ); + check( amount <= itr->balance, "insufficient funds" ); + _rexfunds.modify( itr, same_payer, [&]( auto& fund ) { + fund.balance.amount -= amount.amount; + }); + } + + /** + * @brief Transfers tokens to owner REX fund + * + * @param owner - owner account name + * @param amount - tokens to be transfered to REX fund + */ + void system_contract::transfer_to_fund( const name& owner, const asset& amount ) + { + check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount to REX fund" ); + auto itr = _rexfunds.find( owner.value ); + if ( itr == _rexfunds.end() ) { + _rexfunds.emplace( owner, [&]( auto& fund ) { + fund.owner = owner; + fund.balance = amount; + }); + } else { + _rexfunds.modify( itr, same_payer, [&]( auto& fund ) { + fund.balance.amount += amount.amount; + }); + } + } + + /** + * @brief Processes owner filled sellrex order and updates vote weight + * + * Checks if user has a scheduled sellrex order that has been filled, completes its processing, + * and deletes it. Processing entails transfering proceeds to user REX fund and updating user + * vote weight. Additional proceeds and stake change can be passed as arguments. This function + * is called only by actions pushed by owner. + * + * @param owner - owner account name + * @param proceeds - additional proceeds to be transfered to owner REX fund + * @param delta_stake - additional stake to be added to owner vote weight + * @param force_vote_update - if true, vote weight is updated even if vote stake didn't change + * + * @return asset - REX amount of owner unfilled sell order if one exists + */ + asset system_contract::update_rex_account( const name& owner, const asset& proceeds, const asset& delta_stake, bool force_vote_update ) + { + asset to_fund( proceeds ); + asset to_stake( delta_stake ); + asset rex_in_sell_order( 0, rex_symbol ); + auto itr = _rexorders.find( owner.value ); + if ( itr != _rexorders.end() ) { + if ( itr->is_open ) { + rex_in_sell_order.amount = itr->rex_requested.amount; + } else { + to_fund.amount += itr->proceeds.amount; + to_stake.amount += itr->stake_change.amount; + _rexorders.erase( itr ); + } + } + + if ( to_fund.amount > 0 ) + transfer_to_fund( owner, to_fund ); + if ( force_vote_update || to_stake.amount != 0 ) + update_voting_power( owner, to_stake ); + + return rex_in_sell_order; + } + + /** + * @brief Channels system fees to REX pool + * + * @param from - account from which asset is transfered to REX pool + * @param amount - amount of tokens to be transfered + */ + void system_contract::channel_to_rex( const name& from, const asset& amount ) + { +#if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX + if ( rex_available() ) { + add_to_rex_return_pool( amount ); + // inline transfer to rex_account + token::transfer_action transfer_act{ token_account, { from, active_permission } }; + transfer_act.send( from, rex_account, amount, + std::string("transfer from ") + from.to_string() + " to eosio.rex" ); + } +#endif + } + + /** + * @brief Updates namebid proceeds to be transfered to REX pool + * + * @param highest_bid - highest bidding amount of closed namebid + */ + void system_contract::channel_namebid_to_rex( const int64_t highest_bid ) + { +#if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX + if ( rex_available() ) { + _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rp ) { + rp.namebid_proceeds.amount += highest_bid; + }); + } +#endif + } + + /** + * @brief Calculates maturity time of purchased REX tokens which is 4 days from end + * of the day UTC + * + * @return time_point_sec + */ + time_point_sec system_contract::get_rex_maturity() + { + const uint32_t num_of_maturity_buckets = 5; + static const uint32_t now = current_time_point().sec_since_epoch(); + static const uint32_t r = now % seconds_per_day; + static const time_point_sec rms{ now - r + num_of_maturity_buckets * seconds_per_day }; + return rms; + } + + /** + * @brief Updates REX owner maturity buckets + * + * @param bitr - iterator pointing to rex_balance object + */ + void system_contract::process_rex_maturities( const rex_balance_table::const_iterator& bitr ) + { + const time_point_sec now = current_time_point(); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + while ( !rb.rex_maturities.empty() && rb.rex_maturities.front().first <= now ) { + rb.matured_rex += rb.rex_maturities.front().second; + rb.rex_maturities.pop_front(); + } + }); + } + + /** + * @brief Consolidates REX maturity buckets into one + * + * @param bitr - iterator pointing to rex_balance object + * @param rex_in_sell_order - REX tokens in owner unfilled sell order, if one exists + */ + void system_contract::consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, + const asset& rex_in_sell_order ) + { + const int64_t rex_in_savings = read_rex_savings( bitr ); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + int64_t total = rb.matured_rex - rex_in_sell_order.amount; + rb.matured_rex = rex_in_sell_order.amount; + while ( !rb.rex_maturities.empty() ) { + total += rb.rex_maturities.front().second; + rb.rex_maturities.pop_front(); + } + if ( total > 0 ) { + rb.rex_maturities.emplace_back( get_rex_maturity(), total ); + } + }); + put_rex_savings( bitr, rex_in_savings ); + } + + /** + * @brief Updates REX pool balances upon REX purchase + * + * @param payment - amount of core tokens paid + * + * @return asset - calculated amount of REX tokens purchased + */ + asset system_contract::add_to_rex_pool( const asset& payment ) + { + /** + * If CORE_SYMBOL is (EOS,4), maximum supply is 10^10 tokens (10 billion tokens), i.e., maximum amount + * of indivisible units is 10^14. rex_ratio = 10^4 sets the upper bound on (REX,4) indivisible units to + * 10^18 and that is within the maximum allowable amount field of asset type which is set to 2^62 + * (approximately 4.6 * 10^18). For a different CORE_SYMBOL, and in order for maximum (REX,4) amount not + * to exceed that limit, maximum amount of indivisible units cannot be set to a value larger than 4 * 10^14. + * If precision of CORE_SYMBOL is 4, that corresponds to a maximum supply of 40 billion tokens. + */ + const int64_t rex_ratio = 10000; + const asset init_total_rent( 20'000'0000, core_symbol() ); /// base balance prevents renting profitably until at least a minimum number of core_symbol() is made available + asset rex_received( 0, rex_symbol ); + auto itr = _rexpool.begin(); + if ( !rex_system_initialized() ) { + /// initialize REX pool + _rexpool.emplace( get_self(), [&]( auto& rp ) { + rex_received.amount = payment.amount * rex_ratio; + rp.total_lendable = payment; + rp.total_lent = asset( 0, core_symbol() ); + rp.total_unlent = rp.total_lendable - rp.total_lent; + rp.total_rent = init_total_rent; + rp.total_rex = rex_received; + rp.namebid_proceeds = asset( 0, core_symbol() ); + }); + } else if ( !rex_available() ) { /// should be a rare corner case, REX pool is initialized but empty + _rexpool.modify( itr, same_payer, [&]( auto& rp ) { + rex_received.amount = payment.amount * rex_ratio; + rp.total_lendable.amount = payment.amount; + rp.total_lent.amount = 0; + rp.total_unlent.amount = rp.total_lendable.amount - rp.total_lent.amount; + rp.total_rent.amount = init_total_rent.amount; + rp.total_rex.amount = rex_received.amount; + }); + } else { + /// total_lendable > 0 if total_rex > 0 except in a rare case and due to rounding errors + check( itr->total_lendable.amount > 0, "lendable REX pool is empty" ); + const int64_t S0 = itr->total_lendable.amount; + const int64_t S1 = S0 + payment.amount; + const int64_t R0 = itr->total_rex.amount; + const int64_t R1 = (uint128_t(S1) * R0) / S0; + rex_received.amount = R1 - R0; + _rexpool.modify( itr, same_payer, [&]( auto& rp ) { + rp.total_lendable.amount = S1; + rp.total_rex.amount = R1; + rp.total_unlent.amount = rp.total_lendable.amount - rp.total_lent.amount; + check( rp.total_unlent.amount >= 0, "programmer error, this should never go negative" ); + }); + } + + return rex_received; + } + + /** + * @brief Adds an amount of core tokens to the REX return pool + * + * @param fee - amount to be added + */ + void system_contract::add_to_rex_return_pool( const asset& fee ) + { + update_rex_pool(); + if ( fee.amount <= 0 ) { + return; + } + + const time_point_sec ct = current_time_point(); + const uint32_t cts = ct.sec_since_epoch(); + const uint32_t bucket_interval = rex_return_pool::hours_per_bucket * seconds_per_hour; + const time_point_sec effective_time{cts - cts % bucket_interval + bucket_interval}; + const auto return_pool_elem = _rexretpool.begin(); + if ( return_pool_elem == _rexretpool.end() ) { + _rexretpool.emplace( get_self(), [&]( auto& rp ) { + rp.last_dist_time = effective_time; + rp.pending_bucket_proceeds = fee.amount; + rp.pending_bucket_time = effective_time; + rp.proceeds = fee.amount; + }); + _rexretbuckets.emplace( get_self(), [&]( auto& rb ) { } ); + } else { + _rexretpool.modify( return_pool_elem, same_payer, [&]( auto& rp ) { + rp.pending_bucket_proceeds += fee.amount; + rp.proceeds += fee.amount; + if ( rp.pending_bucket_time == time_point_sec::maximum() ) { + rp.pending_bucket_time = effective_time; + } + }); + } + } + + /** + * @brief Updates owner REX balance upon buying REX tokens + * + * @param owner - account name of REX owner + * @param payment - amount core tokens paid to buy REX + * @param rex_received - amount of purchased REX tokens + * + * @return asset - change in owner REX vote stake + */ + asset system_contract::add_to_rex_balance( const name& owner, const asset& payment, const asset& rex_received ) + { + asset init_rex_stake( 0, core_symbol() ); + asset current_rex_stake( 0, core_symbol() ); + auto bitr = _rexbalance.find( owner.value ); + if ( bitr == _rexbalance.end() ) { + bitr = _rexbalance.emplace( owner, [&]( auto& rb ) { + rb.owner = owner; + rb.vote_stake = payment; + rb.rex_balance = rex_received; + }); + current_rex_stake.amount = payment.amount; + } else { + init_rex_stake.amount = bitr->vote_stake.amount; + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + rb.rex_balance.amount += rex_received.amount; + rb.vote_stake.amount = ( uint128_t(rb.rex_balance.amount) * _rexpool.begin()->total_lendable.amount ) + / _rexpool.begin()->total_rex.amount; + }); + current_rex_stake.amount = bitr->vote_stake.amount; + } + + const int64_t rex_in_savings = read_rex_savings( bitr ); + process_rex_maturities( bitr ); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + const time_point_sec maturity = get_rex_maturity(); + if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { + rb.rex_maturities.back().second += rex_received.amount; + } else { + rb.rex_maturities.emplace_back( maturity, rex_received.amount ); + } + }); + put_rex_savings( bitr, rex_in_savings ); + return current_rex_stake - init_rex_stake; + } + + /** + * @brief Reads amount of REX in savings bucket and removes the bucket from maturities + * + * Reads and (temporarily) removes REX savings bucket from REX maturities in order to + * allow uniform processing of remaining buckets as savings is a special case. This + * function is used in conjunction with put_rex_savings. + * + * @param bitr - iterator pointing to rex_balance object + * + * @return int64_t - amount of REX in savings bucket + */ + int64_t system_contract::read_rex_savings( const rex_balance_table::const_iterator& bitr ) + { + int64_t rex_in_savings = 0; + static const time_point_sec end_of_days = time_point_sec::maximum(); + if ( !bitr->rex_maturities.empty() && bitr->rex_maturities.back().first == end_of_days ) { + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + rex_in_savings = rb.rex_maturities.back().second; + rb.rex_maturities.pop_back(); + }); + } + return rex_in_savings; + } + + /** + * @brief Adds a specified REX amount to savings bucket + * + * @param bitr - iterator pointing to rex_balance object + * @param rex - amount of REX to be added + */ + void system_contract::put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex ) + { + if ( rex == 0 ) return; + static const time_point_sec end_of_days = time_point_sec::maximum(); + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) { + rb.rex_maturities.back().second += rex; + } else { + rb.rex_maturities.emplace_back( end_of_days, rex ); + } + }); + } + + /** + * @brief Updates voter REX vote stake to the current value of REX tokens held + * + * @param voter - account name of voter + */ + void system_contract::update_rex_stake( const name& voter ) + { + int64_t delta_stake = 0; + auto bitr = _rexbalance.find( voter.value ); + if ( bitr != _rexbalance.end() && rex_available() ) { + asset init_vote_stake = bitr->vote_stake; + asset current_vote_stake( 0, core_symbol() ); + current_vote_stake.amount = ( uint128_t(bitr->rex_balance.amount) * _rexpool.begin()->total_lendable.amount ) + / _rexpool.begin()->total_rex.amount; + _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { + rb.vote_stake.amount = current_vote_stake.amount; + }); + delta_stake = current_vote_stake.amount - init_vote_stake.amount; + } + + if ( delta_stake != 0 ) { + auto vitr = _voters.find( voter.value ); + if ( vitr != _voters.end() ) { + _voters.modify( vitr, same_payer, [&]( auto& vinfo ) { + vinfo.staked += delta_stake; + }); + } + } + } + +}; /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp new file mode 100644 index 0000000000..1aabca02ce --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp @@ -0,0 +1,11 @@ +#include + +void rex_results::buyresult( const asset& rex_received ) { } + +void rex_results::sellresult( const asset& proceeds ) { } + +void rex_results::orderresult( const name& owner, const asset& proceeds ) { } + +void rex_results::rentresult( const asset& rented_tokens ) { } + +extern "C" void apply( uint64_t, uint64_t, uint64_t ) { } diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp new file mode 100644 index 0000000000..53bb298757 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp @@ -0,0 +1,415 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace eosiosystem { + + using eosio::const_mem_fun; + using eosio::current_time_point; + using eosio::indexed_by; + using eosio::microseconds; + using eosio::singleton; + + void system_contract::register_producer( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ) { + auto prod = _producers.find( producer.value ); + const auto ct = current_time_point(); + + eosio::public_key producer_key{}; + + std::visit( [&](auto&& auth ) { + if( auth.keys.size() == 1 ) { + // if the producer_authority consists of a single key, use that key in the legacy producer_key field + producer_key = auth.keys[0].key; + } + }, producer_authority ); + + if ( prod != _producers.end() ) { + _producers.modify( prod, producer, [&]( producer_info& info ){ + info.producer_key = producer_key; + info.is_active = true; + info.url = url; + info.location = location; + info.producer_authority.emplace( producer_authority ); + if ( info.last_claim_time == time_point() ) + info.last_claim_time = ct; + }); + + auto prod2 = _producers2.find( producer.value ); + if ( prod2 == _producers2.end() ) { + _producers2.emplace( producer, [&]( producer_info2& info ){ + info.owner = producer; + info.last_votepay_share_update = ct; + }); + update_total_votepay_share( ct, 0.0, prod->total_votes ); + // When introducing the producer2 table row for the first time, the producer's votes must also be accounted for in the global total_producer_votepay_share at the same time. + } + } else { + _producers.emplace( producer, [&]( producer_info& info ){ + info.owner = producer; + info.total_votes = 0; + info.producer_key = producer_key; + info.is_active = true; + info.url = url; + info.location = location; + info.last_claim_time = ct; + info.producer_authority.emplace( producer_authority ); + }); + _producers2.emplace( producer, [&]( producer_info2& info ){ + info.owner = producer; + info.last_votepay_share_update = ct; + }); + } + + } + + void system_contract::regproducer( const name& producer, const eosio::public_key& producer_key, const std::string& url, uint16_t location ) { + require_auth( producer ); + check( url.size() < 512, "url too long" ); + + register_producer( producer, convert_to_block_signing_authority( producer_key ), url, location ); + } + + void system_contract::regproducer2( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ) { + require_auth( producer ); + check( url.size() < 512, "url too long" ); + + std::visit( [&](auto&& auth ) { + check( auth.is_valid(), "invalid producer authority" ); + }, producer_authority ); + + register_producer( producer, producer_authority, url, location ); + } + + void system_contract::unregprod( const name& producer ) { + require_auth( producer ); + + const auto& prod = _producers.get( producer.value, "producer not found" ); + _producers.modify( prod, same_payer, [&]( producer_info& info ){ + info.deactivate(); + }); + } + + void system_contract::update_elected_producers( const block_timestamp& block_time ) { + _gstate.last_producer_schedule_update = block_time; + + auto idx = _producers.get_index<"prototalvote"_n>(); + + using value_type = std::pair; + std::vector< value_type > top_producers; + top_producers.reserve(21); + + for( auto it = idx.cbegin(); it != idx.cend() && top_producers.size() < 21 && 0 < it->total_votes && it->active(); ++it ) { + top_producers.emplace_back( + eosio::producer_authority{ + .producer_name = it->owner, + .authority = it->get_producer_authority() + }, + it->location + ); + } + + if( top_producers.size() == 0 || top_producers.size() < _gstate.last_producer_schedule_size ) { + return; + } + + std::sort( top_producers.begin(), top_producers.end(), []( const value_type& lhs, const value_type& rhs ) { + return lhs.first.producer_name < rhs.first.producer_name; // sort by producer name + // return lhs.second < rhs.second; // sort by location + } ); + + std::vector producers; + + producers.reserve(top_producers.size()); + for( auto& item : top_producers ) + producers.push_back( std::move(item.first) ); + + if( set_proposed_producers( producers ) >= 0 ) { + _gstate.last_producer_schedule_size = static_cast( top_producers.size() ); + } + } + + double stake2vote( int64_t staked ) { + /// TODO subtract 2080 brings the large numbers closer to this decade + double weight = int64_t( (current_time_point().sec_since_epoch() - (block_timestamp::block_timestamp_epoch / 1000)) / (seconds_per_day * 7) ) / double( 52 ); + return double(staked) * std::pow( 2, weight ); + } + + double system_contract::update_total_votepay_share( const time_point& ct, + double additional_shares_delta, + double shares_rate_delta ) + { + double delta_total_votepay_share = 0.0; + if( ct > _gstate3.last_vpay_state_update ) { + delta_total_votepay_share = _gstate3.total_vpay_share_change_rate + * double( (ct - _gstate3.last_vpay_state_update).count() / 1E6 ); + } + + delta_total_votepay_share += additional_shares_delta; + if( delta_total_votepay_share < 0 && _gstate2.total_producer_votepay_share < -delta_total_votepay_share ) { + _gstate2.total_producer_votepay_share = 0.0; + } else { + _gstate2.total_producer_votepay_share += delta_total_votepay_share; + } + + if( shares_rate_delta < 0 && _gstate3.total_vpay_share_change_rate < -shares_rate_delta ) { + _gstate3.total_vpay_share_change_rate = 0.0; + } else { + _gstate3.total_vpay_share_change_rate += shares_rate_delta; + } + + _gstate3.last_vpay_state_update = ct; + + return _gstate2.total_producer_votepay_share; + } + + double system_contract::update_producer_votepay_share( const producers_table2::const_iterator& prod_itr, + const time_point& ct, + double shares_rate, + bool reset_to_zero ) + { + double delta_votepay_share = 0.0; + if( shares_rate > 0.0 && ct > prod_itr->last_votepay_share_update ) { + delta_votepay_share = shares_rate * double( (ct - prod_itr->last_votepay_share_update).count() / 1E6 ); // cannot be negative + } + + double new_votepay_share = prod_itr->votepay_share + delta_votepay_share; + _producers2.modify( prod_itr, same_payer, [&](auto& p) { + if( reset_to_zero ) + p.votepay_share = 0.0; + else + p.votepay_share = new_votepay_share; + + p.last_votepay_share_update = ct; + } ); + + return new_votepay_share; + } + + void system_contract::voteproducer( const name& voter_name, const name& proxy, const std::vector& producers ) { + require_auth( voter_name ); + vote_stake_updater( voter_name ); + update_votes( voter_name, proxy, producers, true ); + auto rex_itr = _rexbalance.find( voter_name.value ); + if( rex_itr != _rexbalance.end() && rex_itr->rex_balance.amount > 0 ) { + check_voting_requirement( voter_name, "voter holding REX tokens must vote for at least 21 producers or for a proxy" ); + } + } + + void system_contract::update_votes( const name& voter_name, const name& proxy, const std::vector& producers, bool voting ) { + //validate input + if ( proxy ) { + check( producers.size() == 0, "cannot vote for producers and proxy at same time" ); + check( voter_name != proxy, "cannot proxy to self" ); + } else { + check( producers.size() <= 30, "attempt to vote for too many producers" ); + for( size_t i = 1; i < producers.size(); ++i ) { + check( producers[i-1] < producers[i], "producer votes must be unique and sorted" ); + } + } + + auto voter = _voters.find( voter_name.value ); + check( voter != _voters.end(), "user must stake before they can vote" ); /// staking creates voter object + check( !proxy || !voter->is_proxy, "account registered as a proxy is not allowed to use a proxy" ); + + /** + * The first time someone votes we calculate and set last_vote_weight. Since they cannot unstake until + * after the chain has been activated, we can use last_vote_weight to determine that this is + * their first vote and should consider their stake activated. + */ + if( _gstate.thresh_activated_stake_time == time_point() && voter->last_vote_weight <= 0.0 ) { + _gstate.total_activated_stake += voter->staked; + if( _gstate.total_activated_stake >= min_activated_stake ) { + _gstate.thresh_activated_stake_time = current_time_point(); + } + } + + auto new_vote_weight = stake2vote( voter->staked ); + if( voter->is_proxy ) { + new_vote_weight += voter->proxied_vote_weight; + } + + std::map > producer_deltas; + if ( voter->last_vote_weight > 0 ) { + if( voter->proxy ) { + auto old_proxy = _voters.find( voter->proxy.value ); + check( old_proxy != _voters.end(), "old proxy not found" ); //data corruption + _voters.modify( old_proxy, same_payer, [&]( auto& vp ) { + vp.proxied_vote_weight -= voter->last_vote_weight; + }); + propagate_weight_change( *old_proxy ); + } else { + for( const auto& p : voter->producers ) { + auto& d = producer_deltas[p]; + d.first -= voter->last_vote_weight; + d.second = false; + } + } + } + + if( proxy ) { + auto new_proxy = _voters.find( proxy.value ); + check( new_proxy != _voters.end(), "invalid proxy specified" ); //if ( !voting ) { data corruption } else { wrong vote } + check( !voting || new_proxy->is_proxy, "proxy not found" ); + if ( new_vote_weight >= 0 ) { + _voters.modify( new_proxy, same_payer, [&]( auto& vp ) { + vp.proxied_vote_weight += new_vote_weight; + }); + propagate_weight_change( *new_proxy ); + } + } else { + if( new_vote_weight >= 0 ) { + for( const auto& p : producers ) { + auto& d = producer_deltas[p]; + d.first += new_vote_weight; + d.second = true; + } + } + } + + const auto ct = current_time_point(); + double delta_change_rate = 0.0; + double total_inactive_vpay_share = 0.0; + for( const auto& pd : producer_deltas ) { + auto pitr = _producers.find( pd.first.value ); + if( pitr != _producers.end() ) { + if( voting && !pitr->active() && pd.second.second /* from new set */ ) { + check( false, ( "producer " + pitr->owner.to_string() + " is not currently registered" ).data() ); + } + double init_total_votes = pitr->total_votes; + _producers.modify( pitr, same_payer, [&]( auto& p ) { + p.total_votes += pd.second.first; + if ( p.total_votes < 0 ) { // floating point arithmetics can give small negative numbers + p.total_votes = 0; + } + _gstate.total_producer_vote_weight += pd.second.first; + //check( p.total_votes >= 0, "something bad happened" ); + }); + auto prod2 = _producers2.find( pd.first.value ); + if( prod2 != _producers2.end() ) { + const auto last_claim_plus_3days = pitr->last_claim_time + microseconds(3 * useconds_per_day); + bool crossed_threshold = (last_claim_plus_3days <= ct); + bool updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update); + // Note: updated_after_threshold implies cross_threshold + + double new_votepay_share = update_producer_votepay_share( prod2, + ct, + updated_after_threshold ? 0.0 : init_total_votes, + crossed_threshold && !updated_after_threshold // only reset votepay_share once after threshold + ); + + if( !crossed_threshold ) { + delta_change_rate += pd.second.first; + } else if( !updated_after_threshold ) { + total_inactive_vpay_share += new_votepay_share; + delta_change_rate -= init_total_votes; + } + } + } else { + if( pd.second.second ) { + check( false, ( "producer " + pd.first.to_string() + " is not registered" ).data() ); + } + } + } + + update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate ); + + _voters.modify( voter, same_payer, [&]( auto& av ) { + av.last_vote_weight = new_vote_weight; + av.producers = producers; + av.proxy = proxy; + }); + } + + void system_contract::regproxy( const name& proxy, bool isproxy ) { + require_auth( proxy ); + + auto pitr = _voters.find( proxy.value ); + if ( pitr != _voters.end() ) { + check( isproxy != pitr->is_proxy, "action has no effect" ); + check( !isproxy || !pitr->proxy, "account that uses a proxy is not allowed to become a proxy" ); + _voters.modify( pitr, same_payer, [&]( auto& p ) { + p.is_proxy = isproxy; + }); + propagate_weight_change( *pitr ); + } else { + _voters.emplace( proxy, [&]( auto& p ) { + p.owner = proxy; + p.is_proxy = isproxy; + }); + } + } + + void system_contract::propagate_weight_change( const voter_info& voter ) { + check( !voter.proxy || !voter.is_proxy, "account registered as a proxy is not allowed to use a proxy" ); + double new_weight = stake2vote( voter.staked ); + if ( voter.is_proxy ) { + new_weight += voter.proxied_vote_weight; + } + + /// don't propagate small changes (1 ~= epsilon) + if ( fabs( new_weight - voter.last_vote_weight ) > 1 ) { + if ( voter.proxy ) { + auto& proxy = _voters.get( voter.proxy.value, "proxy not found" ); //data corruption + _voters.modify( proxy, same_payer, [&]( auto& p ) { + p.proxied_vote_weight += new_weight - voter.last_vote_weight; + } + ); + propagate_weight_change( proxy ); + } else { + auto delta = new_weight - voter.last_vote_weight; + const auto ct = current_time_point(); + double delta_change_rate = 0; + double total_inactive_vpay_share = 0; + for ( auto acnt : voter.producers ) { + auto& prod = _producers.get( acnt.value, "producer not found" ); //data corruption + const double init_total_votes = prod.total_votes; + _producers.modify( prod, same_payer, [&]( auto& p ) { + p.total_votes += delta; + _gstate.total_producer_vote_weight += delta; + }); + auto prod2 = _producers2.find( acnt.value ); + if ( prod2 != _producers2.end() ) { + const auto last_claim_plus_3days = prod.last_claim_time + microseconds(3 * useconds_per_day); + bool crossed_threshold = (last_claim_plus_3days <= ct); + bool updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update); + // Note: updated_after_threshold implies cross_threshold + + double new_votepay_share = update_producer_votepay_share( prod2, + ct, + updated_after_threshold ? 0.0 : init_total_votes, + crossed_threshold && !updated_after_threshold // only reset votepay_share once after threshold + ); + + if( !crossed_threshold ) { + delta_change_rate += delta; + } else if( !updated_after_threshold ) { + total_inactive_vpay_share += new_votepay_share; + delta_change_rate -= init_total_votes; + } + } + } + + update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate ); + } + } + _voters.modify( voter, same_payer, [&]( auto& v ) { + v.last_vote_weight = new_weight; + } + ); + } + +} /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt new file mode 100644 index 0000000000..cf53f62cd8 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt @@ -0,0 +1,13 @@ +add_contract(eosio.token eosio.token ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.token.cpp) + +target_include_directories(eosio.token + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set_target_properties(eosio.token + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.token.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.token.contracts.md @ONLY ) + +target_compile_options( eosio.token PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp new file mode 100644 index 0000000000..56e17d9851 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp @@ -0,0 +1,146 @@ +#pragma once + +#include +#include + +#include + +namespace eosiosystem { + class system_contract; +} + +namespace eosio { + + using std::string; + + /** + * The `eosio.token` sample system contract defines the structures and actions that allow users to create, issue, and manage tokens for EOSIO based blockchains. It demonstrates one way to implement a smart contract which allows for creation and management of tokens. It is possible for one to create a similar contract which suits different needs. However, it is recommended that if one only needs a token with the below listed actions, that one uses the `eosio.token` contract instead of developing their own. + * + * The `eosio.token` contract class also implements two useful public static methods: `get_supply` and `get_balance`. The first allows one to check the total supply of a specified token, created by an account and the second allows one to check the balance of a token for a specified account (the token creator account has to be specified as well). + * + * The `eosio.token` contract manages the set of tokens, accounts and their corresponding balances, by using two internal multi-index structures: the `accounts` and `stats`. The `accounts` multi-index table holds, for each row, instances of `account` object and the `account` object holds information about the balance of one token. The `accounts` table is scoped to an EOSIO account, and it keeps the rows indexed based on the token's symbol. This means that when one queries the `accounts` multi-index table for an account name the result is all the tokens that account holds at the moment. + * + * Similarly, the `stats` multi-index table, holds instances of `currency_stats` objects for each row, which contains information about current supply, maximum supply, and the creator account for a symbol token. The `stats` table is scoped to the token symbol. Therefore, when one queries the `stats` table for a token symbol the result is one single entry/row corresponding to the queried symbol token if it was previously created, or nothing, otherwise. + */ + class [[eosio::contract("eosio.token")]] token : public contract { + public: + using contract::contract; + + /** + * Allows `issuer` account to create a token in supply of `maximum_supply`. If validation is successful a new entry in statstable for token symbol scope gets created. + * + * @param issuer - the account that creates the token, + * @param maximum_supply - the maximum supply set for the token created. + * + * @pre Token symbol has to be valid, + * @pre Token symbol must not be already created, + * @pre maximum_supply has to be smaller than the maximum supply allowed by the system: 1^62 - 1. + * @pre Maximum supply must be positive; + */ + [[eosio::action]] + void create( const name& issuer, + const asset& maximum_supply); + /** + * This action issues to `to` account a `quantity` of tokens. + * + * @param to - the account to issue tokens to, it must be the same as the issuer, + * @param quntity - the amount of tokens to be issued, + * @memo - the memo string that accompanies the token issue transaction. + */ + [[eosio::action]] + void issue( const name& to, const asset& quantity, const string& memo ); + + /** + * The opposite for create action, if all validations succeed, + * it debits the statstable.supply amount. + * + * @param quantity - the quantity of tokens to retire, + * @param memo - the memo string to accompany the transaction. + */ + [[eosio::action]] + void retire( const asset& quantity, const string& memo ); + + /** + * Allows `from` account to transfer to `to` account the `quantity` tokens. + * One account is debited and the other is credited with quantity tokens. + * + * @param from - the account to transfer from, + * @param to - the account to be transferred to, + * @param quantity - the quantity of tokens to be transferred, + * @param memo - the memo string to accompany the transaction. + */ + [[eosio::action]] + void transfer( const name& from, + const name& to, + const asset& quantity, + const string& memo ); + /** + * Allows `ram_payer` to create an account `owner` with zero balance for + * token `symbol` at the expense of `ram_payer`. + * + * @param owner - the account to be created, + * @param symbol - the token to be payed with by `ram_payer`, + * @param ram_payer - the account that supports the cost of this action. + * + * More information can be read [here](https://github.com/EOSIO/eosio.contracts/issues/62) + * and [here](https://github.com/EOSIO/eosio.contracts/issues/61). + */ + [[eosio::action]] + void open( const name& owner, const symbol& symbol, const name& ram_payer ); + + /** + * This action is the opposite for open, it closes the account `owner` + * for token `symbol`. + * + * @param owner - the owner account to execute the close action for, + * @param symbol - the symbol of the token to execute the close action for. + * + * @pre The pair of owner plus symbol has to exist otherwise no action is executed, + * @pre If the pair of owner plus symbol exists, the balance has to be zero. + */ + [[eosio::action]] + void close( const name& owner, const symbol& symbol ); + + static asset get_supply( const name& token_contract_account, const symbol_code& sym_code ) + { + stats statstable( token_contract_account, sym_code.raw() ); + const auto& st = statstable.get( sym_code.raw() ); + return st.supply; + } + + static asset get_balance( const name& token_contract_account, const name& owner, const symbol_code& sym_code ) + { + accounts accountstable( token_contract_account, owner.value ); + const auto& ac = accountstable.get( sym_code.raw() ); + return ac.balance; + } + + using create_action = eosio::action_wrapper<"create"_n, &token::create>; + using issue_action = eosio::action_wrapper<"issue"_n, &token::issue>; + using retire_action = eosio::action_wrapper<"retire"_n, &token::retire>; + using transfer_action = eosio::action_wrapper<"transfer"_n, &token::transfer>; + using open_action = eosio::action_wrapper<"open"_n, &token::open>; + using close_action = eosio::action_wrapper<"close"_n, &token::close>; + private: + struct [[eosio::table]] account { + asset balance; + + uint64_t primary_key()const { return balance.symbol.code().raw(); } + }; + + struct [[eosio::table]] currency_stats { + asset supply; + asset max_supply; + name issuer; + + uint64_t primary_key()const { return supply.symbol.code().raw(); } + }; + + typedef eosio::multi_index< "accounts"_n, account > accounts; + typedef eosio::multi_index< "stat"_n, currency_stats > stats; + + void sub_balance( const name& owner, const asset& value ); + void add_balance( const name& owner, const asset& value, const name& ram_payer ); + }; + +} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in new file mode 100644 index 0000000000..f050eec773 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in @@ -0,0 +1,95 @@ +

close

+ +--- +spec_version: "0.2.0" +title: Close Token Balance +summary: 'Close {{nowrap owner}}’s zero quantity balance' +icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ +--- + +{{owner}} agrees to close their zero quantity balance for the {{symbol_to_symbol_code symbol}} token. + +RAM will be refunded to the RAM payer of the {{symbol_to_symbol_code symbol}} token balance for {{owner}}. + +

create

+ +--- +spec_version: "0.2.0" +title: Create New Token +summary: 'Create a new token' +icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ +--- + +{{$action.account}} agrees to create a new token with symbol {{asset_to_symbol_code maximum_supply}} to be managed by {{issuer}}. + +This action will not result any any tokens being issued into circulation. + +{{issuer}} will be allowed to issue tokens into circulation, up to a maximum supply of {{maximum_supply}}. + +RAM will deducted from {{$action.account}}’s resources to create the necessary records. + +

issue

+ +--- +spec_version: "0.2.0" +title: Issue Tokens into Circulation +summary: 'Issue {{nowrap quantity}} into circulation and transfer into {{nowrap to}}’s account' +icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ +--- + +The token manager agrees to issue {{quantity}} into circulation, and transfer it into {{to}}’s account. + +{{#if memo}}There is a memo attached to the transfer stating: +{{memo}} +{{/if}} + +If {{to}} does not have a balance for {{asset_to_symbol_code quantity}}, or the token manager does not have a balance for {{asset_to_symbol_code quantity}}, the token manager will be designated as the RAM payer of the {{asset_to_symbol_code quantity}} token balance for {{to}}. As a result, RAM will be deducted from the token manager’s resources to create the necessary records. + +This action does not allow the total quantity to exceed the max allowed supply of the token. + +

open

+ +--- +spec_version: "0.2.0" +title: Open Token Balance +summary: 'Open a zero quantity balance for {{nowrap owner}}' +icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ +--- + +{{ram_payer}} agrees to establish a zero quantity balance for {{owner}} for the {{symbol_to_symbol_code symbol}} token. + +If {{owner}} does not have a balance for {{symbol_to_symbol_code symbol}}, {{ram_payer}} will be designated as the RAM payer of the {{symbol_to_symbol_code symbol}} token balance for {{owner}}. As a result, RAM will be deducted from {{ram_payer}}’s resources to create the necessary records. + +

retire

+ +--- +spec_version: "0.2.0" +title: Remove Tokens from Circulation +summary: 'Remove {{nowrap quantity}} from circulation' +icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ +--- + +The token manager agrees to remove {{quantity}} from circulation, taken from their own account. + +{{#if memo}} There is a memo attached to the action stating: +{{memo}} +{{/if}} + +

transfer

+ +--- +spec_version: "0.2.0" +title: Transfer Tokens +summary: 'Send {{nowrap quantity}} from {{nowrap from}} to {{nowrap to}}' +icon: @ICON_BASE_URL@/@TRANSFER_ICON_URI@ +--- + +{{from}} agrees to send {{quantity}} to {{to}}. + +{{#if memo}}There is a memo attached to the transfer stating: +{{memo}} +{{/if}} + +If {{from}} is not already the RAM payer of their {{asset_to_symbol_code quantity}} token balance, {{from}} will be designated as such. As a result, RAM will be deducted from {{from}}’s resources to refund the original RAM payer. + +If {{to}} does not have a balance for {{asset_to_symbol_code quantity}}, {{from}} will be designated as the RAM payer of the {{asset_to_symbol_code quantity}} token balance for {{to}}. As a result, RAM will be deducted from {{from}}’s resources to create the necessary records. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp new file mode 100644 index 0000000000..8dda907e3d --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp @@ -0,0 +1,159 @@ +#include + +namespace eosio { + +void token::create( const name& issuer, + const asset& maximum_supply ) +{ + require_auth( get_self() ); + + auto sym = maximum_supply.symbol; + check( sym.is_valid(), "invalid symbol name" ); + check( maximum_supply.is_valid(), "invalid supply"); + check( maximum_supply.amount > 0, "max-supply must be positive"); + + stats statstable( get_self(), sym.code().raw() ); + auto existing = statstable.find( sym.code().raw() ); + check( existing == statstable.end(), "token with symbol already exists" ); + + statstable.emplace( get_self(), [&]( auto& s ) { + s.supply.symbol = maximum_supply.symbol; + s.max_supply = maximum_supply; + s.issuer = issuer; + }); +} + + +void token::issue( const name& to, const asset& quantity, const string& memo ) +{ + auto sym = quantity.symbol; + check( sym.is_valid(), "invalid symbol name" ); + check( memo.size() <= 256, "memo has more than 256 bytes" ); + + stats statstable( get_self(), sym.code().raw() ); + auto existing = statstable.find( sym.code().raw() ); + check( existing != statstable.end(), "token with symbol does not exist, create token before issue" ); + const auto& st = *existing; + check( to == st.issuer, "tokens can only be issued to issuer account" ); + + require_auth( st.issuer ); + check( quantity.is_valid(), "invalid quantity" ); + check( quantity.amount > 0, "must issue positive quantity" ); + + check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); + check( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply"); + + statstable.modify( st, same_payer, [&]( auto& s ) { + s.supply += quantity; + }); + + add_balance( st.issuer, quantity, st.issuer ); +} + +void token::retire( const asset& quantity, const string& memo ) +{ + auto sym = quantity.symbol; + check( sym.is_valid(), "invalid symbol name" ); + check( memo.size() <= 256, "memo has more than 256 bytes" ); + + stats statstable( get_self(), sym.code().raw() ); + auto existing = statstable.find( sym.code().raw() ); + check( existing != statstable.end(), "token with symbol does not exist" ); + const auto& st = *existing; + + require_auth( st.issuer ); + check( quantity.is_valid(), "invalid quantity" ); + check( quantity.amount > 0, "must retire positive quantity" ); + + check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); + + statstable.modify( st, same_payer, [&]( auto& s ) { + s.supply -= quantity; + }); + + sub_balance( st.issuer, quantity ); +} + +void token::transfer( const name& from, + const name& to, + const asset& quantity, + const string& memo ) +{ + check( from != to, "cannot transfer to self" ); + require_auth( from ); + check( is_account( to ), "to account does not exist"); + auto sym = quantity.symbol.code(); + stats statstable( get_self(), sym.raw() ); + const auto& st = statstable.get( sym.raw() ); + + require_recipient( from ); + require_recipient( to ); + + check( quantity.is_valid(), "invalid quantity" ); + check( quantity.amount > 0, "must transfer positive quantity" ); + check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); + check( memo.size() <= 256, "memo has more than 256 bytes" ); + + auto payer = has_auth( to ) ? to : from; + + sub_balance( from, quantity ); + add_balance( to, quantity, payer ); +} + +void token::sub_balance( const name& owner, const asset& value ) { + accounts from_acnts( get_self(), owner.value ); + + const auto& from = from_acnts.get( value.symbol.code().raw(), "no balance object found" ); + check( from.balance.amount >= value.amount, "overdrawn balance" ); + + from_acnts.modify( from, owner, [&]( auto& a ) { + a.balance -= value; + }); +} + +void token::add_balance( const name& owner, const asset& value, const name& ram_payer ) +{ + accounts to_acnts( get_self(), owner.value ); + auto to = to_acnts.find( value.symbol.code().raw() ); + if( to == to_acnts.end() ) { + to_acnts.emplace( ram_payer, [&]( auto& a ){ + a.balance = value; + }); + } else { + to_acnts.modify( to, same_payer, [&]( auto& a ) { + a.balance += value; + }); + } +} + +void token::open( const name& owner, const symbol& symbol, const name& ram_payer ) +{ + require_auth( ram_payer ); + + check( is_account( owner ), "owner account does not exist" ); + + auto sym_code_raw = symbol.code().raw(); + stats statstable( get_self(), sym_code_raw ); + const auto& st = statstable.get( sym_code_raw, "symbol does not exist" ); + check( st.supply.symbol == symbol, "symbol precision mismatch" ); + + accounts acnts( get_self(), owner.value ); + auto it = acnts.find( sym_code_raw ); + if( it == acnts.end() ) { + acnts.emplace( ram_payer, [&]( auto& a ){ + a.balance = asset{0, symbol}; + }); + } +} + +void token::close( const name& owner, const symbol& symbol ) +{ + require_auth( owner ); + accounts acnts( get_self(), owner.value ); + auto it = acnts.find( symbol.code().raw() ); + check( it != acnts.end(), "Balance row already deleted or never existed. Action won't have any effect." ); + check( it->balance.amount == 0, "Cannot close because the balance is not zero." ); + acnts.erase( it ); +} + +} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt new file mode 100644 index 0000000000..27d4291947 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt @@ -0,0 +1,13 @@ +add_contract(eosio.wrap eosio.wrap ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.wrap.cpp) + +target_include_directories(eosio.wrap + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +set_target_properties(eosio.wrap + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.wrap.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.wrap.contracts.md @ONLY ) + +target_compile_options( eosio.wrap PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp new file mode 100644 index 0000000000..9c6134df8e --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace eosio { + /** + * The `eosio.wrap` system contract allows block producers to bypass authorization checks or run privileged actions with 15/21 producer approval and thus simplifies block producers superuser actions. It also makes these actions easier to audit. + * + * It does not give block producers any additional powers or privileges that do not already exist within the EOSIO based blockchains. As it is implemented, in an EOSIO based blockchain, 15/21 block producers can change an account's permissions or modify an account's contract code if they decided it is beneficial for the blockchain and community. However, the current method is opaque and leaves undesirable side effects on specific system accounts, and thus the `eosio.wrap `contract solves this matter by providing an easier method of executing important governance actions. + * + * The only action implemented by the `eosio.wrap` system contract is the `exec` action. This action allows for execution of a transaction, which is passed to the `exec` method in the form of a packed transaction in json format via the 'trx' parameter and the `executer` account that executes the transaction. The same `executer` account will also be used to pay the RAM and CPU fees needed to execute the transaction. + */ + class [[eosio::contract("eosio.wrap")]] wrap : public contract { + public: + using contract::contract; + + /** + * Execute action. + * + * Execute a transaction while bypassing regular authorization checks. + * + * Preconditions: + * - Requires authorization of eosio.wrap which needs to be a privileged account. + * + * Postconditions: + * - Deferred transaction RAM usage is billed to 'executer' + * + * @param executer - account executing the transaction, + * @param trx - the transaction to be executed. + */ + [[eosio::action]] + void exec( ignore executer, ignore trx ); + + using exec_action = eosio::action_wrapper<"exec"_n, &wrap::exec>; + }; +} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in new file mode 100644 index 0000000000..8077a34729 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in @@ -0,0 +1,13 @@ +

exec

+ +--- +spec_version: "0.2.0" +title: Privileged Execute +summary: '{{nowrap executer}} executes a transaction while bypassing authority checks' +icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ +--- + +{{executer}} executes the following transaction while bypassing authority checks: +{{to_json trx}} + +{{$action.account}} must also authorize this action. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp new file mode 100644 index 0000000000..12056bf1e0 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp @@ -0,0 +1,16 @@ +#include + +namespace eosio { + +void wrap::exec( ignore, ignore ) { + require_auth( get_self() ); + + name executer; + _ds >> executer; + + require_auth( executer ); + + send_deferred( (uint128_t(executer.value) << 64) | (uint64_t)current_time_point().time_since_epoch().count(), executer, _ds.pos(), _ds.remaining() ); +} + +} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/icons/account.png b/tests/unit/test_contracts/eosio.contracts/icons/account.png new file mode 100644 index 0000000000000000000000000000000000000000..7686587824ac663ffb79f3ee30d06821a26e6c26 GIT binary patch literal 3795 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 JPDHLkV1nnf{$~IH literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/account.svg b/tests/unit/test_contracts/eosio.contracts/icons/account.svg new file mode 100644 index 0000000000..511f064991 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/account.svg @@ -0,0 +1 @@ +Account \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/admin.png b/tests/unit/test_contracts/eosio.contracts/icons/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..ae95364725ed2e9f6143044631d2ff136201fa2b GIT binary patch literal 2937 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 V=0Y3_AOE>PN6S$2rMg|je*nyqJZk^| literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/admin.svg b/tests/unit/test_contracts/eosio.contracts/icons/admin.svg new file mode 100644 index 0000000000..fbc057179e --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/admin.svg @@ -0,0 +1 @@ +Admin \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/multisig.png b/tests/unit/test_contracts/eosio.contracts/icons/multisig.png new file mode 100644 index 0000000000000000000000000000000000000000..00fa7564c88ea2785f53d641f150b32ec68a7b47 GIT binary patch literal 1411 zcmZ`%doqG{65Om;k{$VjOf%}jL|q`{EKZ_{CyDS4zk z(jazeJThn}$`&WHgx7r|KPRBU)0;w2-ok=^j-Q{e~v!@bbksTMV)7UQT}8uv~^<) zI$fbW_lQS}W+oVL*;zwR1mJXycr06F{D&lRfmxK^r3bp`3GuFvkN6LW)?nAOQ93fq z{NKv|vE^}24I13&Wh`o#ToS$=*UB6&S+wz!O>_dYbMCipI)V7ntxsjw*ty)zAVSf@ z&_Dim2n9$=tBf-Io~;tQ+n($H@(}~A^u%D4Pc8_u^V;B0OaS5lReOLObZmOx;;G}#;8*|+;c2be4I3`gN zhA*dU-77eHcf@h4FR%G z^aZ0lMorCFr^CEtC{Ygw9Xv9H7uht5$`Kn7A&0(ix?>tq_wz@c;Czv+$H0B<@fC5_ z9^O&Zp-ZogJ!__ydf~*kd1zwdI~!h10hy_Vp5G`5#au9wFm3rbh33#LfS1xF(Rp3d zJI;SCx#J!;ZMoY>*CG>nzF~t|Qfl^Un0IA!!>OAI5=x`k}(g&Oz9Nt*cBh=0VG`vkBgj_>ji16&N=NetoxA@plV?sUDV0z&_Nds zzHBn;p*55~yBEcF0$Ep$~xv06jiA|H79p$LBE)8|Qx+v=u(O)N^t zRtH#rkk{syuG>An9-Go<{SB_$5xc_H^jz1y*RJg1-A7NY4@lW*x7xn&+;8m~?QvN>T-E3}C(Y{}`$>K-Ikm~!K&AM#mwr|S`X<*0X7g}z{(qH(X( z31&CWc{@Zxd}+R_Q0^=S3M|f29sr(*nyMR5^)AyiJZyIQWmk5p1~sg^zjorepylGp zgj$P1Xqrvaelf)=g)FDMUFW5j)@VI^UT~$UKB*WO%k%fCyfRP-|G?X8V*)#&nfEj6 YBtQ`z6CivXCH{CNh3rMDa-pC88^Gy0WB>pF literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/multisig.svg b/tests/unit/test_contracts/eosio.contracts/icons/multisig.svg new file mode 100644 index 0000000000..d9167fb2cb --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/multisig.svg @@ -0,0 +1 @@ +Multi Sig \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/resource.png b/tests/unit/test_contracts/eosio.contracts/icons/resource.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1bd127de7b7adb74c5d5765db70a036fd0aa8f GIT binary patch literal 1913 zcmZ`)X;72r7Jk3v3rUbAhzdcFutg}ftU*A)ghh$S9s{zJkjPRZ5D~&=hYv_)5n2{6 z?uyE-vKAEx2$cwEr6L6t1QZDr1=%H(2(sKvXYSnktNTnXHn@iK#cQN17U1Bia1wiuy-wzn?2E zO-ntqxw)ymt=dhtt`hI#;VVnOFkn!9fenCME8UsKNE(_M9=v~(qySh)w|l>qrVZJg zWJY=qo{cPeD#^dHs%8Ln%;nhGqDbIxAel~zjS$&rI~(y%4F0VbW!k&0TgYU^kI^7QI zo*6`4>LJIf#+h74bp(8*sJsY1!NVw#taO-eetMQ@tT+L-otSu);kn-hVIP10>p&D- z3f(ul5tG}Q_^k1w;Sgj_HPvB`hA@^T9_6g|-^@hd`i`iKiI3SI`ntBxSRW99d3O=q zP5Y*IHc25^-aZHs&RbzU_E+!a%=;B!6cz$hjRN~GWCf%jDS&qU;SLoZ8BL{^S$aMJ zvG_a2q=_yQ)9i6ctQ@I7HuI;9O=%r|ui>TGwPKO6hz4YLz4g#>Mbg>nB%;Krc;k zR%ztmV@_fbb&zA*#|GXLy9WX(st#$xl9iKl-;X;oRU=Wg!l=im;~e%!TV7u)2*HSA zQSEcc+%3+V}|AVxANkq;~ra21-~uijQef``8a|4 z&-&>(->x)nJ4`Nvz8<3-<7@ax=_uSgo-XnV9yzzx$BAQd2|&#x=RFpP%){dFU4g z11agtk9rcj@~uLuaHTHZXKVOT%3c;JGhib60d6~$g|)86Bbc56s5^jyR-C4nn8R{BxO?@e^c|@teZ#tpFnYf3RS&Rs##B z%BcGvOhC!--W#CfiktR-Pu<@K&lU2b6nVw2ED@3mPG;xqYtw{874!0{fhjgw&M^!U zj%EKc(i?r){I2GM-J;jr%Y{%&>tmvt=)AdZyc2S#U6Ez`1{~7EinU@8T0d-9ZGU%D zSUt-X8aJs$H=l|QrIbPobD(7Q7nS=&`YmVoJcrNkYaiBSJ4N+EnJG78OmCF zZzz@pBbvzmLMdv80n$wSGfzd7Z0|j}&X8gk__t4`IjLr_pJ*%1xf7|&sZRZ1eAKgfsSDGj%m`;!J#Cf65piZ5<5Rj8_2@Y8)GI^_-~#s_AB12g`?0(nO|i|= z6Ud<^(@2>m-riQ(?mte8aom~M;W`cD5rF##;vmb`SZ9cY|AMnmyAqUY7IRlA zQBl*e^=#?A5N5W3!wvOd3raS|O&oszyqWP%t4 lQzThj4saQu7QLp*0xy<3x9!Ue2$20bKzH$Qu5n~#{0CM4EzSS{ literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/resource.svg b/tests/unit/test_contracts/eosio.contracts/icons/resource.svg new file mode 100644 index 0000000000..4a47559285 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/resource.svg @@ -0,0 +1 @@ +Resource \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/rex.png b/tests/unit/test_contracts/eosio.contracts/icons/rex.png new file mode 100644 index 0000000000000000000000000000000000000000..b43ee27fcf6180649c35b0739a74e37bc34d08ea GIT binary patch literal 2770 zcmY*bX*|>m7e4=)F=lKr_9X^c5*pdU7$aMjkmMapLP;fYBfI=YtgOC*`shx_F{&xhwczu!5}^YNUEuFiJC0&)TXAZ)+i+6@5NY7rn{ zwmIs4FD$=YE#O`*&*On;0F1VU3a0Dw-F=0SD53$%h6o-`sH_D_A8ch#^tdT zKEty!sZf3nV(yfa!Zl!?dcIxo9`HcP)R{&)1Oy3fc6fSMIq&(1B_Vl z9vCd8kKoYcgTC3-#_8;0wER>bww2lBt6Kk@pln`Lf;ip7hNvjC92ss&3D|ulEl(Ze z=g~-GEf8mo?R=us&+#H~=#UTi(}%w8D!4AJ ziwjEP(Pl5qv6_d10a(|$$}Ibz0V&?0Dbv;zpRDelVo>O7@S%%vtzdIHP>oE$Ugqb} z8r-$X4)G7v>8pF1?9eNBhmS5U8bD5?C_m@>#u;ek_7@^WcAp6kN%nwnkUx+;Duhd<#r}b%ahfm5zo7o0+b>f#0>Z5LazX`u=S0hD7o&S7-kO=pZADN zUe)>}CQ8f}=U+c&Q7QdNh0ZT^oxl*EqE7473X)gnQbjR&Bet^$rR300Jpv^|@x2*n zwmnh642bL3(vnO;o4^fnAId2vK_bqlCZkh&lV5Qjj>w7Y zwb;BOWr`#g?HeI3-T{_*Ouag2@2+#o0_sJ!5l|TTeyDJYoKZ(KyQNR#06U&!3)-{v zK6SwCO!5F%&Awg%RJKFT2n!vnMp!?1>ZcE}`XE_( zbnX@&&a?)fxrB~mDF%;hjD%M{xizi7a8n}-aTV*O!zj^IGQ!O;MFjws@uJX4X zr5Q`<_vTjLz@g^1Hx~~LaGht@s#2Sw^FWOdhW@R*z50d#YIu)Vpw%3CIgIPE2WGC% z1|ClT7ApTK4=)l$Q*0CP{u{LhR&te-6qK3V%Zu`|Leg2~nHq}sYNWaSRwa)S#^xidGr9HoJ_54o9nUqrYWAF>1iALlq5QiS(w=tDnx!q^*+Sh%T0Rl{>> zo?H(+DC9T(lL4R3riAZWPWU}2G_AT+^n6;b%3tHL{@$KYad?!7yV}>gf;3%y5~zcO znGdyGJWD{!2}4HS)mVt;gciPq3Ug*=+6xb{$Z4o@0R=r`)>Oct-VU7K!;Ve6?zjU@ zp^e#WfVFFn=8=!XvBuOs@XMPf3?2_|kha-{jKnI=B;u~<%UyU~Sl4``;5KB`dZ~}j zZ(Myiq_O)(ATF935kjJ={-feT?acRJBoVmk?Vp|1^kN&cJMS}Mo6OXayUZn$N{qeE zgqx1YE!?IN5wG=h?eP7C9}utJsNG9w{YNm|1B*&C5ehLkj<%+h?a#)dKUdr<`hSW4 zXXZ3SJ25}w5`@N!xSFqS%6={}aAz;8lnZfQ??4GOt+0g+ws>RdNuXsd?3$M-Yb+eg z^lGaL7BV2tYx?VH$9}`fD#YvMnLvhpbi%MIMe5YvGO0i3~7QGl!x1&d z8+sbY5?ZX-znKhYICdHvIDStr^4L;%p7E3lp6pQb%r3ZSV%f1XZACv2G(HMntU|!djWZ zrYBR>M%8>ac(6jLGT8;o5Zt908(ZW2eqnY4P3{%8-ItYX!4JI6v>;_4YU0+N!3E5& zj8AqEIo=A*&5GuUXJN?v{OG%|UmGSELUt-)vP*)VW6-=eu@xeVlskURE0uSaj+R{Oz;6Ynl5@#v(UT4GXnYzEt1K?OwB*S$u)gE=y-K3yRe#M-CMh;r7~5cvHbo`3{)`ugOT+V%Sm3yzJ<+A zmDyfca^F%h{_4s#{DoK7JC{c(7Tu;U7ymFXGPoSixjN%y^vl7jP~@vOWUi28qT-lg zQR7XnC2W2?Urk>q5d#bLw{OWUc9~@~ zzjHsH->t&J$xE_wm=WEmtXgGo#`!$MarDBCn%1TZVB>tP&P$bE2{dNTWPtDv2Y-bN zr9d4cO}J|XH#qEh#>ctc%u8|1^nCKvFwo_86)?8B z4a|{_|3|m0HK-CN`(2PaLz>`H{I?CMg&@)I)!Lp0+M0hMP4w$8=Qw7D_P$^E!x3O= z|N8Ge;=0JTrCR^cj)_k^I2XRUJ*&wJd>x t8r&`rkbbJN6JuapUk~*lca%q@K$2uhugGzw16vOy*xNW;v-kNi{|A}y)Mfwx literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/rex.svg b/tests/unit/test_contracts/eosio.contracts/icons/rex.svg new file mode 100644 index 0000000000..99f77f3731 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/rex.svg @@ -0,0 +1 @@ +Rex \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/token.png b/tests/unit/test_contracts/eosio.contracts/icons/token.png new file mode 100644 index 0000000000000000000000000000000000000000..445188a0247749231e2de8832ac651a32a9a6c88 GIT binary patch literal 4268 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 literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/token.svg b/tests/unit/test_contracts/eosio.contracts/icons/token.svg new file mode 100644 index 0000000000..67ff415bad --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/token.svg @@ -0,0 +1 @@ +Token_1 \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/transfer.png b/tests/unit/test_contracts/eosio.contracts/icons/transfer.png new file mode 100644 index 0000000000000000000000000000000000000000..d9c021957516ff0e1873a54feb4f40f547c62bed GIT binary patch literal 3856 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 C9O3i; literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/transfer.svg b/tests/unit/test_contracts/eosio.contracts/icons/transfer.svg new file mode 100644 index 0000000000..06da6325c5 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/transfer.svg @@ -0,0 +1 @@ +Transfer_1 \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/voting.png b/tests/unit/test_contracts/eosio.contracts/icons/voting.png new file mode 100644 index 0000000000000000000000000000000000000000..0356bddaf2b0f61f82f8dbd7cfad977e0757b187 GIT binary patch literal 3238 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 Z0VhTSOesfxtUm={pRKdagFTE({{svY!SDb8 literal 0 HcmV?d00001 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/voting.svg b/tests/unit/test_contracts/eosio.contracts/icons/voting.svg new file mode 100644 index 0000000000..fac8708984 --- /dev/null +++ b/tests/unit/test_contracts/eosio.contracts/icons/voting.svg @@ -0,0 +1 @@ +Voting_1 \ No newline at end of file From 1ee067bd00f5818819d526d8abfae4306a50cdf2 Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Tue, 17 Aug 2021 15:05:40 -0400 Subject: [PATCH 04/11] verify std::string issue --- tests/unit/test_contracts/CMakeLists.txt | 2 ++ tests/unit/test_contracts/minimal_tests_with_name.cpp | 9 +++++++++ tests/unit/test_contracts/minimal_tests_with_string.cpp | 9 +++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/unit/test_contracts/minimal_tests_with_name.cpp create mode 100644 tests/unit/test_contracts/minimal_tests_with_string.cpp diff --git a/tests/unit/test_contracts/CMakeLists.txt b/tests/unit/test_contracts/CMakeLists.txt index dfd75dce4a..7ca08ab6bb 100644 --- a/tests/unit/test_contracts/CMakeLists.txt +++ b/tests/unit/test_contracts/CMakeLists.txt @@ -12,6 +12,8 @@ add_contract(kv_map_tests kv_map_tests kv_map_tests.cpp) add_contract(capi_tests capi_tests capi/capi.c capi/action.c capi/chain.c capi/crypto.c capi/db.c capi/permission.c capi/print.c capi/privileged.c capi/system.c capi/transaction.c) add_contract(kv_bios kv_bios kv_bios/kv_bios.cpp) +add_contract(minimal_tests_with_string minimal_tests_with_string minimal_tests_with_string.cpp) +add_contract(minimal_tests_with_name minimal_tests_with_name minimal_tests_with_name.cpp) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/simple_wrong.abi ${CMAKE_CURRENT_BINARY_DIR}/simple_wrong.abi COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/capi/capi_tests.abi ${CMAKE_CURRENT_BINARY_DIR}/capi_tests.abi COPYONLY ) diff --git a/tests/unit/test_contracts/minimal_tests_with_name.cpp b/tests/unit/test_contracts/minimal_tests_with_name.cpp new file mode 100644 index 0000000000..81ac6608aa --- /dev/null +++ b/tests/unit/test_contracts/minimal_tests_with_name.cpp @@ -0,0 +1,9 @@ +// Verifies that the dispatching code is self-contained +#include + +class [[eosio::contract]] minimal_tests_with_name { + public: + template + explicit constexpr minimal_tests_with_name(const N&, const N&, const DS&) {} + [[eosio::action]] void test1(eosio::name memo) {} +}; diff --git a/tests/unit/test_contracts/minimal_tests_with_string.cpp b/tests/unit/test_contracts/minimal_tests_with_string.cpp new file mode 100644 index 0000000000..f70912f64b --- /dev/null +++ b/tests/unit/test_contracts/minimal_tests_with_string.cpp @@ -0,0 +1,9 @@ +// Verifies that the dispatching code is self-contained +#include + +class [[eosio::contract]] minimal_tests_with_string { + public: + template + explicit constexpr minimal_tests_with_string(const N&, const N&, const DS&) {} + [[eosio::action]] void test1(std::string memo) {} +}; From f4c475e0f318b2bdf46d13d49dc4a0bf4200116f Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Tue, 24 Aug 2021 13:08:04 -0400 Subject: [PATCH 05/11] check allowed imports in lld --- eosio_llvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eosio_llvm b/eosio_llvm index d700c78db1..6190c58257 160000 --- a/eosio_llvm +++ b/eosio_llvm @@ -1 +1 @@ -Subproject commit d700c78db1918c7a2783c37a97d42d6fcd223e4d +Subproject commit 6190c58257cc3a81505e604130e2b0e92c40b166 From a0fe7392b4b2a8be32cfdb183863c9c62ea5c14b Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Sat, 28 Aug 2021 00:28:03 -0400 Subject: [PATCH 06/11] update lld --- eosio_llvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eosio_llvm b/eosio_llvm index 6190c58257..02b5084217 160000 --- a/eosio_llvm +++ b/eosio_llvm @@ -1 +1 @@ -Subproject commit 6190c58257cc3a81505e604130e2b0e92c40b166 +Subproject commit 02b5084217982bf026412df18314350bc932562a From 542c4ba2f71f78a6ae49c8d95be9278f79395815 Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Tue, 31 Aug 2021 11:04:49 -0400 Subject: [PATCH 07/11] update lld --- eosio_llvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eosio_llvm b/eosio_llvm index 02b5084217..d03789bf02 160000 --- a/eosio_llvm +++ b/eosio_llvm @@ -1 +1 @@ -Subproject commit 02b5084217982bf026412df18314350bc932562a +Subproject commit d03789bf027ce977f083d6797185da18853b05d6 From a4eb1abe3ed7780330488de4e7f3a13b1579ad8a Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Tue, 31 Aug 2021 11:18:54 -0400 Subject: [PATCH 08/11] update lld --- eosio_llvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eosio_llvm b/eosio_llvm index d03789bf02..ff264c9345 160000 --- a/eosio_llvm +++ b/eosio_llvm @@ -1 +1 @@ -Subproject commit d03789bf027ce977f083d6797185da18853b05d6 +Subproject commit ff264c9345e5815176881f55ee1df04fb1c668b4 From 98570020815e327ccda67f3178eef2cadb4bc77a Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Tue, 31 Aug 2021 15:58:04 -0400 Subject: [PATCH 09/11] remove previous partial fix --- libraries/eosiolib/CMakeLists.txt | 5 ----- libraries/eosiolib/contract_name.cpp | 6 ------ libraries/eosiolib/eosiolib.cpp | 3 +++ tools/include/compiler_options.hpp.in | 1 - 4 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 libraries/eosiolib/contract_name.cpp diff --git a/libraries/eosiolib/CMakeLists.txt b/libraries/eosiolib/CMakeLists.txt index 1201cc45a9..a174ee32bc 100644 --- a/libraries/eosiolib/CMakeLists.txt +++ b/libraries/eosiolib/CMakeLists.txt @@ -18,10 +18,6 @@ add_library(eosio_cmem memory.cpp ${HEADERS}) -add_library(eosio_contract_name - contract_name.cpp - ${HEADERS}) - set_target_properties(eosio_malloc PROPERTIES LINKER_LANGUAGE C) target_include_directories(eosio PUBLIC @@ -38,7 +34,6 @@ add_custom_command( TARGET eosio POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) add_custom_command( TARGET eosio_dsm POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) add_custom_command( TARGET eosio_cmem POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) -add_custom_command( TARGET eosio_contract_name POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ ${BASE_BINARY_DIR}/lib ) if (ENABLE_NATIVE_COMPILER) add_native_library(native_eosio diff --git a/libraries/eosiolib/contract_name.cpp b/libraries/eosiolib/contract_name.cpp deleted file mode 100644 index b5fa83a0e0..0000000000 --- a/libraries/eosiolib/contract_name.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -extern "C" { - volatile uint64_t eosio_contract_name = 0; - volatile void eosio_set_contract_name(uint64_t n) { eosio_contract_name = n; } // LLVM creates the call to this at the beginning of apply -} diff --git a/libraries/eosiolib/eosiolib.cpp b/libraries/eosiolib/eosiolib.cpp index fc51fdb64c..b7e980c2ba 100644 --- a/libraries/eosiolib/eosiolib.cpp +++ b/libraries/eosiolib/eosiolib.cpp @@ -5,6 +5,9 @@ #include +extern "C" volatile uint64_t eosio_contract_name = 0; +extern "C" volatile void eosio_set_contract_name(uint64_t n) { eosio_contract_name = n; } // LLVM creates the call to this at the beginning of apply + namespace eosio { extern "C" { __attribute__((eosio_wasm_import)) diff --git a/tools/include/compiler_options.hpp.in b/tools/include/compiler_options.hpp.in index fdc247b86b..a335f887a3 100644 --- a/tools/include/compiler_options.hpp.in +++ b/tools/include/compiler_options.hpp.in @@ -507,7 +507,6 @@ static void GetLdDefaults(std::vector& ldopts) { ldopts.emplace_back("-lc++"); ldopts.emplace_back("-lc"); ldopts.emplace_back("-leosio"); - ldopts.emplace_back("-leosio_contract_name"); if (use_old_malloc_opt) ldopts.emplace_back("-leosio_malloc"); else From 4d87246e40e7044bf8f8243374fd2d8df6bdf3ca Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Wed, 1 Sep 2021 15:42:41 -0400 Subject: [PATCH 10/11] update lld --- eosio_llvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eosio_llvm b/eosio_llvm index ff264c9345..32dc78042d 160000 --- a/eosio_llvm +++ b/eosio_llvm @@ -1 +1 @@ -Subproject commit ff264c9345e5815176881f55ee1df04fb1c668b4 +Subproject commit 32dc78042d8589b29391a22843dfae73114ee294 From bd8db52c7a874dd275c8d7e70aad45e241115aac Mon Sep 17 00:00:00 2001 From: Qing Yang Date: Wed, 1 Sep 2021 17:20:37 -0400 Subject: [PATCH 11/11] remove extra test contracts --- libraries/eosiolib/CMakeLists.txt | 1 + tests/unit/test_contracts/CMakeLists.txt | 2 - .../eosio.contracts/CMakeLists.txt | 23 - .../eosio.contracts/eosio.bios/CMakeLists.txt | 13 - .../include/eosio.bios/eosio.bios.hpp | 282 ---- .../ricardian/eosio.bios.contracts.md.in | 189 --- .../eosio.bios/src/eosio.bios.cpp | 57 - .../eosio.contracts/eosio.msig/CMakeLists.txt | 13 - .../include/eosio.msig/eosio.msig.hpp | 162 -- .../ricardian/eosio.msig.contracts.md.in | 73 - .../eosio.msig/src/eosio.msig.cpp | 207 --- .../eosio.system/CMakeLists.txt | 33 - .../include/eosio.system/eosio.system.hpp | 1314 ----------------- .../include/eosio.system/exchange_state.hpp | 48 - .../include/eosio.system/native.hpp | 260 ---- .../include/eosio.system/rex.results.hpp | 59 - .../ricardian/eosio.system.clauses.md | 63 - .../ricardian/eosio.system.contracts.md.in | 703 --------- .../eosio.system/src/delegate_bandwidth.cpp | 413 ------ .../eosio.system/src/eosio.system.cpp | 400 ----- .../eosio.system/src/exchange_state.cpp | 110 -- .../eosio.system/src/name_bidding.cpp | 80 - .../eosio.system/src/native.cpp | 11 - .../eosio.system/src/producer_pay.cpp | 191 --- .../eosio.contracts/eosio.system/src/rex.cpp | 1218 --------------- .../eosio.system/src/rex.results.cpp | 11 - .../eosio.system/src/voting.cpp | 415 ------ .../eosio.token/CMakeLists.txt | 13 - .../include/eosio.token/eosio.token.hpp | 146 -- .../ricardian/eosio.token.contracts.md.in | 95 -- .../eosio.token/src/eosio.token.cpp | 159 -- .../eosio.contracts/eosio.wrap/CMakeLists.txt | 13 - .../include/eosio.wrap/eosio.wrap.hpp | 38 - .../ricardian/eosio.wrap.contracts.md.in | 13 - .../eosio.wrap/src/eosio.wrap.cpp | 16 - .../eosio.contracts/icons/account.png | Bin 3795 -> 0 bytes .../eosio.contracts/icons/account.svg | 1 - .../eosio.contracts/icons/admin.png | Bin 2937 -> 0 bytes .../eosio.contracts/icons/admin.svg | 1 - .../eosio.contracts/icons/multisig.png | Bin 1411 -> 0 bytes .../eosio.contracts/icons/multisig.svg | 1 - .../eosio.contracts/icons/resource.png | Bin 1913 -> 0 bytes .../eosio.contracts/icons/resource.svg | 1 - .../eosio.contracts/icons/rex.png | Bin 2770 -> 0 bytes .../eosio.contracts/icons/rex.svg | 1 - .../eosio.contracts/icons/token.png | Bin 4268 -> 0 bytes .../eosio.contracts/icons/token.svg | 1 - .../eosio.contracts/icons/transfer.png | Bin 3856 -> 0 bytes .../eosio.contracts/icons/transfer.svg | 1 - .../eosio.contracts/icons/voting.png | Bin 3238 -> 0 bytes .../eosio.contracts/icons/voting.svg | 1 - .../minimal_tests_with_name.cpp | 9 - .../minimal_tests_with_string.cpp | 9 - 53 files changed, 1 insertion(+), 6869 deletions(-) delete mode 100644 tests/unit/test_contracts/eosio.contracts/CMakeLists.txt delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in delete mode 100644 tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/account.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/account.svg delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/admin.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/admin.svg delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/multisig.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/multisig.svg delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/resource.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/resource.svg delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/rex.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/rex.svg delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/token.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/token.svg delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/transfer.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/transfer.svg delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/voting.png delete mode 100644 tests/unit/test_contracts/eosio.contracts/icons/voting.svg delete mode 100644 tests/unit/test_contracts/minimal_tests_with_name.cpp delete mode 100644 tests/unit/test_contracts/minimal_tests_with_string.cpp diff --git a/libraries/eosiolib/CMakeLists.txt b/libraries/eosiolib/CMakeLists.txt index a174ee32bc..345506346d 100644 --- a/libraries/eosiolib/CMakeLists.txt +++ b/libraries/eosiolib/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(eosio_cmem memory.cpp ${HEADERS}) + set_target_properties(eosio_malloc PROPERTIES LINKER_LANGUAGE C) target_include_directories(eosio PUBLIC diff --git a/tests/unit/test_contracts/CMakeLists.txt b/tests/unit/test_contracts/CMakeLists.txt index 7ca08ab6bb..dfd75dce4a 100644 --- a/tests/unit/test_contracts/CMakeLists.txt +++ b/tests/unit/test_contracts/CMakeLists.txt @@ -12,8 +12,6 @@ add_contract(kv_map_tests kv_map_tests kv_map_tests.cpp) add_contract(capi_tests capi_tests capi/capi.c capi/action.c capi/chain.c capi/crypto.c capi/db.c capi/permission.c capi/print.c capi/privileged.c capi/system.c capi/transaction.c) add_contract(kv_bios kv_bios kv_bios/kv_bios.cpp) -add_contract(minimal_tests_with_string minimal_tests_with_string minimal_tests_with_string.cpp) -add_contract(minimal_tests_with_name minimal_tests_with_name minimal_tests_with_name.cpp) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/simple_wrong.abi ${CMAKE_CURRENT_BINARY_DIR}/simple_wrong.abi COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/capi/capi_tests.abi ${CMAKE_CURRENT_BINARY_DIR}/capi_tests.abi COPYONLY ) diff --git a/tests/unit/test_contracts/eosio.contracts/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/CMakeLists.txt deleted file mode 100644 index 69d438e146..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required( VERSION 3.5 ) - -project(contracts) - -set(EOSIO_WASM_OLD_BEHAVIOR "Off") -find_package(eosio.cdt) - -set(ICON_BASE_URL "http://127.0.0.1/ricardian_assets/eosio.contracts/icons") - -set(ACCOUNT_ICON_URI "account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f") -set(ADMIN_ICON_URI "admin.png#9bf1cec664863bd6aaac0f814b235f8799fb02c850e9aa5da34e8a004bd6518e") -set(MULTISIG_ICON_URI "multisig.png#4fb41d3cf02d0dd2d35a29308e93c2d826ec770d6bb520db668f530764be7153") -set(RESOURCE_ICON_URI "resource.png#3830f1ce8cb07f7757dbcf383b1ec1b11914ac34a1f9d8b065f07600fa9dac19") -set(REX_ICON_URI "rex.png#d229837fa62a464b9c71e06060aa86179adf0b3f4e3b8c4f9702f4f4b0c340a8") -set(TOKEN_ICON_URI "token.png#207ff68b0406eaa56618b08bda81d6a0954543f36adc328ab3065f31a5c5d654") -set(TRANSFER_ICON_URI "transfer.png#5dfad0df72772ee1ccc155e670c1d124f5c5122f1d5027565df38b418042d1dd") -set(VOTING_ICON_URI "voting.png#db28cd3db6e62d4509af3644ce7d377329482a14bb4bfaca2aa5f1400d8e8a84") - -add_subdirectory(eosio.bios) -add_subdirectory(eosio.msig) -add_subdirectory(eosio.system) -add_subdirectory(eosio.token) -add_subdirectory(eosio.wrap) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt deleted file mode 100644 index 3709f70c18..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.bios/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_contract(eosio.bios eosio.bios ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.bios.cpp) - -target_include_directories(eosio.bios - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -set_target_properties(eosio.bios - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - -configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.bios.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.bios.contracts.md @ONLY ) - -target_compile_options( eosio.bios PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp deleted file mode 100644 index 643e562c8f..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp +++ /dev/null @@ -1,282 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace eosiobios { - - using eosio::action_wrapper; - using eosio::check; - using eosio::checksum256; - using eosio::ignore; - using eosio::name; - using eosio::permission_level; - using eosio::public_key; - - struct permission_level_weight { - permission_level permission; - uint16_t weight; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( permission_level_weight, (permission)(weight) ) - }; - - struct key_weight { - eosio::public_key key; - uint16_t weight; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( key_weight, (key)(weight) ) - }; - - struct wait_weight { - uint32_t wait_sec; - uint16_t weight; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( wait_weight, (wait_sec)(weight) ) - }; - - struct authority { - uint32_t threshold = 0; - std::vector keys; - std::vector accounts; - std::vector waits; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( authority, (threshold)(keys)(accounts)(waits) ) - }; - - struct block_header { - uint32_t timestamp; - name producer; - uint16_t confirmed = 0; - checksum256 previous; - checksum256 transaction_mroot; - checksum256 action_mroot; - uint32_t schedule_version = 0; - std::optional new_producers; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE(block_header, (timestamp)(producer)(confirmed)(previous)(transaction_mroot)(action_mroot) - (schedule_version)(new_producers)) - }; - - /** - * The `eosio.bios` is the first sample of system contract provided by `block.one` through the EOSIO platform. It is a minimalist system contract because it only supplies the actions that are absolutely critical to bootstrap a chain and nothing more. This allows for a chain agnostic approach to bootstrapping a chain. - * - * Just like in the `eosio.system` sample contract implementation, there are a few actions which are not implemented at the contract level (`newaccount`, `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, `canceldelay`, `onerror`, `setabi`, `setcode`), they are just declared in the contract so they will show in the contract's ABI and users will be able to push those actions to the chain via the account holding the `eosio.system` contract, but the implementation is at the EOSIO core level. They are referred to as EOSIO native actions. - */ - class [[eosio::contract("eosio.bios")]] bios : public eosio::contract { - public: - using contract::contract; - /** - * New account action, called after a new account is created. This code enforces resource-limits rules - * for new accounts as well as new 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 newaccount( name creator, - name name, - ignore owner, - ignore active){} - /** - * Update authorization action updates pemission for an account. - * - * @param account - the account for which the permission is updated, - * @param pemission - the permission name which is updated, - * @param parem - the parent of the permission which is updated, - * @param aut - the json describing the permission authorization. - */ - [[eosio::action]] - void updateauth( ignore account, - ignore permission, - ignore parent, - ignore auth ) {} - - /** - * Delete authorization action deletes the authorization for an account's permission. - * - * @param account - the account for which the permission authorization is deleted, - * @param permission - the permission name been deleted. - */ - [[eosio::action]] - void deleteauth( ignore account, - ignore permission ) {} - - /** - * Link authorization action assigns a specific action from a contract to a permission you have created. Five system - * actions can not be linked `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, and `canceldelay`. - * This is useful because when doing authorization checks, the EOSIO based blockchain starts with the - * action needed to be authorized (and the contract belonging to), and looks up which permission - * is needed to pass authorization validation. If a link is set, that permission is used for authoraization - * validation otherwise then active is the default, with the exception of `eosio.any`. - * `eosio.any` is an implicit permission which exists on every account; you can link actions to `eosio.any` - * and that will make it so linked actions are accessible to any permissions defined for the account. - * - * @param account - the permission's owner to be linked and the payer of the RAM needed to store this link, - * @param code - the owner of the action to be linked, - * @param type - the action to be linked, - * @param requirement - the permission to be linked. - */ - [[eosio::action]] - void linkauth( ignore account, - ignore code, - ignore type, - ignore requirement ) {} - - /** - * Unlink authorization action it's doing the reverse of linkauth action, by unlinking the given action. - * - * @param account - the owner of the permission to be unlinked and the receiver of the freed RAM, - * @param code - the owner of the action to be unlinked, - * @param type - the action to be unlinked. - */ - [[eosio::action]] - void unlinkauth( ignore account, - ignore code, - ignore type ) {} - - /** - * Cancel delay action cancels a deferred transaction. - * - * @param canceling_auth - the permission that authorizes this action, - * @param trx_id - the deferred transaction id to be cancelled. - */ - [[eosio::action]] - void canceldelay( ignore canceling_auth, ignore trx_id ) {} - - /** - * Set code action sets the contract code for an account. - * - * @param account - the account for which to set the contract code. - * @param vmtype - reserved, set it to zero. - * @param vmversion - reserved, set it to zero. - * @param code - the code content to be set, in the form of a blob binary.. - */ - [[eosio::action]] - void setcode( name account, uint8_t vmtype, uint8_t vmversion, const std::vector& code ) {} - - /** - * Set abi action sets the abi for contract identified by `account` name. Creates an entry in the abi_hash_table - * index, with `account` name as key, if it is not already present and sets its value with the abi hash. - * Otherwise it is updating the current abi hash value for the existing `account` key. - * - * @param account - the name of the account to set the abi for - * @param abi - the abi hash represented as a vector of characters - */ - [[eosio::action]] - void setabi( name account, const std::vector& abi ); - - /** - * On error action, notification of this action is delivered to the sender of a deferred transaction - * when an objective error occurs while executing the deferred transaction. - * This action is not meant to be called directly. - * - * @param sender_id - the id for the deferred transaction chosen by the sender, - * @param sent_trx - the deferred transaction that failed. - */ - [[eosio::action]] - void onerror( ignore sender_id, ignore> sent_trx ); - - /** - * Set privilege action allows to set privilege status for an account (turn it on/off). - * @param account - the account to set the privileged status for. - * @param is_priv - 0 for false, > 0 for true. - */ - [[eosio::action]] - void setpriv( name account, uint8_t is_priv ); - - /** - * Sets the resource limits of an account - * - * @param account - name of the account whose resource limit to be set - * @param ram_bytes - ram limit in absolute bytes - * @param net_weight - fractionally proportionate net limit of available resources based on (weight / total_weight_of_all_accounts) - * @param cpu_weight - fractionally proportionate cpu limit of available resources based on (weight / total_weight_of_all_accounts) - */ - [[eosio::action]] - void setalimits( name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ); - - /** - * Set producers action, sets a new list of active producers, by proposing a schedule change, once the block that - * contains the proposal becomes irreversible, the schedule is promoted to "pending" - * automatically. Once the block that promotes the schedule is irreversible, the schedule will - * become "active". - * - * @param schedule - New list of active producers to set - */ - [[eosio::action]] - void setprods( const std::vector& schedule ); - - /** - * Set params action, sets the blockchain parameters. By tuning these parameters, various degrees of customization can be achieved. - * - * @param params - New blockchain parameters to set - */ - [[eosio::action]] - void setparams( const eosio::blockchain_parameters& params ); - - /** - * Require authorization action, checks if the account name `from` passed in as param has authorization to access - * current action, that is, if it is listed in the action’s allowed permissions vector. - * - * @param from - the account name to authorize - */ - [[eosio::action]] - void reqauth( name from ); - - /** - * Activate action, activates a protocol feature - * - * @param feature_digest - hash of the protocol feature to activate. - */ - [[eosio::action]] - void activate( const eosio::checksum256& feature_digest ); - - /** - * Require activated action, asserts that a protocol feature has been activated - * - * @param feature_digest - hash of the protocol feature to check for activation. - */ - [[eosio::action]] - void reqactivated( const eosio::checksum256& feature_digest ); - - struct [[eosio::table]] abi_hash { - name owner; - checksum256 hash; - uint64_t primary_key()const { return owner.value; } - - EOSLIB_SERIALIZE( abi_hash, (owner)(hash) ) - }; - - typedef eosio::multi_index< "abihash"_n, abi_hash > abi_hash_table; - - using newaccount_action = action_wrapper<"newaccount"_n, &bios::newaccount>; - 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>; - using unlinkauth_action = action_wrapper<"unlinkauth"_n, &bios::unlinkauth>; - using canceldelay_action = action_wrapper<"canceldelay"_n, &bios::canceldelay>; - using setcode_action = action_wrapper<"setcode"_n, &bios::setcode>; - using setabi_action = action_wrapper<"setabi"_n, &bios::setabi>; - using setpriv_action = action_wrapper<"setpriv"_n, &bios::setpriv>; - using setalimits_action = action_wrapper<"setalimits"_n, &bios::setalimits>; - using setprods_action = action_wrapper<"setprods"_n, &bios::setprods>; - using setparams_action = action_wrapper<"setparams"_n, &bios::setparams>; - using reqauth_action = action_wrapper<"reqauth"_n, &bios::reqauth>; - using activate_action = action_wrapper<"activate"_n, &bios::activate>; - using reqactivated_action = action_wrapper<"reqactivated"_n, &bios::reqactivated>; - }; -} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in deleted file mode 100644 index a08b66e433..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.bios/ricardian/eosio.bios.contracts.md.in +++ /dev/null @@ -1,189 +0,0 @@ -

activate

- ---- -spec_version: "0.2.0" -title: Activate Protocol Feature -summary: 'Activate protocol feature {{nowrap feature_digest}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} activates the protocol feature with a digest of {{feature_digest}}. - -

canceldelay

- ---- -spec_version: "0.2.0" -title: Cancel Delayed Transaction -summary: '{{nowrap canceling_auth.actor}} cancels a delayed transaction' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{canceling_auth.actor}} cancels the delayed transaction with id {{trx_id}}. - -

deleteauth

- ---- -spec_version: "0.2.0" -title: Delete Account Permission -summary: 'Delete the {{nowrap permission}} permission of {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Delete the {{permission}} permission of {{account}}. - -

linkauth

- ---- -spec_version: "0.2.0" -title: Link Action to Permission -summary: '{{nowrap account}} sets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract to {{nowrap requirement}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{account}} sets the minimum required permission for the {{#if type}}{{type}} action of the{{/if}} {{code}} contract to {{requirement}}. - -{{#if type}}{{else}}Any links explicitly associated to specific actions of {{code}} will take precedence.{{/if}} - -

newaccount

- ---- -spec_version: "0.2.0" -title: Create New Account -summary: '{{nowrap creator}} creates a new account with the name {{nowrap name}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{creator}} creates a new account with the name {{name}} and the following permissions: - -owner permission with authority: -{{to_json owner}} - -active permission with authority: -{{to_json active}} - -

reqactivated

- ---- -spec_version: "0.2.0" -title: Assert Protocol Feature Activation -summary: 'Assert that protocol feature {{nowrap feature_digest}} has been activated' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -Assert that the protocol feature with a digest of {{feature_digest}} has been activated. - -

reqauth

- ---- -spec_version: "0.2.0" -title: Assert Authorization -summary: 'Assert that authorization by {{nowrap from}} is provided' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Assert that authorization by {{from}} is provided. - -

setabi

- ---- -spec_version: "0.2.0" -title: Deploy Contract ABI -summary: 'Deploy contract ABI on account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Deploy the ABI file associated with the contract on account {{account}}. - -

setalimits

- ---- -spec_version: "0.2.0" -title: Adjust Resource Limits of Account -summary: 'Adjust resource limits of account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} updates {{account}}’s resource limits to have a RAM quota of {{ram_bytes}} bytes, a NET bandwidth quota of {{net_weight}} and a CPU bandwidth quota of {{cpu_weight}}. - -

setcode

- ---- -spec_version: "0.2.0" -title: Deploy Contract Code -summary: 'Deploy contract code on account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Deploy compiled contract code to the account {{account}}. - -

setparams

- ---- -spec_version: "0.2.0" -title: Set System Parameters -summary: 'Set system parameters' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} sets system parameters to: -{{to_json params}} - -

setpriv

- ---- -spec_version: "0.2.0" -title: Make an Account Privileged or Unprivileged -summary: '{{#if is_priv}}Make {{nowrap account}} privileged{{else}}Remove privileged status of {{nowrap account}}{{/if}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{#if is_priv}} -{{$action.account}} makes {{account}} privileged. -{{else}} -{{$action.account}} removes privileged status of {{account}}. -{{/if}} - -

setprods

- ---- -spec_version: "0.2.0" -title: Set Block Producers -summary: 'Set block producer schedule' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} proposes a block producer schedule of: -{{#each schedule}} - 1. {{this.producer_name}} -{{/each}} - -The block signing authorities of each of the producers in the above schedule are listed below: -{{#each schedule}} -### {{this.producer_name}} -{{to_json this.authority}} -{{/each}} - -

unlinkauth

- ---- -spec_version: "0.2.0" -title: Unlink Action from Permission -summary: '{{nowrap account}} unsets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{account}} removes the association between the {{#if type}}{{type}} action of the{{/if}} {{code}} contract and its minimum required permission. - -{{#if type}}{{else}}This will not remove any links explicitly associated to specific actions of {{code}}.{{/if}} - -

updateauth

- ---- -spec_version: "0.2.0" -title: Modify Account Permission -summary: 'Add or update the {{nowrap permission}} permission of {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Modify, and create if necessary, the {{permission}} permission of {{account}} to have a parent permission of {{parent}} and the following authority: -{{to_json auth}} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp deleted file mode 100644 index 69d6758f34..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.bios/src/eosio.bios.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include - -namespace eosiobios { - -void bios::setabi( name account, const std::vector& abi ) { - abi_hash_table table(get_self(), get_self().value); - auto itr = table.find( account.value ); - if( itr == table.end() ) { - table.emplace( account, [&]( auto& row ) { - row.owner = account; - row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); - }); - } else { - table.modify( itr, eosio::same_payer, [&]( auto& row ) { - row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); - }); - } -} - -void bios::onerror( ignore, ignore> ) { - check( false, "the onerror action cannot be called directly" ); -} - -void bios::setpriv( name account, uint8_t is_priv ) { - require_auth( get_self() ); - set_privileged( account, is_priv ); -} - -void bios::setalimits( name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ) { - require_auth( get_self() ); - set_resource_limits( account, ram_bytes, net_weight, cpu_weight ); -} - -void bios::setprods( const std::vector& schedule ) { - require_auth( get_self() ); - set_proposed_producers( schedule ); -} - -void bios::setparams( const eosio::blockchain_parameters& params ) { - require_auth( get_self() ); - set_blockchain_parameters( params ); -} - -void bios::reqauth( name from ) { - require_auth( from ); -} - -void bios::activate( const eosio::checksum256& feature_digest ) { - require_auth( get_self() ); - preactivate_feature( feature_digest ); -} - -void bios::reqactivated( const eosio::checksum256& feature_digest ) { - check( is_feature_activated( feature_digest ), "protocol feature is not activated" ); -} - -} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt deleted file mode 100644 index 0947ea9d0e..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.msig/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_contract(eosio.msig eosio.msig ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.msig.cpp) - -target_include_directories(eosio.msig - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -set_target_properties(eosio.msig - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - -configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.msig.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.msig.contracts.md @ONLY ) - -target_compile_options( eosio.msig PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp deleted file mode 100644 index 19f2600cc1..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp +++ /dev/null @@ -1,162 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace eosio { - - /** - * The `eosio.msig` system contract allows for creation of proposed transactions which require authorization from a list of accounts, approval of the proposed transactions by those accounts required to approve it, and finally, it also allows the execution of the approved transactions on the blockchain. - * - * In short, the workflow to propose, review, approve and then executed a transaction it can be described by the following: - * - first you create a transaction json file, - * - then you submit this proposal to the `eosio.msig` contract, and you also insert the account permissions required to approve this proposal into the command that submits the proposal to the blockchain, - * - the proposal then gets stored on the blockchain by the `eosio.msig` contract, and is accessible for review and approval to those accounts required to approve it, - * - after each of the appointed accounts required to approve the proposed transactions reviews and approves it, you can execute the proposed transaction. The `eosio.msig` contract will execute it automatically, but not before validating that the transaction has not expired, it is not cancelled, and it has been signed by all the permissions in the initial proposal's required permission list. - */ - class [[eosio::contract("eosio.msig")]] multisig : public contract { - public: - using contract::contract; - - /** - * Propose action, creates a proposal containing one transaction. - * Allows an account `proposer` to make a proposal `proposal_name` which has `requested` - * permission levels expected to approve the proposal, and if approved by all expected - * permission levels then `trx` transaction can we executed by this proposal. - * The `proposer` account is authorized and the `trx` transaction is verified if it was - * authorized by the provided keys and permissions, and if the proposal name doesn’t - * already exist; if all validations pass the `proposal_name` and `trx` trasanction are - * saved in the proposals table and the `requested` permission levels to the - * approvals table (for the `proposer` context). Storage changes are billed to `proposer`. - * - * @param proposer - The account proposing a transaction - * @param proposal_name - The name of the proposal (should be unique for proposer) - * @param requested - Permission levels expected to approve the proposal - * @param trx - Proposed transaction - */ - [[eosio::action]] - void propose(ignore proposer, ignore proposal_name, - ignore> requested, ignore trx); - /** - * Approve action approves an existing proposal. Allows an account, the owner of `level` permission, to approve a proposal `proposal_name` - * proposed by `proposer`. If the proposal's requested approval list contains the `level` - * permission then the `level` permission is moved from internal `requested_approvals` list to - * internal `provided_approvals` list of the proposal, thus persisting the approval for - * the `proposal_name` proposal. Storage changes are billed to `proposer`. - * - * @param proposer - The account proposing a transaction - * @param proposal_name - The name of the proposal (should be unique for proposer) - * @param level - Permission level approving the transaction - * @param proposal_hash - Transaction's checksum - */ - [[eosio::action]] - void approve( name proposer, name proposal_name, permission_level level, - const eosio::binary_extension& proposal_hash ); - /** - * Unapprove action revokes an existing proposal. This action is the reverse of the `approve` action: if all validations pass - * the `level` permission is erased from internal `provided_approvals` and added to the internal - * `requested_approvals` list, and thus un-approve or revoke the proposal. - * - * @param proposer - The account proposing a transaction - * @param proposal_name - The name of the proposal (should be an existing proposal) - * @param level - Permission level revoking approval for proposal - */ - [[eosio::action]] - void unapprove( name proposer, name proposal_name, permission_level level ); - /** - * Cancel action cancels an existing proposal. - * - * @param proposer - The account proposing a transaction - * @param proposal_name - The name of the proposal (should be an existing proposal) - * @param canceler - The account cancelling the proposal (only the proposer can cancel an unexpired transaction, and the canceler has to be different than the proposer) - * - * Allows the `canceler` account to cancel the `proposal_name` proposal, created by a `proposer`, - * only after time has expired on the proposed transaction. It removes corresponding entries from - * internal proptable and from approval (or old approvals) tables as well. - */ - [[eosio::action]] - void cancel( name proposer, name proposal_name, name canceler ); - /** - * Exec action allows an `executer` account to execute a proposal. - * - * Preconditions: - * - `executer` has authorization, - * - `proposal_name` is found in the proposals table, - * - all requested approvals are received, - * - proposed transaction is not expired, - * - and approval accounts are not found in invalidations table. - * - * If all preconditions are met the transaction is executed as a deferred transaction, - * and the proposal is erased from the proposals table. - * - * @param proposer - The account proposing a transaction - * @param proposal_name - The name of the proposal (should be an existing proposal) - * @param executer - The account executing the transaction - */ - [[eosio::action]] - void exec( name proposer, name proposal_name, name executer ); - /** - * Invalidate action allows an `account` to invalidate itself, that is, its name is added to - * the invalidations table and this table will be cross referenced when exec is performed. - * - * @param account - The account invalidating the transaction - */ - [[eosio::action]] - void invalidate( name account ); - - using propose_action = eosio::action_wrapper<"propose"_n, &multisig::propose>; - using approve_action = eosio::action_wrapper<"approve"_n, &multisig::approve>; - using unapprove_action = eosio::action_wrapper<"unapprove"_n, &multisig::unapprove>; - using cancel_action = eosio::action_wrapper<"cancel"_n, &multisig::cancel>; - using exec_action = eosio::action_wrapper<"exec"_n, &multisig::exec>; - using invalidate_action = eosio::action_wrapper<"invalidate"_n, &multisig::invalidate>; - - private: - struct [[eosio::table]] proposal { - name proposal_name; - std::vector packed_transaction; - - uint64_t primary_key()const { return proposal_name.value; } - }; - - typedef eosio::multi_index< "proposal"_n, proposal > proposals; - - struct [[eosio::table]] old_approvals_info { - name proposal_name; - std::vector requested_approvals; - std::vector provided_approvals; - - uint64_t primary_key()const { return proposal_name.value; } - }; - typedef eosio::multi_index< "approvals"_n, old_approvals_info > old_approvals; - - struct approval { - permission_level level; - time_point time; - }; - - struct [[eosio::table]] approvals_info { - uint8_t version = 1; - name proposal_name; - //requested approval doesn't need to cointain time, but we want requested approval - //to be of exact the same size ad provided approval, in this case approve/unapprove - //doesn't change serialized data size. So, we use the same type. - std::vector requested_approvals; - std::vector provided_approvals; - - uint64_t primary_key()const { return proposal_name.value; } - }; - typedef eosio::multi_index< "approvals2"_n, approvals_info > approvals; - - struct [[eosio::table]] invalidation { - name account; - time_point last_invalidation_time; - - uint64_t primary_key() const { return account.value; } - }; - - typedef eosio::multi_index< "invals"_n, invalidation > invalidations; - }; -} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in deleted file mode 100644 index b9f3f35f85..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.msig/ricardian/eosio.msig.contracts.md.in +++ /dev/null @@ -1,73 +0,0 @@ -

approve

- ---- -spec_version: "0.2.0" -title: Approve Proposed Transaction -summary: '{{nowrap level.actor}} approves the {{nowrap proposal_name}} proposal' -icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ ---- - -{{level.actor}} approves the {{proposal_name}} proposal proposed by {{proposer}} with the {{level.permission}} permission of {{level.actor}}. - -

cancel

- ---- -spec_version: "0.2.0" -title: Cancel Proposed Transaction -summary: '{{nowrap canceler}} cancels the {{nowrap proposal_name}} proposal' -icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ ---- - -{{canceler}} cancels the {{proposal_name}} proposal submitted by {{proposer}}. - -

exec

- ---- -spec_version: "0.2.0" -title: Execute Proposed Transaction -summary: '{{nowrap executer}} executes the {{nowrap proposal_name}} proposal' -icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ ---- - -{{executer}} executes the {{proposal_name}} proposal submitted by {{proposer}} if the minimum required approvals for the proposal have been secured. - -

invalidate

- ---- -spec_version: "0.2.0" -title: Invalidate All Approvals -summary: '{{nowrap account}} invalidates approvals on outstanding proposals' -icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ ---- - -{{account}} invalidates all approvals on proposals which have not yet executed. - -

propose

- ---- -spec_version: "0.2.0" -title: Propose Transaction -summary: '{{nowrap proposer}} creates the {{nowrap proposal_name}}' -icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ ---- - -{{proposer}} creates the {{proposal_name}} proposal for the following transaction: -{{to_json trx}} - -The proposal requests approvals from the following accounts at the specified permission levels: -{{#each requested}} - + {{this.permission}} permission of {{this.actor}} -{{/each}} - -If the proposed transaction is not executed prior to {{trx.expiration}}, the proposal will automatically expire. - -

unapprove

- ---- -spec_version: "0.2.0" -title: Unapprove Proposed Transaction -summary: '{{nowrap level.actor}} revokes the approval previously provided to {{nowrap proposal_name}} proposal' -icon: @ICON_BASE_URL@/@MULTISIG_ICON_URI@ ---- - -{{level.actor}} revokes the approval previously provided at their {{level.permission}} permission level from the {{proposal_name}} proposal proposed by {{proposer}}. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp deleted file mode 100644 index 1769219290..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.msig/src/eosio.msig.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include -#include -#include - -#include - -namespace eosio { - -void multisig::propose( ignore proposer, - ignore proposal_name, - ignore> requested, - ignore trx ) -{ - name _proposer; - name _proposal_name; - std::vector _requested; - transaction_header _trx_header; - - _ds >> _proposer >> _proposal_name >> _requested; - - const char* trx_pos = _ds.pos(); - size_t size = _ds.remaining(); - _ds >> _trx_header; - - require_auth( _proposer ); - check( _trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); - //check( trx_header.actions.size() > 0, "transaction must have at least one action" ); - - proposals proptable( get_self(), _proposer.value ); - check( proptable.find( _proposal_name.value ) == proptable.end(), "proposal with the same name exists" ); - - auto packed_requested = pack(_requested); - auto res = check_transaction_authorization( - trx_pos, size, - (const char*)0, 0, - packed_requested.data(), packed_requested.size() - ); - - check( res > 0, "transaction authorization failed" ); - - std::vector pkd_trans; - pkd_trans.resize(size); - memcpy((char*)pkd_trans.data(), trx_pos, size); - proptable.emplace( _proposer, [&]( auto& prop ) { - prop.proposal_name = _proposal_name; - prop.packed_transaction = pkd_trans; - }); - - approvals apptable( get_self(), _proposer.value ); - apptable.emplace( _proposer, [&]( auto& a ) { - a.proposal_name = _proposal_name; - a.requested_approvals.reserve( _requested.size() ); - for ( auto& level : _requested ) { - a.requested_approvals.push_back( approval{ level, time_point{ microseconds{0} } } ); - } - }); -} - -void multisig::approve( name proposer, name proposal_name, permission_level level, - const eosio::binary_extension& proposal_hash ) -{ - require_auth( level ); - - if( proposal_hash ) { - proposals proptable( get_self(), proposer.value ); - auto& prop = proptable.get( proposal_name.value, "proposal not found" ); - assert_sha256( prop.packed_transaction.data(), prop.packed_transaction.size(), *proposal_hash ); - } - - approvals apptable( get_self(), proposer.value ); - auto apps_it = apptable.find( proposal_name.value ); - if ( apps_it != apptable.end() ) { - auto itr = std::find_if( apps_it->requested_approvals.begin(), apps_it->requested_approvals.end(), [&](const approval& a) { return a.level == level; } ); - check( itr != apps_it->requested_approvals.end(), "approval is not on the list of requested approvals" ); - - apptable.modify( apps_it, proposer, [&]( auto& a ) { - a.provided_approvals.push_back( approval{ level, current_time_point() } ); - a.requested_approvals.erase( itr ); - }); - } else { - old_approvals old_apptable( get_self(), proposer.value ); - auto& apps = old_apptable.get( proposal_name.value, "proposal not found" ); - - auto itr = std::find( apps.requested_approvals.begin(), apps.requested_approvals.end(), level ); - check( itr != apps.requested_approvals.end(), "approval is not on the list of requested approvals" ); - - old_apptable.modify( apps, proposer, [&]( auto& a ) { - a.provided_approvals.push_back( level ); - a.requested_approvals.erase( itr ); - }); - } -} - -void multisig::unapprove( name proposer, name proposal_name, permission_level level ) { - require_auth( level ); - - approvals apptable( get_self(), proposer.value ); - auto apps_it = apptable.find( proposal_name.value ); - if ( apps_it != apptable.end() ) { - auto itr = std::find_if( apps_it->provided_approvals.begin(), apps_it->provided_approvals.end(), [&](const approval& a) { return a.level == level; } ); - check( itr != apps_it->provided_approvals.end(), "no approval previously granted" ); - apptable.modify( apps_it, proposer, [&]( auto& a ) { - a.requested_approvals.push_back( approval{ level, current_time_point() } ); - a.provided_approvals.erase( itr ); - }); - } else { - old_approvals old_apptable( get_self(), proposer.value ); - auto& apps = old_apptable.get( proposal_name.value, "proposal not found" ); - auto itr = std::find( apps.provided_approvals.begin(), apps.provided_approvals.end(), level ); - check( itr != apps.provided_approvals.end(), "no approval previously granted" ); - old_apptable.modify( apps, proposer, [&]( auto& a ) { - a.requested_approvals.push_back( level ); - a.provided_approvals.erase( itr ); - }); - } -} - -void multisig::cancel( name proposer, name proposal_name, name canceler ) { - require_auth( canceler ); - - proposals proptable( get_self(), proposer.value ); - auto& prop = proptable.get( proposal_name.value, "proposal not found" ); - - if( canceler != proposer ) { - check( unpack( prop.packed_transaction ).expiration < eosio::time_point_sec(current_time_point()), "cannot cancel until expiration" ); - } - proptable.erase(prop); - - //remove from new table - approvals apptable( get_self(), proposer.value ); - auto apps_it = apptable.find( proposal_name.value ); - if ( apps_it != apptable.end() ) { - apptable.erase(apps_it); - } else { - old_approvals old_apptable( get_self(), proposer.value ); - auto apps_it = old_apptable.find( proposal_name.value ); - check( apps_it != old_apptable.end(), "proposal not found" ); - old_apptable.erase(apps_it); - } -} - -void multisig::exec( name proposer, name proposal_name, name executer ) { - require_auth( executer ); - - proposals proptable( get_self(), proposer.value ); - auto& prop = proptable.get( proposal_name.value, "proposal not found" ); - transaction_header trx_header; - datastream ds( prop.packed_transaction.data(), prop.packed_transaction.size() ); - ds >> trx_header; - check( trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); - - approvals apptable( get_self(), proposer.value ); - auto apps_it = apptable.find( proposal_name.value ); - std::vector approvals; - invalidations inv_table( get_self(), get_self().value ); - if ( apps_it != apptable.end() ) { - approvals.reserve( apps_it->provided_approvals.size() ); - for ( auto& p : apps_it->provided_approvals ) { - auto it = inv_table.find( p.level.actor.value ); - if ( it == inv_table.end() || it->last_invalidation_time < p.time ) { - approvals.push_back(p.level); - } - } - apptable.erase(apps_it); - } else { - old_approvals old_apptable( get_self(), proposer.value ); - auto& apps = old_apptable.get( proposal_name.value, "proposal not found" ); - for ( auto& level : apps.provided_approvals ) { - auto it = inv_table.find( level.actor.value ); - if ( it == inv_table.end() ) { - approvals.push_back( level ); - } - } - old_apptable.erase(apps); - } - auto packed_provided_approvals = pack(approvals); - auto res = check_transaction_authorization( - prop.packed_transaction.data(), prop.packed_transaction.size(), - (const char*)0, 0, - packed_provided_approvals.data(), packed_provided_approvals.size() - ); - - check( res > 0, "transaction authorization failed" ); - - send_deferred( (uint128_t(proposer.value) << 64) | proposal_name.value, executer, - prop.packed_transaction.data(), prop.packed_transaction.size() ); - - proptable.erase(prop); -} - -void multisig::invalidate( name account ) { - require_auth( account ); - invalidations inv_table( get_self(), get_self().value ); - auto it = inv_table.find( account.value ); - if ( it == inv_table.end() ) { - inv_table.emplace( account, [&](auto& i) { - i.account = account; - i.last_invalidation_time = current_time_point(); - }); - } else { - inv_table.modify( it, account, [&](auto& i) { - i.last_invalidation_time = current_time_point(); - }); - } -} - -} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt deleted file mode 100644 index a1642fa291..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -add_contract(eosio.system eosio.system - ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.system.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/delegate_bandwidth.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/name_bidding.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/voting.cpp -) - -target_include_directories(eosio.system - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/../eosio.token/include) - -set_target_properties(eosio.system - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - -add_contract(rex.results rex.results ${CMAKE_CURRENT_SOURCE_DIR}/src/rex.results.cpp) - -target_include_directories(rex.results - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -set_target_properties(rex.results - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.rex") - -configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.system.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.system.contracts.md @ONLY ) - -target_compile_options( eosio.system PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp deleted file mode 100644 index e4205a0c63..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/eosio.system.hpp +++ /dev/null @@ -1,1314 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#ifdef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX -#undef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX -#endif -// CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX macro determines whether ramfee and namebid proceeds are -// channeled to REX pool. In order to stop these proceeds from being channeled, the macro must -// be set to 0. -#define CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX 1 - -namespace eosiosystem { - - using eosio::asset; - using eosio::block_timestamp; - using eosio::check; - using eosio::const_mem_fun; - using eosio::datastream; - using eosio::indexed_by; - using eosio::name; - using eosio::same_payer; - using eosio::symbol; - using eosio::symbol_code; - using eosio::time_point; - using eosio::time_point_sec; - using eosio::unsigned_int; - - template - static inline auto has_field( F flags, E field ) - -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && - std::is_enum_v && std::is_same_v< F, std::underlying_type_t >, bool> - { - return ( (flags & static_cast(field)) != 0 ); - } - - template - static inline auto set_field( F flags, E field, bool value = true ) - -> std::enable_if_t< std::is_integral_v && std::is_unsigned_v && - std::is_enum_v && std::is_same_v< F, std::underlying_type_t >, F > - { - if( value ) - return ( flags | static_cast(field) ); - else - return ( flags & ~static_cast(field) ); - } - - static constexpr uint32_t seconds_per_year = 52 * 7 * 24 * 3600; - static constexpr uint32_t seconds_per_day = 24 * 3600; - static constexpr uint32_t seconds_per_hour = 3600; - static constexpr int64_t useconds_per_year = int64_t(seconds_per_year) * 1000'000ll; - static constexpr int64_t useconds_per_day = int64_t(seconds_per_day) * 1000'000ll; - static constexpr int64_t useconds_per_hour = int64_t(seconds_per_hour) * 1000'000ll; - static constexpr uint32_t blocks_per_day = 2 * seconds_per_day; // half seconds per day - - static constexpr int64_t min_activated_stake = 150'000'000'0000; - static constexpr int64_t ram_gift_bytes = 1400; - static constexpr int64_t min_pervote_daily_pay = 100'0000; - static constexpr uint32_t refund_delay_sec = 3 * seconds_per_day; - - static constexpr int64_t inflation_precision = 100; // 2 decimals - static constexpr int64_t default_annual_rate = 500; // 5% annual rate - static constexpr int64_t pay_factor_precision = 10000; - static constexpr int64_t default_inflation_pay_factor = 50000; // producers pay share = 10000 / 50000 = 20% of the inflation - static constexpr int64_t default_votepay_factor = 40000; // per-block pay share = 10000 / 40000 = 25% of the producer pay - - // A name bid, which consists of: - // - a `newname` name that the bid is for - // - a `high_bidder` account name that is the one with the highest bid so far - // - the `high_bid` which is amount of highest bid - // - and `last_bid_time` which is the time of the highest bid - struct [[eosio::table, eosio::contract("eosio.system")]] name_bid { - name newname; - name high_bidder; - int64_t high_bid = 0; ///< negative high_bid == closed auction waiting to be claimed - time_point last_bid_time; - - uint64_t primary_key()const { return newname.value; } - uint64_t by_high_bid()const { return static_cast(-high_bid); } - }; - - // A bid refund, which is defined by: - // - the `bidder` account name owning the refund - // - the `amount` to be refunded - struct [[eosio::table, eosio::contract("eosio.system")]] bid_refund { - name bidder; - asset amount; - - uint64_t primary_key()const { return bidder.value; } - }; - typedef eosio::multi_index< "namebids"_n, name_bid, - indexed_by<"highbid"_n, const_mem_fun > - > name_bid_table; - - typedef eosio::multi_index< "bidrefunds"_n, bid_refund > bid_refund_table; - - // Defines new global state parameters. - struct [[eosio::table("global"), eosio::contract("eosio.system")]] eosio_global_state : eosio::blockchain_parameters { - uint64_t free_ram()const { return max_ram_size - total_ram_bytes_reserved; } - - uint64_t max_ram_size = 64ll*1024 * 1024 * 1024; - uint64_t total_ram_bytes_reserved = 0; - int64_t total_ram_stake = 0; - - block_timestamp last_producer_schedule_update; - time_point last_pervote_bucket_fill; - int64_t pervote_bucket = 0; - int64_t perblock_bucket = 0; - uint32_t total_unpaid_blocks = 0; /// all blocks which have been produced but not paid - int64_t total_activated_stake = 0; - time_point thresh_activated_stake_time; - uint16_t last_producer_schedule_size = 0; - double total_producer_vote_weight = 0; /// the sum of all producer votes - block_timestamp last_name_close; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE_DERIVED( eosio_global_state, eosio::blockchain_parameters, - (max_ram_size)(total_ram_bytes_reserved)(total_ram_stake) - (last_producer_schedule_update)(last_pervote_bucket_fill) - (pervote_bucket)(perblock_bucket)(total_unpaid_blocks)(total_activated_stake)(thresh_activated_stake_time) - (last_producer_schedule_size)(total_producer_vote_weight)(last_name_close) ) - }; - - // Defines new global state parameters added after version 1.0 - struct [[eosio::table("global2"), eosio::contract("eosio.system")]] eosio_global_state2 { - eosio_global_state2(){} - - uint16_t new_ram_per_block = 0; - block_timestamp last_ram_increase; - block_timestamp last_block_num; /* deprecated */ - double total_producer_votepay_share = 0; - uint8_t revision = 0; ///< used to track version updates in the future. - - EOSLIB_SERIALIZE( eosio_global_state2, (new_ram_per_block)(last_ram_increase)(last_block_num) - (total_producer_votepay_share)(revision) ) - }; - - // Defines new global state parameters added after version 1.3.0 - struct [[eosio::table("global3"), eosio::contract("eosio.system")]] eosio_global_state3 { - eosio_global_state3() { } - time_point last_vpay_state_update; - double total_vpay_share_change_rate = 0; - - EOSLIB_SERIALIZE( eosio_global_state3, (last_vpay_state_update)(total_vpay_share_change_rate) ) - }; - - // Defines new global state parameters to store inflation rate and distribution - struct [[eosio::table("global4"), eosio::contract("eosio.system")]] eosio_global_state4 { - eosio_global_state4() { } - double continuous_rate; - int64_t inflation_pay_factor; - int64_t votepay_factor; - - EOSLIB_SERIALIZE( eosio_global_state4, (continuous_rate)(inflation_pay_factor)(votepay_factor) ) - }; - - inline eosio::block_signing_authority convert_to_block_signing_authority( const eosio::public_key& producer_key ) { - return eosio::block_signing_authority_v0{ .threshold = 1, .keys = {{producer_key, 1}} }; - } - - // Defines `producer_info` structure to be stored in `producer_info` table, added after version 1.0 - struct [[eosio::table, eosio::contract("eosio.system")]] producer_info { - name owner; - double total_votes = 0; - eosio::public_key producer_key; /// a packed public key object - bool is_active = true; - std::string url; - uint32_t unpaid_blocks = 0; - time_point last_claim_time; - uint16_t location = 0; - eosio::binary_extension producer_authority; // added in version 1.9.0 - - uint64_t primary_key()const { return owner.value; } - double by_votes()const { return is_active ? -total_votes : total_votes; } - bool active()const { return is_active; } - void deactivate() { producer_key = public_key(); producer_authority.reset(); is_active = false; } - - eosio::block_signing_authority get_producer_authority()const { - if( producer_authority.has_value() ) { - bool zero_threshold = std::visit( [](auto&& auth ) -> bool { - return (auth.threshold == 0); - }, *producer_authority ); - // zero_threshold could be true despite the validation done in regproducer2 because the v1.9.0 eosio.system - // contract has a bug which may have modified the producer table such that the producer_authority field - // contains a default constructed eosio::block_signing_authority (which has a 0 threshold and so is invalid). - if( !zero_threshold ) return *producer_authority; - } - return convert_to_block_signing_authority( producer_key ); - } - - // The unregprod and claimrewards actions modify unrelated fields of the producers table and under the default - // serialization behavior they would increase the size of the serialized table if the producer_authority field - // was not already present. This is acceptable (though not necessarily desired) because those two actions require - // the authority of the producer who pays for the table rows. - // However, the rmvproducer action and the onblock transaction would also modify the producer table in a similar - // way and increasing its serialized size is not acceptable in that context. - // So, a custom serialization is defined to handle the binary_extension producer_authority - // field in the desired way. (Note: v1.9.0 did not have this custom serialization behavior.) - - template - friend DataStream& operator << ( DataStream& ds, const producer_info& t ) { - ds << t.owner - << t.total_votes - << t.producer_key - << t.is_active - << t.url - << t.unpaid_blocks - << t.last_claim_time - << t.location; - - if( !t.producer_authority.has_value() ) return ds; - - return ds << t.producer_authority; - } - - template - friend DataStream& operator >> ( DataStream& ds, producer_info& t ) { - return ds >> t.owner - >> t.total_votes - >> t.producer_key - >> t.is_active - >> t.url - >> t.unpaid_blocks - >> t.last_claim_time - >> t.location - >> t.producer_authority; - } - }; - - // Defines new producer info structure to be stored in new producer info table, added after version 1.3.0 - struct [[eosio::table, eosio::contract("eosio.system")]] producer_info2 { - name owner; - double votepay_share = 0; - time_point last_votepay_share_update; - - uint64_t primary_key()const { return owner.value; } - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( producer_info2, (owner)(votepay_share)(last_votepay_share_update) ) - }; - - // Voter info. Voter info stores information about the voter: - // - `owner` the voter - // - `proxy` the proxy set by the voter, if any - // - `producers` the producers approved by this voter if no proxy set - // - `staked` the amount staked - struct [[eosio::table, eosio::contract("eosio.system")]] voter_info { - name owner; /// the voter - name proxy; /// the proxy set by the voter, if any - std::vector producers; /// the producers approved by this voter if no proxy set - int64_t staked = 0; - - // Every time a vote is cast we must first "undo" the last vote weight, before casting the - // new vote weight. Vote weight is calculated as: - // stated.amount * 2 ^ ( weeks_since_launch/weeks_per_year) - double last_vote_weight = 0; /// the vote weight cast the last time the vote was updated - - // Total vote weight delegated to this voter. - double proxied_vote_weight= 0; /// the total vote weight delegated to this voter as a proxy - bool is_proxy = 0; /// whether the voter is a proxy for others - - - uint32_t flags1 = 0; - uint32_t reserved2 = 0; - eosio::asset reserved3; - - uint64_t primary_key()const { return owner.value; } - - enum class flags1_fields : uint32_t { - ram_managed = 1, - net_managed = 2, - cpu_managed = 4 - }; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(producers)(staked)(last_vote_weight)(proxied_vote_weight)(is_proxy)(flags1)(reserved2)(reserved3) ) - }; - - - typedef eosio::multi_index< "voters"_n, voter_info > voters_table; - - - typedef eosio::multi_index< "producers"_n, producer_info, - indexed_by<"prototalvote"_n, const_mem_fun > - > producers_table; - - typedef eosio::multi_index< "producers2"_n, producer_info2 > producers_table2; - - - typedef eosio::singleton< "global"_n, eosio_global_state > global_state_singleton; - - typedef eosio::singleton< "global2"_n, eosio_global_state2 > global_state2_singleton; - - typedef eosio::singleton< "global3"_n, eosio_global_state3 > global_state3_singleton; - - typedef eosio::singleton< "global4"_n, eosio_global_state4 > global_state4_singleton; - - struct [[eosio::table, eosio::contract("eosio.system")]] user_resources { - name owner; - asset net_weight; - asset cpu_weight; - int64_t ram_bytes = 0; - - bool is_empty()const { return net_weight.amount == 0 && cpu_weight.amount == 0 && ram_bytes == 0; } - uint64_t primary_key()const { return owner.value; } - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( user_resources, (owner)(net_weight)(cpu_weight)(ram_bytes) ) - }; - - // Every user 'from' has a scope/table that uses every receipient 'to' as the primary key. - struct [[eosio::table, eosio::contract("eosio.system")]] delegated_bandwidth { - name from; - name to; - asset net_weight; - asset cpu_weight; - - bool is_empty()const { return net_weight.amount == 0 && cpu_weight.amount == 0; } - uint64_t primary_key()const { return to.value; } - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( delegated_bandwidth, (from)(to)(net_weight)(cpu_weight) ) - - }; - - struct [[eosio::table, eosio::contract("eosio.system")]] refund_request { - name owner; - time_point_sec request_time; - eosio::asset net_amount; - eosio::asset cpu_amount; - - bool is_empty()const { return net_amount.amount == 0 && cpu_amount.amount == 0; } - uint64_t primary_key()const { return owner.value; } - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( refund_request, (owner)(request_time)(net_amount)(cpu_amount) ) - }; - - - typedef eosio::multi_index< "userres"_n, user_resources > user_resources_table; - typedef eosio::multi_index< "delband"_n, delegated_bandwidth > del_bandwidth_table; - typedef eosio::multi_index< "refunds"_n, refund_request > refunds_table; - - // `rex_pool` structure underlying the rex pool table. A rex pool table entry is defined by: - // - `version` defaulted to zero, - // - `total_lent` total amount of CORE_SYMBOL in open rex_loans - // - `total_unlent` total amount of CORE_SYMBOL available to be lent (connector), - // - `total_rent` fees received in exchange for lent (connector), - // - `total_lendable` total amount of CORE_SYMBOL that have been lent (total_unlent + total_lent), - // - `total_rex` total number of REX shares allocated to contributors to total_lendable, - // - `namebid_proceeds` the amount of CORE_SYMBOL to be transferred from namebids to REX pool, - // - `loan_num` increments with each new loan - struct [[eosio::table,eosio::contract("eosio.system")]] rex_pool { - uint8_t version = 0; - asset total_lent; - asset total_unlent; - asset total_rent; - asset total_lendable; - asset total_rex; - asset namebid_proceeds; - uint64_t loan_num = 0; - - uint64_t primary_key()const { return 0; } - }; - - typedef eosio::multi_index< "rexpool"_n, rex_pool > rex_pool_table; - - // `rex_return_pool` structure underlying the rex return pool table. A rex return pool table entry is defined by: - // - `version` defaulted to zero, - // - `last_dist_time` the last time proceeds from renting, ram fees, and name bids were added to the rex pool, - // - `pending_bucket_time` timestamp of the pending 12-hour return bucket, - // - `oldest_bucket_time` cached timestamp of the oldest 12-hour return bucket, - // - `pending_bucket_proceeds` proceeds in the pending 12-hour return bucket, - // - `current_rate_of_increase` the current rate per dist_interval at which proceeds are added to the rex pool, - // - `proceeds` the maximum amount of proceeds that can be added to the rex pool at any given time - struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_pool { - uint8_t version = 0; - time_point_sec last_dist_time; - time_point_sec pending_bucket_time = time_point_sec::maximum(); - time_point_sec oldest_bucket_time = time_point_sec::min(); - int64_t pending_bucket_proceeds = 0; - int64_t current_rate_of_increase = 0; - int64_t proceeds = 0; - - static constexpr uint32_t total_intervals = 30 * 144; // 30 days - static constexpr uint32_t dist_interval = 10 * 60; // 10 minutes - static constexpr uint8_t hours_per_bucket = 12; - static_assert( total_intervals * dist_interval == 30 * seconds_per_day ); - - uint64_t primary_key()const { return 0; } - }; - - typedef eosio::multi_index< "rexretpool"_n, rex_return_pool > rex_return_pool_table; - - // `rex_return_buckets` structure underlying the rex return buckets table. A rex return buckets table is defined by: - // - `version` defaulted to zero, - // - `return_buckets` buckets of proceeds accumulated in 12-hour intervals - struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_buckets { - uint8_t version = 0; - std::map return_buckets; - - uint64_t primary_key()const { return 0; } - }; - - typedef eosio::multi_index< "retbuckets"_n, rex_return_buckets > rex_return_buckets_table; - - // `rex_fund` structure underlying the rex fund table. A rex fund table entry is defined by: - // - `version` defaulted to zero, - // - `owner` the owner of the rex fund, - // - `balance` the balance of the fund. - struct [[eosio::table,eosio::contract("eosio.system")]] rex_fund { - uint8_t version = 0; - name owner; - asset balance; - - uint64_t primary_key()const { return owner.value; } - }; - - typedef eosio::multi_index< "rexfund"_n, rex_fund > rex_fund_table; - - // `rex_balance` structure underlying the rex balance table. A rex balance table entry is defined by: - // - `version` defaulted to zero, - // - `owner` the owner of the rex fund, - // - `vote_stake` the amount of CORE_SYMBOL currently included in owner's vote, - // - `rex_balance` the amount of REX owned by owner, - // - `matured_rex` matured REX available for selling - struct [[eosio::table,eosio::contract("eosio.system")]] rex_balance { - uint8_t version = 0; - name owner; - asset vote_stake; - asset rex_balance; - int64_t matured_rex = 0; - std::deque> rex_maturities; /// REX daily maturity buckets - - uint64_t primary_key()const { return owner.value; } - }; - - typedef eosio::multi_index< "rexbal"_n, rex_balance > rex_balance_table; - - // `rex_loan` structure underlying the `rex_cpu_loan_table` and `rex_net_loan_table`. A rex net/cpu loan table entry is defined by: - // - `version` defaulted to zero, - // - `from` account creating and paying for loan, - // - `receiver` account receiving rented resources, - // - `payment` SYS tokens paid for the loan, - // - `balance` is the amount of SYS tokens available to be used for loan auto-renewal, - // - `total_staked` total amount staked, - // - `loan_num` loan number/id, - // - `expiration` the expiration time when loan will be either closed or renewed - // If payment <= balance, the loan is renewed, and closed otherwise. - struct [[eosio::table,eosio::contract("eosio.system")]] rex_loan { - uint8_t version = 0; - name from; - name receiver; - asset payment; - asset balance; - asset total_staked; - uint64_t loan_num; - eosio::time_point expiration; - - uint64_t primary_key()const { return loan_num; } - uint64_t by_expr()const { return expiration.elapsed.count(); } - uint64_t by_owner()const { return from.value; } - }; - - typedef eosio::multi_index< "cpuloan"_n, rex_loan, - indexed_by<"byexpr"_n, const_mem_fun>, - indexed_by<"byowner"_n, const_mem_fun> - > rex_cpu_loan_table; - - typedef eosio::multi_index< "netloan"_n, rex_loan, - indexed_by<"byexpr"_n, const_mem_fun>, - indexed_by<"byowner"_n, const_mem_fun> - > rex_net_loan_table; - - struct [[eosio::table,eosio::contract("eosio.system")]] rex_order { - uint8_t version = 0; - name owner; - asset rex_requested; - asset proceeds; - asset stake_change; - eosio::time_point order_time; - bool is_open = true; - - void close() { is_open = false; } - uint64_t primary_key()const { return owner.value; } - uint64_t by_time()const { return is_open ? order_time.elapsed.count() : std::numeric_limits::max(); } - }; - - typedef eosio::multi_index< "rexqueue"_n, rex_order, - indexed_by<"bytime"_n, const_mem_fun>> rex_order_table; - - struct rex_order_outcome { - bool success; - asset proceeds; - asset stake_change; - }; - - /** - * The `eosio.system` smart contract is provided by `block.one` as a sample system contract, and it defines the structures and actions needed for blockchain's core functionality. - * - * Just like in the `eosio.bios` sample contract implementation, there are a few actions which are not implemented at the contract level (`newaccount`, `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, `canceldelay`, `onerror`, `setabi`, `setcode`), they are just declared in the contract so they will show in the contract's ABI and users will be able to push those actions to the chain via the account holding the `eosio.system` contract, but the implementation is at the EOSIO core level. They are referred to as EOSIO native actions. - * - * - Users can stake tokens for CPU and Network bandwidth, and then vote for producers or - * delegate their vote to a proxy. - * - Producers register in order to be voted for, and can claim per-block and per-vote rewards. - * - Users can buy and sell RAM at a market-determined price. - * - Users can bid on premium names. - * - A resource exchange system (REX) allows token holders to lend their tokens, - * and users to rent CPU and Network resources in return for a market-determined fee. - */ - class [[eosio::contract("eosio.system")]] system_contract : public native { - - private: - voters_table _voters; - producers_table _producers; - producers_table2 _producers2; - global_state_singleton _global; - global_state2_singleton _global2; - global_state3_singleton _global3; - global_state4_singleton _global4; - eosio_global_state _gstate; - eosio_global_state2 _gstate2; - eosio_global_state3 _gstate3; - eosio_global_state4 _gstate4; - rammarket _rammarket; - rex_pool_table _rexpool; - rex_return_pool_table _rexretpool; - rex_return_buckets_table _rexretbuckets; - rex_fund_table _rexfunds; - rex_balance_table _rexbalance; - rex_order_table _rexorders; - - public: - static constexpr eosio::name active_permission{"active"_n}; - static constexpr eosio::name token_account{"eosio.token"_n}; - static constexpr eosio::name ram_account{"eosio.ram"_n}; - static constexpr eosio::name ramfee_account{"eosio.ramfee"_n}; - static constexpr eosio::name stake_account{"eosio.stake"_n}; - static constexpr eosio::name bpay_account{"eosio.bpay"_n}; - static constexpr eosio::name vpay_account{"eosio.vpay"_n}; - static constexpr eosio::name names_account{"eosio.names"_n}; - static constexpr eosio::name saving_account{"eosio.saving"_n}; - static constexpr eosio::name rex_account{"eosio.rex"_n}; - static constexpr eosio::name null_account{"eosio.null"_n}; - static constexpr symbol ramcore_symbol = symbol(symbol_code("RAMCORE"), 4); - static constexpr symbol ram_symbol = symbol(symbol_code("RAM"), 0); - static constexpr symbol rex_symbol = symbol(symbol_code("REX"), 4); - - system_contract( name s, name code, datastream ds ); - ~system_contract(); - - // Returns the core symbol by system account name - // @param system_account - the system account to get the core symbol for. - static symbol get_core_symbol( name system_account = "eosio"_n ) { - rammarket rm(system_account, system_account.value); - const static auto sym = get_core_symbol( rm ); - return sym; - } - - // Actions: - /** - * The Init action initializes the system contract for a version and a symbol. - * Only succeeds when: - * - version is 0 and - * - symbol is found and - * - system token supply is greater than 0, - * - and system contract wasn’t already been initialized. - * - * @param version - the version, has to be 0, - * @param core - the system symbol. - */ - [[eosio::action]] - void init( unsigned_int version, const symbol& core ); - - /** - * On block action. This special action is triggered when a block is applied by the given producer - * and cannot be generated from any other source. It is used to pay producers and calculate - * missed blocks of other producers. Producer pay is deposited into the producer's stake - * balance and can be withdrawn over time. If blocknum is the start of a new round this may - * update the active producer config from the producer votes. - * - * @param header - the block header produced. - */ - [[eosio::action]] - void onblock( ignore header ); - - /** - * Set account limits action sets the resource limits of an account - * - * @param account - name of the account whose resource limit to be set, - * @param ram_bytes - ram limit in absolute bytes, - * @param net_weight - fractionally proportionate net limit of available resources based on (weight / total_weight_of_all_accounts), - * @param cpu_weight - fractionally proportionate cpu limit of available resources based on (weight / total_weight_of_all_accounts). - */ - [[eosio::action]] - void setalimits( const name& account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ); - - /** - * Set account RAM limits action, which sets the RAM limits of an account - * - * @param account - name of the account whose resource limit to be set, - * @param ram_bytes - ram limit in absolute bytes. - */ - [[eosio::action]] - void setacctram( const name& account, const std::optional& ram_bytes ); - - /** - * Set account NET limits action, which sets the NET limits of an account - * - * @param account - name of the account whose resource limit to be set, - * @param net_weight - fractionally proportionate net limit of available resources based on (weight / total_weight_of_all_accounts). - */ - [[eosio::action]] - void setacctnet( const name& account, const std::optional& net_weight ); - - /** - * Set account CPU limits action, which sets the CPU limits of an account - * - * @param account - name of the account whose resource limit to be set, - * @param cpu_weight - fractionally proportionate cpu limit of available resources based on (weight / total_weight_of_all_accounts). - */ - [[eosio::action]] - void setacctcpu( const name& account, const std::optional& cpu_weight ); - - - /** - * The activate action, activates a protocol feature - * - * @param feature_digest - hash of the protocol feature to activate. - */ - [[eosio::action]] - void activate( const eosio::checksum256& feature_digest ); - - // functions defined in delegate_bandwidth.cpp - - /** - * Delegate bandwidth and/or cpu action. Stakes SYS from the balance of `from` for the benefit of `receiver`. - * - * @param from - the account to delegate bandwidth from, that is, the account holding - * tokens to be staked, - * @param receiver - the account to delegate bandwith to, that is, the account to - * whose resources staked tokens are added - * @param stake_net_quantity - tokens staked for NET bandwidth, - * @param stake_cpu_quantity - tokens staked for CPU bandwidth, - * @param transfer - if true, ownership of staked tokens is transfered to `receiver`. - * - * @post All producers `from` account has voted for will have their votes updated immediately. - */ - [[eosio::action]] - void delegatebw( const name& from, const name& receiver, - const asset& stake_net_quantity, const asset& stake_cpu_quantity, bool transfer ); - - /** - * Setrex action, sets total_rent balance of REX pool to the passed value. - * @param balance - amount to set the REX pool balance. - */ - [[eosio::action]] - void setrex( const asset& balance ); - - /** - * Deposit to REX fund action. Deposits core tokens to user REX fund. - * All proceeds and expenses related to REX are added to or taken out of this fund. - * An inline transfer from 'owner' liquid balance is executed. - * All REX-related costs and proceeds are deducted from and added to 'owner' REX fund, - * with one exception being buying REX using staked tokens. - * Storage change is billed to 'owner'. - * - * @param owner - REX fund owner account, - * @param amount - amount of tokens to be deposited. - */ - [[eosio::action]] - void deposit( const name& owner, const asset& amount ); - - /** - * Withdraw from REX fund action, withdraws core tokens from user REX fund. - * An inline token transfer to user balance is executed. - * - * @param owner - REX fund owner account, - * @param amount - amount of tokens to be withdrawn. - */ - [[eosio::action]] - void withdraw( const name& owner, const asset& amount ); - - /** - * Buyrex action, buys REX in exchange for tokens taken out of user's REX fund by transfering - * core tokens from user REX fund and converts them to REX stake. By buying REX, user is - * lending tokens in order to be rented as CPU or NET resourses. - * Storage change is billed to 'from' account. - * - * @param from - owner account name, - * @param amount - amount of tokens taken out of 'from' REX fund. - * - * @pre A voting requirement must be satisfied before action can be executed. - * @pre User must vote for at least 21 producers or delegate vote to proxy before buying REX. - * - * @post User votes are updated following this action. - * @post Tokens used in purchase are added to user's voting power. - * @post Bought REX cannot be sold before 4 days counting from end of day of purchase. - */ - [[eosio::action]] - void buyrex( const name& from, const asset& amount ); - - /** - * Unstaketorex action, uses staked core tokens to buy REX. - * Storage change is billed to 'owner' account. - * - * @param owner - owner of staked tokens, - * @param receiver - account name that tokens have previously been staked to, - * @param from_net - amount of tokens to be unstaked from NET bandwidth and used for REX purchase, - * @param from_cpu - amount of tokens to be unstaked from CPU bandwidth and used for REX purchase. - * - * @pre A voting requirement must be satisfied before action can be executed. - * @pre User must vote for at least 21 producers or delegate vote to proxy before buying REX. - * - * @post User votes are updated following this action. - * @post Tokens used in purchase are added to user's voting power. - * @post Bought REX cannot be sold before 4 days counting from end of day of purchase. - */ - [[eosio::action]] - void unstaketorex( const name& owner, const name& receiver, const asset& from_net, const asset& from_cpu ); - - /** - * Sellrex action, sells REX in exchange for core tokens by converting REX stake back into core tokens - * at current exchange rate. If order cannot be processed, it gets queued until there is enough - * in REX pool to fill order, and will be processed within 30 days at most. If successful, user - * votes are updated, that is, proceeds are deducted from user's voting power. In case sell order - * is queued, storage change is billed to 'from' account. - * - * @param from - owner account of REX, - * @param rex - amount of REX to be sold. - */ - [[eosio::action]] - void sellrex( const name& from, const asset& rex ); - - /** - * Cnclrexorder action, cancels unfilled REX sell order by owner if one exists. - * - * @param owner - owner account name. - * - * @pre Order cannot be cancelled once it's been filled. - */ - [[eosio::action]] - void cnclrexorder( const name& owner ); - - /** - * Rentcpu action, uses payment to rent as many SYS tokens as possible as determined by market price and - * stake them for CPU for the benefit of receiver, after 30 days the rented core delegation of CPU - * will expire. At expiration, if balance is greater than or equal to `loan_payment`, `loan_payment` - * is taken out of loan balance and used to renew the loan. Otherwise, the loan is closed and user - * is refunded any remaining balance. - * Owner can fund or refund a loan at any time before its expiration. - * All loan expenses and refunds come out of or are added to owner's REX fund. - * - * @param from - account creating and paying for CPU loan, 'from' account can add tokens to loan - * balance using action `fundcpuloan` and withdraw from loan balance using `defcpuloan` - * @param receiver - account receiving rented CPU resources, - * @param loan_payment - tokens paid for the loan, it has to be greater than zero, - * amount of rented resources is calculated from `loan_payment`, - * @param loan_fund - additional tokens can be zero, and is added to loan balance. - * Loan balance represents a reserve that is used at expiration for automatic loan renewal. - */ - [[eosio::action]] - void rentcpu( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ); - - /** - * Rentnet action, uses payment to rent as many SYS tokens as possible as determined by market price and - * stake them for NET for the benefit of receiver, after 30 days the rented core delegation of NET - * will expire. At expiration, if balance is greater than or equal to `loan_payment`, `loan_payment` - * is taken out of loan balance and used to renew the loan. Otherwise, the loan is closed and user - * is refunded any remaining balance. - * Owner can fund or refund a loan at any time before its expiration. - * All loan expenses and refunds come out of or are added to owner's REX fund. - * - * @param from - account creating and paying for NET loan, 'from' account can add tokens to loan - * balance using action `fundnetloan` and withdraw from loan balance using `defnetloan`, - * @param receiver - account receiving rented NET resources, - * @param loan_payment - tokens paid for the loan, it has to be greater than zero, - * amount of rented resources is calculated from `loan_payment`, - * @param loan_fund - additional tokens can be zero, and is added to loan balance. - * Loan balance represents a reserve that is used at expiration for automatic loan renewal. - */ - [[eosio::action]] - void rentnet( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ); - - /** - * Fundcpuloan action, transfers tokens from REX fund to the fund of a specific CPU loan in order to - * be used for loan renewal at expiry. - * - * @param from - loan creator account, - * @param loan_num - loan id, - * @param payment - tokens transfered from REX fund to loan fund. - */ - [[eosio::action]] - void fundcpuloan( const name& from, uint64_t loan_num, const asset& payment ); - - /** - * Fundnetloan action, transfers tokens from REX fund to the fund of a specific NET loan in order to - * be used for loan renewal at expiry. - * - * @param from - loan creator account, - * @param loan_num - loan id, - * @param payment - tokens transfered from REX fund to loan fund. - */ - [[eosio::action]] - void fundnetloan( const name& from, uint64_t loan_num, const asset& payment ); - - /** - * Defcpuloan action, withdraws tokens from the fund of a specific CPU loan and adds them to REX fund. - * - * @param from - loan creator account, - * @param loan_num - loan id, - * @param amount - tokens transfered from CPU loan fund to REX fund. - */ - [[eosio::action]] - void defcpuloan( const name& from, uint64_t loan_num, const asset& amount ); - - /** - * Defnetloan action, withdraws tokens from the fund of a specific NET loan and adds them to REX fund. - * - * @param from - loan creator account, - * @param loan_num - loan id, - * @param amount - tokens transfered from NET loan fund to REX fund. - */ - [[eosio::action]] - void defnetloan( const name& from, uint64_t loan_num, const asset& amount ); - - /** - * Updaterex action, updates REX owner vote weight to current value of held REX tokens. - * - * @param owner - REX owner account. - */ - [[eosio::action]] - void updaterex( const name& owner ); - - /** - * Rexexec action, processes max CPU loans, max NET loans, and max queued sellrex orders. - * Action does not execute anything related to a specific user. - * - * @param user - any account can execute this action, - * @param max - number of each of CPU loans, NET loans, and sell orders to be processed. - */ - [[eosio::action]] - void rexexec( const name& user, uint16_t max ); - - /** - * Consolidate action, consolidates REX maturity buckets into one bucket that can be sold after 4 days - * starting from the end of the day. - * - * @param owner - REX owner account name. - */ - [[eosio::action]] - void consolidate( const name& owner ); - - /** - * Mvtosavings action, moves a specified amount of REX into savings bucket. REX savings bucket - * never matures. In order for it to be sold, it has to be moved explicitly - * out of that bucket. Then the moved amount will have the regular maturity - * period of 4 days starting from the end of the day. - * - * @param owner - REX owner account name. - * @param rex - amount of REX to be moved. - */ - [[eosio::action]] - void mvtosavings( const name& owner, const asset& rex ); - - /** - * Mvfrsavings action, moves a specified amount of REX out of savings bucket. The moved amount - * will have the regular REX maturity period of 4 days. - * - * @param owner - REX owner account name. - * @param rex - amount of REX to be moved. - */ - [[eosio::action]] - void mvfrsavings( const name& owner, const asset& rex ); - - /** - * Closerex action, deletes owner records from REX tables and frees used RAM. Owner must not have - * an outstanding REX balance. - * - * @param owner - user account name. - * - * @pre If owner has a non-zero REX balance, the action fails; otherwise, - * owner REX balance entry is deleted. - * @pre If owner has no outstanding loans and a zero REX fund balance, - * REX fund entry is deleted. - */ - [[eosio::action]] - void closerex( const name& owner ); - - /** - * Undelegate bandwitdh action, decreases the total tokens delegated by `from` to `receiver` and/or - * frees the memory associated with the delegation if there is nothing - * left to delegate. - * This will cause an immediate reduction in net/cpu bandwidth of the - * receiver. - * A transaction is scheduled to send the tokens back to `from` after - * the staking period has passed. If existing transaction is scheduled, it - * will be canceled and a new transaction issued that has the combined - * undelegated amount. - * The `from` account loses voting power as a result of this call and - * all producer tallies are updated. - * - * @param from - the account to undelegate bandwidth from, that is, - * the account whose tokens will be unstaked, - * @param receiver - the account to undelegate bandwith to, that is, - * the account to whose benefit tokens have been staked, - * @param unstake_net_quantity - tokens to be unstaked from NET bandwidth, - * @param unstake_cpu_quantity - tokens to be unstaked from CPU bandwidth, - * - * @post Unstaked tokens are transferred to `from` liquid balance via a - * deferred transaction with a delay of 3 days. - * @post If called during the delay period of a previous `undelegatebw` - * action, pending action is canceled and timer is reset. - * @post All producers `from` account has voted for will have their votes updated immediately. - * @post Bandwidth and storage for the deferred transaction are billed to `from`. - */ - [[eosio::action]] - void undelegatebw( const name& from, const name& receiver, - const asset& unstake_net_quantity, const asset& unstake_cpu_quantity ); - - /** - * Buy ram action, increases receiver's ram quota based upon current price and quantity of - * tokens provided. An inline transfer from receiver to system contract of - * tokens will be executed. - * - * @param payer - the ram buyer, - * @param receiver - the ram receiver, - * @param quant - the quntity of tokens to buy ram with. - */ - [[eosio::action]] - void buyram( const name& payer, const name& receiver, const asset& quant ); - - /** - * Buy a specific amount of ram bytes action. Increases receiver's ram in quantity of bytes provided. - * An inline transfer from receiver to system contract of tokens will be executed. - * - * @param payer - the ram buyer, - * @param receiver - the ram receiver, - * @param bytes - the quntity of ram to buy specified in bytes. - */ - [[eosio::action]] - void buyrambytes( const name& payer, const name& receiver, uint32_t bytes ); - - /** - * Sell ram action, reduces quota by bytes and then performs an inline transfer of tokens - * to receiver based upon the average purchase price of the original quota. - * - * @param account - the ram seller account, - * @param bytes - the amount of ram to sell in bytes. - */ - [[eosio::action]] - void sellram( const name& account, int64_t bytes ); - - /** - * Refund action, this action is called after the delegation-period to claim all pending - * unstaked tokens belonging to owner. - * - * @param owner - the owner of the tokens claimed. - */ - [[eosio::action]] - void refund( const name& owner ); - - // functions defined in voting.cpp - - /** - * Register producer action, indicates that a particular account wishes to become a producer, - * this action will create a `producer_config` and a `producer_info` object for `producer` scope - * in producers tables. - * - * @param producer - account registering to be a producer candidate, - * @param producer_key - the public key of the block producer, this is the key used by block producer to sign blocks, - * @param url - the url of the block producer, normally the url of the block producer presentation website, - * @param location - is the country code as defined in the ISO 3166, https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes - * - * @pre Producer to register is an account - * @pre Authority of producer to register - */ - [[eosio::action]] - void regproducer( const name& producer, const public_key& producer_key, const std::string& url, uint16_t location ); - - /** - * Register producer action, indicates that a particular account wishes to become a producer, - * this action will create a `producer_config` and a `producer_info` object for `producer` scope - * in producers tables. - * - * @param producer - account registering to be a producer candidate, - * @param producer_authority - the weighted threshold multisig block signing authority of the block producer used to sign blocks, - * @param url - the url of the block producer, normally the url of the block producer presentation website, - * @param location - is the country code as defined in the ISO 3166, https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes - * - * @pre Producer to register is an account - * @pre Authority of producer to register - */ - [[eosio::action]] - void regproducer2( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ); - - /** - * Unregister producer action, deactivates the block producer with account name `producer`. - * - * Deactivate the block producer with account name `producer`. - * @param producer - the block producer account to unregister. - */ - [[eosio::action]] - void unregprod( const name& producer ); - - /** - * Set ram action sets the ram supply. - * @param max_ram_size - the amount of ram supply to set. - */ - [[eosio::action]] - void setram( uint64_t max_ram_size ); - - /** - * Set ram rate action, sets the rate of increase of RAM in bytes per block. It is capped by the uint16_t to - * a maximum rate of 3 TB per year. If update_ram_supply hasn't been called for the most recent block, - * then new ram will be allocated at the old rate up to the present block before switching the rate. - * - * @param bytes_per_block - the amount of bytes per block increase to set. - */ - [[eosio::action]] - void setramrate( uint16_t bytes_per_block ); - - /** - * Vote producer action, votes for a set of producers. This action updates the list of `producers` voted for, - * for `voter` account. If voting for a `proxy`, the producer votes will not change until the - * proxy updates their own vote. Voter can vote for a proxy __or__ a list of at most 30 producers. - * Storage change is billed to `voter`. - * - * @param voter - the account to change the voted producers for, - * @param proxy - the proxy to change the voted producers for, - * @param producers - the list of producers to vote for, a maximum of 30 producers is allowed. - * - * @pre Producers must be sorted from lowest to highest and must be registered and active - * @pre If proxy is set then no producers can be voted for - * @pre If proxy is set then proxy account must exist and be registered as a proxy - * @pre Every listed producer or proxy must have been previously registered - * @pre Voter must authorize this action - * @pre Voter must have previously staked some EOS for voting - * @pre Voter->staked must be up to date - * - * @post Every producer previously voted for will have vote reduced by previous vote weight - * @post Every producer newly voted for will have vote increased by new vote amount - * @post Prior proxy will proxied_vote_weight decremented by previous vote weight - * @post New proxy will proxied_vote_weight incremented by new vote weight - */ - [[eosio::action]] - void voteproducer( const name& voter, const name& proxy, const std::vector& producers ); - - /** - * Register proxy action, sets `proxy` account as proxy. - * An account marked as a proxy can vote with the weight of other accounts which - * have selected it as a proxy. Other accounts must refresh their voteproducer to - * update the proxy's weight. - * Storage change is billed to `proxy`. - * - * @param rpoxy - the account registering as voter proxy (or unregistering), - * @param isproxy - if true, proxy is registered; if false, proxy is unregistered. - * - * @pre Proxy must have something staked (existing row in voters table) - * @pre New state must be different than current state - */ - [[eosio::action]] - void regproxy( const name& proxy, bool isproxy ); - - /** - * Set the blockchain parameters. By tunning these parameters a degree of - * customization can be achieved. - * @param params - New blockchain parameters to set. - */ - [[eosio::action]] - void setparams( const eosio::blockchain_parameters& params ); - - /** - * Claim rewards action, claims block producing and vote rewards. - * @param owner - producer account claiming per-block and per-vote rewards. - */ - [[eosio::action]] - void claimrewards( const name& owner ); - - /** - * Set privilege status for an account. Allows to set privilege status for an account (turn it on/off). - * @param account - the account to set the privileged status for. - * @param is_priv - 0 for false, > 0 for true. - */ - [[eosio::action]] - void setpriv( const name& account, uint8_t is_priv ); - - /** - * Remove producer action, deactivates a producer by name, if not found asserts. - * @param producer - the producer account to deactivate. - */ - [[eosio::action]] - void rmvproducer( const name& producer ); - - /** - * Update revision action, updates the current revision. - * @param revision - it has to be incremented by 1 compared with current revision. - * - * @pre Current revision can not be higher than 254, and has to be smaller - * than or equal 1 (“set upper bound to greatest revision supported in the code”). - */ - [[eosio::action]] - void updtrevision( uint8_t revision ); - - /** - * Bid name action, allows an account `bidder` to place a bid for a name `newname`. - * @param bidder - the account placing the bid, - * @param newname - the name the bid is placed for, - * @param bid - the amount of system tokens payed for the bid. - * - * @pre Bids can be placed only on top-level suffix, - * @pre Non empty name, - * @pre Names longer than 12 chars are not allowed, - * @pre Names equal with 12 chars can be created without placing a bid, - * @pre Bid has to be bigger than zero, - * @pre Bid's symbol must be system token, - * @pre Bidder account has to be different than current highest bidder, - * @pre Bid must increase current bid by 10%, - * @pre Auction must still be opened. - */ - [[eosio::action]] - void bidname( const name& bidder, const name& newname, const asset& bid ); - - /** - * Bid refund action, allows the account `bidder` to get back the amount it bid so far on a `newname` name. - * - * @param bidder - the account that gets refunded, - * @param newname - the name for which the bid was placed and now it gets refunded for. - */ - [[eosio::action]] - void bidrefund( const name& bidder, const name& newname ); - - /** - * Change the annual inflation rate of the core token supply and specify how - * the new issued tokens will be distributed based on the following structure. - * - * @param annual_rate - Annual inflation rate of the core token supply. - * (eg. For 5% Annual inflation => annual_rate=500 - * For 1.5% Annual inflation => annual_rate=150 - * @param inflation_pay_factor - Inverse of the fraction of the inflation used to reward block producers. - * The remaining inflation will be sent to the `eosio.saving` account. - * (eg. For 20% of inflation going to block producer rewards => inflation_pay_factor = 50000 - * For 100% of inflation going to block producer rewards => inflation_pay_factor = 10000). - * @param votepay_factor - Inverse of the fraction of the block producer rewards to be distributed proportional to blocks produced. - * The remaining rewards will be distributed proportional to votes received. - * (eg. For 25% of block producer rewards going towards block pay => votepay_factor = 40000 - * For 75% of block producer rewards going towards block pay => votepay_factor = 13333). - */ - [[eosio::action]] - void setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor ); - - using init_action = eosio::action_wrapper<"init"_n, &system_contract::init>; - using setacctram_action = eosio::action_wrapper<"setacctram"_n, &system_contract::setacctram>; - using setacctnet_action = eosio::action_wrapper<"setacctnet"_n, &system_contract::setacctnet>; - using setacctcpu_action = eosio::action_wrapper<"setacctcpu"_n, &system_contract::setacctcpu>; - using activate_action = eosio::action_wrapper<"activate"_n, &system_contract::activate>; - using delegatebw_action = eosio::action_wrapper<"delegatebw"_n, &system_contract::delegatebw>; - using deposit_action = eosio::action_wrapper<"deposit"_n, &system_contract::deposit>; - using withdraw_action = eosio::action_wrapper<"withdraw"_n, &system_contract::withdraw>; - using buyrex_action = eosio::action_wrapper<"buyrex"_n, &system_contract::buyrex>; - using unstaketorex_action = eosio::action_wrapper<"unstaketorex"_n, &system_contract::unstaketorex>; - using sellrex_action = eosio::action_wrapper<"sellrex"_n, &system_contract::sellrex>; - using cnclrexorder_action = eosio::action_wrapper<"cnclrexorder"_n, &system_contract::cnclrexorder>; - using rentcpu_action = eosio::action_wrapper<"rentcpu"_n, &system_contract::rentcpu>; - using rentnet_action = eosio::action_wrapper<"rentnet"_n, &system_contract::rentnet>; - using fundcpuloan_action = eosio::action_wrapper<"fundcpuloan"_n, &system_contract::fundcpuloan>; - using fundnetloan_action = eosio::action_wrapper<"fundnetloan"_n, &system_contract::fundnetloan>; - using defcpuloan_action = eosio::action_wrapper<"defcpuloan"_n, &system_contract::defcpuloan>; - using defnetloan_action = eosio::action_wrapper<"defnetloan"_n, &system_contract::defnetloan>; - using updaterex_action = eosio::action_wrapper<"updaterex"_n, &system_contract::updaterex>; - using rexexec_action = eosio::action_wrapper<"rexexec"_n, &system_contract::rexexec>; - using setrex_action = eosio::action_wrapper<"setrex"_n, &system_contract::setrex>; - using mvtosavings_action = eosio::action_wrapper<"mvtosavings"_n, &system_contract::mvtosavings>; - using mvfrsavings_action = eosio::action_wrapper<"mvfrsavings"_n, &system_contract::mvfrsavings>; - using consolidate_action = eosio::action_wrapper<"consolidate"_n, &system_contract::consolidate>; - using closerex_action = eosio::action_wrapper<"closerex"_n, &system_contract::closerex>; - using undelegatebw_action = eosio::action_wrapper<"undelegatebw"_n, &system_contract::undelegatebw>; - using buyram_action = eosio::action_wrapper<"buyram"_n, &system_contract::buyram>; - using buyrambytes_action = eosio::action_wrapper<"buyrambytes"_n, &system_contract::buyrambytes>; - using sellram_action = eosio::action_wrapper<"sellram"_n, &system_contract::sellram>; - using refund_action = eosio::action_wrapper<"refund"_n, &system_contract::refund>; - using regproducer_action = eosio::action_wrapper<"regproducer"_n, &system_contract::regproducer>; - using regproducer2_action = eosio::action_wrapper<"regproducer2"_n, &system_contract::regproducer2>; - using unregprod_action = eosio::action_wrapper<"unregprod"_n, &system_contract::unregprod>; - using setram_action = eosio::action_wrapper<"setram"_n, &system_contract::setram>; - using setramrate_action = eosio::action_wrapper<"setramrate"_n, &system_contract::setramrate>; - using voteproducer_action = eosio::action_wrapper<"voteproducer"_n, &system_contract::voteproducer>; - using regproxy_action = eosio::action_wrapper<"regproxy"_n, &system_contract::regproxy>; - using claimrewards_action = eosio::action_wrapper<"claimrewards"_n, &system_contract::claimrewards>; - using rmvproducer_action = eosio::action_wrapper<"rmvproducer"_n, &system_contract::rmvproducer>; - using updtrevision_action = eosio::action_wrapper<"updtrevision"_n, &system_contract::updtrevision>; - using bidname_action = eosio::action_wrapper<"bidname"_n, &system_contract::bidname>; - using bidrefund_action = eosio::action_wrapper<"bidrefund"_n, &system_contract::bidrefund>; - using setpriv_action = eosio::action_wrapper<"setpriv"_n, &system_contract::setpriv>; - using setalimits_action = eosio::action_wrapper<"setalimits"_n, &system_contract::setalimits>; - using setparams_action = eosio::action_wrapper<"setparams"_n, &system_contract::setparams>; - using setinflation_action = eosio::action_wrapper<"setinflation"_n, &system_contract::setinflation>; - - private: - // Implementation details: - - static symbol get_core_symbol( const rammarket& rm ) { - auto itr = rm.find(ramcore_symbol.raw()); - check(itr != rm.end(), "system contract must first be initialized"); - return itr->quote.balance.symbol; - } - - //defined in eosio.system.cpp - static eosio_global_state get_default_parameters(); - static eosio_global_state4 get_default_inflation_parameters(); - symbol core_symbol()const; - void update_ram_supply(); - - // defined in rex.cpp - void runrex( uint16_t max ); - void update_rex_pool(); - void update_resource_limits( const name& from, const name& receiver, int64_t delta_net, int64_t delta_cpu ); - void check_voting_requirement( const name& owner, - const char* error_msg = "must vote for at least 21 producers or for a proxy before buying REX" )const; - rex_order_outcome fill_rex_order( const rex_balance_table::const_iterator& bitr, const asset& rex ); - asset update_rex_account( const name& owner, const asset& proceeds, const asset& unstake_quant, bool force_vote_update = false ); - void channel_to_rex( const name& from, const asset& amount ); - void channel_namebid_to_rex( const int64_t highest_bid ); - template - int64_t rent_rex( T& table, const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ); - template - void fund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& payment ); - template - void defund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& amount ); - void transfer_from_fund( const name& owner, const asset& amount ); - void transfer_to_fund( const name& owner, const asset& amount ); - bool rex_loans_available()const; - bool rex_system_initialized()const { return _rexpool.begin() != _rexpool.end(); } - bool rex_available()const { return rex_system_initialized() && _rexpool.begin()->total_rex.amount > 0; } - static time_point_sec get_rex_maturity(); - asset add_to_rex_balance( const name& owner, const asset& payment, const asset& rex_received ); - asset add_to_rex_pool( const asset& payment ); - void add_to_rex_return_pool( const asset& fee ); - void process_rex_maturities( const rex_balance_table::const_iterator& bitr ); - void consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, - const asset& rex_in_sell_order ); - int64_t read_rex_savings( const rex_balance_table::const_iterator& bitr ); - void put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex ); - void update_rex_stake( const name& voter ); - - void add_loan_to_rex_pool( const asset& payment, int64_t rented_tokens, bool new_loan ); - void remove_loan_from_rex_pool( const rex_loan& loan ); - template - int64_t update_renewed_loan( Index& idx, const Iterator& itr, int64_t rented_tokens ); - - // defined in delegate_bandwidth.cpp - void changebw( name from, const name& receiver, - const asset& stake_net_quantity, const asset& stake_cpu_quantity, bool transfer ); - void update_voting_power( const name& voter, const asset& total_update ); - - // defined in voting.cpp - void register_producer( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ); - void update_elected_producers( const block_timestamp& timestamp ); - void update_votes( const name& voter, const name& proxy, const std::vector& producers, bool voting ); - void propagate_weight_change( const voter_info& voter ); - double update_producer_votepay_share( const producers_table2::const_iterator& prod_itr, - const time_point& ct, - double shares_rate, bool reset_to_zero = false ); - double update_total_votepay_share( const time_point& ct, - double additional_shares_delta = 0.0, double shares_rate_delta = 0.0 ); - - template - class registration { - public: - template - struct for_each { - template - static constexpr void call( system_contract* this_contract, Args&&... args ) - { - std::invoke( P, this_contract, args... ); - for_each::call( this_contract, std::forward(args)... ); - } - }; - template - struct for_each

{ - template - static constexpr void call( system_contract* this_contract, Args&&... args ) - { - std::invoke( P, this_contract, std::forward(args)... ); - } - }; - - template - constexpr void operator() ( Args&&... args ) - { - for_each::call( this_contract, std::forward(args)... ); - } - - system_contract* this_contract; - }; - - registration<&system_contract::update_rex_stake> vote_stake_updater{ this }; - }; - -} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp deleted file mode 100644 index c339b97060..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/exchange_state.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include - -namespace eosiosystem { - - using eosio::asset; - using eosio::symbol; - - /** - * Uses Bancor math to create a 50/50 relay between two asset types. - * - * The state of the bancor exchange is entirely contained within this struct. - * There are no external side effects associated with using this API. - */ - struct [[eosio::table, eosio::contract("eosio.system")]] exchange_state { - asset supply; - - struct connector { - asset balance; - double weight = .5; - - EOSLIB_SERIALIZE( connector, (balance)(weight) ) - }; - - connector base; - connector quote; - - uint64_t primary_key()const { return supply.symbol.raw(); } - - asset convert_to_exchange( connector& reserve, const asset& payment ); - asset convert_from_exchange( connector& reserve, const asset& tokens ); - asset convert( const asset& from, const symbol& to ); - asset direct_convert( const asset& from, const symbol& to ); - - static int64_t get_bancor_output( int64_t inp_reserve, - int64_t out_reserve, - int64_t inp ); - static int64_t get_bancor_input( int64_t out_reserve, - int64_t inp_reserve, - int64_t out ); - - EOSLIB_SERIALIZE( exchange_state, (supply)(base)(quote) ) - }; - - typedef eosio::multi_index< "rammarket"_n, exchange_state > rammarket; -} /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp deleted file mode 100644 index a0931cde26..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/native.hpp +++ /dev/null @@ -1,260 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace eosiosystem { - - using eosio::checksum256; - using eosio::ignore; - using eosio::name; - using eosio::permission_level; - using eosio::public_key; - - /** - * A weighted permission. - * - * Defines a weighted permission, that is a permission which has a weight associated. - * A permission is defined by an account name plus a permission name. - */ - struct permission_level_weight { - permission_level permission; - uint16_t weight; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( permission_level_weight, (permission)(weight) ) - }; - - /** - * Weighted key. - * - * A weighted key is defined by a public key and an associated weight. - */ - struct key_weight { - eosio::public_key key; - uint16_t weight; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( key_weight, (key)(weight) ) - }; - - /** - * Wait weight. - * - * A wait weight is defined by a number of seconds to wait for and a weight. - */ - struct wait_weight { - uint32_t wait_sec; - uint16_t weight; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( wait_weight, (wait_sec)(weight) ) - }; - - /** - * Blockchain authority. - * - * An authority is defined by: - * - a vector of key_weights (a key_weight is a public key plus a wieght), - * - a vector of permission_level_weights, (a permission_level is an account name plus a permission name) - * - a vector of wait_weights (a wait_weight is defined by a number of seconds to wait and a weight) - * - a threshold value - */ - struct authority { - uint32_t threshold = 0; - std::vector keys; - std::vector accounts; - std::vector waits; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE( authority, (threshold)(keys)(accounts)(waits) ) - }; - - /** - * Blockchain block header. - * - * A block header is defined by: - * - a timestamp, - * - the producer that created it, - * - a confirmed flag default as zero, - * - a link to previous block, - * - a link to the transaction merkel root, - * - a link to action root, - * - a schedule version, - * - and a producers' schedule. - */ - struct block_header { - uint32_t timestamp; - name producer; - uint16_t confirmed = 0; - checksum256 previous; - checksum256 transaction_mroot; - checksum256 action_mroot; - uint32_t schedule_version = 0; - std::optional new_producers; - - // explicit serialization macro is not necessary, used here only to improve compilation time - EOSLIB_SERIALIZE(block_header, (timestamp)(producer)(confirmed)(previous)(transaction_mroot)(action_mroot) - (schedule_version)(new_producers)) - }; - - /** - * abi_hash is the structure underlying the abihash table and consists of: - * - `owner`: the account owner of the contract's abi - * - `hash`: is the sha256 hash of the abi/binary - */ - struct [[eosio::table("abihash"), eosio::contract("eosio.system")]] abi_hash { - name owner; - checksum256 hash; - uint64_t primary_key()const { return owner.value; } - - EOSLIB_SERIALIZE( abi_hash, (owner)(hash) ) - }; - - // Method parameters commented out to prevent generation of code that parses input data. - /** - * The EOSIO core `native` contract that governs authorization and contracts' abi. - */ - class [[eosio::contract("eosio.system")]] native : public eosio::contract { - public: - - using eosio::contract::contract; - - /** - * These actions map one-on-one with the ones defined in core layer of EOSIO, that's where their implementation - * actually is done. - * They are present here only so they can show up in the abi file and thus user can send them - * to this contract, but they have no specific implementation at this contract level, - * they will execute the implementation at the core layer and nothing else. - */ - /** - * New account action is called after a new account is created. This code enforces resource-limits rules - * for new accounts as well as new 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 newaccount( const name& creator, - const name& name, - ignore owner, - ignore active); - - /** - * Update authorization action updates pemission for an account. - * - * @param account - the account for which the permission is updated - * @param pemission - the permission name which is updated - * @param parem - the parent of the permission which is updated - * @param aut - the json describing the permission authorization - */ - [[eosio::action]] - void updateauth( ignore account, - ignore permission, - ignore parent, - ignore auth ) {} - - /** - * Delete authorization action deletes the authorization for an account's permission. - * - * @param account - the account for which the permission authorization is deleted, - * @param permission - the permission name been deleted. - */ - [[eosio::action]] - void deleteauth( ignore account, - ignore permission ) {} - - /** - * Link authorization action assigns a specific action from a contract to a permission you have created. Five system - * actions can not be linked `updateauth`, `deleteauth`, `linkauth`, `unlinkauth`, and `canceldelay`. - * This is useful because when doing authorization checks, the EOSIO based blockchain starts with the - * action needed to be authorized (and the contract belonging to), and looks up which permission - * is needed to pass authorization validation. If a link is set, that permission is used for authoraization - * validation otherwise then active is the default, with the exception of `eosio.any`. - * `eosio.any` is an implicit permission which exists on every account; you can link actions to `eosio.any` - * and that will make it so linked actions are accessible to any permissions defined for the account. - * - * @param account - the permission's owner to be linked and the payer of the RAM needed to store this link, - * @param code - the owner of the action to be linked, - * @param type - the action to be linked, - * @param requirement - the permission to be linked. - */ - [[eosio::action]] - void linkauth( ignore account, - ignore code, - ignore type, - ignore requirement ) {} - - /** - * Unlink authorization action it's doing the reverse of linkauth action, by unlinking the given action. - * - * @param account - the owner of the permission to be unlinked and the receiver of the freed RAM, - * @param code - the owner of the action to be unlinked, - * @param type - the action to be unlinked. - */ - [[eosio::action]] - void unlinkauth( ignore account, - ignore code, - ignore type ) {} - - /** - * Cancel delay action cancels a deferred transaction. - * - * @param canceling_auth - the permission that authorizes this action, - * @param trx_id - the deferred transaction id to be cancelled. - */ - [[eosio::action]] - void canceldelay( ignore canceling_auth, ignore trx_id ) {} - - /** - * On error action, notification of this action is delivered to the sender of a deferred transaction - * when an objective error occurs while executing the deferred transaction. - * This action is not meant to be called directly. - * - * @param sender_id - the id for the deferred transaction chosen by the sender, - * @param sent_trx - the deferred transaction that failed. - */ - [[eosio::action]] - void onerror( ignore sender_id, ignore> sent_trx ); - - /** - * Set abi action sets the contract abi for an account. - * - * @param account - the account for which to set the contract abi. - * @param abi - the abi content to be set, in the form of a blob binary. - */ - [[eosio::action]] - void setabi( const name& account, const std::vector& abi ); - - /** - * Set code action sets the contract code for an account. - * - * @param account - the account for which to set the contract code. - * @param vmtype - reserved, set it to zero. - * @param vmversion - reserved, set it to zero. - * @param code - the code content to be set, in the form of a blob binary.. - */ - [[eosio::action]] - void setcode( const name& account, uint8_t vmtype, uint8_t vmversion, const std::vector& code ) {} - - using newaccount_action = eosio::action_wrapper<"newaccount"_n, &native::newaccount>; - using updateauth_action = eosio::action_wrapper<"updateauth"_n, &native::updateauth>; - using deleteauth_action = eosio::action_wrapper<"deleteauth"_n, &native::deleteauth>; - using linkauth_action = eosio::action_wrapper<"linkauth"_n, &native::linkauth>; - using unlinkauth_action = eosio::action_wrapper<"unlinkauth"_n, &native::unlinkauth>; - using canceldelay_action = eosio::action_wrapper<"canceldelay"_n, &native::canceldelay>; - using setcode_action = eosio::action_wrapper<"setcode"_n, &native::setcode>; - using setabi_action = eosio::action_wrapper<"setabi"_n, &native::setabi>; - }; -} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp deleted file mode 100644 index 378d6add31..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/include/eosio.system/rex.results.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include -#include - -using eosio::action_wrapper; -using eosio::asset; -using eosio::name; - -/** - * The actions `buyresult`, `sellresult`, `rentresult`, and `orderresult` of `rex.results` are all no-ops. - * They are added as inline convenience actions to `rentnet`, `rentcpu`, `buyrex`, `unstaketorex`, and `sellrex`. - * An inline convenience action does not have any effect, however, - * its data includes the result of the parent action and appears in its trace. - */ -class [[eosio::contract("rex.results")]] rex_results : eosio::contract { - public: - - using eosio::contract::contract; - - /** - * Buyresult action. - * - * @param rex_received - amount of tokens used in buy order - */ - [[eosio::action]] - void buyresult( const asset& rex_received ); - - /** - * Sellresult action. - * - * @param proceeds - amount of tokens used in sell order - */ - [[eosio::action]] - void sellresult( const asset& proceeds ); - - /** - * Orderresult action. - * - * @param owner - the owner of the order - * @param proceeds - amount of tokens used in order - */ - [[eosio::action]] - void orderresult( const name& owner, const asset& proceeds ); - - /** - * Rentresult action. - * - * @param rented_tokens - amount of rented tokens - */ - [[eosio::action]] - void rentresult( const asset& rented_tokens ); - - using buyresult_action = action_wrapper<"buyresult"_n, &rex_results::buyresult>; - using sellresult_action = action_wrapper<"sellresult"_n, &rex_results::sellresult>; - using orderresult_action = action_wrapper<"orderresult"_n, &rex_results::orderresult>; - using rentresult_action = action_wrapper<"rentresult"_n, &rex_results::rentresult>; -}; diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md b/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md deleted file mode 100644 index ef5bfc9877..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.clauses.md +++ /dev/null @@ -1,63 +0,0 @@ -

UserAgreement

- -User agreement for the chain can go here. - -

BlockProducerAgreement

- -I, {{producer}}, hereby nominate myself for consideration as an elected block producer. - -If {{producer}} is selected to produce blocks by the system contract, I will sign blocks with my registered block signing keys and I hereby attest that I will keep these keys secret and secure. - -If {{producer}} is unable to perform obligations under this contract I will resign my position using the unregprod action. - -I acknowledge that a block is 'objectively valid' if it conforms to the deterministic blockchain rules in force at the time of its creation, and is 'objectively invalid' if it fails to conform to those rules. - -{{producer}} hereby agrees to only use my registered block signing keys to sign messages under the following scenarios: - -* proposing an objectively valid block at the time appointed by the block scheduling algorithm; -* pre-confirming a block produced by another producer in the schedule when I find said block objectively valid; -* and, confirming a block for which {{producer}} has received pre-confirmation messages from more than two-thirds of the active block producers. - -I hereby accept liability for any and all provable damages that result from my: - -* signing two different block proposals with the same timestamp; -* signing two different block proposals with the same block number; -* signing any block proposal which builds off of an objectively invalid block; -* signing a pre-confirmation for an objectively invalid block; -* or, signing a confirmation for a block for which I do not possess pre-confirmation messages from more than two-thirds of the active block producers. - -I hereby agree that double-signing for a timestamp or block number in concert with two or more other block producers shall automatically be deemed malicious and cause {{producer}} to be subject to: - -* a fine equal to the past year of compensation received, -* immediate disqualification from being a producer, -* and/or other damages. - -An exception may be made if {{producer}} can demonstrate that the double-signing occurred due to a bug in the reference software; the burden of proof is on {{producer}}. - -I hereby agree not to interfere with the producer election process. I agree to process all producer election transactions that occur in blocks I create, to sign all objectively valid blocks I create that contain election transactions, and to sign all pre-confirmations and confirmations necessary to facilitate transfer of control to the next set of producers as determined by the system contract. - -I hereby acknowledge that more than two-thirds of the active block producers may vote to disqualify {{producer}} in the event {{producer}} is unable to produce blocks or is unable to be reached, according to criteria agreed to among block producers. - -If {{producer}} qualifies for and chooses to collect compensation due to votes received, {{producer}} will provide a public endpoint allowing at least 100 peers to maintain synchronization with the blockchain and/or submit transactions to be included. {{producer}} shall maintain at least one validating node with full state and signature checking and shall report any objectively invalid blocks produced by the active block producers. Reporting shall be via a method to be agreed to among block producers, said method and reports to be made public. - -The community agrees to allow {{producer}} to authenticate peers as necessary to prevent abuse and denial of service attacks; however, {{producer}} agrees not to discriminate against non-abusive peers. - -I agree to process transactions on a FIFO (first in, first out) best-effort basis and to honestly bill transactions for measured execution time. - -I {{producer}} agree not to manipulate the contents of blocks in order to derive profit from: the order in which transactions are included, or the hash of the block that is produced. - -I, {{producer}}, hereby agree to disclose and attest under penalty of perjury all ultimate beneficial owners of my business entity who own more than 10% and all direct shareholders. - -I, {{producer}}, hereby agree to cooperate with other block producers to carry out our respective and mutual obligations under this agreement, including but not limited to maintaining network stability and a valid blockchain. - -I, {{producer}}, agree to maintain a website hosted at {{url}} which contains up-to-date information on all disclosures required by this contract. - -I, {{producer}}, agree to set the location value of {{location}} such that {{producer}} is scheduled with minimal latency between my previous and next peer. - -I, {{producer}}, agree to maintain time synchronization within 10 ms of global atomic clock time, using a method agreed to among block producers. - -I, {{producer}}, agree not to produce blocks before my scheduled time unless I have received all blocks produced by the prior block producer. - -I, {{producer}}, agree not to publish blocks with timestamps more than 500ms in the future unless the prior block is more than 75% full by either NET or CPU bandwidth metrics. - -I, {{producer}}, agree not to set the RAM supply to more RAM than my nodes contain and to resign if I am unable to provide the RAM approved by more than two-thirds of active block producers, as shown in the system parameters. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in deleted file mode 100644 index 9ab33cd888..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/ricardian/eosio.system.contracts.md.in +++ /dev/null @@ -1,703 +0,0 @@ -

activate

- ---- -spec_version: "0.2.0" -title: Activate Protocol Feature -summary: 'Activate protocol feature {{nowrap feature_digest}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} activates the protocol feature with a digest of {{feature_digest}}. - -

bidname

- ---- -spec_version: "0.2.0" -title: Bid On a Premium Account Name -summary: '{{nowrap bidder}} bids on the premium account name {{nowrap newname}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{bidder}} bids {{bid}} on an auction to own the premium account name {{newname}}. - -{{bidder}} transfers {{bid}} to the system to cover the cost of the bid, which will be returned to {{bidder}} only if {{bidder}} is later outbid in the auction for {{newname}} by another account. - -If the auction for {{newname}} closes with {{bidder}} remaining as the highest bidder, {{bidder}} will be authorized to create the account with name {{newname}}. - -## Bid refund behavior - -If {{bidder}}’s bid on {{newname}} is later outbid by another account, {{bidder}} will be able to claim back the transferred amount of {{bid}}. The system will attempt to automatically do this on behalf of {{bidder}}, but the automatic refund may occasionally fail which will then require {{bidder}} to manually claim the refund with the bidrefund action. - -## Auction close criteria - -The system should automatically close the auction for {{newname}} if it satisfies the condition that over a period of two minutes the following two properties continuously hold: - -- no one has bid on {{newname}} within the last 24 hours; -- and, the value of the latest bid on {{newname}} is greater than the value of the bids on each of the other open auctions. - -Be aware that the condition to close the auction described above are sufficient but not necessary. The auction for {{newname}} cannot close unless both of the properties are simultaneously satisfied, but it may be closed without requiring the properties to hold for a period of 2 minutes. - -

bidrefund

- ---- -spec_version: "0.2.0" -title: Claim Refund on Name Bid -summary: 'Claim refund on {{nowrap newname}} bid' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{bidder}} claims refund on {{newname}} bid after being outbid by someone else. - -

buyram

- ---- -spec_version: "0.2.0" -title: Buy RAM -summary: '{{nowrap payer}} buys RAM on behalf of {{nowrap receiver}} by paying {{nowrap quant}}' -icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ ---- - -{{payer}} buys RAM on behalf of {{receiver}} by paying {{quant}}. This transaction will incur a 0.5% fee out of {{quant}} and the amount of RAM delivered will depend on market rates. - -

buyrambytes

- ---- -spec_version: "0.2.0" -title: Buy RAM -summary: '{{nowrap payer}} buys {{nowrap bytes}} bytes of RAM on behalf of {{nowrap receiver}}' -icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ ---- - -{{payer}} buys approximately {{bytes}} bytes of RAM on behalf of {{receiver}} by paying market rates for RAM. This transaction will incur a 0.5% fee and the cost will depend on market rates. - -

buyrex

- ---- -spec_version: "0.2.0" -title: Buy REX Tokens -summary: '{{nowrap from}} buys REX tokens in exchange for {{nowrap amount}} and their vote stake increases by {{nowrap amount}}' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{amount}} is taken out of {{from}}’s REX fund and used to purchase REX tokens at the current market exchange rate. In order for the action to succeed, {{from}} must have voted for a proxy or at least 21 block producers. {{amount}} is added to {{from}}’s vote stake. - -A sell order of the purchased amount can only be initiated after waiting for the maturity period of 4 to 5 days to pass. Even then, depending on the market conditions, the initiated sell order may not be executed immediately. - -

canceldelay

- ---- -spec_version: "0.2.0" -title: Cancel Delayed Transaction -summary: '{{nowrap canceling_auth.actor}} cancels a delayed transaction' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{canceling_auth.actor}} cancels the delayed transaction with id {{trx_id}}. - -

claimrewards

- ---- -spec_version: "0.2.0" -title: Claim Block Producer Rewards -summary: '{{nowrap owner}} claims block and vote rewards' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{owner}} claims block and vote rewards from the system. - -

closerex

- ---- -spec_version: "0.2.0" -title: Cleanup Unused REX Data -summary: 'Delete REX related DB entries and free associated RAM' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -Delete REX related DB entries and free associated RAM for {{owner}}. - -To fully delete all REX related DB entries, {{owner}} must ensure that their REX balance and REX fund amounts are both zero and they have no outstanding loans. - -

cnclrexorder

- ---- -spec_version: "0.2.0" -title: Cancel Scheduled REX Sell Order -summary: '{{nowrap owner}} cancels a scheduled sell order if not yet filled' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{owner}} cancels their open sell order. - -

consolidate

- ---- -spec_version: "0.2.0" -title: Consolidate REX Maturity Buckets Into One -summary: 'Consolidate REX maturity buckets into one' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -Consolidate REX maturity buckets into one bucket that {{owner}} will not be able to sell until 4 to 5 days later. - -

defcpuloan

- ---- -spec_version: "0.2.0" -title: Withdraw from the Fund of a Specific CPU Loan -summary: '{{nowrap from}} transfers {{nowrap amount}} from the fund of CPU loan number {{nowrap loan_num}} back to REX fund' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from}} transfers {{amount}} from the fund of CPU loan number {{loan_num}} back to REX fund. - -

defnetloan

- ---- -spec_version: "0.2.0" -title: Withdraw from the Fund of a Specific NET Loan -summary: '{{nowrap from}} transfers {{nowrap amount}} from the fund of NET loan number {{nowrap loan_num}} back to REX fund' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from}} transfers {{amount}} from the fund of NET loan number {{loan_num}} back to REX fund. - -

delegatebw

- ---- -spec_version: "0.2.0" -title: Stake Tokens for NET and/or CPU -summary: 'Stake tokens for NET and/or CPU and optionally transfer ownership' -icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ ---- - -{{#if transfer}} {{from}} stakes on behalf of {{receiver}} {{stake_net_quantity}} for NET bandwidth and {{stake_cpu_quantity}} for CPU bandwidth. - -Staked tokens will also be transferred to {{receiver}}. The sum of these two quantities will be deducted from {{from}}’s liquid balance and add to the vote weight of {{receiver}}. -{{else}} -{{from}} stakes to self and delegates to {{receiver}} {{stake_net_quantity}} for NET bandwidth and {{stake_cpu_quantity}} for CPU bandwidth. - -The sum of these two quantities add to the vote weight of {{from}}. -{{/if}} - -

deleteauth

- ---- -spec_version: "0.2.0" -title: Delete Account Permission -summary: 'Delete the {{nowrap permission}} permission of {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Delete the {{permission}} permission of {{account}}. - -

deposit

- ---- -spec_version: "0.2.0" -title: Deposit Into REX Fund -summary: 'Add to {{nowrap owner}}’s REX fund by transferring {{nowrap amount}} from {{nowrap owner}}’s liquid balance' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -Transfer {{amount}} from {{owner}}’s liquid balance to {{owner}}’s REX fund. All proceeds and expenses related to REX are added to or taken out of this fund. - -

fundcpuloan

- ---- -spec_version: "0.2.0" -title: Deposit into the Fund of a Specific CPU Loan -summary: '{{nowrap from}} funds a CPU loan' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from}} transfers {{payment}} from REX fund to the fund of CPU loan number {{loan_num}} in order to be used in loan renewal at expiry. {{from}} can withdraw the total balance of the loan fund at any time. - -

fundnetloan

- ---- -spec_version: "0.2.0" -title: Deposit into the Fund of a Specific NET Loan -summary: '{{nowrap from}} funds a NET loan' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from}} transfers {{payment}} from REX fund to the fund of NET loan number {{loan_num}} in order to be used in loan renewal at expiry. {{from}} can withdraw the total balance of the loan fund at any time. - -

init

- ---- -spec_version: "0.2.0" -title: Initialize System Contract -summary: 'Initialize system contract' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -Initialize system contract. The core token symbol will be set to {{core}}. - -

linkauth

- ---- -spec_version: "0.2.0" -title: Link Action to Permission -summary: '{{nowrap account}} sets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract to {{nowrap requirement}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{account}} sets the minimum required permission for the {{#if type}}{{type}} action of the{{/if}} {{code}} contract to {{requirement}}. - -{{#if type}}{{else}}Any links explicitly associated to specific actions of {{code}} will take precedence.{{/if}} - -

newaccount

- ---- -spec_version: "0.2.0" -title: Create New Account -summary: '{{nowrap creator}} creates a new account with the name {{nowrap name}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{creator}} creates a new account with the name {{name}} and the following permissions: - -owner permission with authority: -{{to_json owner}} - -active permission with authority: -{{to_json active}} - -

mvfrsavings

- ---- -spec_version: "0.2.0" -title: Unlock REX Tokens -summary: '{{nowrap owner}} unlocks REX Tokens' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{owner}} unlocks {{rex}} by moving it out of the REX savings bucket. The unlocked REX tokens cannot be sold until 4 to 5 days later. - -

mvtosavings

- ---- -spec_version: "0.2.0" -title: Lock REX Tokens -summary: '{{nowrap owner}} locks REX Tokens' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{owner}} locks {{rex}} by moving it into the REX savings bucket. The locked REX tokens cannot be sold directly and will have to be unlocked explicitly before selling. - -

refund

- ---- -spec_version: "0.2.0" -title: Claim Unstaked Tokens -summary: 'Return previously unstaked tokens to {{nowrap owner}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Return previously unstaked tokens to {{owner}} after the unstaking period has elapsed. - -

regproducer

- ---- -spec_version: "0.2.0" -title: Register as a Block Producer Candidate -summary: 'Register {{nowrap producer}} account as a block producer candidate' -icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ ---- - -Register {{producer}} account as a block producer candidate. - -URL: {{url}} -Location code: {{location}} -Block signing key: {{producer_key}} - -## Block Producer Agreement -{{$clauses.BlockProducerAgreement}} - -

regproducer2

- ---- -spec_version: "0.2.0" -title: Register as a Block Producer Candidate -summary: 'Register {{nowrap producer}} account as a block producer candidate' -icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ ---- - -Register {{producer}} account as a block producer candidate. - -URL: {{url}} -Location code: {{location}} -Block signing authority: -{{to_json producer_authority}} - -## Block Producer Agreement -{{$clauses.BlockProducerAgreement}} - -

regproxy

- ---- -spec_version: "0.2.0" -title: Register/unregister as a Proxy -summary: 'Register/unregister {{nowrap proxy}} as a proxy account' -icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ ---- - -{{#if isproxy}} -{{proxy}} registers as a proxy that can vote on behalf of accounts that appoint it as their proxy. -{{else}} -{{proxy}} unregisters as a proxy that can vote on behalf of accounts that appoint it as their proxy. -{{/if}} - -

rentcpu

- ---- -spec_version: "0.2.0" -title: Rent CPU Bandwidth for 30 Days -summary: '{{nowrap from}} pays {{nowrap loan_payment}} to rent CPU bandwidth for {{nowrap receiver}}' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from}} pays {{loan_payment}} to rent CPU bandwidth on behalf of {{receiver}} for a period of 30 days. - -{{loan_payment}} is taken out of {{from}}’s REX fund. The market price determines the number of tokens to be staked to {{receiver}}’s CPU resources. In addition, {{from}} provides {{loan_fund}}, which is also taken out of {{from}}’s REX fund, to be used for automatic renewal of the loan. - -At expiration, if the loan has less funds than {{loan_payment}}, it is closed and lent tokens that have been staked are taken out of {{receiver}}’s CPU bandwidth. Otherwise, it is renewed at the market price at the time of renewal, that is, the number of staked tokens is recalculated and {{receiver}}’s CPU bandwidth is updated accordingly. {{from}} can fund or defund a loan at any time before expiration. When the loan is closed, {{from}} is refunded any tokens remaining in the loan fund. - -

rentnet

- ---- -spec_version: "0.2.0" -title: Rent NET Bandwidth for 30 Days -summary: '{{nowrap from}} pays {{nowrap loan_payment}} to rent NET bandwidth for {{nowrap receiver}}' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from}} pays {{loan_payment}} to rent NET bandwidth on behalf of {{receiver}} for a period of 30 days. - -{{loan_payment}} is taken out of {{from}}’s REX fund. The market price determines the number of tokens to be staked to {{receiver}}’s NET resources for 30 days. In addition, {{from}} provides {{loan_fund}}, which is also taken out of {{from}}’s REX fund, to be used for automatic renewal of the loan. - -At expiration, if the loan has less funds than {{loan_payment}}, it is closed and lent tokens that have been staked are taken out of {{receiver}}’s NET bandwidth. Otherwise, it is renewed at the market price at the time of renewal, that is, the number of staked tokens is recalculated and {{receiver}}’s NET bandwidth is updated accordingly. {{from}} can fund or defund a loan at any time before expiration. When the loan is closed, {{from}} is refunded any tokens remaining in the loan fund. - -

rexexec

- ---- -spec_version: "0.2.0" -title: Perform REX Maintenance -summary: 'Process sell orders and expired loans' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -Performs REX maintenance by processing a maximum of {{max}} REX sell orders and expired loans. Any account can execute this action. - -

rmvproducer

- ---- -spec_version: "0.2.0" -title: Forcibly Unregister a Block Producer Candidate -summary: '{{nowrap producer}} is unregistered as a block producer candidate' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} unregisters {{producer}} as a block producer candidate. {{producer}} account will retain its votes and those votes can change based on voter stake changes or votes removed from {{producer}}. However new voters will not be able to vote for {{producer}} while it remains unregistered. - -

sellram

- ---- -spec_version: "0.2.0" -title: Sell RAM From Account -summary: 'Sell unused RAM from {{nowrap account}}' -icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ ---- - -Sell {{bytes}} bytes of unused RAM from account {{account}} at market price. This transaction will incur a 0.5% fee on the proceeds which depend on market rates. - -

sellrex

- ---- -spec_version: "0.2.0" -title: Sell REX Tokens in Exchange for EOS -summary: '{{nowrap from}} sells {{nowrap rex}} tokens' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from}} initiates a sell order to sell {{rex}} tokens at the market exchange rate during the time at which the order is ultimately executed. If {{from}} already has an open sell order in the sell queue, {{rex}} will be added to the amount of the sell order without change the position of the sell order within the queue. Once the sell order is executed, proceeds are added to {{from}}’s REX fund, the value of sold REX tokens is deducted from {{from}}’s vote stake, and votes are updated accordingly. - -Depending on the market conditions, it may not be possible to fill the entire sell order immediately. In such a case, the sell order is added to the back of a sell queue. A sell order at the front of the sell queue will automatically be executed when the market conditions allow for the entire order to be filled. Regardless of the market conditions, the system is designed to execute this sell order within 30 days. {{from}} can cancel the order at any time before it is filled using the cnclrexorder action. - -

setabi

- ---- -spec_version: "0.2.0" -title: Deploy Contract ABI -summary: 'Deploy contract ABI on account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Deploy the ABI file associated with the contract on account {{account}}. - -

setacctcpu

- ---- -spec_version: "0.2.0" -title: Explicitly Manage the CPU Quota of Account -summary: 'Explicitly manage the CPU bandwidth quota of account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{#if_has_value cpu_weight}} -Explicitly manage the CPU bandwidth quota of account {{account}} by pinning it to a weight of {{cpu_weight}}. - -{{account}} can stake and unstake, however, it will not change their CPU bandwidth quota as long as it remains pinned. -{{else}} -Unpin the CPU bandwidth quota of account {{account}}. The CPU bandwidth quota of {{account}} will be driven by the current tokens staked for CPU bandwidth by {{account}}. -{{/if_has_value}} - -

setacctnet

- ---- -spec_version: "0.2.0" -title: Explicitly Manage the NET Quota of Account -summary: 'Explicitly manage the NET bandwidth quota of account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{#if_has_value net_weight}} -Explicitly manage the network bandwidth quota of account {{account}} by pinning it to a weight of {{net_weight}}. - -{{account}} can stake and unstake, however, it will not change their NET bandwidth quota as long as it remains pinned. -{{else}} -Unpin the NET bandwidth quota of account {{account}}. The NET bandwidth quota of {{account}} will be driven by the current tokens staked for NET bandwidth by {{account}}. -{{/if_has_value}} - -

setacctram

- ---- -spec_version: "0.2.0" -title: Explicitly Manage the RAM Quota of Account -summary: 'Explicitly manage the RAM quota of account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{#if_has_value ram_bytes}} -Explicitly manage the RAM quota of account {{account}} by pinning it to {{ram_bytes}} bytes. - -{{account}} can buy and sell RAM, however, it will not change their RAM quota as long as it remains pinned. -{{else}} -Unpin the RAM quota of account {{account}}. The RAM quota of {{account}} will be driven by the current RAM holdings of {{account}}. -{{/if_has_value}} - -

setalimits

- ---- -spec_version: "0.2.0" -title: Adjust Resource Limits of Account -summary: 'Adjust resource limits of account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} updates {{account}}’s resource limits to have a RAM quota of {{ram_bytes}} bytes, a NET bandwidth quota of {{net_weight}} and a CPU bandwidth quota of {{cpu_weight}}. - -

setcode

- ---- -spec_version: "0.2.0" -title: Deploy Contract Code -summary: 'Deploy contract code on account {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Deploy compiled contract code to the account {{account}}. - -

setparams

- ---- -spec_version: "0.2.0" -title: Set System Parameters -summary: 'Set System Parameters' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} sets system parameters to: -{{to_json params}} - -

setpriv

- ---- -spec_version: "0.2.0" -title: Make an Account Privileged or Unprivileged -summary: '{{#if is_priv}}Make {{nowrap account}} privileged{{else}}Remove privileged status of {{nowrap account}}{{/if}}' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{#if is_priv}} -{{$action.account}} makes {{account}} privileged. -{{else}} -{{$action.account}} removes privileged status of {{account}}. -{{/if}} - -

setram

- ---- -spec_version: "0.2.0" -title: Configure the Available RAM -summary: 'Configure the available RAM' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} configures the available RAM to {{max_ram_size}} bytes. - -

setramrate

- ---- -spec_version: "0.2.0" -title: Set the Rate of Increase of RAM -summary: 'Set the rate of increase of RAM per block' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} sets the rate of increase of RAM to {{bytes_per_block}} bytes/block. - -

setrex

- ---- -spec_version: "0.2.0" -title: Adjust REX Pool Virtual Balance -summary: 'Adjust REX Pool Virtual Balance' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} adjusts REX loan rate by setting REX pool virtual balance to {{balance}}. No token transfer or issue is executed in this action. - -

setinflation

- ---- -spec_version: "0.2.0" -title: Set Inflation Parameters -summary: 'Set inflation parameters' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} sets the inflation parameters as follows: - -* Annual inflation rate (in units of a hundredth of a percent): {{annual_rate}} -* Fraction of inflation used to reward block producers: 10000/{{inflation_pay_factor}} -* Fraction of block producer rewards to be distributed proportional to blocks produced: 10000/{{votepay_factor}} - -

undelegatebw

- ---- -spec_version: "0.2.0" -title: Unstake Tokens for NET and/or CPU -summary: 'Unstake tokens for NET and/or CPU from {{nowrap receiver}}' -icon: @ICON_BASE_URL@/@RESOURCE_ICON_URI@ ---- - -{{from}} unstakes from {{receiver}} {{unstake_net_quantity}} for NET bandwidth and {{unstake_cpu_quantity}} for CPU bandwidth. - -The sum of these two quantities will be removed from the vote weight of {{receiver}} and will be made available to {{from}} after an uninterrupted 3 day period without further unstaking by {{from}}. After the uninterrupted 3 day period passes, the system will attempt to automatically return the funds to {{from}}’s regular token balance. However, this automatic refund may occasionally fail which will then require {{from}} to manually claim the funds with the refund action. - -

unlinkauth

- ---- -spec_version: "0.2.0" -title: Unlink Action from Permission -summary: '{{nowrap account}} unsets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -{{account}} removes the association between the {{#if type}}{{type}} action of the{{/if}} {{code}} contract and its minimum required permission. - -{{#if type}}{{else}}This will not remove any links explicitly associated to specific actions of {{code}}.{{/if}} - -

unregprod

- ---- -spec_version: "0.2.0" -title: Unregister as a Block Producer Candidate -summary: '{{nowrap producer}} unregisters as a block producer candidate' -icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ ---- - -{{producer}} unregisters as a block producer candidate. {{producer}} account will retain its votes and those votes can change based on voter stake changes or votes removed from {{producer}}. However new voters will not be able to vote for {{producer}} while it remains unregistered. - -

unstaketorex

- ---- -spec_version: "0.2.0" -title: Buy REX Tokens Using Staked Tokens -summary: '{{nowrap owner}} buys REX tokens in exchange for tokens currently staked to NET and/or CPU' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -{{from_net}} and {{from_cpu}} are withdrawn from {{receiver}}’s NET and CPU bandwidths respectively. These funds are used to purchase REX tokens at the current market exchange rate. In order for the action to succeed, {{owner}} must have voted for a proxy or at least 21 block producers. - -A sell order of the purchased amount can only be initiated after waiting for the maturity period of 4 to 5 days to pass. Even then, depending on the market conditions, the initiated sell order may not be executed immediately. - -

updateauth

- ---- -spec_version: "0.2.0" -title: Modify Account Permission -summary: 'Add or update the {{nowrap permission}} permission of {{nowrap account}}' -icon: @ICON_BASE_URL@/@ACCOUNT_ICON_URI@ ---- - -Modify, and create if necessary, the {{permission}} permission of {{account}} to have a parent permission of {{parent}} and the following authority: -{{to_json auth}} - -

updaterex

- ---- -spec_version: "0.2.0" -title: Update REX Owner Vote Weight -summary: 'Update vote weight to current value of held REX tokens' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -Update vote weight of {{owner}} account to current value of held REX tokens. - -

updtrevision

- ---- -spec_version: "0.2.0" -title: Update System Contract Revision Number -summary: 'Update system contract revision number' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{$action.account}} advances the system contract revision number to {{revision}}. - -

voteproducer

- ---- -spec_version: "0.2.0" -title: Vote for Block Producers -summary: '{{nowrap voter}} votes for {{#if proxy}}the proxy {{nowrap proxy}}{{else}}up to 30 block producer candidates{{/if}}' -icon: @ICON_BASE_URL@/@VOTING_ICON_URI@ ---- - -{{#if proxy}} -{{voter}} votes for the proxy {{proxy}}. -At the time of voting the full weight of voter’s staked (CPU + NET) tokens will be cast towards each of the producers voted by {{proxy}}. -{{else}} -{{voter}} votes for the following block producer candidates: - -{{#each producers}} - + {{this}} -{{/each}} - -At the time of voting the full weight of voter’s staked (CPU + NET) tokens will be cast towards each of the above producers. -{{/if}} - -

withdraw

- ---- -spec_version: "0.2.0" -title: Withdraw from REX Fund -summary: 'Withdraw {{nowrap amount}} from {{nowrap owner}}’s REX fund by transferring to {{owner}}’s liquid balance' -icon: @ICON_BASE_URL@/@REX_ICON_URI@ ---- - -Withdraws {{amount}} from {{owner}}’s REX fund and transfer them to {{owner}}’s liquid balance. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp deleted file mode 100644 index a64506c010..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/delegate_bandwidth.cpp +++ /dev/null @@ -1,413 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace eosiosystem { - - using eosio::asset; - using eosio::const_mem_fun; - using eosio::current_time_point; - using eosio::indexed_by; - using eosio::permission_level; - using eosio::seconds; - using eosio::time_point_sec; - using eosio::token; - - /** - * This action will buy an exact amount of ram and bill the payer the current market price. - */ - void system_contract::buyrambytes( const name& payer, const name& receiver, uint32_t bytes ) { - auto itr = _rammarket.find(ramcore_symbol.raw()); - const int64_t ram_reserve = itr->base.balance.amount; - const int64_t eos_reserve = itr->quote.balance.amount; - const int64_t cost = exchange_state::get_bancor_input( ram_reserve, eos_reserve, bytes ); - const int64_t cost_plus_fee = cost / double(0.995); - buyram( payer, receiver, asset{ cost_plus_fee, core_symbol() } ); - } - - - /** - * When buying ram the payer irreversiblly transfers quant to system contract and only - * the receiver may reclaim the tokens via the sellram action. The receiver pays for the - * storage of all database records associated with this action. - * - * RAM is a scarce resource whose supply is defined by global properties max_ram_size. RAM is - * priced using the bancor algorithm such that price-per-byte with a constant reserve ratio of 100:1. - */ - void system_contract::buyram( const name& payer, const name& receiver, const asset& quant ) - { - require_auth( payer ); - update_ram_supply(); - - check( quant.symbol == core_symbol(), "must buy ram with core token" ); - check( quant.amount > 0, "must purchase a positive amount" ); - - auto fee = quant; - fee.amount = ( fee.amount + 199 ) / 200; /// .5% fee (round up) - // fee.amount cannot be 0 since that is only possible if quant.amount is 0 which is not allowed by the assert above. - // If quant.amount == 1, then fee.amount == 1, - // otherwise if quant.amount > 1, then 0 < fee.amount < quant.amount. - auto quant_after_fee = quant; - quant_after_fee.amount -= fee.amount; - // quant_after_fee.amount should be > 0 if quant.amount > 1. - // If quant.amount == 1, then quant_after_fee.amount == 0 and the next inline transfer will fail causing the buyram action to fail. - { - token::transfer_action transfer_act{ token_account, { {payer, active_permission}, {ram_account, active_permission} } }; - transfer_act.send( payer, ram_account, quant_after_fee, "buy ram" ); - } - if ( fee.amount > 0 ) { - token::transfer_action transfer_act{ token_account, { {payer, active_permission} } }; - transfer_act.send( payer, ramfee_account, fee, "ram fee" ); - channel_to_rex( ramfee_account, fee ); - } - - int64_t bytes_out; - - const auto& market = _rammarket.get(ramcore_symbol.raw(), "ram market does not exist"); - _rammarket.modify( market, same_payer, [&]( auto& es ) { - bytes_out = es.direct_convert( quant_after_fee, ram_symbol ).amount; - }); - - check( bytes_out > 0, "must reserve a positive amount" ); - - _gstate.total_ram_bytes_reserved += uint64_t(bytes_out); - _gstate.total_ram_stake += quant_after_fee.amount; - - user_resources_table userres( get_self(), receiver.value ); - auto res_itr = userres.find( receiver.value ); - if( res_itr == userres.end() ) { - res_itr = userres.emplace( receiver, [&]( auto& res ) { - res.owner = receiver; - res.net_weight = asset( 0, core_symbol() ); - res.cpu_weight = asset( 0, core_symbol() ); - res.ram_bytes = bytes_out; - }); - } else { - userres.modify( res_itr, receiver, [&]( auto& res ) { - res.ram_bytes += bytes_out; - }); - } - - auto voter_itr = _voters.find( res_itr->owner.value ); - if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) { - int64_t ram_bytes, net, cpu; - get_resource_limits( res_itr->owner, ram_bytes, net, cpu ); - set_resource_limits( res_itr->owner, res_itr->ram_bytes + ram_gift_bytes, net, cpu ); - } - } - - /** - * The system contract now buys and sells RAM allocations at prevailing market prices. - * This may result in traders buying RAM today in anticipation of potential shortages - * tomorrow. Overall this will result in the market balancing the supply and demand - * for RAM over time. - */ - void system_contract::sellram( const name& account, int64_t bytes ) { - require_auth( account ); - update_ram_supply(); - - check( bytes > 0, "cannot sell negative byte" ); - - user_resources_table userres( get_self(), account.value ); - auto res_itr = userres.find( account.value ); - check( res_itr != userres.end(), "no resource row" ); - check( res_itr->ram_bytes >= bytes, "insufficient quota" ); - - asset tokens_out; - auto itr = _rammarket.find(ramcore_symbol.raw()); - _rammarket.modify( itr, same_payer, [&]( auto& es ) { - /// the cast to int64_t of bytes is safe because we certify bytes is <= quota which is limited by prior purchases - tokens_out = es.direct_convert( asset(bytes, ram_symbol), core_symbol()); - }); - - check( tokens_out.amount > 1, "token amount received from selling ram is too low" ); - - _gstate.total_ram_bytes_reserved -= static_cast(bytes); // bytes > 0 is asserted above - _gstate.total_ram_stake -= tokens_out.amount; - - //// this shouldn't happen, but just in case it does we should prevent it - check( _gstate.total_ram_stake >= 0, "error, attempt to unstake more tokens than previously staked" ); - - userres.modify( res_itr, account, [&]( auto& res ) { - res.ram_bytes -= bytes; - }); - - auto voter_itr = _voters.find( res_itr->owner.value ); - if( voter_itr == _voters.end() || !has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ) ) { - int64_t ram_bytes, net, cpu; - get_resource_limits( res_itr->owner, ram_bytes, net, cpu ); - set_resource_limits( res_itr->owner, res_itr->ram_bytes + ram_gift_bytes, net, cpu ); - } - - { - token::transfer_action transfer_act{ token_account, { {ram_account, active_permission}, {account, active_permission} } }; - transfer_act.send( ram_account, account, asset(tokens_out), "sell ram" ); - } - auto fee = ( tokens_out.amount + 199 ) / 200; /// .5% fee (round up) - // since tokens_out.amount was asserted to be at least 2 earlier, fee.amount < tokens_out.amount - if ( fee > 0 ) { - token::transfer_action transfer_act{ token_account, { {account, active_permission} } }; - transfer_act.send( account, ramfee_account, asset(fee, core_symbol()), "sell ram fee" ); - channel_to_rex( ramfee_account, asset(fee, core_symbol() )); - } - } - - void validate_b1_vesting( int64_t stake ) { - const int64_t base_time = 1527811200; /// 2018-06-01 - const int64_t max_claimable = 100'000'000'0000ll; - const int64_t claimable = int64_t(max_claimable * double(current_time_point().sec_since_epoch() - base_time) / (10*seconds_per_year) ); - - check( max_claimable - claimable <= stake, "b1 can only claim their tokens over 10 years" ); - } - - void system_contract::changebw( name from, const name& receiver, - const asset& stake_net_delta, const asset& stake_cpu_delta, bool transfer ) - { - require_auth( from ); - check( stake_net_delta.amount != 0 || stake_cpu_delta.amount != 0, "should stake non-zero amount" ); - check( std::abs( (stake_net_delta + stake_cpu_delta).amount ) - >= std::max( std::abs( stake_net_delta.amount ), std::abs( stake_cpu_delta.amount ) ), - "net and cpu deltas cannot be opposite signs" ); - - name source_stake_from = from; - if ( transfer ) { - from = receiver; - } - - // update stake delegated from "from" to "receiver" - { - del_bandwidth_table del_tbl( get_self(), from.value ); - auto itr = del_tbl.find( receiver.value ); - if( itr == del_tbl.end() ) { - itr = del_tbl.emplace( from, [&]( auto& dbo ){ - dbo.from = from; - dbo.to = receiver; - dbo.net_weight = stake_net_delta; - dbo.cpu_weight = stake_cpu_delta; - }); - } - else { - del_tbl.modify( itr, same_payer, [&]( auto& dbo ){ - dbo.net_weight += stake_net_delta; - dbo.cpu_weight += stake_cpu_delta; - }); - } - check( 0 <= itr->net_weight.amount, "insufficient staked net bandwidth" ); - check( 0 <= itr->cpu_weight.amount, "insufficient staked cpu bandwidth" ); - if ( itr->is_empty() ) { - del_tbl.erase( itr ); - } - } // itr can be invalid, should go out of scope - - // update totals of "receiver" - { - user_resources_table totals_tbl( get_self(), receiver.value ); - auto tot_itr = totals_tbl.find( receiver.value ); - if( tot_itr == totals_tbl.end() ) { - tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) { - tot.owner = receiver; - tot.net_weight = stake_net_delta; - tot.cpu_weight = stake_cpu_delta; - }); - } else { - totals_tbl.modify( tot_itr, from == receiver ? from : same_payer, [&]( auto& tot ) { - tot.net_weight += stake_net_delta; - tot.cpu_weight += stake_cpu_delta; - }); - } - check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); - check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); - - { - bool ram_managed = false; - bool net_managed = false; - bool cpu_managed = false; - - auto voter_itr = _voters.find( receiver.value ); - if( voter_itr != _voters.end() ) { - ram_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::ram_managed ); - net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed ); - cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed ); - } - - if( !(net_managed && cpu_managed) ) { - int64_t ram_bytes, net, cpu; - get_resource_limits( receiver, ram_bytes, net, cpu ); - - set_resource_limits( receiver, - ram_managed ? ram_bytes : std::max( tot_itr->ram_bytes + ram_gift_bytes, ram_bytes ), - net_managed ? net : tot_itr->net_weight.amount, - cpu_managed ? cpu : tot_itr->cpu_weight.amount ); - } - } - - if ( tot_itr->is_empty() ) { - totals_tbl.erase( tot_itr ); - } - } // tot_itr can be invalid, should go out of scope - - // create refund or update from existing refund - if ( stake_account != source_stake_from ) { //for eosio both transfer and refund make no sense - refunds_table refunds_tbl( get_self(), from.value ); - auto req = refunds_tbl.find( from.value ); - - //create/update/delete refund - auto net_balance = stake_net_delta; - auto cpu_balance = stake_cpu_delta; - bool need_deferred_trx = false; - - - // net and cpu are same sign by assertions in delegatebw and undelegatebw - // redundant assertion also at start of changebw to protect against misuse of changebw - bool is_undelegating = (net_balance.amount + cpu_balance.amount ) < 0; - bool is_delegating_to_self = (!transfer && from == receiver); - - if( is_delegating_to_self || is_undelegating ) { - if ( req != refunds_tbl.end() ) { //need to update refund - refunds_tbl.modify( req, same_payer, [&]( refund_request& r ) { - if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) { - r.request_time = current_time_point(); - } - r.net_amount -= net_balance; - if ( r.net_amount.amount < 0 ) { - net_balance = -r.net_amount; - r.net_amount.amount = 0; - } else { - net_balance.amount = 0; - } - r.cpu_amount -= cpu_balance; - if ( r.cpu_amount.amount < 0 ){ - cpu_balance = -r.cpu_amount; - r.cpu_amount.amount = 0; - } else { - cpu_balance.amount = 0; - } - }); - - check( 0 <= req->net_amount.amount, "negative net refund amount" ); //should never happen - check( 0 <= req->cpu_amount.amount, "negative cpu refund amount" ); //should never happen - - if ( req->is_empty() ) { - refunds_tbl.erase( req ); - need_deferred_trx = false; - } else { - need_deferred_trx = true; - } - } else if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) { //need to create refund - refunds_tbl.emplace( from, [&]( refund_request& r ) { - r.owner = from; - if ( net_balance.amount < 0 ) { - r.net_amount = -net_balance; - net_balance.amount = 0; - } else { - r.net_amount = asset( 0, core_symbol() ); - } - if ( cpu_balance.amount < 0 ) { - r.cpu_amount = -cpu_balance; - cpu_balance.amount = 0; - } else { - r.cpu_amount = asset( 0, core_symbol() ); - } - r.request_time = current_time_point(); - }); - need_deferred_trx = true; - } // else stake increase requested with no existing row in refunds_tbl -> nothing to do with refunds_tbl - } /// end if is_delegating_to_self || is_undelegating - - if ( need_deferred_trx ) { - eosio::transaction out; - out.actions.emplace_back( permission_level{from, active_permission}, - get_self(), "refund"_n, - from - ); - out.delay_sec = refund_delay_sec; - eosio::cancel_deferred( from.value ); // TODO: Remove this line when replacing deferred trxs is fixed - out.send( from.value, from, true ); - } else { - eosio::cancel_deferred( from.value ); - } - - auto transfer_amount = net_balance + cpu_balance; - if ( 0 < transfer_amount.amount ) { - token::transfer_action transfer_act{ token_account, { {source_stake_from, active_permission} } }; - transfer_act.send( source_stake_from, stake_account, asset(transfer_amount), "stake bandwidth" ); - } - } - - vote_stake_updater( from ); - update_voting_power( from, stake_net_delta + stake_cpu_delta ); - } - - void system_contract::update_voting_power( const name& voter, const asset& total_update ) - { - auto voter_itr = _voters.find( voter.value ); - if( voter_itr == _voters.end() ) { - voter_itr = _voters.emplace( voter, [&]( auto& v ) { - v.owner = voter; - v.staked = total_update.amount; - }); - } else { - _voters.modify( voter_itr, same_payer, [&]( auto& v ) { - v.staked += total_update.amount; - }); - } - - check( 0 <= voter_itr->staked, "stake for voting cannot be negative" ); - - if( voter == "b1"_n ) { - validate_b1_vesting( voter_itr->staked ); - } - - if( voter_itr->producers.size() || voter_itr->proxy ) { - update_votes( voter, voter_itr->proxy, voter_itr->producers, false ); - } - } - - void system_contract::delegatebw( const name& from, const name& receiver, - const asset& stake_net_quantity, - const asset& stake_cpu_quantity, bool transfer ) - { - asset zero_asset( 0, core_symbol() ); - check( stake_cpu_quantity >= zero_asset, "must stake a positive amount" ); - check( stake_net_quantity >= zero_asset, "must stake a positive amount" ); - check( stake_net_quantity.amount + stake_cpu_quantity.amount > 0, "must stake a positive amount" ); - check( !transfer || from != receiver, "cannot use transfer flag if delegating to self" ); - - changebw( from, receiver, stake_net_quantity, stake_cpu_quantity, transfer); - } // delegatebw - - void system_contract::undelegatebw( const name& from, const name& receiver, - const asset& unstake_net_quantity, const asset& unstake_cpu_quantity ) - { - asset zero_asset( 0, core_symbol() ); - check( unstake_cpu_quantity >= zero_asset, "must unstake a positive amount" ); - check( unstake_net_quantity >= zero_asset, "must unstake a positive amount" ); - check( unstake_cpu_quantity.amount + unstake_net_quantity.amount > 0, "must unstake a positive amount" ); - check( _gstate.thresh_activated_stake_time != time_point(), - "cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" ); - - changebw( from, receiver, -unstake_net_quantity, -unstake_cpu_quantity, false); - } // undelegatebw - - - void system_contract::refund( const name& owner ) { - require_auth( owner ); - - refunds_table refunds_tbl( get_self(), owner.value ); - auto req = refunds_tbl.find( owner.value ); - check( req != refunds_tbl.end(), "refund request not found" ); - check( req->request_time + seconds(refund_delay_sec) <= current_time_point(), - "refund is not available yet" ); - token::transfer_action transfer_act{ token_account, { {stake_account, active_permission}, {req->owner, active_permission} } }; - transfer_act.send( stake_account, req->owner, req->net_amount + req->cpu_amount, "unstake" ); - refunds_tbl.erase( req ); - } - - -} //namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp deleted file mode 100644 index 202210c7d8..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/eosio.system.cpp +++ /dev/null @@ -1,400 +0,0 @@ -#include -#include - -#include -#include - -#include - -namespace eosiosystem { - - using eosio::current_time_point; - using eosio::token; - - double get_continuous_rate(int64_t annual_rate) { - return std::log1p(double(annual_rate)/double(100*inflation_precision)); - } - - system_contract::system_contract( name s, name code, datastream ds ) - :native(s,code,ds), - _voters(get_self(), get_self().value), - _producers(get_self(), get_self().value), - _producers2(get_self(), get_self().value), - _global(get_self(), get_self().value), - _global2(get_self(), get_self().value), - _global3(get_self(), get_self().value), - _global4(get_self(), get_self().value), - _rammarket(get_self(), get_self().value), - _rexpool(get_self(), get_self().value), - _rexretpool(get_self(), get_self().value), - _rexretbuckets(get_self(), get_self().value), - _rexfunds(get_self(), get_self().value), - _rexbalance(get_self(), get_self().value), - _rexorders(get_self(), get_self().value) - { - _gstate = _global.exists() ? _global.get() : get_default_parameters(); - _gstate2 = _global2.exists() ? _global2.get() : eosio_global_state2{}; - _gstate3 = _global3.exists() ? _global3.get() : eosio_global_state3{}; - _gstate4 = _global4.exists() ? _global4.get() : get_default_inflation_parameters(); - } - - eosio_global_state system_contract::get_default_parameters() { - eosio_global_state dp; - get_blockchain_parameters(dp); - return dp; - } - - eosio_global_state4 system_contract::get_default_inflation_parameters() { - eosio_global_state4 gs4; - gs4.continuous_rate = get_continuous_rate(default_annual_rate); - gs4.inflation_pay_factor = default_inflation_pay_factor; - gs4.votepay_factor = default_votepay_factor; - return gs4; - } - - symbol system_contract::core_symbol()const { - const static auto sym = get_core_symbol( _rammarket ); - return sym; - } - - system_contract::~system_contract() { - _global.set( _gstate, get_self() ); - _global2.set( _gstate2, get_self() ); - _global3.set( _gstate3, get_self() ); - _global4.set( _gstate4, get_self() ); - } - - void system_contract::setram( uint64_t max_ram_size ) { - require_auth( get_self() ); - - check( _gstate.max_ram_size < max_ram_size, "ram may only be increased" ); /// decreasing ram might result market maker issues - check( max_ram_size < 1024ll*1024*1024*1024*1024, "ram size is unrealistic" ); - check( max_ram_size > _gstate.total_ram_bytes_reserved, "attempt to set max below reserved" ); - - auto delta = int64_t(max_ram_size) - int64_t(_gstate.max_ram_size); - auto itr = _rammarket.find(ramcore_symbol.raw()); - - /** - * Increase the amount of ram for sale based upon the change in max ram size. - */ - _rammarket.modify( itr, same_payer, [&]( auto& m ) { - m.base.balance.amount += delta; - }); - - _gstate.max_ram_size = max_ram_size; - } - - void system_contract::update_ram_supply() { - auto cbt = eosio::current_block_time(); - - if( cbt <= _gstate2.last_ram_increase ) return; - - auto itr = _rammarket.find(ramcore_symbol.raw()); - auto new_ram = (cbt.slot - _gstate2.last_ram_increase.slot)*_gstate2.new_ram_per_block; - _gstate.max_ram_size += new_ram; - - /** - * Increase the amount of ram for sale based upon the change in max ram size. - */ - _rammarket.modify( itr, same_payer, [&]( auto& m ) { - m.base.balance.amount += new_ram; - }); - _gstate2.last_ram_increase = cbt; - } - - void system_contract::setramrate( uint16_t bytes_per_block ) { - require_auth( get_self() ); - - update_ram_supply(); - _gstate2.new_ram_per_block = bytes_per_block; - } - - void system_contract::setparams( const eosio::blockchain_parameters& params ) { - require_auth( get_self() ); - (eosio::blockchain_parameters&)(_gstate) = params; - check( 3 <= _gstate.max_authority_depth, "max_authority_depth should be at least 3" ); - set_blockchain_parameters( params ); - } - - void system_contract::setpriv( const name& account, uint8_t ispriv ) { - require_auth( get_self() ); - set_privileged( account, ispriv ); - } - - void system_contract::setalimits( const name& account, int64_t ram, int64_t net, int64_t cpu ) { - require_auth( get_self() ); - - user_resources_table userres( get_self(), account.value ); - auto ritr = userres.find( account.value ); - check( ritr == userres.end(), "only supports unlimited accounts" ); - - auto vitr = _voters.find( account.value ); - if( vitr != _voters.end() ) { - bool ram_managed = has_field( vitr->flags1, voter_info::flags1_fields::ram_managed ); - bool net_managed = has_field( vitr->flags1, voter_info::flags1_fields::net_managed ); - bool cpu_managed = has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed ); - check( !(ram_managed || net_managed || cpu_managed), "cannot use setalimits on an account with managed resources" ); - } - - set_resource_limits( account, ram, net, cpu ); - } - - void system_contract::setacctram( const name& account, const std::optional& ram_bytes ) { - require_auth( get_self() ); - - int64_t current_ram, current_net, current_cpu; - get_resource_limits( account, current_ram, current_net, current_cpu ); - - int64_t ram = 0; - - if( !ram_bytes ) { - auto vitr = _voters.find( account.value ); - check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::ram_managed ), - "RAM of account is already unmanaged" ); - - user_resources_table userres( get_self(), account.value ); - auto ritr = userres.find( account.value ); - - ram = ram_gift_bytes; - if( ritr != userres.end() ) { - ram += ritr->ram_bytes; - } - - _voters.modify( vitr, same_payer, [&]( auto& v ) { - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, false ); - }); - } else { - check( *ram_bytes >= 0, "not allowed to set RAM limit to unlimited" ); - - auto vitr = _voters.find( account.value ); - if ( vitr != _voters.end() ) { - _voters.modify( vitr, same_payer, [&]( auto& v ) { - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true ); - }); - } else { - _voters.emplace( account, [&]( auto& v ) { - v.owner = account; - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::ram_managed, true ); - }); - } - - ram = *ram_bytes; - } - - set_resource_limits( account, ram, current_net, current_cpu ); - } - - void system_contract::setacctnet( const name& account, const std::optional& net_weight ) { - require_auth( get_self() ); - - int64_t current_ram, current_net, current_cpu; - get_resource_limits( account, current_ram, current_net, current_cpu ); - - int64_t net = 0; - - if( !net_weight ) { - auto vitr = _voters.find( account.value ); - check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::net_managed ), - "Network bandwidth of account is already unmanaged" ); - - user_resources_table userres( get_self(), account.value ); - auto ritr = userres.find( account.value ); - - if( ritr != userres.end() ) { - net = ritr->net_weight.amount; - } - - _voters.modify( vitr, same_payer, [&]( auto& v ) { - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, false ); - }); - } else { - check( *net_weight >= -1, "invalid value for net_weight" ); - - auto vitr = _voters.find( account.value ); - if ( vitr != _voters.end() ) { - _voters.modify( vitr, same_payer, [&]( auto& v ) { - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true ); - }); - } else { - _voters.emplace( account, [&]( auto& v ) { - v.owner = account; - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::net_managed, true ); - }); - } - - net = *net_weight; - } - - set_resource_limits( account, current_ram, net, current_cpu ); - } - - void system_contract::setacctcpu( const name& account, const std::optional& cpu_weight ) { - require_auth( get_self() ); - - int64_t current_ram, current_net, current_cpu; - get_resource_limits( account, current_ram, current_net, current_cpu ); - - int64_t cpu = 0; - - if( !cpu_weight ) { - auto vitr = _voters.find( account.value ); - check( vitr != _voters.end() && has_field( vitr->flags1, voter_info::flags1_fields::cpu_managed ), - "CPU bandwidth of account is already unmanaged" ); - - user_resources_table userres( get_self(), account.value ); - auto ritr = userres.find( account.value ); - - if( ritr != userres.end() ) { - cpu = ritr->cpu_weight.amount; - } - - _voters.modify( vitr, same_payer, [&]( auto& v ) { - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, false ); - }); - } else { - check( *cpu_weight >= -1, "invalid value for cpu_weight" ); - - auto vitr = _voters.find( account.value ); - if ( vitr != _voters.end() ) { - _voters.modify( vitr, same_payer, [&]( auto& v ) { - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true ); - }); - } else { - _voters.emplace( account, [&]( auto& v ) { - v.owner = account; - v.flags1 = set_field( v.flags1, voter_info::flags1_fields::cpu_managed, true ); - }); - } - - cpu = *cpu_weight; - } - - set_resource_limits( account, current_ram, current_net, cpu ); - } - - void system_contract::activate( const eosio::checksum256& feature_digest ) { - require_auth( get_self() ); - preactivate_feature( feature_digest ); - } - - void system_contract::rmvproducer( const name& producer ) { - require_auth( get_self() ); - auto prod = _producers.find( producer.value ); - check( prod != _producers.end(), "producer not found" ); - _producers.modify( prod, same_payer, [&](auto& p) { - p.deactivate(); - }); - } - - void system_contract::updtrevision( uint8_t revision ) { - require_auth( get_self() ); - check( _gstate2.revision < 255, "can not increment revision" ); // prevent wrap around - check( revision == _gstate2.revision + 1, "can only increment revision by one" ); - check( revision <= 1, // set upper bound to greatest revision supported in the code - "specified revision is not yet supported by the code" ); - _gstate2.revision = revision; - } - - void system_contract::setinflation( int64_t annual_rate, int64_t inflation_pay_factor, int64_t votepay_factor ) { - require_auth(get_self()); - check(annual_rate >= 0, "annual_rate can't be negative"); - if ( inflation_pay_factor < pay_factor_precision ) { - check( false, "inflation_pay_factor must not be less than " + std::to_string(pay_factor_precision) ); - } - if ( votepay_factor < pay_factor_precision ) { - check( false, "votepay_factor must not be less than " + std::to_string(pay_factor_precision) ); - } - _gstate4.continuous_rate = get_continuous_rate(annual_rate); - _gstate4.inflation_pay_factor = inflation_pay_factor; - _gstate4.votepay_factor = votepay_factor; - _global4.set( _gstate4, get_self() ); - } - - /** - * Called after a new account is created. This code enforces resource-limits rules - * for new accounts as well as new account naming conventions. - * - * Account names containing '.' symbols must have a suffix equal to the name of the creator. - * This allows users who buy a premium name (shorter than 12 characters with no dots) to be the only ones - * who can create accounts with the creator's name as a suffix. - * - */ - void native::newaccount( const name& creator, - const name& newact, - ignore owner, - ignore active ) { - - if( creator != get_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(get_self(), get_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( get_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( const name& acnt, const std::vector& abi ) { - eosio::multi_index< "abihash"_n, abi_hash > table(get_self(), get_self().value); - auto itr = table.find( acnt.value ); - if( itr == table.end() ) { - table.emplace( acnt, [&]( auto& row ) { - row.owner = acnt; - row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); - }); - } else { - table.modify( itr, same_payer, [&]( auto& row ) { - row.hash = eosio::sha256(const_cast(abi.data()), abi.size()); - }); - } - } - - void system_contract::init( unsigned_int version, const symbol& core ) { - require_auth( get_self() ); - check( version.value == 0, "unsupported version for init action" ); - - auto itr = _rammarket.find(ramcore_symbol.raw()); - check( itr == _rammarket.end(), "system contract has already been initialized" ); - - auto system_token_supply = eosio::token::get_supply(token_account, core.code() ); - check( system_token_supply.symbol == core, "specified core symbol does not exist (precision mismatch)" ); - - check( system_token_supply.amount > 0, "system token supply must be greater than 0" ); - _rammarket.emplace( get_self(), [&]( auto& m ) { - m.supply.amount = 100000000000000ll; - m.supply.symbol = ramcore_symbol; - m.base.balance.amount = int64_t(_gstate.free_ram()); - m.base.balance.symbol = ram_symbol; - m.quote.balance.amount = system_token_supply.amount / 1000; - m.quote.balance.symbol = core; - }); - - token::open_action open_act{ token_account, { {get_self(), active_permission} } }; - open_act.send( rex_account, core, get_self() ); - } - -} /// eosio.system diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp deleted file mode 100644 index 8f9734bcb0..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/exchange_state.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include - -#include - -#include - -namespace eosiosystem { - - using eosio::check; - - asset exchange_state::convert_to_exchange( connector& reserve, const asset& payment ) - { - const double S0 = supply.amount; - const double R0 = reserve.balance.amount; - const double dR = payment.amount; - const double F = reserve.weight; - - double dS = S0 * ( std::pow(1. + dR / R0, F) - 1. ); - if ( dS < 0 ) dS = 0; // rounding errors - reserve.balance += payment; - supply.amount += int64_t(dS); - return asset( int64_t(dS), supply.symbol ); - } - - asset exchange_state::convert_from_exchange( connector& reserve, const asset& tokens ) - { - const double R0 = reserve.balance.amount; - const double S0 = supply.amount; - const double dS = -tokens.amount; // dS < 0, tokens are subtracted from supply - const double Fi = double(1) / reserve.weight; - - double dR = R0 * ( std::pow(1. + dS / S0, Fi) - 1. ); // dR < 0 since dS < 0 - if ( dR > 0 ) dR = 0; // rounding errors - reserve.balance.amount -= int64_t(-dR); - supply -= tokens; - return asset( int64_t(-dR), reserve.balance.symbol ); - } - - asset exchange_state::convert( const asset& from, const symbol& to ) - { - const auto& sell_symbol = from.symbol; - const auto& base_symbol = base.balance.symbol; - const auto& quote_symbol = quote.balance.symbol; - check( sell_symbol != to, "cannot convert to the same symbol" ); - - asset out( 0, to ); - if ( sell_symbol == base_symbol && to == quote_symbol ) { - const asset tmp = convert_to_exchange( base, from ); - out = convert_from_exchange( quote, tmp ); - } else if ( sell_symbol == quote_symbol && to == base_symbol ) { - const asset tmp = convert_to_exchange( quote, from ); - out = convert_from_exchange( base, tmp ); - } else { - check( false, "invalid conversion" ); - } - return out; - } - - asset exchange_state::direct_convert( const asset& from, const symbol& to ) - { - const auto& sell_symbol = from.symbol; - const auto& base_symbol = base.balance.symbol; - const auto& quote_symbol = quote.balance.symbol; - check( sell_symbol != to, "cannot convert to the same symbol" ); - - asset out( 0, to ); - if ( sell_symbol == base_symbol && to == quote_symbol ) { - out.amount = get_bancor_output( base.balance.amount, quote.balance.amount, from.amount ); - base.balance += from; - quote.balance -= out; - } else if ( sell_symbol == quote_symbol && to == base_symbol ) { - out.amount = get_bancor_output( quote.balance.amount, base.balance.amount, from.amount ); - quote.balance += from; - base.balance -= out; - } else { - check( false, "invalid conversion" ); - } - return out; - } - - int64_t exchange_state::get_bancor_output( int64_t inp_reserve, - int64_t out_reserve, - int64_t inp ) - { - const double ib = inp_reserve; - const double ob = out_reserve; - const double in = inp; - - int64_t out = int64_t( (in * ob) / (ib + in) ); - - if ( out < 0 ) out = 0; - - return out; - } - - int64_t exchange_state::get_bancor_input( int64_t out_reserve, - int64_t inp_reserve, - int64_t out ) - { - const double ob = out_reserve; - const double ib = inp_reserve; - - int64_t inp = (ib * out) / (ob - out); - - if ( inp < 0 ) inp = 0; - - return inp; - } - -} /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp deleted file mode 100644 index 4c01f353a1..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/name_bidding.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include - -#include - -namespace eosiosystem { - - using eosio::current_time_point; - using eosio::token; - - void system_contract::bidname( const name& bidder, const name& newname, const asset& bid ) { - require_auth( bidder ); - check( newname.suffix() == newname, "you can only bid on top-level suffix" ); - - check( (bool)newname, "the empty name is not a valid account name to bid on" ); - check( (newname.value & 0xFull) == 0, "13 character names are not valid account names to bid on" ); - check( (newname.value & 0x1F0ull) == 0, "accounts with 12 character names and no dots can be created without bidding required" ); - check( !is_account( newname ), "account already exists" ); - check( bid.symbol == core_symbol(), "asset must be system token" ); - check( bid.amount > 0, "insufficient bid" ); - token::transfer_action transfer_act{ token_account, { {bidder, active_permission} } }; - transfer_act.send( bidder, names_account, bid, std::string("bid name ")+ newname.to_string() ); - name_bid_table bids(get_self(), get_self().value); - print( name{bidder}, " bid ", bid, " on ", name{newname}, "\n" ); - auto current = bids.find( newname.value ); - if( current == bids.end() ) { - bids.emplace( bidder, [&]( auto& b ) { - b.newname = newname; - b.high_bidder = bidder; - b.high_bid = bid.amount; - b.last_bid_time = current_time_point(); - }); - } else { - check( current->high_bid > 0, "this auction has already closed" ); - check( bid.amount - current->high_bid > (current->high_bid / 10), "must increase bid by 10%" ); - check( current->high_bidder != bidder, "account is already highest bidder" ); - - bid_refund_table refunds_table(get_self(), newname.value); - - auto it = refunds_table.find( current->high_bidder.value ); - if ( it != refunds_table.end() ) { - refunds_table.modify( it, same_payer, [&](auto& r) { - r.amount += asset( current->high_bid, core_symbol() ); - }); - } else { - refunds_table.emplace( bidder, [&](auto& r) { - r.bidder = current->high_bidder; - r.amount = asset( current->high_bid, core_symbol() ); - }); - } - - eosio::transaction t; - t.actions.emplace_back( permission_level{current->high_bidder, active_permission}, - get_self(), "bidrefund"_n, - std::make_tuple( current->high_bidder, newname ) - ); - t.delay_sec = 0; - uint128_t deferred_id = (uint128_t(newname.value) << 64) | current->high_bidder.value; - eosio::cancel_deferred( deferred_id ); - t.send( deferred_id, bidder ); - - bids.modify( current, bidder, [&]( auto& b ) { - b.high_bidder = bidder; - b.high_bid = bid.amount; - b.last_bid_time = current_time_point(); - }); - } - } - - void system_contract::bidrefund( const name& bidder, const name& newname ) { - bid_refund_table refunds_table(get_self(), newname.value); - auto it = refunds_table.find( bidder.value ); - check( it != refunds_table.end(), "refund not found" ); - - token::transfer_action transfer_act{ token_account, { {names_account, active_permission}, {bidder, active_permission} } }; - transfer_act.send( names_account, bidder, asset(it->amount), std::string("refund bid on name ")+(name{newname}).to_string() ); - refunds_table.erase( it ); - } - -} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp deleted file mode 100644 index df98daabf2..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/native.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -#include - -namespace eosiosystem { - - void native::onerror( ignore, ignore> ) { - eosio::check( false, "the onerror action cannot be called directly" ); - } - -} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp deleted file mode 100644 index 4db2f77403..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/producer_pay.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include -#include - -namespace eosiosystem { - - using eosio::current_time_point; - using eosio::microseconds; - using eosio::token; - - void system_contract::onblock( ignore ) { - using namespace eosio; - - require_auth(get_self()); - - block_timestamp timestamp; - name producer; - _ds >> timestamp >> producer; - - // _gstate2.last_block_num is not used anywhere in the system contract code anymore. - // Although this field is deprecated, we will continue updating it for now until the last_block_num field - // is eventually completely removed, at which point this line can be removed. - _gstate2.last_block_num = timestamp; - - /** until activation, no new rewards are paid */ - if( _gstate.thresh_activated_stake_time == time_point() ) - return; - - if( _gstate.last_pervote_bucket_fill == time_point() ) /// start the presses - _gstate.last_pervote_bucket_fill = current_time_point(); - - - /** - * At startup the initial producer may not be one that is registered / elected - * and therefore there may be no producer object for them. - */ - auto prod = _producers.find( producer.value ); - if ( prod != _producers.end() ) { - _gstate.total_unpaid_blocks++; - _producers.modify( prod, same_payer, [&](auto& p ) { - p.unpaid_blocks++; - }); - } - - /// only update block producers once every minute, block_timestamp is in half seconds - if( timestamp.slot - _gstate.last_producer_schedule_update.slot > 120 ) { - update_elected_producers( timestamp ); - - if( (timestamp.slot - _gstate.last_name_close.slot) > blocks_per_day ) { - name_bid_table bids(get_self(), get_self().value); - auto idx = bids.get_index<"highbid"_n>(); - auto highest = idx.lower_bound( std::numeric_limits::max()/2 ); - if( highest != idx.end() && - highest->high_bid > 0 && - (current_time_point() - highest->last_bid_time) > microseconds(useconds_per_day) && - _gstate.thresh_activated_stake_time > time_point() && - (current_time_point() - _gstate.thresh_activated_stake_time) > microseconds(14 * useconds_per_day) - ) { - _gstate.last_name_close = timestamp; - channel_namebid_to_rex( highest->high_bid ); - idx.modify( highest, same_payer, [&]( auto& b ){ - b.high_bid = -b.high_bid; - }); - } - } - } - } - - void system_contract::claimrewards( const name& owner ) { - require_auth( owner ); - - const auto& prod = _producers.get( owner.value ); - check( prod.active(), "producer does not have an active key" ); - - check( _gstate.thresh_activated_stake_time != time_point(), - "cannot claim rewards until the chain is activated (at least 15% of all tokens participate in voting)" ); - - const auto ct = current_time_point(); - - check( ct - prod.last_claim_time > microseconds(useconds_per_day), "already claimed rewards within past day" ); - - const asset token_supply = token::get_supply(token_account, core_symbol().code() ); - const auto usecs_since_last_fill = (ct - _gstate.last_pervote_bucket_fill).count(); - - if( usecs_since_last_fill > 0 && _gstate.last_pervote_bucket_fill > time_point() ) { - double additional_inflation = (_gstate4.continuous_rate * double(token_supply.amount) * double(usecs_since_last_fill)) / double(useconds_per_year); - check( additional_inflation <= double(std::numeric_limits::max() - ((1ll << 10) - 1)), - "overflow in calculating new tokens to be issued; inflation rate is too high" ); - int64_t new_tokens = (additional_inflation < 0.0) ? 0 : static_cast(additional_inflation); - - int64_t to_producers = (new_tokens * uint128_t(pay_factor_precision)) / _gstate4.inflation_pay_factor; - int64_t to_savings = new_tokens - to_producers; - int64_t to_per_block_pay = (to_producers * uint128_t(pay_factor_precision)) / _gstate4.votepay_factor; - int64_t to_per_vote_pay = to_producers - to_per_block_pay; - - if( new_tokens > 0 ) { - { - token::issue_action issue_act{ token_account, { {get_self(), active_permission} } }; - issue_act.send( get_self(), asset(new_tokens, core_symbol()), "issue tokens for producer pay and savings" ); - } - { - token::transfer_action transfer_act{ token_account, { {get_self(), active_permission} } }; - if( to_savings > 0 ) { - transfer_act.send( get_self(), saving_account, asset(to_savings, core_symbol()), "unallocated inflation" ); - } - if( to_per_block_pay > 0 ) { - transfer_act.send( get_self(), bpay_account, asset(to_per_block_pay, core_symbol()), "fund per-block bucket" ); - } - if( to_per_vote_pay > 0 ) { - transfer_act.send( get_self(), vpay_account, asset(to_per_vote_pay, core_symbol()), "fund per-vote bucket" ); - } - } - } - - _gstate.pervote_bucket += to_per_vote_pay; - _gstate.perblock_bucket += to_per_block_pay; - _gstate.last_pervote_bucket_fill = ct; - } - - auto prod2 = _producers2.find( owner.value ); - - /// New metric to be used in pervote pay calculation. Instead of vote weight ratio, we combine vote weight and - /// time duration the vote weight has been held into one metric. - const auto last_claim_plus_3days = prod.last_claim_time + microseconds(3 * useconds_per_day); - - bool crossed_threshold = (last_claim_plus_3days <= ct); - bool updated_after_threshold = true; - if ( prod2 != _producers2.end() ) { - updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update); - } else { - prod2 = _producers2.emplace( owner, [&]( producer_info2& info ) { - info.owner = owner; - info.last_votepay_share_update = ct; - }); - } - - // Note: updated_after_threshold implies cross_threshold (except if claiming rewards when the producers2 table row did not exist). - // The exception leads to updated_after_threshold to be treated as true regardless of whether the threshold was crossed. - // This is okay because in this case the producer will not get paid anything either way. - // In fact it is desired behavior because the producers votes need to be counted in the global total_producer_votepay_share for the first time. - - int64_t producer_per_block_pay = 0; - if( _gstate.total_unpaid_blocks > 0 ) { - producer_per_block_pay = (_gstate.perblock_bucket * prod.unpaid_blocks) / _gstate.total_unpaid_blocks; - } - - double new_votepay_share = update_producer_votepay_share( prod2, - ct, - updated_after_threshold ? 0.0 : prod.total_votes, - true // reset votepay_share to zero after updating - ); - - int64_t producer_per_vote_pay = 0; - if( _gstate2.revision > 0 ) { - double total_votepay_share = update_total_votepay_share( ct ); - if( total_votepay_share > 0 && !crossed_threshold ) { - producer_per_vote_pay = int64_t((new_votepay_share * _gstate.pervote_bucket) / total_votepay_share); - if( producer_per_vote_pay > _gstate.pervote_bucket ) - producer_per_vote_pay = _gstate.pervote_bucket; - } - } else { - if( _gstate.total_producer_vote_weight > 0 ) { - producer_per_vote_pay = int64_t((_gstate.pervote_bucket * prod.total_votes) / _gstate.total_producer_vote_weight); - } - } - - if( producer_per_vote_pay < min_pervote_daily_pay ) { - producer_per_vote_pay = 0; - } - - _gstate.pervote_bucket -= producer_per_vote_pay; - _gstate.perblock_bucket -= producer_per_block_pay; - _gstate.total_unpaid_blocks -= prod.unpaid_blocks; - - update_total_votepay_share( ct, -new_votepay_share, (updated_after_threshold ? prod.total_votes : 0.0) ); - - _producers.modify( prod, same_payer, [&](auto& p) { - p.last_claim_time = ct; - p.unpaid_blocks = 0; - }); - - if ( producer_per_block_pay > 0 ) { - token::transfer_action transfer_act{ token_account, { {bpay_account, active_permission}, {owner, active_permission} } }; - transfer_act.send( bpay_account, owner, asset(producer_per_block_pay, core_symbol()), "producer block pay" ); - } - if ( producer_per_vote_pay > 0 ) { - token::transfer_action transfer_act{ token_account, { {vpay_account, active_permission}, {owner, active_permission} } }; - transfer_act.send( vpay_account, owner, asset(producer_per_vote_pay, core_symbol()), "producer vote pay" ); - } - } - -} //namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp deleted file mode 100644 index 8e9d881f36..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.cpp +++ /dev/null @@ -1,1218 +0,0 @@ -#include -#include -#include - -namespace eosiosystem { - - using eosio::current_time_point; - using eosio::token; - using eosio::seconds; - - void system_contract::deposit( const name& owner, const asset& amount ) - { - require_auth( owner ); - - check( amount.symbol == core_symbol(), "must deposit core token" ); - check( 0 < amount.amount, "must deposit a positive amount" ); - // inline transfer from owner's token balance - { - token::transfer_action transfer_act{ token_account, { owner, active_permission } }; - transfer_act.send( owner, rex_account, amount, "deposit to REX fund" ); - } - transfer_to_fund( owner, amount ); - } - - void system_contract::withdraw( const name& owner, const asset& amount ) - { - require_auth( owner ); - - check( amount.symbol == core_symbol(), "must withdraw core token" ); - check( 0 < amount.amount, "must withdraw a positive amount" ); - update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - transfer_from_fund( owner, amount ); - // inline transfer to owner's token balance - { - token::transfer_action transfer_act{ token_account, { rex_account, active_permission } }; - transfer_act.send( rex_account, owner, amount, "withdraw from REX fund" ); - } - } - - void system_contract::buyrex( const name& from, const asset& amount ) - { - require_auth( from ); - - check( amount.symbol == core_symbol(), "asset must be core token" ); - check( 0 < amount.amount, "must use positive amount" ); - check_voting_requirement( from ); - transfer_from_fund( from, amount ); - const asset rex_received = add_to_rex_pool( amount ); - const asset delta_rex_stake = add_to_rex_balance( from, amount, rex_received ); - runrex(2); - update_rex_account( from, asset( 0, core_symbol() ), delta_rex_stake ); - // dummy action added so that amount of REX tokens purchased shows up in action trace - rex_results::buyresult_action buyrex_act( rex_account, std::vector{ } ); - buyrex_act.send( rex_received ); - } - - void system_contract::unstaketorex( const name& owner, const name& receiver, const asset& from_net, const asset& from_cpu ) - { - require_auth( owner ); - - check( from_net.symbol == core_symbol() && from_cpu.symbol == core_symbol(), "asset must be core token" ); - check( (0 <= from_net.amount) && (0 <= from_cpu.amount) && (0 < from_net.amount || 0 < from_cpu.amount), - "must unstake a positive amount to buy rex" ); - check_voting_requirement( owner ); - - { - del_bandwidth_table dbw_table( get_self(), owner.value ); - auto del_itr = dbw_table.require_find( receiver.value, "delegated bandwidth record does not exist" ); - check( from_net.amount <= del_itr->net_weight.amount, "amount exceeds tokens staked for net"); - check( from_cpu.amount <= del_itr->cpu_weight.amount, "amount exceeds tokens staked for cpu"); - dbw_table.modify( del_itr, same_payer, [&]( delegated_bandwidth& dbw ) { - dbw.net_weight.amount -= from_net.amount; - dbw.cpu_weight.amount -= from_cpu.amount; - }); - if ( del_itr->is_empty() ) { - dbw_table.erase( del_itr ); - } - } - - update_resource_limits( name(0), receiver, -from_net.amount, -from_cpu.amount ); - - const asset payment = from_net + from_cpu; - // inline transfer from stake_account to rex_account - { - token::transfer_action transfer_act{ token_account, { stake_account, active_permission } }; - transfer_act.send( stake_account, rex_account, payment, "buy REX with staked tokens" ); - } - const asset rex_received = add_to_rex_pool( payment ); - add_to_rex_balance( owner, payment, rex_received ); - runrex(2); - update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ), true ); - // dummy action added so that amount of REX tokens purchased shows up in action trace - rex_results::buyresult_action buyrex_act( rex_account, std::vector{ } ); - buyrex_act.send( rex_received ); - } - - void system_contract::sellrex( const name& from, const asset& rex ) - { - require_auth( from ); - - runrex(2); - - auto bitr = _rexbalance.require_find( from.value, "user must first buyrex" ); - check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, - "asset must be a positive amount of (REX, 4)" ); - process_rex_maturities( bitr ); - check( rex.amount <= bitr->matured_rex, "insufficient available rex" ); - - const auto current_order = fill_rex_order( bitr, rex ); - if ( current_order.success && current_order.proceeds.amount == 0 ) { - check( false, "proceeds are negligible" ); - } - asset pending_sell_order = update_rex_account( from, current_order.proceeds, current_order.stake_change ); - if ( !current_order.success ) { - if ( from == "b1"_n ) { - check( false, "b1 sellrex orders should not be queued" ); - } - /** - * REX order couldn't be filled and is added to queue. - * If account already has an open order, requested rex is added to existing order. - */ - auto oitr = _rexorders.find( from.value ); - if ( oitr == _rexorders.end() ) { - oitr = _rexorders.emplace( from, [&]( auto& order ) { - order.owner = from; - order.rex_requested = rex; - order.is_open = true; - order.proceeds = asset( 0, core_symbol() ); - order.stake_change = asset( 0, core_symbol() ); - order.order_time = current_time_point(); - }); - } else { - _rexorders.modify( oitr, same_payer, [&]( auto& order ) { - order.rex_requested.amount += rex.amount; - }); - } - pending_sell_order.amount = oitr->rex_requested.amount; - } - check( pending_sell_order.amount <= bitr->matured_rex, "insufficient funds for current and scheduled orders" ); - // dummy action added so that sell order proceeds show up in action trace - if ( current_order.success ) { - rex_results::sellresult_action sellrex_act( rex_account, std::vector{ } ); - sellrex_act.send( current_order.proceeds ); - } - } - - void system_contract::cnclrexorder( const name& owner ) - { - require_auth( owner ); - - auto itr = _rexorders.require_find( owner.value, "no sellrex order is scheduled" ); - check( itr->is_open, "sellrex order has been filled and cannot be canceled" ); - _rexorders.erase( itr ); - } - - void system_contract::rentcpu( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ) - { - require_auth( from ); - - rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); - int64_t rented_tokens = rent_rex( cpu_loans, from, receiver, loan_payment, loan_fund ); - update_resource_limits( from, receiver, 0, rented_tokens ); - } - - void system_contract::rentnet( const name& from, const name& receiver, const asset& loan_payment, const asset& loan_fund ) - { - require_auth( from ); - - rex_net_loan_table net_loans( get_self(), get_self().value ); - int64_t rented_tokens = rent_rex( net_loans, from, receiver, loan_payment, loan_fund ); - update_resource_limits( from, receiver, rented_tokens, 0 ); - } - - void system_contract::fundcpuloan( const name& from, uint64_t loan_num, const asset& payment ) - { - require_auth( from ); - - rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); - fund_rex_loan( cpu_loans, from, loan_num, payment ); - } - - void system_contract::fundnetloan( const name& from, uint64_t loan_num, const asset& payment ) - { - require_auth( from ); - - rex_net_loan_table net_loans( get_self(), get_self().value ); - fund_rex_loan( net_loans, from, loan_num, payment ); - } - - void system_contract::defcpuloan( const name& from, uint64_t loan_num, const asset& amount ) - { - require_auth( from ); - - rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); - defund_rex_loan( cpu_loans, from, loan_num, amount ); - } - - void system_contract::defnetloan( const name& from, uint64_t loan_num, const asset& amount ) - { - require_auth( from ); - - rex_net_loan_table net_loans( get_self(), get_self().value ); - defund_rex_loan( net_loans, from, loan_num, amount ); - } - - void system_contract::updaterex( const name& owner ) - { - require_auth( owner ); - - runrex(2); - - auto itr = _rexbalance.require_find( owner.value, "account has no REX balance" ); - const asset init_stake = itr->vote_stake; - - auto rexp_itr = _rexpool.begin(); - const int64_t total_rex = rexp_itr->total_rex.amount; - const int64_t total_lendable = rexp_itr->total_lendable.amount; - const int64_t rex_balance = itr->rex_balance.amount; - - asset current_stake( 0, core_symbol() ); - if ( total_rex > 0 ) { - current_stake.amount = ( uint128_t(rex_balance) * total_lendable ) / total_rex; - } - _rexbalance.modify( itr, same_payer, [&]( auto& rb ) { - rb.vote_stake = current_stake; - }); - - update_rex_account( owner, asset( 0, core_symbol() ), current_stake - init_stake, true ); - process_rex_maturities( itr ); - } - - void system_contract::setrex( const asset& balance ) - { - require_auth( "eosio"_n ); - - check( balance.amount > 0, "balance must be set to have a positive amount" ); - check( balance.symbol == core_symbol(), "balance symbol must be core symbol" ); - check( rex_system_initialized(), "rex system is not initialized" ); - _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& pool ) { - pool.total_rent = balance; - }); - } - - void system_contract::rexexec( const name& user, uint16_t max ) - { - require_auth( user ); - - runrex( max ); - } - - void system_contract::consolidate( const name& owner ) - { - require_auth( owner ); - - runrex(2); - - auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); - asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - consolidate_rex_balance( bitr, rex_in_sell_order ); - } - - void system_contract::mvtosavings( const name& owner, const asset& rex ) - { - require_auth( owner ); - - runrex(2); - - auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); - check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); - const asset rex_in_sell_order = update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - const int64_t rex_in_savings = read_rex_savings( bitr ); - check( rex.amount + rex_in_sell_order.amount + rex_in_savings <= bitr->rex_balance.amount, - "insufficient REX balance" ); - process_rex_maturities( bitr ); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - int64_t moved_rex = 0; - while ( !rb.rex_maturities.empty() && moved_rex < rex.amount) { - const int64_t drex = std::min( rex.amount - moved_rex, rb.rex_maturities.back().second ); - rb.rex_maturities.back().second -= drex; - moved_rex += drex; - if ( rb.rex_maturities.back().second == 0 ) { - rb.rex_maturities.pop_back(); - } - } - if ( moved_rex < rex.amount ) { - const int64_t drex = rex.amount - moved_rex; - rb.matured_rex -= drex; - moved_rex += drex; - check( rex_in_sell_order.amount <= rb.matured_rex, "logic error in mvtosavings" ); - } - check( moved_rex == rex.amount, "programmer error in mvtosavings" ); - }); - put_rex_savings( bitr, rex_in_savings + rex.amount ); - } - - void system_contract::mvfrsavings( const name& owner, const asset& rex ) - { - require_auth( owner ); - - runrex(2); - - auto bitr = _rexbalance.require_find( owner.value, "account has no REX balance" ); - check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); - const int64_t rex_in_savings = read_rex_savings( bitr ); - check( rex.amount <= rex_in_savings, "insufficient REX in savings" ); - process_rex_maturities( bitr ); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - const time_point_sec maturity = get_rex_maturity(); - if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { - rb.rex_maturities.back().second += rex.amount; - } else { - rb.rex_maturities.emplace_back( maturity, rex.amount ); - } - }); - put_rex_savings( bitr, rex_in_savings - rex.amount ); - update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - } - - void system_contract::closerex( const name& owner ) - { - require_auth( owner ); - - if ( rex_system_initialized() ) - runrex(2); - - update_rex_account( owner, asset( 0, core_symbol() ), asset( 0, core_symbol() ) ); - - /// check for any outstanding loans or rex fund - { - rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); - auto cpu_idx = cpu_loans.get_index<"byowner"_n>(); - bool no_outstanding_cpu_loans = ( cpu_idx.find( owner.value ) == cpu_idx.end() ); - - rex_net_loan_table net_loans( get_self(), get_self().value ); - auto net_idx = net_loans.get_index<"byowner"_n>(); - bool no_outstanding_net_loans = ( net_idx.find( owner.value ) == net_idx.end() ); - - auto fund_itr = _rexfunds.find( owner.value ); - bool no_outstanding_rex_fund = ( fund_itr != _rexfunds.end() ) && ( fund_itr->balance.amount == 0 ); - - if ( no_outstanding_cpu_loans && no_outstanding_net_loans && no_outstanding_rex_fund ) { - _rexfunds.erase( fund_itr ); - } - } - - /// check for remaining rex balance - { - auto rex_itr = _rexbalance.find( owner.value ); - if ( rex_itr != _rexbalance.end() ) { - check( rex_itr->rex_balance.amount == 0, "account has remaining REX balance, must sell first"); - _rexbalance.erase( rex_itr ); - } - } - } - - /** - * @brief Updates account NET and CPU resource limits - * - * @param from - account charged for RAM if there is a need - * @param receiver - account whose resource limits are updated - * @param delta_net - change in NET bandwidth limit - * @param delta_cpu - change in CPU bandwidth limit - */ - void system_contract::update_resource_limits( const name& from, const name& receiver, int64_t delta_net, int64_t delta_cpu ) - { - if ( delta_cpu == 0 && delta_net == 0 ) { // nothing to update - return; - } - - user_resources_table totals_tbl( get_self(), receiver.value ); - auto tot_itr = totals_tbl.find( receiver.value ); - if ( tot_itr == totals_tbl.end() ) { - check( 0 <= delta_net && 0 <= delta_cpu, "logic error, should not occur"); - tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) { - tot.owner = receiver; - tot.net_weight = asset( delta_net, core_symbol() ); - tot.cpu_weight = asset( delta_cpu, core_symbol() ); - }); - } else { - totals_tbl.modify( tot_itr, same_payer, [&]( auto& tot ) { - tot.net_weight.amount += delta_net; - tot.cpu_weight.amount += delta_cpu; - }); - } - check( 0 <= tot_itr->net_weight.amount, "insufficient staked total net bandwidth" ); - check( 0 <= tot_itr->cpu_weight.amount, "insufficient staked total cpu bandwidth" ); - - { - bool net_managed = false; - bool cpu_managed = false; - - auto voter_itr = _voters.find( receiver.value ); - if( voter_itr != _voters.end() ) { - net_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::net_managed ); - cpu_managed = has_field( voter_itr->flags1, voter_info::flags1_fields::cpu_managed ); - } - - if( !(net_managed && cpu_managed) ) { - int64_t ram_bytes = 0, net = 0, cpu = 0; - get_resource_limits( receiver, ram_bytes, net, cpu ); - - set_resource_limits( receiver, - ram_bytes, - net_managed ? net : tot_itr->net_weight.amount, - cpu_managed ? cpu : tot_itr->cpu_weight.amount ); - } - } - - if ( tot_itr->is_empty() ) { - totals_tbl.erase( tot_itr ); - } - } - - /** - * @brief Checks if account satisfies voting requirement (voting for a proxy or 21 producers) - * for buying REX - * - * @param owner - account buying or already holding REX tokens - * @err_msg - error message - */ - void system_contract::check_voting_requirement( const name& owner, const char* error_msg )const - { - auto vitr = _voters.find( owner.value ); - check( vitr != _voters.end() && ( vitr->proxy || 21 <= vitr->producers.size() ), error_msg ); - } - - /** - * @brief Checks if CPU and Network loans are available - * - * Loans are available if 1) REX pool lendable balance is nonempty, and 2) there are no - * unfilled sellrex orders. - */ - bool system_contract::rex_loans_available()const - { - if ( !rex_available() ) { - return false; - } else { - if ( _rexorders.begin() == _rexorders.end() ) { - return true; // no outstanding sellrex orders - } else { - auto idx = _rexorders.get_index<"bytime"_n>(); - return !idx.begin()->is_open; // no outstanding unfilled sellrex orders - } - } - } - - /** - * @brief Updates rex_pool balances upon creating a new loan or renewing an existing one - * - * @param payment - loan fee paid - * @param rented_tokens - amount of tokens to be staked to loan receiver - * @param new_loan - flag indicating whether the loan is new or being renewed - */ - void system_contract::add_loan_to_rex_pool( const asset& payment, int64_t rented_tokens, bool new_loan ) - { - add_to_rex_return_pool( payment ); - _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rt ) { - // add payment to total_rent - rt.total_rent.amount += payment.amount; - // move rented_tokens from total_unlent to total_lent - rt.total_unlent.amount -= rented_tokens; - rt.total_lent.amount += rented_tokens; - // increment loan_num if a new loan is being created - if ( new_loan ) { - rt.loan_num++; - } - }); - } - - /** - * @brief Updates rex_pool balances upon closing an expired loan - * - * @param loan - loan to be closed - */ - void system_contract::remove_loan_from_rex_pool( const rex_loan& loan ) - { - const auto& pool = _rexpool.begin(); - const int64_t delta_total_rent = exchange_state::get_bancor_output( pool->total_unlent.amount, - pool->total_rent.amount, - loan.total_staked.amount ); - _rexpool.modify( pool, same_payer, [&]( auto& rt ) { - // deduct calculated delta_total_rent from total_rent - rt.total_rent.amount -= delta_total_rent; - // move rented tokens from total_lent to total_unlent - rt.total_unlent.amount += loan.total_staked.amount; - rt.total_lent.amount -= loan.total_staked.amount; - rt.total_lendable.amount = rt.total_unlent.amount + rt.total_lent.amount; - }); - } - - /** - * @brief Updates the fields of an existing loan that is being renewed - */ - template - int64_t system_contract::update_renewed_loan( Index& idx, const Iterator& itr, int64_t rented_tokens ) - { - int64_t delta_stake = rented_tokens - itr->total_staked.amount; - idx.modify ( itr, same_payer, [&]( auto& loan ) { - loan.total_staked.amount = rented_tokens; - loan.expiration += eosio::days(30); - loan.balance.amount -= loan.payment.amount; - }); - return delta_stake; - } - - /** - * @brief Performs maintenance operations on expired NET and CPU loans and sellrex orders - * - * @param max - maximum number of each of the three categories to be processed - */ - void system_contract::runrex( uint16_t max ) - { - check( rex_system_initialized(), "rex system not initialized yet" ); - - update_rex_pool(); - - const auto& pool = _rexpool.begin(); - - auto process_expired_loan = [&]( auto& idx, const auto& itr ) -> std::pair { - /// update rex_pool in order to delete existing loan - remove_loan_from_rex_pool( *itr ); - bool delete_loan = false; - int64_t delta_stake = 0; - /// calculate rented tokens at current price - int64_t rented_tokens = exchange_state::get_bancor_output( pool->total_rent.amount, - pool->total_unlent.amount, - itr->payment.amount ); - /// conditions for loan renewal - bool renew_loan = itr->payment <= itr->balance /// loan has sufficient balance - && itr->payment.amount < rented_tokens /// loan has favorable return - && rex_loans_available(); /// no pending sell orders - if ( renew_loan ) { - /// update rex_pool in order to account for renewed loan - add_loan_to_rex_pool( itr->payment, rented_tokens, false ); - /// update renewed loan fields - delta_stake = update_renewed_loan( idx, itr, rented_tokens ); - } else { - delete_loan = true; - delta_stake = -( itr->total_staked.amount ); - /// refund "from" account if the closed loan balance is positive - if ( itr->balance.amount > 0 ) { - transfer_to_fund( itr->from, itr->balance ); - } - } - - return { delete_loan, delta_stake }; - }; - - /// transfer from eosio.names to eosio.rex - if ( pool->namebid_proceeds.amount > 0 ) { - channel_to_rex( names_account, pool->namebid_proceeds ); - _rexpool.modify( pool, same_payer, [&]( auto& rt ) { - rt.namebid_proceeds.amount = 0; - }); - } - - /// process cpu loans - { - rex_cpu_loan_table cpu_loans( get_self(), get_self().value ); - auto cpu_idx = cpu_loans.get_index<"byexpr"_n>(); - for ( uint16_t i = 0; i < max; ++i ) { - auto itr = cpu_idx.begin(); - if ( itr == cpu_idx.end() || itr->expiration > current_time_point() ) break; - - auto result = process_expired_loan( cpu_idx, itr ); - if ( result.second != 0 ) - update_resource_limits( itr->from, itr->receiver, 0, result.second ); - - if ( result.first ) - cpu_idx.erase( itr ); - } - } - - /// process net loans - { - rex_net_loan_table net_loans( get_self(), get_self().value ); - auto net_idx = net_loans.get_index<"byexpr"_n>(); - for ( uint16_t i = 0; i < max; ++i ) { - auto itr = net_idx.begin(); - if ( itr == net_idx.end() || itr->expiration > current_time_point() ) break; - - auto result = process_expired_loan( net_idx, itr ); - if ( result.second != 0 ) - update_resource_limits( itr->from, itr->receiver, result.second, 0 ); - - if ( result.first ) - net_idx.erase( itr ); - } - } - - /// process sellrex orders - if ( _rexorders.begin() != _rexorders.end() ) { - auto idx = _rexorders.get_index<"bytime"_n>(); - auto oitr = idx.begin(); - for ( uint16_t i = 0; i < max; ++i ) { - if ( oitr == idx.end() || !oitr->is_open ) break; - auto next = oitr; - ++next; - auto bitr = _rexbalance.find( oitr->owner.value ); - if ( bitr != _rexbalance.end() ) { // should always be true - auto result = fill_rex_order( bitr, oitr->rex_requested ); - if ( result.success ) { - const name order_owner = oitr->owner; - idx.modify( oitr, same_payer, [&]( auto& order ) { - order.proceeds.amount = result.proceeds.amount; - order.stake_change.amount = result.stake_change.amount; - order.close(); - }); - /// send dummy action to show owner and proceeds of filled sellrex order - rex_results::orderresult_action order_act( rex_account, std::vector{ } ); - order_act.send( order_owner, result.proceeds ); - } - } - oitr = next; - } - } - - } - - /** - * @brief Adds returns from the REX return pool to the REX pool - */ - void system_contract::update_rex_pool() - { - auto get_elapsed_intervals = [&]( const time_point_sec& t1, const time_point_sec& t0 ) -> uint32_t { - return ( t1.sec_since_epoch() - t0.sec_since_epoch() ) / rex_return_pool::dist_interval; - }; - - const time_point_sec ct = current_time_point(); - const uint32_t cts = ct.sec_since_epoch(); - const time_point_sec effective_time{cts - cts % rex_return_pool::dist_interval}; - - const auto ret_pool_elem = _rexretpool.begin(); - const auto ret_buckets_elem = _rexretbuckets.begin(); - - if ( ret_pool_elem == _rexretpool.end() || effective_time <= ret_pool_elem->last_dist_time ) { - return; - } - - const int64_t current_rate = ret_pool_elem->current_rate_of_increase; - const uint32_t elapsed_intervals = get_elapsed_intervals( effective_time, ret_pool_elem->last_dist_time ); - int64_t change_estimate = current_rate * elapsed_intervals; - - { - const bool new_return_bucket = ret_pool_elem->pending_bucket_time <= effective_time; - int64_t new_bucket_rate = 0; - time_point_sec new_bucket_time = time_point_sec::min(); - _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) { - if ( new_return_bucket ) { - int64_t remainder = rp.pending_bucket_proceeds % rex_return_pool::total_intervals; - new_bucket_rate = ( rp.pending_bucket_proceeds - remainder ) / rex_return_pool::total_intervals; - new_bucket_time = rp.pending_bucket_time; - rp.current_rate_of_increase += new_bucket_rate; - change_estimate += remainder + new_bucket_rate * get_elapsed_intervals( effective_time, rp.pending_bucket_time ); - rp.pending_bucket_proceeds = 0; - rp.pending_bucket_time = time_point_sec::maximum(); - if ( new_bucket_time < rp.oldest_bucket_time ) { - rp.oldest_bucket_time = new_bucket_time; - } - } - rp.proceeds -= change_estimate; - rp.last_dist_time = effective_time; - }); - - if ( new_return_bucket ) { - _rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) { - rb.return_buckets[new_bucket_time] = new_bucket_rate; - }); - } - } - - const time_point_sec time_threshold = effective_time - seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval); - if ( ret_pool_elem->oldest_bucket_time <= time_threshold ) { - int64_t expired_rate = 0; - int64_t surplus = 0; - _rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) { - auto& return_buckets = rb.return_buckets; - auto iter = return_buckets.begin(); - while ( iter != return_buckets.end() && iter->first <= time_threshold ) { - auto next = iter; - ++next; - const uint32_t overtime = get_elapsed_intervals( effective_time, - iter->first + seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval) ); - surplus += iter->second * overtime; - expired_rate += iter->second; - return_buckets.erase(iter); - iter = next; - } - }); - - _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) { - if ( !ret_buckets_elem->return_buckets.empty() ) { - rp.oldest_bucket_time = ret_buckets_elem->return_buckets.begin()->first; - } else { - rp.oldest_bucket_time = time_point_sec::min(); - } - if ( expired_rate > 0) { - rp.current_rate_of_increase -= expired_rate; - } - if ( surplus > 0 ) { - change_estimate -= surplus; - rp.proceeds += surplus; - } - }); - } - - if ( change_estimate > 0 && ret_pool_elem->proceeds < 0 ) { - _rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) { - change_estimate += rp.proceeds; - rp.proceeds = 0; - }); - } - - if ( change_estimate > 0 ) { - _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& pool ) { - pool.total_unlent.amount += change_estimate; - pool.total_lendable = pool.total_unlent + pool.total_lent; - }); - } - } - - template - int64_t system_contract::rent_rex( T& table, const name& from, const name& receiver, const asset& payment, const asset& fund ) - { - runrex(2); - - check( rex_loans_available(), "rex loans are currently not available" ); - check( payment.symbol == core_symbol() && fund.symbol == core_symbol(), "must use core token" ); - check( 0 < payment.amount && 0 <= fund.amount, "must use positive asset amount" ); - - transfer_from_fund( from, payment + fund ); - - const auto& pool = _rexpool.begin(); /// already checked that _rexpool.begin() != _rexpool.end() in rex_loans_available() - - int64_t rented_tokens = exchange_state::get_bancor_output( pool->total_rent.amount, - pool->total_unlent.amount, - payment.amount ); - check( payment.amount < rented_tokens, "loan price does not favor renting" ); - add_loan_to_rex_pool( payment, rented_tokens, true ); - - table.emplace( from, [&]( auto& c ) { - c.from = from; - c.receiver = receiver; - c.payment = payment; - c.balance = fund; - c.total_staked = asset( rented_tokens, core_symbol() ); - c.expiration = current_time_point() + eosio::days(30); - c.loan_num = pool->loan_num; - }); - - rex_results::rentresult_action rentresult_act{ rex_account, std::vector{ } }; - rentresult_act.send( asset{ rented_tokens, core_symbol() } ); - return rented_tokens; - } - - /** - * @brief Processes a sellrex order and returns object containing the results - * - * Processes an incoming or already scheduled sellrex order. If REX pool has enough core - * tokens not frozen in loans, order is filled. In this case, REX pool totals, user rex_balance - * and user vote_stake are updated. However, this function does not update user voting power. The - * function returns success flag, order proceeds, and vote stake delta. These are used later in a - * different function to complete order processing, i.e. transfer proceeds to user REX fund and - * update user vote weight. - * - * @param bitr - iterator pointing to rex_balance database record - * @param rex - amount of rex to be sold - * - * @return rex_order_outcome - a struct containing success flag, order proceeds, and resultant - * vote stake change - */ - rex_order_outcome system_contract::fill_rex_order( const rex_balance_table::const_iterator& bitr, const asset& rex ) - { - auto rexitr = _rexpool.begin(); - const int64_t S0 = rexitr->total_lendable.amount; - const int64_t R0 = rexitr->total_rex.amount; - const int64_t p = (uint128_t(rex.amount) * S0) / R0; - const int64_t R1 = R0 - rex.amount; - const int64_t S1 = S0 - p; - asset proceeds( p, core_symbol() ); - asset stake_change( 0, core_symbol() ); - bool success = false; - - const int64_t unlent_lower_bound = rexitr->total_lent.amount / 10; - const int64_t available_unlent = rexitr->total_unlent.amount - unlent_lower_bound; // available_unlent <= 0 is possible - if ( proceeds.amount <= available_unlent ) { - const int64_t init_vote_stake_amount = bitr->vote_stake.amount; - const int64_t current_stake_value = ( uint128_t(bitr->rex_balance.amount) * S0 ) / R0; - _rexpool.modify( rexitr, same_payer, [&]( auto& rt ) { - rt.total_rex.amount = R1; - rt.total_lendable.amount = S1; - rt.total_unlent.amount = rt.total_lendable.amount - rt.total_lent.amount; - }); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - rb.vote_stake.amount = current_stake_value - proceeds.amount; - rb.rex_balance.amount -= rex.amount; - rb.matured_rex -= rex.amount; - }); - stake_change.amount = bitr->vote_stake.amount - init_vote_stake_amount; - success = true; - } else { - proceeds.amount = 0; - } - - return { success, proceeds, stake_change }; - } - - template - void system_contract::fund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& payment ) - { - check( payment.symbol == core_symbol(), "must use core token" ); - transfer_from_fund( from, payment ); - auto itr = table.require_find( loan_num, "loan not found" ); - check( itr->from == from, "user must be loan creator" ); - check( itr->expiration > current_time_point(), "loan has already expired" ); - table.modify( itr, same_payer, [&]( auto& loan ) { - loan.balance.amount += payment.amount; - }); - } - - template - void system_contract::defund_rex_loan( T& table, const name& from, uint64_t loan_num, const asset& amount ) - { - check( amount.symbol == core_symbol(), "must use core token" ); - auto itr = table.require_find( loan_num, "loan not found" ); - check( itr->from == from, "user must be loan creator" ); - check( itr->expiration > current_time_point(), "loan has already expired" ); - check( itr->balance >= amount, "insufficent loan balance" ); - table.modify( itr, same_payer, [&]( auto& loan ) { - loan.balance.amount -= amount.amount; - }); - transfer_to_fund( from, amount ); - } - - /** - * @brief Transfers tokens from owner REX fund - * - * @pre - owner REX fund has sufficient balance - * - * @param owner - owner account name - * @param amount - tokens to be transfered out of REX fund - */ - void system_contract::transfer_from_fund( const name& owner, const asset& amount ) - { - check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount from REX fund" ); - auto itr = _rexfunds.require_find( owner.value, "must deposit to REX fund first" ); - check( amount <= itr->balance, "insufficient funds" ); - _rexfunds.modify( itr, same_payer, [&]( auto& fund ) { - fund.balance.amount -= amount.amount; - }); - } - - /** - * @brief Transfers tokens to owner REX fund - * - * @param owner - owner account name - * @param amount - tokens to be transfered to REX fund - */ - void system_contract::transfer_to_fund( const name& owner, const asset& amount ) - { - check( 0 < amount.amount && amount.symbol == core_symbol(), "must transfer positive amount to REX fund" ); - auto itr = _rexfunds.find( owner.value ); - if ( itr == _rexfunds.end() ) { - _rexfunds.emplace( owner, [&]( auto& fund ) { - fund.owner = owner; - fund.balance = amount; - }); - } else { - _rexfunds.modify( itr, same_payer, [&]( auto& fund ) { - fund.balance.amount += amount.amount; - }); - } - } - - /** - * @brief Processes owner filled sellrex order and updates vote weight - * - * Checks if user has a scheduled sellrex order that has been filled, completes its processing, - * and deletes it. Processing entails transfering proceeds to user REX fund and updating user - * vote weight. Additional proceeds and stake change can be passed as arguments. This function - * is called only by actions pushed by owner. - * - * @param owner - owner account name - * @param proceeds - additional proceeds to be transfered to owner REX fund - * @param delta_stake - additional stake to be added to owner vote weight - * @param force_vote_update - if true, vote weight is updated even if vote stake didn't change - * - * @return asset - REX amount of owner unfilled sell order if one exists - */ - asset system_contract::update_rex_account( const name& owner, const asset& proceeds, const asset& delta_stake, bool force_vote_update ) - { - asset to_fund( proceeds ); - asset to_stake( delta_stake ); - asset rex_in_sell_order( 0, rex_symbol ); - auto itr = _rexorders.find( owner.value ); - if ( itr != _rexorders.end() ) { - if ( itr->is_open ) { - rex_in_sell_order.amount = itr->rex_requested.amount; - } else { - to_fund.amount += itr->proceeds.amount; - to_stake.amount += itr->stake_change.amount; - _rexorders.erase( itr ); - } - } - - if ( to_fund.amount > 0 ) - transfer_to_fund( owner, to_fund ); - if ( force_vote_update || to_stake.amount != 0 ) - update_voting_power( owner, to_stake ); - - return rex_in_sell_order; - } - - /** - * @brief Channels system fees to REX pool - * - * @param from - account from which asset is transfered to REX pool - * @param amount - amount of tokens to be transfered - */ - void system_contract::channel_to_rex( const name& from, const asset& amount ) - { -#if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX - if ( rex_available() ) { - add_to_rex_return_pool( amount ); - // inline transfer to rex_account - token::transfer_action transfer_act{ token_account, { from, active_permission } }; - transfer_act.send( from, rex_account, amount, - std::string("transfer from ") + from.to_string() + " to eosio.rex" ); - } -#endif - } - - /** - * @brief Updates namebid proceeds to be transfered to REX pool - * - * @param highest_bid - highest bidding amount of closed namebid - */ - void system_contract::channel_namebid_to_rex( const int64_t highest_bid ) - { -#if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX - if ( rex_available() ) { - _rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rp ) { - rp.namebid_proceeds.amount += highest_bid; - }); - } -#endif - } - - /** - * @brief Calculates maturity time of purchased REX tokens which is 4 days from end - * of the day UTC - * - * @return time_point_sec - */ - time_point_sec system_contract::get_rex_maturity() - { - const uint32_t num_of_maturity_buckets = 5; - static const uint32_t now = current_time_point().sec_since_epoch(); - static const uint32_t r = now % seconds_per_day; - static const time_point_sec rms{ now - r + num_of_maturity_buckets * seconds_per_day }; - return rms; - } - - /** - * @brief Updates REX owner maturity buckets - * - * @param bitr - iterator pointing to rex_balance object - */ - void system_contract::process_rex_maturities( const rex_balance_table::const_iterator& bitr ) - { - const time_point_sec now = current_time_point(); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - while ( !rb.rex_maturities.empty() && rb.rex_maturities.front().first <= now ) { - rb.matured_rex += rb.rex_maturities.front().second; - rb.rex_maturities.pop_front(); - } - }); - } - - /** - * @brief Consolidates REX maturity buckets into one - * - * @param bitr - iterator pointing to rex_balance object - * @param rex_in_sell_order - REX tokens in owner unfilled sell order, if one exists - */ - void system_contract::consolidate_rex_balance( const rex_balance_table::const_iterator& bitr, - const asset& rex_in_sell_order ) - { - const int64_t rex_in_savings = read_rex_savings( bitr ); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - int64_t total = rb.matured_rex - rex_in_sell_order.amount; - rb.matured_rex = rex_in_sell_order.amount; - while ( !rb.rex_maturities.empty() ) { - total += rb.rex_maturities.front().second; - rb.rex_maturities.pop_front(); - } - if ( total > 0 ) { - rb.rex_maturities.emplace_back( get_rex_maturity(), total ); - } - }); - put_rex_savings( bitr, rex_in_savings ); - } - - /** - * @brief Updates REX pool balances upon REX purchase - * - * @param payment - amount of core tokens paid - * - * @return asset - calculated amount of REX tokens purchased - */ - asset system_contract::add_to_rex_pool( const asset& payment ) - { - /** - * If CORE_SYMBOL is (EOS,4), maximum supply is 10^10 tokens (10 billion tokens), i.e., maximum amount - * of indivisible units is 10^14. rex_ratio = 10^4 sets the upper bound on (REX,4) indivisible units to - * 10^18 and that is within the maximum allowable amount field of asset type which is set to 2^62 - * (approximately 4.6 * 10^18). For a different CORE_SYMBOL, and in order for maximum (REX,4) amount not - * to exceed that limit, maximum amount of indivisible units cannot be set to a value larger than 4 * 10^14. - * If precision of CORE_SYMBOL is 4, that corresponds to a maximum supply of 40 billion tokens. - */ - const int64_t rex_ratio = 10000; - const asset init_total_rent( 20'000'0000, core_symbol() ); /// base balance prevents renting profitably until at least a minimum number of core_symbol() is made available - asset rex_received( 0, rex_symbol ); - auto itr = _rexpool.begin(); - if ( !rex_system_initialized() ) { - /// initialize REX pool - _rexpool.emplace( get_self(), [&]( auto& rp ) { - rex_received.amount = payment.amount * rex_ratio; - rp.total_lendable = payment; - rp.total_lent = asset( 0, core_symbol() ); - rp.total_unlent = rp.total_lendable - rp.total_lent; - rp.total_rent = init_total_rent; - rp.total_rex = rex_received; - rp.namebid_proceeds = asset( 0, core_symbol() ); - }); - } else if ( !rex_available() ) { /// should be a rare corner case, REX pool is initialized but empty - _rexpool.modify( itr, same_payer, [&]( auto& rp ) { - rex_received.amount = payment.amount * rex_ratio; - rp.total_lendable.amount = payment.amount; - rp.total_lent.amount = 0; - rp.total_unlent.amount = rp.total_lendable.amount - rp.total_lent.amount; - rp.total_rent.amount = init_total_rent.amount; - rp.total_rex.amount = rex_received.amount; - }); - } else { - /// total_lendable > 0 if total_rex > 0 except in a rare case and due to rounding errors - check( itr->total_lendable.amount > 0, "lendable REX pool is empty" ); - const int64_t S0 = itr->total_lendable.amount; - const int64_t S1 = S0 + payment.amount; - const int64_t R0 = itr->total_rex.amount; - const int64_t R1 = (uint128_t(S1) * R0) / S0; - rex_received.amount = R1 - R0; - _rexpool.modify( itr, same_payer, [&]( auto& rp ) { - rp.total_lendable.amount = S1; - rp.total_rex.amount = R1; - rp.total_unlent.amount = rp.total_lendable.amount - rp.total_lent.amount; - check( rp.total_unlent.amount >= 0, "programmer error, this should never go negative" ); - }); - } - - return rex_received; - } - - /** - * @brief Adds an amount of core tokens to the REX return pool - * - * @param fee - amount to be added - */ - void system_contract::add_to_rex_return_pool( const asset& fee ) - { - update_rex_pool(); - if ( fee.amount <= 0 ) { - return; - } - - const time_point_sec ct = current_time_point(); - const uint32_t cts = ct.sec_since_epoch(); - const uint32_t bucket_interval = rex_return_pool::hours_per_bucket * seconds_per_hour; - const time_point_sec effective_time{cts - cts % bucket_interval + bucket_interval}; - const auto return_pool_elem = _rexretpool.begin(); - if ( return_pool_elem == _rexretpool.end() ) { - _rexretpool.emplace( get_self(), [&]( auto& rp ) { - rp.last_dist_time = effective_time; - rp.pending_bucket_proceeds = fee.amount; - rp.pending_bucket_time = effective_time; - rp.proceeds = fee.amount; - }); - _rexretbuckets.emplace( get_self(), [&]( auto& rb ) { } ); - } else { - _rexretpool.modify( return_pool_elem, same_payer, [&]( auto& rp ) { - rp.pending_bucket_proceeds += fee.amount; - rp.proceeds += fee.amount; - if ( rp.pending_bucket_time == time_point_sec::maximum() ) { - rp.pending_bucket_time = effective_time; - } - }); - } - } - - /** - * @brief Updates owner REX balance upon buying REX tokens - * - * @param owner - account name of REX owner - * @param payment - amount core tokens paid to buy REX - * @param rex_received - amount of purchased REX tokens - * - * @return asset - change in owner REX vote stake - */ - asset system_contract::add_to_rex_balance( const name& owner, const asset& payment, const asset& rex_received ) - { - asset init_rex_stake( 0, core_symbol() ); - asset current_rex_stake( 0, core_symbol() ); - auto bitr = _rexbalance.find( owner.value ); - if ( bitr == _rexbalance.end() ) { - bitr = _rexbalance.emplace( owner, [&]( auto& rb ) { - rb.owner = owner; - rb.vote_stake = payment; - rb.rex_balance = rex_received; - }); - current_rex_stake.amount = payment.amount; - } else { - init_rex_stake.amount = bitr->vote_stake.amount; - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - rb.rex_balance.amount += rex_received.amount; - rb.vote_stake.amount = ( uint128_t(rb.rex_balance.amount) * _rexpool.begin()->total_lendable.amount ) - / _rexpool.begin()->total_rex.amount; - }); - current_rex_stake.amount = bitr->vote_stake.amount; - } - - const int64_t rex_in_savings = read_rex_savings( bitr ); - process_rex_maturities( bitr ); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - const time_point_sec maturity = get_rex_maturity(); - if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == maturity ) { - rb.rex_maturities.back().second += rex_received.amount; - } else { - rb.rex_maturities.emplace_back( maturity, rex_received.amount ); - } - }); - put_rex_savings( bitr, rex_in_savings ); - return current_rex_stake - init_rex_stake; - } - - /** - * @brief Reads amount of REX in savings bucket and removes the bucket from maturities - * - * Reads and (temporarily) removes REX savings bucket from REX maturities in order to - * allow uniform processing of remaining buckets as savings is a special case. This - * function is used in conjunction with put_rex_savings. - * - * @param bitr - iterator pointing to rex_balance object - * - * @return int64_t - amount of REX in savings bucket - */ - int64_t system_contract::read_rex_savings( const rex_balance_table::const_iterator& bitr ) - { - int64_t rex_in_savings = 0; - static const time_point_sec end_of_days = time_point_sec::maximum(); - if ( !bitr->rex_maturities.empty() && bitr->rex_maturities.back().first == end_of_days ) { - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - rex_in_savings = rb.rex_maturities.back().second; - rb.rex_maturities.pop_back(); - }); - } - return rex_in_savings; - } - - /** - * @brief Adds a specified REX amount to savings bucket - * - * @param bitr - iterator pointing to rex_balance object - * @param rex - amount of REX to be added - */ - void system_contract::put_rex_savings( const rex_balance_table::const_iterator& bitr, int64_t rex ) - { - if ( rex == 0 ) return; - static const time_point_sec end_of_days = time_point_sec::maximum(); - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - if ( !rb.rex_maturities.empty() && rb.rex_maturities.back().first == end_of_days ) { - rb.rex_maturities.back().second += rex; - } else { - rb.rex_maturities.emplace_back( end_of_days, rex ); - } - }); - } - - /** - * @brief Updates voter REX vote stake to the current value of REX tokens held - * - * @param voter - account name of voter - */ - void system_contract::update_rex_stake( const name& voter ) - { - int64_t delta_stake = 0; - auto bitr = _rexbalance.find( voter.value ); - if ( bitr != _rexbalance.end() && rex_available() ) { - asset init_vote_stake = bitr->vote_stake; - asset current_vote_stake( 0, core_symbol() ); - current_vote_stake.amount = ( uint128_t(bitr->rex_balance.amount) * _rexpool.begin()->total_lendable.amount ) - / _rexpool.begin()->total_rex.amount; - _rexbalance.modify( bitr, same_payer, [&]( auto& rb ) { - rb.vote_stake.amount = current_vote_stake.amount; - }); - delta_stake = current_vote_stake.amount - init_vote_stake.amount; - } - - if ( delta_stake != 0 ) { - auto vitr = _voters.find( voter.value ); - if ( vitr != _voters.end() ) { - _voters.modify( vitr, same_payer, [&]( auto& vinfo ) { - vinfo.staked += delta_stake; - }); - } - } - } - -}; /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp deleted file mode 100644 index 1aabca02ce..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/rex.results.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -void rex_results::buyresult( const asset& rex_received ) { } - -void rex_results::sellresult( const asset& proceeds ) { } - -void rex_results::orderresult( const name& owner, const asset& proceeds ) { } - -void rex_results::rentresult( const asset& rented_tokens ) { } - -extern "C" void apply( uint64_t, uint64_t, uint64_t ) { } diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp deleted file mode 100644 index 53bb298757..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.system/src/voting.cpp +++ /dev/null @@ -1,415 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace eosiosystem { - - using eosio::const_mem_fun; - using eosio::current_time_point; - using eosio::indexed_by; - using eosio::microseconds; - using eosio::singleton; - - void system_contract::register_producer( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ) { - auto prod = _producers.find( producer.value ); - const auto ct = current_time_point(); - - eosio::public_key producer_key{}; - - std::visit( [&](auto&& auth ) { - if( auth.keys.size() == 1 ) { - // if the producer_authority consists of a single key, use that key in the legacy producer_key field - producer_key = auth.keys[0].key; - } - }, producer_authority ); - - if ( prod != _producers.end() ) { - _producers.modify( prod, producer, [&]( producer_info& info ){ - info.producer_key = producer_key; - info.is_active = true; - info.url = url; - info.location = location; - info.producer_authority.emplace( producer_authority ); - if ( info.last_claim_time == time_point() ) - info.last_claim_time = ct; - }); - - auto prod2 = _producers2.find( producer.value ); - if ( prod2 == _producers2.end() ) { - _producers2.emplace( producer, [&]( producer_info2& info ){ - info.owner = producer; - info.last_votepay_share_update = ct; - }); - update_total_votepay_share( ct, 0.0, prod->total_votes ); - // When introducing the producer2 table row for the first time, the producer's votes must also be accounted for in the global total_producer_votepay_share at the same time. - } - } else { - _producers.emplace( producer, [&]( producer_info& info ){ - info.owner = producer; - info.total_votes = 0; - info.producer_key = producer_key; - info.is_active = true; - info.url = url; - info.location = location; - info.last_claim_time = ct; - info.producer_authority.emplace( producer_authority ); - }); - _producers2.emplace( producer, [&]( producer_info2& info ){ - info.owner = producer; - info.last_votepay_share_update = ct; - }); - } - - } - - void system_contract::regproducer( const name& producer, const eosio::public_key& producer_key, const std::string& url, uint16_t location ) { - require_auth( producer ); - check( url.size() < 512, "url too long" ); - - register_producer( producer, convert_to_block_signing_authority( producer_key ), url, location ); - } - - void system_contract::regproducer2( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location ) { - require_auth( producer ); - check( url.size() < 512, "url too long" ); - - std::visit( [&](auto&& auth ) { - check( auth.is_valid(), "invalid producer authority" ); - }, producer_authority ); - - register_producer( producer, producer_authority, url, location ); - } - - void system_contract::unregprod( const name& producer ) { - require_auth( producer ); - - const auto& prod = _producers.get( producer.value, "producer not found" ); - _producers.modify( prod, same_payer, [&]( producer_info& info ){ - info.deactivate(); - }); - } - - void system_contract::update_elected_producers( const block_timestamp& block_time ) { - _gstate.last_producer_schedule_update = block_time; - - auto idx = _producers.get_index<"prototalvote"_n>(); - - using value_type = std::pair; - std::vector< value_type > top_producers; - top_producers.reserve(21); - - for( auto it = idx.cbegin(); it != idx.cend() && top_producers.size() < 21 && 0 < it->total_votes && it->active(); ++it ) { - top_producers.emplace_back( - eosio::producer_authority{ - .producer_name = it->owner, - .authority = it->get_producer_authority() - }, - it->location - ); - } - - if( top_producers.size() == 0 || top_producers.size() < _gstate.last_producer_schedule_size ) { - return; - } - - std::sort( top_producers.begin(), top_producers.end(), []( const value_type& lhs, const value_type& rhs ) { - return lhs.first.producer_name < rhs.first.producer_name; // sort by producer name - // return lhs.second < rhs.second; // sort by location - } ); - - std::vector producers; - - producers.reserve(top_producers.size()); - for( auto& item : top_producers ) - producers.push_back( std::move(item.first) ); - - if( set_proposed_producers( producers ) >= 0 ) { - _gstate.last_producer_schedule_size = static_cast( top_producers.size() ); - } - } - - double stake2vote( int64_t staked ) { - /// TODO subtract 2080 brings the large numbers closer to this decade - double weight = int64_t( (current_time_point().sec_since_epoch() - (block_timestamp::block_timestamp_epoch / 1000)) / (seconds_per_day * 7) ) / double( 52 ); - return double(staked) * std::pow( 2, weight ); - } - - double system_contract::update_total_votepay_share( const time_point& ct, - double additional_shares_delta, - double shares_rate_delta ) - { - double delta_total_votepay_share = 0.0; - if( ct > _gstate3.last_vpay_state_update ) { - delta_total_votepay_share = _gstate3.total_vpay_share_change_rate - * double( (ct - _gstate3.last_vpay_state_update).count() / 1E6 ); - } - - delta_total_votepay_share += additional_shares_delta; - if( delta_total_votepay_share < 0 && _gstate2.total_producer_votepay_share < -delta_total_votepay_share ) { - _gstate2.total_producer_votepay_share = 0.0; - } else { - _gstate2.total_producer_votepay_share += delta_total_votepay_share; - } - - if( shares_rate_delta < 0 && _gstate3.total_vpay_share_change_rate < -shares_rate_delta ) { - _gstate3.total_vpay_share_change_rate = 0.0; - } else { - _gstate3.total_vpay_share_change_rate += shares_rate_delta; - } - - _gstate3.last_vpay_state_update = ct; - - return _gstate2.total_producer_votepay_share; - } - - double system_contract::update_producer_votepay_share( const producers_table2::const_iterator& prod_itr, - const time_point& ct, - double shares_rate, - bool reset_to_zero ) - { - double delta_votepay_share = 0.0; - if( shares_rate > 0.0 && ct > prod_itr->last_votepay_share_update ) { - delta_votepay_share = shares_rate * double( (ct - prod_itr->last_votepay_share_update).count() / 1E6 ); // cannot be negative - } - - double new_votepay_share = prod_itr->votepay_share + delta_votepay_share; - _producers2.modify( prod_itr, same_payer, [&](auto& p) { - if( reset_to_zero ) - p.votepay_share = 0.0; - else - p.votepay_share = new_votepay_share; - - p.last_votepay_share_update = ct; - } ); - - return new_votepay_share; - } - - void system_contract::voteproducer( const name& voter_name, const name& proxy, const std::vector& producers ) { - require_auth( voter_name ); - vote_stake_updater( voter_name ); - update_votes( voter_name, proxy, producers, true ); - auto rex_itr = _rexbalance.find( voter_name.value ); - if( rex_itr != _rexbalance.end() && rex_itr->rex_balance.amount > 0 ) { - check_voting_requirement( voter_name, "voter holding REX tokens must vote for at least 21 producers or for a proxy" ); - } - } - - void system_contract::update_votes( const name& voter_name, const name& proxy, const std::vector& producers, bool voting ) { - //validate input - if ( proxy ) { - check( producers.size() == 0, "cannot vote for producers and proxy at same time" ); - check( voter_name != proxy, "cannot proxy to self" ); - } else { - check( producers.size() <= 30, "attempt to vote for too many producers" ); - for( size_t i = 1; i < producers.size(); ++i ) { - check( producers[i-1] < producers[i], "producer votes must be unique and sorted" ); - } - } - - auto voter = _voters.find( voter_name.value ); - check( voter != _voters.end(), "user must stake before they can vote" ); /// staking creates voter object - check( !proxy || !voter->is_proxy, "account registered as a proxy is not allowed to use a proxy" ); - - /** - * The first time someone votes we calculate and set last_vote_weight. Since they cannot unstake until - * after the chain has been activated, we can use last_vote_weight to determine that this is - * their first vote and should consider their stake activated. - */ - if( _gstate.thresh_activated_stake_time == time_point() && voter->last_vote_weight <= 0.0 ) { - _gstate.total_activated_stake += voter->staked; - if( _gstate.total_activated_stake >= min_activated_stake ) { - _gstate.thresh_activated_stake_time = current_time_point(); - } - } - - auto new_vote_weight = stake2vote( voter->staked ); - if( voter->is_proxy ) { - new_vote_weight += voter->proxied_vote_weight; - } - - std::map > producer_deltas; - if ( voter->last_vote_weight > 0 ) { - if( voter->proxy ) { - auto old_proxy = _voters.find( voter->proxy.value ); - check( old_proxy != _voters.end(), "old proxy not found" ); //data corruption - _voters.modify( old_proxy, same_payer, [&]( auto& vp ) { - vp.proxied_vote_weight -= voter->last_vote_weight; - }); - propagate_weight_change( *old_proxy ); - } else { - for( const auto& p : voter->producers ) { - auto& d = producer_deltas[p]; - d.first -= voter->last_vote_weight; - d.second = false; - } - } - } - - if( proxy ) { - auto new_proxy = _voters.find( proxy.value ); - check( new_proxy != _voters.end(), "invalid proxy specified" ); //if ( !voting ) { data corruption } else { wrong vote } - check( !voting || new_proxy->is_proxy, "proxy not found" ); - if ( new_vote_weight >= 0 ) { - _voters.modify( new_proxy, same_payer, [&]( auto& vp ) { - vp.proxied_vote_weight += new_vote_weight; - }); - propagate_weight_change( *new_proxy ); - } - } else { - if( new_vote_weight >= 0 ) { - for( const auto& p : producers ) { - auto& d = producer_deltas[p]; - d.first += new_vote_weight; - d.second = true; - } - } - } - - const auto ct = current_time_point(); - double delta_change_rate = 0.0; - double total_inactive_vpay_share = 0.0; - for( const auto& pd : producer_deltas ) { - auto pitr = _producers.find( pd.first.value ); - if( pitr != _producers.end() ) { - if( voting && !pitr->active() && pd.second.second /* from new set */ ) { - check( false, ( "producer " + pitr->owner.to_string() + " is not currently registered" ).data() ); - } - double init_total_votes = pitr->total_votes; - _producers.modify( pitr, same_payer, [&]( auto& p ) { - p.total_votes += pd.second.first; - if ( p.total_votes < 0 ) { // floating point arithmetics can give small negative numbers - p.total_votes = 0; - } - _gstate.total_producer_vote_weight += pd.second.first; - //check( p.total_votes >= 0, "something bad happened" ); - }); - auto prod2 = _producers2.find( pd.first.value ); - if( prod2 != _producers2.end() ) { - const auto last_claim_plus_3days = pitr->last_claim_time + microseconds(3 * useconds_per_day); - bool crossed_threshold = (last_claim_plus_3days <= ct); - bool updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update); - // Note: updated_after_threshold implies cross_threshold - - double new_votepay_share = update_producer_votepay_share( prod2, - ct, - updated_after_threshold ? 0.0 : init_total_votes, - crossed_threshold && !updated_after_threshold // only reset votepay_share once after threshold - ); - - if( !crossed_threshold ) { - delta_change_rate += pd.second.first; - } else if( !updated_after_threshold ) { - total_inactive_vpay_share += new_votepay_share; - delta_change_rate -= init_total_votes; - } - } - } else { - if( pd.second.second ) { - check( false, ( "producer " + pd.first.to_string() + " is not registered" ).data() ); - } - } - } - - update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate ); - - _voters.modify( voter, same_payer, [&]( auto& av ) { - av.last_vote_weight = new_vote_weight; - av.producers = producers; - av.proxy = proxy; - }); - } - - void system_contract::regproxy( const name& proxy, bool isproxy ) { - require_auth( proxy ); - - auto pitr = _voters.find( proxy.value ); - if ( pitr != _voters.end() ) { - check( isproxy != pitr->is_proxy, "action has no effect" ); - check( !isproxy || !pitr->proxy, "account that uses a proxy is not allowed to become a proxy" ); - _voters.modify( pitr, same_payer, [&]( auto& p ) { - p.is_proxy = isproxy; - }); - propagate_weight_change( *pitr ); - } else { - _voters.emplace( proxy, [&]( auto& p ) { - p.owner = proxy; - p.is_proxy = isproxy; - }); - } - } - - void system_contract::propagate_weight_change( const voter_info& voter ) { - check( !voter.proxy || !voter.is_proxy, "account registered as a proxy is not allowed to use a proxy" ); - double new_weight = stake2vote( voter.staked ); - if ( voter.is_proxy ) { - new_weight += voter.proxied_vote_weight; - } - - /// don't propagate small changes (1 ~= epsilon) - if ( fabs( new_weight - voter.last_vote_weight ) > 1 ) { - if ( voter.proxy ) { - auto& proxy = _voters.get( voter.proxy.value, "proxy not found" ); //data corruption - _voters.modify( proxy, same_payer, [&]( auto& p ) { - p.proxied_vote_weight += new_weight - voter.last_vote_weight; - } - ); - propagate_weight_change( proxy ); - } else { - auto delta = new_weight - voter.last_vote_weight; - const auto ct = current_time_point(); - double delta_change_rate = 0; - double total_inactive_vpay_share = 0; - for ( auto acnt : voter.producers ) { - auto& prod = _producers.get( acnt.value, "producer not found" ); //data corruption - const double init_total_votes = prod.total_votes; - _producers.modify( prod, same_payer, [&]( auto& p ) { - p.total_votes += delta; - _gstate.total_producer_vote_weight += delta; - }); - auto prod2 = _producers2.find( acnt.value ); - if ( prod2 != _producers2.end() ) { - const auto last_claim_plus_3days = prod.last_claim_time + microseconds(3 * useconds_per_day); - bool crossed_threshold = (last_claim_plus_3days <= ct); - bool updated_after_threshold = (last_claim_plus_3days <= prod2->last_votepay_share_update); - // Note: updated_after_threshold implies cross_threshold - - double new_votepay_share = update_producer_votepay_share( prod2, - ct, - updated_after_threshold ? 0.0 : init_total_votes, - crossed_threshold && !updated_after_threshold // only reset votepay_share once after threshold - ); - - if( !crossed_threshold ) { - delta_change_rate += delta; - } else if( !updated_after_threshold ) { - total_inactive_vpay_share += new_votepay_share; - delta_change_rate -= init_total_votes; - } - } - } - - update_total_votepay_share( ct, -total_inactive_vpay_share, delta_change_rate ); - } - } - _voters.modify( voter, same_payer, [&]( auto& v ) { - v.last_vote_weight = new_weight; - } - ); - } - -} /// namespace eosiosystem diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt deleted file mode 100644 index cf53f62cd8..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.token/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_contract(eosio.token eosio.token ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.token.cpp) - -target_include_directories(eosio.token - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -set_target_properties(eosio.token - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - -configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.token.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.token.contracts.md @ONLY ) - -target_compile_options( eosio.token PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp deleted file mode 100644 index 56e17d9851..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.token/include/eosio.token/eosio.token.hpp +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace eosiosystem { - class system_contract; -} - -namespace eosio { - - using std::string; - - /** - * The `eosio.token` sample system contract defines the structures and actions that allow users to create, issue, and manage tokens for EOSIO based blockchains. It demonstrates one way to implement a smart contract which allows for creation and management of tokens. It is possible for one to create a similar contract which suits different needs. However, it is recommended that if one only needs a token with the below listed actions, that one uses the `eosio.token` contract instead of developing their own. - * - * The `eosio.token` contract class also implements two useful public static methods: `get_supply` and `get_balance`. The first allows one to check the total supply of a specified token, created by an account and the second allows one to check the balance of a token for a specified account (the token creator account has to be specified as well). - * - * The `eosio.token` contract manages the set of tokens, accounts and their corresponding balances, by using two internal multi-index structures: the `accounts` and `stats`. The `accounts` multi-index table holds, for each row, instances of `account` object and the `account` object holds information about the balance of one token. The `accounts` table is scoped to an EOSIO account, and it keeps the rows indexed based on the token's symbol. This means that when one queries the `accounts` multi-index table for an account name the result is all the tokens that account holds at the moment. - * - * Similarly, the `stats` multi-index table, holds instances of `currency_stats` objects for each row, which contains information about current supply, maximum supply, and the creator account for a symbol token. The `stats` table is scoped to the token symbol. Therefore, when one queries the `stats` table for a token symbol the result is one single entry/row corresponding to the queried symbol token if it was previously created, or nothing, otherwise. - */ - class [[eosio::contract("eosio.token")]] token : public contract { - public: - using contract::contract; - - /** - * Allows `issuer` account to create a token in supply of `maximum_supply`. If validation is successful a new entry in statstable for token symbol scope gets created. - * - * @param issuer - the account that creates the token, - * @param maximum_supply - the maximum supply set for the token created. - * - * @pre Token symbol has to be valid, - * @pre Token symbol must not be already created, - * @pre maximum_supply has to be smaller than the maximum supply allowed by the system: 1^62 - 1. - * @pre Maximum supply must be positive; - */ - [[eosio::action]] - void create( const name& issuer, - const asset& maximum_supply); - /** - * This action issues to `to` account a `quantity` of tokens. - * - * @param to - the account to issue tokens to, it must be the same as the issuer, - * @param quntity - the amount of tokens to be issued, - * @memo - the memo string that accompanies the token issue transaction. - */ - [[eosio::action]] - void issue( const name& to, const asset& quantity, const string& memo ); - - /** - * The opposite for create action, if all validations succeed, - * it debits the statstable.supply amount. - * - * @param quantity - the quantity of tokens to retire, - * @param memo - the memo string to accompany the transaction. - */ - [[eosio::action]] - void retire( const asset& quantity, const string& memo ); - - /** - * Allows `from` account to transfer to `to` account the `quantity` tokens. - * One account is debited and the other is credited with quantity tokens. - * - * @param from - the account to transfer from, - * @param to - the account to be transferred to, - * @param quantity - the quantity of tokens to be transferred, - * @param memo - the memo string to accompany the transaction. - */ - [[eosio::action]] - void transfer( const name& from, - const name& to, - const asset& quantity, - const string& memo ); - /** - * Allows `ram_payer` to create an account `owner` with zero balance for - * token `symbol` at the expense of `ram_payer`. - * - * @param owner - the account to be created, - * @param symbol - the token to be payed with by `ram_payer`, - * @param ram_payer - the account that supports the cost of this action. - * - * More information can be read [here](https://github.com/EOSIO/eosio.contracts/issues/62) - * and [here](https://github.com/EOSIO/eosio.contracts/issues/61). - */ - [[eosio::action]] - void open( const name& owner, const symbol& symbol, const name& ram_payer ); - - /** - * This action is the opposite for open, it closes the account `owner` - * for token `symbol`. - * - * @param owner - the owner account to execute the close action for, - * @param symbol - the symbol of the token to execute the close action for. - * - * @pre The pair of owner plus symbol has to exist otherwise no action is executed, - * @pre If the pair of owner plus symbol exists, the balance has to be zero. - */ - [[eosio::action]] - void close( const name& owner, const symbol& symbol ); - - static asset get_supply( const name& token_contract_account, const symbol_code& sym_code ) - { - stats statstable( token_contract_account, sym_code.raw() ); - const auto& st = statstable.get( sym_code.raw() ); - return st.supply; - } - - static asset get_balance( const name& token_contract_account, const name& owner, const symbol_code& sym_code ) - { - accounts accountstable( token_contract_account, owner.value ); - const auto& ac = accountstable.get( sym_code.raw() ); - return ac.balance; - } - - using create_action = eosio::action_wrapper<"create"_n, &token::create>; - using issue_action = eosio::action_wrapper<"issue"_n, &token::issue>; - using retire_action = eosio::action_wrapper<"retire"_n, &token::retire>; - using transfer_action = eosio::action_wrapper<"transfer"_n, &token::transfer>; - using open_action = eosio::action_wrapper<"open"_n, &token::open>; - using close_action = eosio::action_wrapper<"close"_n, &token::close>; - private: - struct [[eosio::table]] account { - asset balance; - - uint64_t primary_key()const { return balance.symbol.code().raw(); } - }; - - struct [[eosio::table]] currency_stats { - asset supply; - asset max_supply; - name issuer; - - uint64_t primary_key()const { return supply.symbol.code().raw(); } - }; - - typedef eosio::multi_index< "accounts"_n, account > accounts; - typedef eosio::multi_index< "stat"_n, currency_stats > stats; - - void sub_balance( const name& owner, const asset& value ); - void add_balance( const name& owner, const asset& value, const name& ram_payer ); - }; - -} diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in deleted file mode 100644 index f050eec773..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.token/ricardian/eosio.token.contracts.md.in +++ /dev/null @@ -1,95 +0,0 @@ -

close

- ---- -spec_version: "0.2.0" -title: Close Token Balance -summary: 'Close {{nowrap owner}}’s zero quantity balance' -icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ ---- - -{{owner}} agrees to close their zero quantity balance for the {{symbol_to_symbol_code symbol}} token. - -RAM will be refunded to the RAM payer of the {{symbol_to_symbol_code symbol}} token balance for {{owner}}. - -

create

- ---- -spec_version: "0.2.0" -title: Create New Token -summary: 'Create a new token' -icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ ---- - -{{$action.account}} agrees to create a new token with symbol {{asset_to_symbol_code maximum_supply}} to be managed by {{issuer}}. - -This action will not result any any tokens being issued into circulation. - -{{issuer}} will be allowed to issue tokens into circulation, up to a maximum supply of {{maximum_supply}}. - -RAM will deducted from {{$action.account}}’s resources to create the necessary records. - -

issue

- ---- -spec_version: "0.2.0" -title: Issue Tokens into Circulation -summary: 'Issue {{nowrap quantity}} into circulation and transfer into {{nowrap to}}’s account' -icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ ---- - -The token manager agrees to issue {{quantity}} into circulation, and transfer it into {{to}}’s account. - -{{#if memo}}There is a memo attached to the transfer stating: -{{memo}} -{{/if}} - -If {{to}} does not have a balance for {{asset_to_symbol_code quantity}}, or the token manager does not have a balance for {{asset_to_symbol_code quantity}}, the token manager will be designated as the RAM payer of the {{asset_to_symbol_code quantity}} token balance for {{to}}. As a result, RAM will be deducted from the token manager’s resources to create the necessary records. - -This action does not allow the total quantity to exceed the max allowed supply of the token. - -

open

- ---- -spec_version: "0.2.0" -title: Open Token Balance -summary: 'Open a zero quantity balance for {{nowrap owner}}' -icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ ---- - -{{ram_payer}} agrees to establish a zero quantity balance for {{owner}} for the {{symbol_to_symbol_code symbol}} token. - -If {{owner}} does not have a balance for {{symbol_to_symbol_code symbol}}, {{ram_payer}} will be designated as the RAM payer of the {{symbol_to_symbol_code symbol}} token balance for {{owner}}. As a result, RAM will be deducted from {{ram_payer}}’s resources to create the necessary records. - -

retire

- ---- -spec_version: "0.2.0" -title: Remove Tokens from Circulation -summary: 'Remove {{nowrap quantity}} from circulation' -icon: @ICON_BASE_URL@/@TOKEN_ICON_URI@ ---- - -The token manager agrees to remove {{quantity}} from circulation, taken from their own account. - -{{#if memo}} There is a memo attached to the action stating: -{{memo}} -{{/if}} - -

transfer

- ---- -spec_version: "0.2.0" -title: Transfer Tokens -summary: 'Send {{nowrap quantity}} from {{nowrap from}} to {{nowrap to}}' -icon: @ICON_BASE_URL@/@TRANSFER_ICON_URI@ ---- - -{{from}} agrees to send {{quantity}} to {{to}}. - -{{#if memo}}There is a memo attached to the transfer stating: -{{memo}} -{{/if}} - -If {{from}} is not already the RAM payer of their {{asset_to_symbol_code quantity}} token balance, {{from}} will be designated as such. As a result, RAM will be deducted from {{from}}’s resources to refund the original RAM payer. - -If {{to}} does not have a balance for {{asset_to_symbol_code quantity}}, {{from}} will be designated as the RAM payer of the {{asset_to_symbol_code quantity}} token balance for {{to}}. As a result, RAM will be deducted from {{from}}’s resources to create the necessary records. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp deleted file mode 100644 index 8dda907e3d..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.token/src/eosio.token.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include - -namespace eosio { - -void token::create( const name& issuer, - const asset& maximum_supply ) -{ - require_auth( get_self() ); - - auto sym = maximum_supply.symbol; - check( sym.is_valid(), "invalid symbol name" ); - check( maximum_supply.is_valid(), "invalid supply"); - check( maximum_supply.amount > 0, "max-supply must be positive"); - - stats statstable( get_self(), sym.code().raw() ); - auto existing = statstable.find( sym.code().raw() ); - check( existing == statstable.end(), "token with symbol already exists" ); - - statstable.emplace( get_self(), [&]( auto& s ) { - s.supply.symbol = maximum_supply.symbol; - s.max_supply = maximum_supply; - s.issuer = issuer; - }); -} - - -void token::issue( const name& to, const asset& quantity, const string& memo ) -{ - auto sym = quantity.symbol; - check( sym.is_valid(), "invalid symbol name" ); - check( memo.size() <= 256, "memo has more than 256 bytes" ); - - stats statstable( get_self(), sym.code().raw() ); - auto existing = statstable.find( sym.code().raw() ); - check( existing != statstable.end(), "token with symbol does not exist, create token before issue" ); - const auto& st = *existing; - check( to == st.issuer, "tokens can only be issued to issuer account" ); - - require_auth( st.issuer ); - check( quantity.is_valid(), "invalid quantity" ); - check( quantity.amount > 0, "must issue positive quantity" ); - - check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); - check( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply"); - - statstable.modify( st, same_payer, [&]( auto& s ) { - s.supply += quantity; - }); - - add_balance( st.issuer, quantity, st.issuer ); -} - -void token::retire( const asset& quantity, const string& memo ) -{ - auto sym = quantity.symbol; - check( sym.is_valid(), "invalid symbol name" ); - check( memo.size() <= 256, "memo has more than 256 bytes" ); - - stats statstable( get_self(), sym.code().raw() ); - auto existing = statstable.find( sym.code().raw() ); - check( existing != statstable.end(), "token with symbol does not exist" ); - const auto& st = *existing; - - require_auth( st.issuer ); - check( quantity.is_valid(), "invalid quantity" ); - check( quantity.amount > 0, "must retire positive quantity" ); - - check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); - - statstable.modify( st, same_payer, [&]( auto& s ) { - s.supply -= quantity; - }); - - sub_balance( st.issuer, quantity ); -} - -void token::transfer( const name& from, - const name& to, - const asset& quantity, - const string& memo ) -{ - check( from != to, "cannot transfer to self" ); - require_auth( from ); - check( is_account( to ), "to account does not exist"); - auto sym = quantity.symbol.code(); - stats statstable( get_self(), sym.raw() ); - const auto& st = statstable.get( sym.raw() ); - - require_recipient( from ); - require_recipient( to ); - - check( quantity.is_valid(), "invalid quantity" ); - check( quantity.amount > 0, "must transfer positive quantity" ); - check( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); - check( memo.size() <= 256, "memo has more than 256 bytes" ); - - auto payer = has_auth( to ) ? to : from; - - sub_balance( from, quantity ); - add_balance( to, quantity, payer ); -} - -void token::sub_balance( const name& owner, const asset& value ) { - accounts from_acnts( get_self(), owner.value ); - - const auto& from = from_acnts.get( value.symbol.code().raw(), "no balance object found" ); - check( from.balance.amount >= value.amount, "overdrawn balance" ); - - from_acnts.modify( from, owner, [&]( auto& a ) { - a.balance -= value; - }); -} - -void token::add_balance( const name& owner, const asset& value, const name& ram_payer ) -{ - accounts to_acnts( get_self(), owner.value ); - auto to = to_acnts.find( value.symbol.code().raw() ); - if( to == to_acnts.end() ) { - to_acnts.emplace( ram_payer, [&]( auto& a ){ - a.balance = value; - }); - } else { - to_acnts.modify( to, same_payer, [&]( auto& a ) { - a.balance += value; - }); - } -} - -void token::open( const name& owner, const symbol& symbol, const name& ram_payer ) -{ - require_auth( ram_payer ); - - check( is_account( owner ), "owner account does not exist" ); - - auto sym_code_raw = symbol.code().raw(); - stats statstable( get_self(), sym_code_raw ); - const auto& st = statstable.get( sym_code_raw, "symbol does not exist" ); - check( st.supply.symbol == symbol, "symbol precision mismatch" ); - - accounts acnts( get_self(), owner.value ); - auto it = acnts.find( sym_code_raw ); - if( it == acnts.end() ) { - acnts.emplace( ram_payer, [&]( auto& a ){ - a.balance = asset{0, symbol}; - }); - } -} - -void token::close( const name& owner, const symbol& symbol ) -{ - require_auth( owner ); - accounts acnts( get_self(), owner.value ); - auto it = acnts.find( symbol.code().raw() ); - check( it != acnts.end(), "Balance row already deleted or never existed. Action won't have any effect." ); - check( it->balance.amount == 0, "Cannot close because the balance is not zero." ); - acnts.erase( it ); -} - -} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt deleted file mode 100644 index 27d4291947..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -add_contract(eosio.wrap eosio.wrap ${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.wrap.cpp) - -target_include_directories(eosio.wrap - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) - -set_target_properties(eosio.wrap - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - -configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/ricardian/eosio.wrap.contracts.md.in ${CMAKE_CURRENT_BINARY_DIR}/ricardian/eosio.wrap.contracts.md @ONLY ) - -target_compile_options( eosio.wrap PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR}/ricardian -R${CMAKE_CURRENT_BINARY_DIR}/ricardian ) diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp deleted file mode 100644 index 9c6134df8e..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/include/eosio.wrap/eosio.wrap.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace eosio { - /** - * The `eosio.wrap` system contract allows block producers to bypass authorization checks or run privileged actions with 15/21 producer approval and thus simplifies block producers superuser actions. It also makes these actions easier to audit. - * - * It does not give block producers any additional powers or privileges that do not already exist within the EOSIO based blockchains. As it is implemented, in an EOSIO based blockchain, 15/21 block producers can change an account's permissions or modify an account's contract code if they decided it is beneficial for the blockchain and community. However, the current method is opaque and leaves undesirable side effects on specific system accounts, and thus the `eosio.wrap `contract solves this matter by providing an easier method of executing important governance actions. - * - * The only action implemented by the `eosio.wrap` system contract is the `exec` action. This action allows for execution of a transaction, which is passed to the `exec` method in the form of a packed transaction in json format via the 'trx' parameter and the `executer` account that executes the transaction. The same `executer` account will also be used to pay the RAM and CPU fees needed to execute the transaction. - */ - class [[eosio::contract("eosio.wrap")]] wrap : public contract { - public: - using contract::contract; - - /** - * Execute action. - * - * Execute a transaction while bypassing regular authorization checks. - * - * Preconditions: - * - Requires authorization of eosio.wrap which needs to be a privileged account. - * - * Postconditions: - * - Deferred transaction RAM usage is billed to 'executer' - * - * @param executer - account executing the transaction, - * @param trx - the transaction to be executed. - */ - [[eosio::action]] - void exec( ignore executer, ignore trx ); - - using exec_action = eosio::action_wrapper<"exec"_n, &wrap::exec>; - }; -} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in deleted file mode 100644 index 8077a34729..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/ricardian/eosio.wrap.contracts.md.in +++ /dev/null @@ -1,13 +0,0 @@ -

exec

- ---- -spec_version: "0.2.0" -title: Privileged Execute -summary: '{{nowrap executer}} executes a transaction while bypassing authority checks' -icon: @ICON_BASE_URL@/@ADMIN_ICON_URI@ ---- - -{{executer}} executes the following transaction while bypassing authority checks: -{{to_json trx}} - -{{$action.account}} must also authorize this action. diff --git a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp b/tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp deleted file mode 100644 index 12056bf1e0..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/eosio.wrap/src/eosio.wrap.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include - -namespace eosio { - -void wrap::exec( ignore, ignore ) { - require_auth( get_self() ); - - name executer; - _ds >> executer; - - require_auth( executer ); - - send_deferred( (uint128_t(executer.value) << 64) | (uint64_t)current_time_point().time_since_epoch().count(), executer, _ds.pos(), _ds.remaining() ); -} - -} /// namespace eosio diff --git a/tests/unit/test_contracts/eosio.contracts/icons/account.png b/tests/unit/test_contracts/eosio.contracts/icons/account.png deleted file mode 100644 index 7686587824ac663ffb79f3ee30d06821a26e6c26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3795 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 JPDHLkV1nnf{$~IH diff --git a/tests/unit/test_contracts/eosio.contracts/icons/account.svg b/tests/unit/test_contracts/eosio.contracts/icons/account.svg deleted file mode 100644 index 511f064991..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/account.svg +++ /dev/null @@ -1 +0,0 @@ -Account \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/admin.png b/tests/unit/test_contracts/eosio.contracts/icons/admin.png deleted file mode 100644 index ae95364725ed2e9f6143044631d2ff136201fa2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2937 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 V=0Y3_AOE>PN6S$2rMg|je*nyqJZk^| diff --git a/tests/unit/test_contracts/eosio.contracts/icons/admin.svg b/tests/unit/test_contracts/eosio.contracts/icons/admin.svg deleted file mode 100644 index fbc057179e..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/admin.svg +++ /dev/null @@ -1 +0,0 @@ -Admin \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/multisig.png b/tests/unit/test_contracts/eosio.contracts/icons/multisig.png deleted file mode 100644 index 00fa7564c88ea2785f53d641f150b32ec68a7b47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1411 zcmZ`%doqG{65Om;k{$VjOf%}jL|q`{EKZ_{CyDS4zk z(jazeJThn}$`&WHgx7r|KPRBU)0;w2-ok=^j-Q{e~v!@bbksTMV)7UQT}8uv~^<) zI$fbW_lQS}W+oVL*;zwR1mJXycr06F{D&lRfmxK^r3bp`3GuFvkN6LW)?nAOQ93fq z{NKv|vE^}24I13&Wh`o#ToS$=*UB6&S+wz!O>_dYbMCipI)V7ntxsjw*ty)zAVSf@ z&_Dim2n9$=tBf-Io~;tQ+n($H@(}~A^u%D4Pc8_u^V;B0OaS5lReOLObZmOx;;G}#;8*|+;c2be4I3`gN zhA*dU-77eHcf@h4FR%G z^aZ0lMorCFr^CEtC{Ygw9Xv9H7uht5$`Kn7A&0(ix?>tq_wz@c;Czv+$H0B<@fC5_ z9^O&Zp-ZogJ!__ydf~*kd1zwdI~!h10hy_Vp5G`5#au9wFm3rbh33#LfS1xF(Rp3d zJI;SCx#J!;ZMoY>*CG>nzF~t|Qfl^Un0IA!!>OAI5=x`k}(g&Oz9Nt*cBh=0VG`vkBgj_>ji16&N=NetoxA@plV?sUDV0z&_Nds zzHBn;p*55~yBEcF0$Ep$~xv06jiA|H79p$LBE)8|Qx+v=u(O)N^t zRtH#rkk{syuG>An9-Go<{SB_$5xc_H^jz1y*RJg1-A7NY4@lW*x7xn&+;8m~?QvN>T-E3}C(Y{}`$>K-Ikm~!K&AM#mwr|S`X<*0X7g}z{(qH(X( z31&CWc{@Zxd}+R_Q0^=S3M|f29sr(*nyMR5^)AyiJZyIQWmk5p1~sg^zjorepylGp zgj$P1Xqrvaelf)=g)FDMUFW5j)@VI^UT~$UKB*WO%k%fCyfRP-|G?X8V*)#&nfEj6 YBtQ`z6CivXCH{CNh3rMDa-pC88^Gy0WB>pF diff --git a/tests/unit/test_contracts/eosio.contracts/icons/multisig.svg b/tests/unit/test_contracts/eosio.contracts/icons/multisig.svg deleted file mode 100644 index d9167fb2cb..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/multisig.svg +++ /dev/null @@ -1 +0,0 @@ -Multi Sig \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/resource.png b/tests/unit/test_contracts/eosio.contracts/icons/resource.png deleted file mode 100644 index 8e1bd127de7b7adb74c5d5765db70a036fd0aa8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1913 zcmZ`)X;72r7Jk3v3rUbAhzdcFutg}ftU*A)ghh$S9s{zJkjPRZ5D~&=hYv_)5n2{6 z?uyE-vKAEx2$cwEr6L6t1QZDr1=%H(2(sKvXYSnktNTnXHn@iK#cQN17U1Bia1wiuy-wzn?2E zO-ntqxw)ymt=dhtt`hI#;VVnOFkn!9fenCME8UsKNE(_M9=v~(qySh)w|l>qrVZJg zWJY=qo{cPeD#^dHs%8Ln%;nhGqDbIxAel~zjS$&rI~(y%4F0VbW!k&0TgYU^kI^7QI zo*6`4>LJIf#+h74bp(8*sJsY1!NVw#taO-eetMQ@tT+L-otSu);kn-hVIP10>p&D- z3f(ul5tG}Q_^k1w;Sgj_HPvB`hA@^T9_6g|-^@hd`i`iKiI3SI`ntBxSRW99d3O=q zP5Y*IHc25^-aZHs&RbzU_E+!a%=;B!6cz$hjRN~GWCf%jDS&qU;SLoZ8BL{^S$aMJ zvG_a2q=_yQ)9i6ctQ@I7HuI;9O=%r|ui>TGwPKO6hz4YLz4g#>Mbg>nB%;Krc;k zR%ztmV@_fbb&zA*#|GXLy9WX(st#$xl9iKl-;X;oRU=Wg!l=im;~e%!TV7u)2*HSA zQSEcc+%3+V}|AVxANkq;~ra21-~uijQef``8a|4 z&-&>(->x)nJ4`Nvz8<3-<7@ax=_uSgo-XnV9yzzx$BAQd2|&#x=RFpP%){dFU4g z11agtk9rcj@~uLuaHTHZXKVOT%3c;JGhib60d6~$g|)86Bbc56s5^jyR-C4nn8R{BxO?@e^c|@teZ#tpFnYf3RS&Rs##B z%BcGvOhC!--W#CfiktR-Pu<@K&lU2b6nVw2ED@3mPG;xqYtw{874!0{fhjgw&M^!U zj%EKc(i?r){I2GM-J;jr%Y{%&>tmvt=)AdZyc2S#U6Ez`1{~7EinU@8T0d-9ZGU%D zSUt-X8aJs$H=l|QrIbPobD(7Q7nS=&`YmVoJcrNkYaiBSJ4N+EnJG78OmCF zZzz@pBbvzmLMdv80n$wSGfzd7Z0|j}&X8gk__t4`IjLr_pJ*%1xf7|&sZRZ1eAKgfsSDGj%m`;!J#Cf65piZ5<5Rj8_2@Y8)GI^_-~#s_AB12g`?0(nO|i|= z6Ud<^(@2>m-riQ(?mte8aom~M;W`cD5rF##;vmb`SZ9cY|AMnmyAqUY7IRlA zQBl*e^=#?A5N5W3!wvOd3raS|O&oszyqWP%t4 lQzThj4saQu7QLp*0xy<3x9!Ue2$20bKzH$Qu5n~#{0CM4EzSS{ diff --git a/tests/unit/test_contracts/eosio.contracts/icons/resource.svg b/tests/unit/test_contracts/eosio.contracts/icons/resource.svg deleted file mode 100644 index 4a47559285..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/resource.svg +++ /dev/null @@ -1 +0,0 @@ -Resource \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/rex.png b/tests/unit/test_contracts/eosio.contracts/icons/rex.png deleted file mode 100644 index b43ee27fcf6180649c35b0739a74e37bc34d08ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2770 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 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/rex.svg b/tests/unit/test_contracts/eosio.contracts/icons/rex.svg deleted file mode 100644 index 99f77f3731..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/rex.svg +++ /dev/null @@ -1 +0,0 @@ -Rex \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/token.png b/tests/unit/test_contracts/eosio.contracts/icons/token.png deleted file mode 100644 index 445188a0247749231e2de8832ac651a32a9a6c88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4268 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 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/token.svg b/tests/unit/test_contracts/eosio.contracts/icons/token.svg deleted file mode 100644 index 67ff415bad..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/token.svg +++ /dev/null @@ -1 +0,0 @@ -Token_1 \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/transfer.png b/tests/unit/test_contracts/eosio.contracts/icons/transfer.png deleted file mode 100644 index d9c021957516ff0e1873a54feb4f40f547c62bed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3856 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 C9O3i; diff --git a/tests/unit/test_contracts/eosio.contracts/icons/transfer.svg b/tests/unit/test_contracts/eosio.contracts/icons/transfer.svg deleted file mode 100644 index 06da6325c5..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/transfer.svg +++ /dev/null @@ -1 +0,0 @@ -Transfer_1 \ No newline at end of file diff --git a/tests/unit/test_contracts/eosio.contracts/icons/voting.png b/tests/unit/test_contracts/eosio.contracts/icons/voting.png deleted file mode 100644 index 0356bddaf2b0f61f82f8dbd7cfad977e0757b187..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3238 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 Z0VhTSOesfxtUm={pRKdagFTE({{svY!SDb8 diff --git a/tests/unit/test_contracts/eosio.contracts/icons/voting.svg b/tests/unit/test_contracts/eosio.contracts/icons/voting.svg deleted file mode 100644 index fac8708984..0000000000 --- a/tests/unit/test_contracts/eosio.contracts/icons/voting.svg +++ /dev/null @@ -1 +0,0 @@ -Voting_1 \ No newline at end of file diff --git a/tests/unit/test_contracts/minimal_tests_with_name.cpp b/tests/unit/test_contracts/minimal_tests_with_name.cpp deleted file mode 100644 index 81ac6608aa..0000000000 --- a/tests/unit/test_contracts/minimal_tests_with_name.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Verifies that the dispatching code is self-contained -#include - -class [[eosio::contract]] minimal_tests_with_name { - public: - template - explicit constexpr minimal_tests_with_name(const N&, const N&, const DS&) {} - [[eosio::action]] void test1(eosio::name memo) {} -}; diff --git a/tests/unit/test_contracts/minimal_tests_with_string.cpp b/tests/unit/test_contracts/minimal_tests_with_string.cpp deleted file mode 100644 index f70912f64b..0000000000 --- a/tests/unit/test_contracts/minimal_tests_with_string.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Verifies that the dispatching code is self-contained -#include - -class [[eosio::contract]] minimal_tests_with_string { - public: - template - explicit constexpr minimal_tests_with_string(const N&, const N&, const DS&) {} - [[eosio::action]] void test1(std::string memo) {} -};