diff --git a/libraries/chain/include/eosio/chain/wasm_interface.hpp b/libraries/chain/include/eosio/chain/wasm_interface.hpp index 85a959a70b..51f91c9ff1 100644 --- a/libraries/chain/include/eosio/chain/wasm_interface.hpp +++ b/libraries/chain/include/eosio/chain/wasm_interface.hpp @@ -63,6 +63,9 @@ namespace eosio { namespace chain { //Immediately exits currently running wasm. UB is called when no wasm running void exit(); + //Returns true if the code is cached + bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const; + // If substitute_apply is set, then apply calls it before doing anything else. If substitute_apply returns true, // then apply returns immediately. std::function parse_initial_memory(const Module& module) { std::vector mem_image; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 49d5e26d12..5b9f5c48b9 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -108,6 +108,10 @@ namespace eosio { namespace chain { my->runtime_interface->immediately_exit_currently_running_module(); } + bool wasm_interface::is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const { + return my->is_code_cached(code_hash, vm_type, vm_version); + } + wasm_instantiated_module_interface::~wasm_instantiated_module_interface() {} wasm_runtime_interface::~wasm_runtime_interface() {} diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index fa588124e0..3d3b4e9dbe 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -307,6 +307,8 @@ namespace eosio { namespace testing { void set_code( account_name name, const vector wasm, const private_key_type* signer = nullptr ); void set_abi( account_name name, const char* abi_json, const private_key_type* signer = nullptr ); + bool is_code_cached( account_name name ) const; + bool chain_has_transaction( const transaction_id_type& txid ) const; const transaction_receipt& get_transaction_receipt( const transaction_id_type& txid ) const; diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 8befa1e5ad..c04ea7ccd4 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -950,6 +950,13 @@ namespace eosio { namespace testing { push_transaction( trx ); } + bool base_tester::is_code_cached( eosio::chain::account_name name ) const { + const auto& db = control->db(); + const account_metadata_object* receiver_account = &db.template get( name ); + if ( receiver_account->code_hash == digest_type() ) return false; + return control->get_wasm_interface().is_code_cached( receiver_account->code_hash, receiver_account->vm_type, receiver_account->vm_version ); + } + bool base_tester::chain_has_transaction( const transaction_id_type& txid ) const { return chain_transactions.count(txid) != 0; diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 9e370306ea..127bb127ab 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -1051,6 +1051,8 @@ BOOST_AUTO_TEST_CASE(checktime_pause_max_trx_cpu_extended_test) { try { // Test deadline is extended when max_transaction_cpu_time is the limiting factor + BOOST_TEST( !t.is_code_cached("pause"_n) ); + // First call to contract which should cause the WASM to load and trx_context.pause_billing_timer() to be called. // Verify that the restriction on the transaction of 24'999 is honored even though there is wall clock time to // load the wasm. If this test fails it is possible that the wasm loaded faster or slower than expected. @@ -1063,7 +1065,8 @@ BOOST_AUTO_TEST_CASE(checktime_pause_max_trx_cpu_extended_test) { try { auto dur = (after - before).count(); dlog("elapsed ${e}us", ("e", dur) ); BOOST_CHECK( dur >= 24'999 ); // should never fail - // This assumes that loading the WASM takes at least 1.5 ms + BOOST_TEST( t.is_code_cached("pause"_n) ); + // This assumes that loading the WASM takes at least 1.5 ms // If this check fails but duration is >= 24'999 (previous check did not fail), then the check here is likely // because WASM took less than 1.5 ms to load. BOOST_CHECK_MESSAGE( dur > 26'500, "elapsed " << dur << "us" ); @@ -1106,6 +1109,8 @@ BOOST_AUTO_TEST_CASE(checktime_pause_max_trx_extended_test) { try { // Test deadline is extended when max_transaction_time is the limiting factor + BOOST_TEST( !t.is_code_cached("pause"_n) ); + // First call to contract which should cause the WASM to load and trx_context.pause_billing_timer() to be called. // Verify that the restriction on the max_transaction_time of 25ms is honored even though there is wall clock time to // load the wasm. If this test fails it is possible that the wasm loaded faster or slower than expected. @@ -1118,6 +1123,7 @@ BOOST_AUTO_TEST_CASE(checktime_pause_max_trx_extended_test) { try { auto dur = (after - before).count(); dlog("elapsed ${e}us", ("e", dur) ); BOOST_CHECK( dur >= 25'000 ); // should never fail + BOOST_TEST( t.is_code_cached("pause"_n) ); // This assumes that loading the WASM takes at least 1.5 ms // If this check fails but duration is >= 25'000 (previous check did not fail), then the check here is likely // because WASM took less than 1.5 ms to load. @@ -1152,6 +1158,8 @@ BOOST_AUTO_TEST_CASE(checktime_pause_block_deadline_not_extended_test) { try { // Test block deadline is not extended when it is the limiting factor // Specify large enough time so that WASM is completely loaded. + BOOST_TEST( !t.is_code_cached("pause"_n) ); + // First call to contract which should cause the WASM to load and trx_context.pause_billing_timer() to be called. auto before = fc::time_point::now(); BOOST_CHECK_EXCEPTION( call_test( t, test_pause_action{}, @@ -1162,6 +1170,8 @@ BOOST_AUTO_TEST_CASE(checktime_pause_block_deadline_not_extended_test) { try { auto dur = (after - before).count(); dlog("elapsed ${e}us", ("e", dur) ); BOOST_CHECK( dur >= 75'000 ); // should never fail + BOOST_TEST( t.is_code_cached("pause"_n) ); + // If this check fails but duration is >= 75'000 (previous check did not fail), then the check here is likely // because it took longer than 10 ms for checktime to trigger, trace to be created, and to get to the now() call. BOOST_CHECK_MESSAGE( dur < 85'000, "elapsed " << dur << "us" ); @@ -1198,6 +1208,8 @@ BOOST_AUTO_TEST_CASE(checktime_pause_block_deadline_not_extended_while_loading_t // This is difficult to determine as checktime is not checked until WASM has completed loading. // We want to test that blocktime is enforced immediately after timer is unpaused. + BOOST_TEST( !t.is_code_cached("pause"_n) ); + // First call to contract which should cause the WASM to load and trx_context.pause_billing_timer() to be called. auto before = fc::time_point::now(); BOOST_CHECK_EXCEPTION( call_test( t, test_pause_action{}, @@ -1209,6 +1221,8 @@ BOOST_AUTO_TEST_CASE(checktime_pause_block_deadline_not_extended_while_loading_t auto dur = (after - before).count(); dlog("elapsed ${e}us", ("e", dur) ); BOOST_CHECK( dur >= 5'000 ); // should never fail + BOOST_TEST( t.is_code_cached("pause"_n) ); + // WASM load times on my machine was 35ms. // Since checktime only kicks in after WASM is loaded this needs to be large enough to load the WASM, but should be // considerably lower than the 150ms max_transaction_time @@ -1264,14 +1278,14 @@ BOOST_FIXTURE_TEST_CASE(checktime_intrinsic, TESTER) { try { set_code( "testapi"_n, ss.str().c_str() ); produce_blocks(1); + BOOST_TEST( !is_code_cached("testapi"_n) ); + //initialize cache BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, 5000, 10, 10 ), deadline_exception, is_deadline_exception ); -// https://github.com/AntelopeIO/leap/issues/260 was created to track this TODO. -// Remove those comments after the issue is resolved. -// #warning TODO validate that the contract was successfully cached + BOOST_TEST( is_code_cached("testapi"_n) ); //it will always call BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, @@ -1303,14 +1317,14 @@ BOOST_FIXTURE_TEST_CASE(checktime_grow_memory, TESTER) { try { set_code( "testapi"_n, ss.str().c_str() ); produce_blocks(1); + BOOST_TEST( !is_code_cached("testapi"_n) ); + //initialize cache BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, 5000, 10, 10 ), deadline_exception, is_deadline_exception ); -// https://github.com/AntelopeIO/leap/issues/260 was created to track this TODO. -// Remove those comments after the issue is resolved. -//#warning TODO validate that the contract was successfully cached + BOOST_TEST( is_code_cached("testapi"_n) ); //it will always call BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, @@ -1325,14 +1339,14 @@ BOOST_FIXTURE_TEST_CASE(checktime_hashing_fail, TESTER) { try { set_code( "testapi"_n, contracts::test_api_wasm() ); produce_blocks(1); + BOOST_TEST( !is_code_cached("testapi"_n) ); + //hit deadline exception, but cache the contract BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{}, 5000, 3, 3 ), deadline_exception, is_deadline_exception ); -// https://github.com/AntelopeIO/leap/issues/260 was created to track this TODO. -// Remove those comments after the issue is resolved. -//#warning TODO validate that the contract was successfully cached + BOOST_TEST( is_code_cached("testapi"_n) ); //the contract should be cached, now we should get deadline_exception because of calls to checktime() from hashing function BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action{},