diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dd2031df6..dedf14021e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,10 +63,12 @@ if(APPLE AND UNIX AND "${OPENSSL_ROOT_DIR}" STREQUAL "") set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl@1.1") endif() +option(ENABLE_OC "Enable eosvm-oc on supported platforms" ON) + # WASM runtimes to enable. Each runtime in this list will have: # * definition EOSIO__RUNTIME_ENABLED defined in public libchain interface # * ctest entries with --runtime -if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32) +if(ENABLE_OC AND CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") list(APPEND EOSIO_WASM_RUNTIMES eos-vm-oc) # EOS VM OC requires LLVM, but move the check up here to a central location so that the EosioTester.cmakes diff --git a/README.md b/README.md index 8caec37472..4e41b3da54 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ We support the following CMake options: -DCMAKE_BUILD_TYPE=DEBUG Debug builds -DDISABLE_WASM_SPEC_TESTS=yes Speed up builds and skip many tests -DCMAKE_INSTALL_PREFIX=/foo/bar Where to install to +-DENABLE_OC=no Disable OC support; useful when this repo is used + as a library -GNinja Use ninja instead of make (faster on high-core-count machines) ``` diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index dec2db6a03..5bb2ca1f31 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3362,6 +3362,39 @@ fc::optional controller::extract_chain_id_from_db( const path& st return {}; } +void controller::replace_producer_keys( const public_key_type& key ) { + ilog("Replace producer keys with ${k}", ("k", key)); + mutable_db().modify( db().get(), [&]( auto& gp ) { + gp.proposed_schedule_block_num = {}; + gp.proposed_schedule.version = 0; + gp.proposed_schedule.producers.clear(); + }); + auto version = my->head->pending_schedule.schedule.version; + my->head->pending_schedule = {}; + my->head->pending_schedule.schedule.version = version; + for (auto& prod: my->head->active_schedule.producers ) { + ilog("${n}", ("n", prod.producer_name)); + prod.authority.visit([&](auto &auth) { + auth.threshold = 1; + auth.keys = {key_weight{key, 1}}; + }); + } +} + +void controller::replace_account_keys( name account, name permission, const public_key_type& key ) { + auto& rlm = get_mutable_resource_limits_manager(); + auto* perm = db().find(boost::make_tuple(account, permission)); + if (!perm) + return; + int64_t old_size = (int64_t)(chain::config::billable_size_v + perm->auth.get_billable_size()); + mutable_db().modify(*perm, [&](auto& p) { + p.auth = authority(key); + }); + int64_t new_size = (int64_t)(chain::config::billable_size_v + perm->auth.get_billable_size()); + rlm.add_pending_ram_usage(account, new_size - old_size); + rlm.verify_account_ram_usage(account); +} + /// Protocol feature activation handlers: template<> diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 2a742609c0..33d065aad2 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -354,6 +354,9 @@ namespace eosio { namespace chain { static fc::optional extract_chain_id_from_db( const path& state_dir ); + void replace_producer_keys( const public_key_type& key ); + void replace_account_keys( name account, name permission, const public_key_type& key ); + private: friend class apply_context; friend class transaction_context; diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 7d656eaaa3..153f3ad78f 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -11,6 +11,7 @@ namespace eosio { namespace chain { transaction_checktime_timer() = delete; transaction_checktime_timer(const transaction_checktime_timer&) = delete; transaction_checktime_timer(transaction_checktime_timer&&) = default; + transaction_checktime_timer(platform_timer& timer); ~transaction_checktime_timer(); void start(fc::time_point tp); @@ -25,7 +26,6 @@ namespace eosio { namespace chain { private: platform_timer& _timer; - transaction_checktime_timer(platform_timer& timer); friend controller_impl; }; diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index d0fb738db5..85a959a70b 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "Runtime/Linker.h" #include "Runtime/Runtime.h" @@ -62,6 +63,10 @@ namespace eosio { namespace chain { //Immediately exits currently running wasm. UB is called when no wasm running void exit(); + // If substitute_apply is set, then apply calls it before doing anything else. If substitute_apply returns true, + // then apply returns immediately. + std::function substitute_apply; private: unique_ptr my; }; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 7a35ec0ca8..49d5e26d12 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -79,6 +79,8 @@ namespace eosio { namespace chain { } void wasm_interface::apply( const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version, apply_context& context ) { + if(substitute_apply && substitute_apply(code_hash, vm_type, vm_version, context)) + return; #ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED if(my->eosvmoc) { const chain::eosvmoc::code_descriptor* cd = nullptr; diff --git a/unittests/chain_tests.cpp b/unittests/chain_tests.cpp new file mode 100644 index 0000000000..f68f70c59a --- /dev/null +++ b/unittests/chain_tests.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + +#include "fork_test_utilities.hpp" + +using namespace eosio; +using namespace eosio::chain; +using namespace eosio::testing; + +BOOST_AUTO_TEST_SUITE(chain_tests) + +BOOST_AUTO_TEST_CASE( replace_producer_keys ) try { + validating_tester tester; + + const auto head_ptr = tester.control->head_block_state(); + BOOST_REQUIRE(head_ptr); + + const auto new_key = get_public_key(name("newkey"), config::active_name.to_string()); + + // make sure new keys is not used + for(const auto& prod : head_ptr->active_schedule.producers) { + for(const auto& key : prod.authority.get().keys){ + BOOST_REQUIRE(key.key != new_key); + } + } + + const auto old_version = head_ptr->pending_schedule.schedule.version; + BOOST_REQUIRE_NO_THROW(tester.control->replace_producer_keys(new_key)); + const auto new_version = head_ptr->pending_schedule.schedule.version; + // make sure version not been changed + BOOST_REQUIRE(old_version == new_version); + + const auto& gpo = tester.control->db().get(); + BOOST_REQUIRE(!gpo.proposed_schedule_block_num); + BOOST_REQUIRE(gpo.proposed_schedule.version == 0); + BOOST_REQUIRE(gpo.proposed_schedule.producers.empty()); + + const uint32_t expected_threshold = 1; + const weight_type expected_key_weight = 1; + for(const auto& prod : head_ptr->active_schedule.producers) { + BOOST_REQUIRE_EQUAL(prod.authority.get().threshold, expected_threshold); + for(const auto& key : prod.authority.get().keys){ + BOOST_REQUIRE_EQUAL(key.key, new_key); + BOOST_REQUIRE_EQUAL(key.weight, expected_key_weight); + } + } +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_CASE( replace_account_keys ) try { + validating_tester tester; + const name usr = config::system_account_name; + const name active_permission = config::active_name; + const auto& rlm = tester.control->get_resource_limits_manager(); + const auto* perm = tester.control->db().find(boost::make_tuple(usr, active_permission)); + BOOST_REQUIRE(perm != NULL); + + const int64_t old_size = (int64_t)(chain::config::billable_size_v + perm->auth.get_billable_size()); + const auto old_usr_auth = perm->auth; + const auto new_key = get_public_key(name("newkey"), "active"); + const authority expected_authority(new_key); + BOOST_REQUIRE(old_usr_auth != expected_authority); + const auto old_ram_usg = rlm.get_account_ram_usage(usr); + + BOOST_REQUIRE_NO_THROW(tester.control->replace_account_keys(usr, active_permission, new_key)); + const int64_t new_size = (int64_t)(chain::config::billable_size_v + perm->auth.get_billable_size()); + const auto new_ram_usg = rlm.get_account_ram_usage(usr); + BOOST_REQUIRE_EQUAL(old_ram_usg + (new_size - old_size), new_ram_usg); + const auto new_usr_auth = perm->auth; + BOOST_REQUIRE(new_usr_auth == expected_authority); + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END()