Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests: Add is_code_cached and use in tests to verify #607

Merged
merged 3 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libraries/chain/include/eosio/chain/wasm_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ namespace eosio { namespace chain {
});
}

bool is_code_cached(const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) const {
wasm_cache_index::iterator it = wasm_instantiation_cache.find( boost::make_tuple(code_hash, vm_type, vm_version) );
return it != wasm_instantiation_cache.end();
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
}

std::vector<uint8_t> parse_initial_memory(const Module& module) {
std::vector<uint8_t> mem_image;

Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/wasm_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}

Expand Down
2 changes: 2 additions & 0 deletions libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ namespace eosio { namespace testing {
void set_code( account_name name, const vector<uint8_t> 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;

Expand Down
7 changes: 7 additions & 0 deletions libraries/testing/tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<account_metadata_object,by_name>( name );
if ( receiver_account->code_hash == digest_type() ) return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to check receiver_account != nullptr first? If the account doesn't exist

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get() throws unknown key if not found.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops yeah I was looking at find()

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;
Expand Down
34 changes: 24 additions & 10 deletions unittests/api_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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" );
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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<TEST_METHOD("test_checktime", "checktime_failure")>{},
Expand All @@ -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" );
Expand Down Expand Up @@ -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<TEST_METHOD("test_checktime", "checktime_failure")>{},
Expand All @@ -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
Expand Down Expand Up @@ -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<TEST_METHOD("doesn't matter", "doesn't matter")>{},
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<TEST_METHOD("doesn't matter", "doesn't matter")>{},
Expand Down Expand Up @@ -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<TEST_METHOD("doesn't matter", "doesn't matter")>{},
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<TEST_METHOD("doesn't matter", "doesn't matter")>{},
Expand All @@ -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<TEST_METHOD("test_checktime", "checktime_sha1_failure")>{},
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<TEST_METHOD("test_checktime", "checktime_sha1_failure")>{},
Expand Down