Files

3861 lines
172 KiB
C++

#include <algorithm>
#include <random>
#include <iostream>
#include <vector>
#include <iterator>
#include <sstream>
#include <numeric>
#include <string>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#include <boost/test/unit_test.hpp>
#pragma GCC diagnostic pop
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <eosio/testing/tester.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/account_object.hpp>
#include <eosio/chain/contract_table_objects.hpp>
#include <eosio/chain/block_summary_object.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <fc/crypto/digest.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/exception/exception.hpp>
#include <fc/variant_object.hpp>
#include <Inline/BasicTypes.h>
#include <IR/Module.h>
#include <IR/Validate.h>
#include <WAST/WAST.h>
#include <WASM/WASM.h>
#include <contracts.hpp>
#include <test_contracts.hpp>
#include "test_cfd_transaction.hpp"
#define DUMMY_ACTION_DEFAULT_A 0x45
#define DUMMY_ACTION_DEFAULT_B 0xab11cd1244556677
#define DUMMY_ACTION_DEFAULT_C 0x7451ae12
static constexpr unsigned int DJBH(const char* cp)
{
unsigned int hash = 5381;
while (*cp)
hash = 33 * hash ^ (unsigned char) *cp++;
return hash;
}
static constexpr unsigned long long WASM_TEST_ACTION(const char* cls, const char* method)
{
return static_cast<unsigned long long>(DJBH(cls)) << 32 | static_cast<unsigned long long>(DJBH(method));
}
using namespace eosio::chain::literals;
struct u128_action {
unsigned __int128 values[3]; //16*3
};
// Deferred Transaction Trigger Action
struct dtt_action {
static uint64_t get_name() {
return WASM_TEST_ACTION("test_transaction", "send_deferred_tx_with_dtt_action");
}
static uint64_t get_account() {
return "testapi"_n.to_uint64_t();
}
uint64_t payer = "testapi"_n.to_uint64_t();
uint64_t deferred_account = "testapi"_n.to_uint64_t();
uint64_t deferred_action = WASM_TEST_ACTION("test_transaction", "deferred_print");
uint64_t permission_name = "active"_n.to_uint64_t();
uint32_t delay_sec = 2;
};
struct invalid_access_action {
uint64_t code;
uint64_t val;
uint32_t index;
bool store;
};
FC_REFLECT( u128_action, (values) )
FC_REFLECT( dtt_action, (payer)(deferred_account)(deferred_action)(permission_name)(delay_sec) )
FC_REFLECT( invalid_access_action, (code)(val)(index)(store) )
using namespace eosio;
using namespace eosio::testing;
using namespace chain;
using namespace fc;
namespace bio = boost::iostreams;
template<uint64_t NAME>
struct test_api_action {
static account_name get_account() {
return "testapi"_n;
}
static action_name get_name() {
return action_name(NAME);
}
};
FC_REFLECT_TEMPLATE((uint64_t T), test_api_action<T>, BOOST_PP_SEQ_NIL)
template<uint64_t NAME>
struct test_pause_action {
static account_name get_account() {
return "pause"_n;
}
static action_name get_name() {
return action_name(NAME);
}
};
FC_REFLECT_TEMPLATE((uint64_t T), test_pause_action<T>, BOOST_PP_SEQ_NIL)
template<uint64_t NAME>
struct test_chain_action {
static account_name get_account() {
return account_name(config::system_account_name);
}
static action_name get_name() {
return action_name(NAME);
}
};
FC_REFLECT_TEMPLATE((uint64_t T), test_chain_action<T>, BOOST_PP_SEQ_NIL)
struct check_auth {
account_name account;
permission_name permission;
vector<public_key_type> pubkeys;
};
FC_REFLECT(check_auth, (account)(permission)(pubkeys) )
struct test_permission_last_used_action {
account_name account;
permission_name permission;
fc::time_point last_used_time;
};
FC_REFLECT( test_permission_last_used_action, (account)(permission)(last_used_time) )
constexpr uint64_t TEST_METHOD(const char* CLASS, const char *METHOD) {
return ( (uint64_t(DJBH(CLASS))<<32) | uint32_t(DJBH(METHOD)) );
}
string I64Str(int64_t i)
{
std::stringstream ss;
ss << i;
return ss.str();
}
string U64Str(uint64_t i)
{
std::stringstream ss;
ss << i;
return ss.str();
}
string U128Str(unsigned __int128 i)
{
return fc::variant(fc::uint128(i)).get_string();
}
template <typename T>
transaction_trace_ptr CallAction(validating_tester& test, T ac, const vector<account_name>& scope = {"testapi"_n}) {
signed_transaction trx;
auto pl = vector<permission_level>{{scope[0], config::active_name}};
if (scope.size() > 1)
for (size_t i = 1; i < scope.size(); i++)
pl.push_back({scope[i], config::active_name});
action act(pl, ac);
trx.actions.push_back(act);
test.set_transaction_headers(trx);
auto sigs = trx.sign(test.get_private_key(scope[0], "active"), test.control->get_chain_id());
flat_set<public_key_type> keys;
trx.get_signature_keys(test.control->get_chain_id(), fc::time_point::maximum(), keys);
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
test.produce_block();
return res;
}
template <typename T, typename Tester>
std::pair<transaction_trace_ptr, signed_block_ptr> _CallFunction(Tester& test, T ac, const vector<char>& data, const vector<account_name>& scope = {"testapi"_n}, bool no_throw = false) {
{
signed_transaction trx;
auto pl = vector<permission_level>{{scope[0], config::active_name}};
if (scope.size() > 1)
for (unsigned int i = 1; i < scope.size(); i++)
pl.push_back({scope[i], config::active_name});
action act(pl, ac);
act.data = data;
act.authorization = {{"testapi"_n, config::active_name}};
trx.actions.push_back(act);
test.set_transaction_headers(trx, test.DEFAULT_EXPIRATION_DELTA);
auto sigs = trx.sign(test.get_private_key(scope[0], "active"), test.control->get_chain_id());
flat_set<public_key_type> keys;
trx.get_signature_keys(test.control->get_chain_id(), fc::time_point::maximum(), keys);
auto res = test.push_transaction(trx, fc::time_point::maximum(), Tester::DEFAULT_BILLED_CPU_TIME_US, no_throw);
if (!no_throw) {
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
}
auto block = test.produce_block();
return { res, block };
}
}
template <typename T, typename Tester>
transaction_trace_ptr CallFunction(Tester& test, T ac, const vector<char>& data, const vector<account_name>& scope = {"testapi"_n}, bool no_throw = false) {
{
return _CallFunction(test, ac, data, scope, no_throw).first;
}
}
#define CALL_TEST_FUNCTION(_TESTER, CLS, MTH, DATA) CallFunction(_TESTER, test_api_action<TEST_METHOD(CLS, MTH)>{}, DATA)
#define CALL_TEST_FUNCTION_WITH_BLOCK(_TESTER, CLS, MTH, DATA) _CallFunction(_TESTER, test_api_action<TEST_METHOD(CLS, MTH)>{}, DATA)
#define CALL_TEST_FUNCTION_SYSTEM(_TESTER, CLS, MTH, DATA) CallFunction(_TESTER, test_chain_action<TEST_METHOD(CLS, MTH)>{}, DATA, {config::system_account_name} )
#define CALL_TEST_FUNCTION_SCOPE(_TESTER, CLS, MTH, DATA, ACCOUNT) CallFunction(_TESTER, test_api_action<TEST_METHOD(CLS, MTH)>{}, DATA, ACCOUNT)
#define CALL_TEST_FUNCTION_NO_THROW(_TESTER, CLS, MTH, DATA) CallFunction(_TESTER, test_api_action<TEST_METHOD(CLS, MTH)>{}, DATA, {"testapi"_n}, true)
#define CALL_TEST_FUNCTION_AND_CHECK_EXCEPTION(_TESTER, CLS, MTH, DATA, EXC, EXC_MESSAGE) \
BOOST_CHECK_EXCEPTION( \
CALL_TEST_FUNCTION( _TESTER, CLS, MTH, DATA), \
EXC, \
[](const EXC& e) { \
return expect_assert_message(e, EXC_MESSAGE); \
} \
);
bool is_access_violation(fc::unhandled_exception const & e) {
try {
std::rethrow_exception(e.get_inner_exception());
}
catch (const eosio::chain::wasm_execution_error& e) {
return true;
} catch (...) {
}
return false;
}
bool is_assert_exception(fc::assert_exception const & e) { return true; }
bool is_page_memory_error(page_memory_error const &e) { return true; }
bool is_unsatisfied_authorization(unsatisfied_authorization const & e) { return true;}
bool is_wasm_execution_error(eosio::chain::wasm_execution_error const& e) {return true;}
bool is_tx_net_usage_exceeded(const tx_net_usage_exceeded& e) { return true; }
bool is_block_net_usage_exceeded(const block_net_usage_exceeded& e) { return true; }
bool is_tx_cpu_usage_exceeded(const tx_cpu_usage_exceeded& e) { return true; }
bool is_block_cpu_usage_exceeded(const block_cpu_usage_exceeded& e) { return true; }
bool is_deadline_exception(const deadline_exception& e) { return true; }
/*
* register test suite `api_tests`
*/
BOOST_AUTO_TEST_SUITE(api_tests)
/*
* Print capturing stuff
*/
std::vector<std::string> capture;
struct MySink : public bio::sink
{
std::streamsize write(const char* s, std::streamsize n)
{
std::string tmp;
tmp.assign(s, n);
capture.push_back(tmp);
std::cout << "stream : [" << tmp << "]" << std::endl;
return n;
}
};
uint32_t last_fnc_err = 0;
BOOST_FIXTURE_TEST_CASE(action_receipt_tests, validating_tester) { try {
produce_blocks(2);
create_account( "test"_n );
set_code( "test"_n, test_contracts::payloadless_wasm() );
produce_blocks(1);
auto call_doit_and_check = [&]( account_name contract, account_name signer, auto&& checker ) {
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{signer, config::active_name}}, contract, "doit"_n, bytes{} );
this->set_transaction_headers( trx, this->DEFAULT_EXPIRATION_DELTA );
trx.sign( this->get_private_key(signer, "active"), control->get_chain_id() );
auto res = this->push_transaction(trx);
checker( res );
};
auto call_provereset_and_check = [&]( account_name contract, account_name signer, auto&& checker ) {
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{signer, config::active_name}}, contract, "provereset"_n, bytes{} );
this->set_transaction_headers( trx, this->DEFAULT_EXPIRATION_DELTA );
trx.sign( this->get_private_key(signer, "active"), control->get_chain_id() );
auto res = this->push_transaction(trx);
checker( res );
};
auto result = push_reqauth( config::system_account_name, "active" );
BOOST_REQUIRE_EQUAL( result->receipt->status, transaction_receipt::executed );
BOOST_REQUIRE( result->action_traces[0].receipt->auth_sequence.find( config::system_account_name )
!= result->action_traces[0].receipt->auth_sequence.end() );
auto base_global_sequence_num = result->action_traces[0].receipt->global_sequence;
auto base_system_recv_seq_num = result->action_traces[0].receipt->recv_sequence;
result->action_traces[0].receipt->auth_sequence[config::system_account_name];
auto base_system_code_seq_num = result->action_traces[0].receipt->code_sequence.value;
auto base_system_abi_seq_num = result->action_traces[0].receipt->abi_sequence.value;
uint64_t base_test_recv_seq_num = 0;
uint64_t base_test_auth_seq_num = 0;
call_doit_and_check( "test"_n, "test"_n, [&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 1 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, 1 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, 0 );
base_test_recv_seq_num = res->action_traces[0].receipt->recv_sequence;
BOOST_CHECK( base_test_recv_seq_num > 0 );
base_test_recv_seq_num--;
const auto& m = res->action_traces[0].receipt->auth_sequence;
BOOST_CHECK_EQUAL( m.size(), 1 );
BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" );
base_test_auth_seq_num = m.begin()->second;
BOOST_CHECK( base_test_auth_seq_num > 0 );
--base_test_auth_seq_num;
} );
set_code( "test"_n, test_contracts::asserter_wasm() );
set_code( config::system_account_name, test_contracts::payloadless_wasm() );
call_provereset_and_check( "test"_n, "test"_n, [&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 4 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->recv_sequence, base_test_recv_seq_num + 2 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, 2 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, 0 );
const auto& m = res->action_traces[0].receipt->auth_sequence;
BOOST_CHECK_EQUAL( m.size(), 1 );
BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" );
BOOST_CHECK_EQUAL( m.begin()->second, base_test_auth_seq_num + 3 );
} );
produce_blocks(1); // Added to avoid the last doit transaction from being considered a duplicate.
// Adding a block also retires an onblock action which increments both the global sequence number
// and the recv and auth sequences numbers for the system account.
call_doit_and_check( config::system_account_name, "test"_n, [&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 6 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->recv_sequence, base_system_recv_seq_num + 4 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, base_system_code_seq_num + 1 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, base_system_abi_seq_num );
const auto& m = res->action_traces[0].receipt->auth_sequence;
BOOST_CHECK_EQUAL( m.size(), 1 );
BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" );
BOOST_CHECK_EQUAL( m.begin()->second, base_test_auth_seq_num + 4 );
} );
set_code( config::system_account_name, contracts::eosio_bios_wasm() );
set_code( "test"_n, contracts::eosio_bios_wasm() );
set_abi( "test"_n, contracts::eosio_bios_abi() );
set_code( "test"_n, test_contracts::payloadless_wasm() );
call_doit_and_check( "test"_n, "test"_n, [&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed);
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->global_sequence, base_global_sequence_num + 11 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->recv_sequence, base_test_recv_seq_num + 3 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->code_sequence.value, 4 );
BOOST_CHECK_EQUAL( res->action_traces[0].receipt->abi_sequence.value, 1 );
const auto& m = res->action_traces[0].receipt->auth_sequence;
BOOST_CHECK_EQUAL( m.size(), 1 );
BOOST_CHECK_EQUAL( m.begin()->first.to_string(), "test" );
BOOST_CHECK_EQUAL( m.begin()->second, base_test_auth_seq_num + 8 );
} );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* action_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(action_tests, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
create_account( "acc1"_n );
create_account( "acc2"_n );
create_account( "acc3"_n );
create_account( "acc4"_n );
produce_blocks(10);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
// test assert_true
CALL_TEST_FUNCTION( *this, "test_action", "assert_true", {});
//test assert_false
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_action", "assert_false", {} ),
eosio_assert_message_exception, eosio_assert_message_is("test_action::assert_false") );
// test read_action_normal
dummy_action dummy13{DUMMY_ACTION_DEFAULT_A, DUMMY_ACTION_DEFAULT_B, DUMMY_ACTION_DEFAULT_C};
CALL_TEST_FUNCTION( *this, "test_action", "read_action_normal", fc::raw::pack(dummy13));
// test read_action_to_0
std::vector<char> raw_bytes((1<<16));
CALL_TEST_FUNCTION( *this, "test_action", "read_action_to_0", raw_bytes );
// test read_action_to_0
raw_bytes.resize((1<<16)+1);
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_action", "read_action_to_0", raw_bytes), eosio::chain::wasm_execution_error,
[](const eosio::chain::wasm_execution_error& e) {
return expect_assert_message(e, "access violation");
}
);
// test read_action_to_64k
raw_bytes.resize(1);
CALL_TEST_FUNCTION( *this, "test_action", "read_action_to_64k", raw_bytes );
// test read_action_to_64k
raw_bytes.resize(3);
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_action", "read_action_to_64k", raw_bytes ), eosio::chain::wasm_execution_error,
[](const eosio::chain::wasm_execution_error& e) {
return expect_assert_message(e, "access violation");
}
);
// test require_notice
auto scope = std::vector<account_name>{"testapi"_n};
auto test_require_notice = [this](auto& test, std::vector<char>& data, std::vector<account_name>& scope){
signed_transaction trx;
auto tm = test_api_action<TEST_METHOD("test_action", "require_notice")>{};
action act(std::vector<permission_level>{{"testapi"_n, config::active_name}}, tm);
vector<char>& dest = *(vector<char> *)(&act.data);
std::copy(data.begin(), data.end(), std::back_inserter(dest));
trx.actions.push_back(act);
test.set_transaction_headers(trx);
trx.sign(test.get_private_key("inita"_n, "active"), control->get_chain_id());
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
};
BOOST_CHECK_EXCEPTION(test_require_notice(*this, raw_bytes, scope), unsatisfied_authorization,
[](const unsatisfied_authorization& e) {
return expect_assert_message(e, "transaction declares authority");
}
);
// test require_auth
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_action", "require_auth", {}), missing_auth_exception,
[](const missing_auth_exception& e) {
return expect_assert_message(e, "missing authority of");
}
);
// test require_auth
auto a3only = std::vector<permission_level>{{"acc3"_n, config::active_name}};
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_action", "require_auth", fc::raw::pack(a3only)), missing_auth_exception,
[](const missing_auth_exception& e) {
return expect_assert_message(e, "missing authority of");
}
);
// test require_auth
auto a4only = std::vector<permission_level>{{"acc4"_n, config::active_name}};
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_action", "require_auth", fc::raw::pack(a4only)), missing_auth_exception,
[](const missing_auth_exception& e) {
return expect_assert_message(e, "missing authority of");
}
);
// test require_auth
auto a3a4 = std::vector<permission_level>{{"acc3"_n, config::active_name}, {"acc4"_n, config::active_name}};
auto a3a4_scope = std::vector<account_name>{"acc3"_n, "acc4"_n};
{
signed_transaction trx;
auto tm = test_api_action<TEST_METHOD("test_action", "require_auth")>{};
auto pl = a3a4;
if (a3a4_scope.size() > 1)
for (unsigned int i=1; i < a3a4_scope.size(); i++)
pl.push_back({a3a4_scope[i], config::active_name});
action act(pl, tm);
auto dat = fc::raw::pack(a3a4);
vector<char>& dest = *(vector<char> *)(&act.data);
std::copy(dat.begin(), dat.end(), std::back_inserter(dest));
act.authorization = {{"testapi"_n, config::active_name}, {"acc3"_n, config::active_name}, {"acc4"_n, config::active_name}};
trx.actions.push_back(act);
set_transaction_headers(trx);
trx.sign(get_private_key("testapi"_n, "active"), control->get_chain_id());
trx.sign(get_private_key("acc3"_n, "active"), control->get_chain_id());
trx.sign(get_private_key("acc4"_n, "active"), control->get_chain_id());
auto res = push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
}
uint64_t now = static_cast<uint64_t>( control->head_block_time().time_since_epoch().count() );
now += config::block_interval_us;
CALL_TEST_FUNCTION( *this, "test_action", "test_current_time", fc::raw::pack(now));
// test current_time
produce_block();
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_action", "test_current_time", fc::raw::pack(now) ),
eosio_assert_message_exception, eosio_assert_message_is("tmp == current_time()") );
// test test_current_receiver
CALL_TEST_FUNCTION( *this, "test_action", "test_current_receiver", fc::raw::pack("testapi"_n));
// test send_action_sender
CALL_TEST_FUNCTION( *this, "test_transaction", "send_action_sender", fc::raw::pack("testapi"_n));
produce_block();
// test_publication_time
uint64_t pub_time = static_cast<uint64_t>( control->head_block_time().time_since_epoch().count() );
pub_time += config::block_interval_us;
CALL_TEST_FUNCTION( *this, "test_action", "test_publication_time", fc::raw::pack(pub_time) );
// test test_abort
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_action", "test_abort", {} ), abort_called,
[](const fc::exception& e) {
return expect_assert_message(e, "abort() called");
}
);
dummy_action da = { DUMMY_ACTION_DEFAULT_A, DUMMY_ACTION_DEFAULT_B, DUMMY_ACTION_DEFAULT_C };
CallAction(*this, da);
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
// test require_recipient loop (doesn't cause infinite loop)
BOOST_FIXTURE_TEST_CASE(require_notice_tests, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
create_account( "acc5"_n );
produce_blocks(1);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
set_code( "acc5"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
// test require_notice
signed_transaction trx;
auto tm = test_api_action<TEST_METHOD( "test_action", "require_notice_tests" )>{};
action act( std::vector<permission_level>{{"testapi"_n, config::active_name}}, tm );
trx.actions.push_back( act );
set_transaction_headers( trx );
trx.sign( get_private_key( "testapi"_n, "active" ), control->get_chain_id() );
auto res = push_transaction( trx );
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(ram_billing_in_notify_tests) { try {
fc::temp_directory tempdir;
validating_tester chain( tempdir, true );
chain.execute_setup_policy( setup_policy::preactivate_feature_and_new_bios );
chain.preactivate_builtin_protocol_features( {builtin_protocol_feature_t::action_return_value} );
chain.preactivate_builtin_protocol_features( {builtin_protocol_feature_t::crypto_primitives} );
chain.produce_blocks(2);
chain.create_account( "testapi"_n );
chain.create_account( "testapi2"_n );
chain.produce_blocks(10);
chain.set_code( "testapi"_n, test_contracts::test_api_wasm() );
chain.produce_blocks(1);
chain.set_code( "testapi2"_n, test_contracts::test_api_wasm() );
chain.produce_blocks(1);
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( chain, "test_action", "test_ram_billing_in_notify",
fc::raw::pack( ((unsigned __int128)"testapi2"_n.to_uint64_t() << 64) | "testapi"_n.to_uint64_t() ) ),
subjective_block_production_exception,
fc_exception_message_is("Cannot charge RAM to other accounts during notify.")
);
CALL_TEST_FUNCTION( chain, "test_action", "test_ram_billing_in_notify", fc::raw::pack( ((unsigned __int128)"testapi2"_n.to_uint64_t() << 64) | 0 ) );
CALL_TEST_FUNCTION( chain, "test_action", "test_ram_billing_in_notify", fc::raw::pack( ((unsigned __int128)"testapi2"_n.to_uint64_t() << 64) | "testapi2"_n.to_uint64_t() ) );
BOOST_REQUIRE_EQUAL( chain.validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* context free action tests
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(cf_action_tests, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
create_account( "dummy"_n );
produce_blocks(10);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
cf_action cfa;
signed_transaction trx;
set_transaction_headers(trx);
// need at least one normal action
BOOST_CHECK_EXCEPTION(push_transaction(trx), tx_no_auths,
[](const fc::assert_exception& e) {
return expect_assert_message(e, "transaction must have at least one authorization");
}
);
action act({}, cfa);
trx.context_free_actions.push_back(act);
trx.context_free_data.emplace_back(fc::raw::pack<uint32_t>(100)); // verify payload matches context free data
trx.context_free_data.emplace_back(fc::raw::pack<uint32_t>(200));
set_transaction_headers(trx);
BOOST_CHECK_EXCEPTION(push_transaction(trx), tx_no_auths,
[](const fc::exception& e) {
return expect_assert_message(e, "transaction must have at least one authorization");
}
);
trx.signatures.clear();
// add a normal action along with cfa
dummy_action da = { DUMMY_ACTION_DEFAULT_A, DUMMY_ACTION_DEFAULT_B, DUMMY_ACTION_DEFAULT_C };
auto pl = vector<permission_level>{{"testapi"_n, config::active_name}};
action act1(pl, da);
trx.actions.push_back(act1);
set_transaction_headers(trx);
// run normal passing case
auto sigs = trx.sign(get_private_key("testapi"_n, "active"), control->get_chain_id());
auto res = push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
// attempt to access context free api in non context free action
da = { DUMMY_ACTION_DEFAULT_A, 200, DUMMY_ACTION_DEFAULT_C };
action act2(pl, da);
trx.signatures.clear();
trx.actions.clear();
trx.actions.push_back(act2);
set_transaction_headers(trx);
// run (dummy_action.b = 200) case looking for invalid use of context_free api
sigs = trx.sign(get_private_key("testapi"_n, "active"), control->get_chain_id());
BOOST_CHECK_EXCEPTION(push_transaction(trx), unaccessible_api,
[](const fc::exception& e) {
return expect_assert_message(e, "this API may only be called from context_free apply");
}
);
{
// back to normal action
action act1(pl, da);
signed_transaction trx;
trx.context_free_actions.push_back(act);
trx.context_free_data.emplace_back(fc::raw::pack<uint32_t>(100)); // verify payload matches context free data
trx.context_free_data.emplace_back(fc::raw::pack<uint32_t>(200));
trx.actions.push_back(act1);
// attempt to access non context free api
for (uint32_t i = 200; i <= 212; ++i) {
trx.context_free_actions.clear();
trx.context_free_data.clear();
cfa.payload = i;
cfa.cfd_idx = 1;
action cfa_act({}, cfa);
trx.context_free_actions.emplace_back(cfa_act);
trx.signatures.clear();
set_transaction_headers(trx);
sigs = trx.sign(get_private_key("testapi"_n, "active"), control->get_chain_id());
BOOST_CHECK_EXCEPTION(push_transaction(trx), unaccessible_api,
[](const fc::exception& e) {
return expect_assert_message(e, "only context free api's can be used in this context" );
}
);
}
}
produce_block();
// test send context free action
auto ttrace = CALL_TEST_FUNCTION( *this, "test_transaction", "send_cf_action", {} );
BOOST_REQUIRE_EQUAL(ttrace->action_traces.size(), 2);
BOOST_CHECK_EQUAL((int)(ttrace->action_traces[1].creator_action_ordinal), 1);
BOOST_CHECK_EQUAL(ttrace->action_traces[1].receiver, account_name("dummy"));
BOOST_CHECK_EQUAL(ttrace->action_traces[1].act.account, account_name("dummy"));
BOOST_CHECK_EQUAL(ttrace->action_traces[1].act.name, account_name("event1"));
BOOST_CHECK_EQUAL(ttrace->action_traces[1].act.authorization.size(), 0);
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "send_cf_action_fail", {} ),
eosio_assert_message_exception,
eosio_assert_message_is("context free actions cannot have authorizations") );
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(cfa_tx_signature, validating_tester) try {
action cfa({}, cf_action());
signed_transaction tx1;
tx1.context_free_data.emplace_back(fc::raw::pack<uint32_t>(100));
tx1.context_free_actions.push_back(cfa);
set_transaction_headers(tx1);
signed_transaction tx2;
tx2.context_free_data.emplace_back(fc::raw::pack<uint32_t>(200));
tx2.context_free_actions.push_back(cfa);
set_transaction_headers(tx2);
const private_key_type& priv_key = get_private_key(name("dummy"), "active");
BOOST_TEST(tx1.sign(priv_key, control->get_chain_id()).to_string() != tx2.sign(priv_key, control->get_chain_id()).to_string());
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE(cfa_stateful_api, validating_tester) try {
create_account( "testapi"_n );
produce_blocks(1);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
account_name a = "testapi2"_n;
account_name creator = config::system_account_name;
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{creator,config::active_name}},
newaccount{
.creator = creator,
.name = a,
.owner = authority( get_public_key( a, "owner" ) ),
.active = authority( get_public_key( a, "active" ) )
});
action act({}, test_api_action<TEST_METHOD("test_transaction", "stateful_api")>{});
trx.context_free_actions.push_back(act);
set_transaction_headers(trx);
trx.sign( get_private_key( creator, "active" ), control->get_chain_id() );
BOOST_CHECK_EXCEPTION(push_transaction( trx ), fc::exception,
[&](const fc::exception &e) {
return expect_assert_message(e, "only context free api's can be used in this context");
});
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE(deferred_cfa_failed, validating_tester_no_disable_deferred_trx) try {
create_account( "testapi"_n );
produce_blocks(1);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
account_name a = "testapi2"_n;
account_name creator = config::system_account_name;
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{creator,config::active_name}},
newaccount{
.creator = creator,
.name = a,
.owner = authority( get_public_key( a, "owner" ) ),
.active = authority( get_public_key( a, "active" ) )
});
action act({}, test_api_action<TEST_METHOD("test_transaction", "stateful_api")>{});
trx.context_free_actions.push_back(act);
set_transaction_headers(trx, 10, 2);
trx.sign( get_private_key( creator, "active" ), control->get_chain_id() );
BOOST_CHECK_EXCEPTION(push_transaction( trx ), fc::exception,
[&](const fc::exception &e) {
return expect_assert_message(e, "only context free api's can be used in this context");
});
produce_blocks(10);
// CFA failed, testapi2 not created
create_account( "testapi2"_n );
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE(deferred_cfa_success, validating_tester_no_disable_deferred_trx) try {
create_account( "testapi"_n );
produce_blocks(1);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
account_name a = "testapi2"_n;
account_name creator = config::system_account_name;
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{creator,config::active_name}},
newaccount{
.creator = creator,
.name = a,
.owner = authority( get_public_key( a, "owner" ) ),
.active = authority( get_public_key( a, "active" ) )
});
action act({}, test_api_action<TEST_METHOD("test_transaction", "context_free_api")>{});
trx.context_free_actions.push_back(act);
set_transaction_headers(trx, 10, 2);
trx.sign( get_private_key( creator, "active" ), control->get_chain_id() );
auto trace = push_transaction( trx );
BOOST_REQUIRE(trace != nullptr);
if (trace) {
BOOST_REQUIRE_EQUAL(transaction_receipt_header::status_enum::delayed, trace->receipt->status);
BOOST_REQUIRE_EQUAL(1, trace->action_traces.size());
}
produce_blocks(10);
// CFA success, testapi2 created
BOOST_CHECK_EXCEPTION(create_account( "testapi2"_n ), fc::exception,
[&](const fc::exception &e) {
return expect_assert_message(e, "Cannot create account named testapi2, as that name is already taken");
});
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_CASE(light_validation_skip_cfa) try {
tester chain(setup_policy::full);
std::vector<signed_block_ptr> blocks;
blocks.push_back(chain.produce_block());
chain.create_account( "testapi"_n );
chain.create_account( "dummy"_n );
blocks.push_back(chain.produce_block());
chain.set_code( "testapi"_n, test_contracts::test_api_wasm() );
blocks.push_back(chain.produce_block());
cf_action cfa;
signed_transaction trx;
action act({}, cfa);
trx.context_free_actions.push_back(act);
trx.context_free_data.emplace_back(fc::raw::pack<uint32_t>(100)); // verify payload matches context free data
trx.context_free_data.emplace_back(fc::raw::pack<uint32_t>(200));
// add a normal action along with cfa
dummy_action da = { DUMMY_ACTION_DEFAULT_A, DUMMY_ACTION_DEFAULT_B, DUMMY_ACTION_DEFAULT_C };
action act1(vector<permission_level>{{"testapi"_n, config::active_name}}, da);
trx.actions.push_back(act1);
chain.set_transaction_headers(trx);
// run normal passing case
auto sigs = trx.sign(chain.get_private_key("testapi"_n, "active"), chain.control->get_chain_id());
auto trace = chain.push_transaction(trx);
blocks.push_back(chain.produce_block());
BOOST_REQUIRE(trace->receipt);
BOOST_CHECK_EQUAL(trace->receipt->status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(2, trace->action_traces.size());
BOOST_CHECK(trace->action_traces.at(0).context_free); // cfa
BOOST_CHECK_EQUAL("test\n", trace->action_traces.at(0).console); // cfa executed
BOOST_CHECK(!trace->action_traces.at(1).context_free); // non-cfa
BOOST_CHECK_EQUAL("", trace->action_traces.at(1).console);
fc::temp_directory tempdir;
auto conf_genesis = tester::default_config( tempdir );
auto& cfg = conf_genesis.first;
cfg.trusted_producers = { "eosio"_n }; // light validation
tester other( conf_genesis.first, conf_genesis.second );
other.execute_setup_policy( setup_policy::full );
transaction_trace_ptr other_trace;
auto cc = other.control->applied_transaction.connect( [&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if( t && t->id == trace->id ) {
other_trace = t;
}
} );
for (auto& new_block : blocks) {
other.push_block(new_block);
}
blocks.clear();
BOOST_REQUIRE(other_trace);
BOOST_REQUIRE(other_trace->receipt);
BOOST_CHECK_EQUAL(other_trace->receipt->status, transaction_receipt::executed);
BOOST_CHECK(*trace->receipt == *other_trace->receipt);
BOOST_CHECK_EQUAL(2, other_trace->action_traces.size());
BOOST_CHECK(other_trace->action_traces.at(0).context_free); // cfa
BOOST_CHECK_EQUAL("", other_trace->action_traces.at(0).console); // cfa not executed for light validation (trusted producer)
BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->global_sequence, other_trace->action_traces.at(0).receipt->global_sequence);
BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->digest(), other_trace->action_traces.at(0).receipt->digest());
BOOST_CHECK(!other_trace->action_traces.at(1).context_free); // non-cfa
BOOST_CHECK_EQUAL("", other_trace->action_traces.at(1).console);
BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->global_sequence, other_trace->action_traces.at(1).receipt->global_sequence);
BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->digest(), other_trace->action_traces.at(1).receipt->digest());
other.close();
} FC_LOG_AND_RETHROW()
/*************************************************************************************
* checktime_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(checktime_pass_tests, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
produce_blocks(10);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
// test checktime_pass
CALL_TEST_FUNCTION( *this, "test_checktime", "checktime_pass", {});
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
template<class T, typename Tester>
void push_trx(Tester& test, T ac, uint32_t billed_cpu_time_us , uint32_t max_cpu_usage_ms, uint32_t max_block_cpu_ms,
bool explicit_bill, std::vector<char> payload = {}, name account = "testapi"_n, transaction_metadata::trx_type trx_type = transaction_metadata::trx_type::input ) {
signed_transaction trx;
action act;
act.account = ac.get_account();
act.name = ac.get_name();
if ( trx_type != transaction_metadata::trx_type::read_only ) {
auto pl = vector<permission_level>{{account, config::active_name}};
act.authorization = pl;
}
act.data = payload;
trx.actions.push_back(act);
test.set_transaction_headers(trx);
if ( trx_type != transaction_metadata::trx_type::read_only ) {
auto sigs = trx.sign(test.get_private_key(account, "active"), test.control->get_chain_id());
}
flat_set<public_key_type> keys;
trx.get_signature_keys(test.control->get_chain_id(), fc::time_point::maximum(), keys);
auto ptrx = std::make_shared<packed_transaction>( std::move(trx) );
auto fut = transaction_metadata::start_recover_keys( std::move( ptrx ), test.control->get_thread_pool(),
test.control->get_chain_id(), fc::microseconds::maximum(),
trx_type );
auto res = test.control->push_transaction( fut.get(), fc::time_point::now() + fc::milliseconds(max_block_cpu_ms),
fc::milliseconds(max_cpu_usage_ms), billed_cpu_time_us, explicit_bill, 0 );
if( res->except_ptr ) std::rethrow_exception( res->except_ptr );
if( res->except ) throw *res->except;
};
template<class T, typename Tester>
void call_test(Tester& test, T ac, uint32_t billed_cpu_time_us , uint32_t max_cpu_usage_ms, uint32_t max_block_cpu_ms,
std::vector<char> payload = {}, name account = "testapi"_n, transaction_metadata::trx_type trx_type = transaction_metadata::trx_type::input ) {
push_trx(test, ac, billed_cpu_time_us, max_cpu_usage_ms, max_block_cpu_ms, billed_cpu_time_us > 0, payload, account, trx_type);
test.produce_block();
}
BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
validating_tester t;
t.produce_blocks(2);
ilog( "create account" );
t.create_account( "testapi"_n );
ilog( "set code" );
t.set_code( "testapi"_n, test_contracts::test_api_wasm() );
ilog( "produce block" );
t.produce_blocks(1);
int64_t x; int64_t net; int64_t cpu;
t.control->get_resource_limits_manager().get_account_limits( "testapi"_n, x, net, cpu );
wdump((net)(cpu));
BOOST_CHECK_EXCEPTION( call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
5000, 200, 200, fc::raw::pack(10000000000000000000ULL) ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 200, 200, fc::raw::pack(10000000000000000000ULL) ),
tx_cpu_usage_exceeded, fc_exception_message_contains("reached on chain max_transaction_cpu_usage") );
BOOST_CHECK_EXCEPTION( push_trx( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
5000, 10, 200, false, fc::raw::pack(10000000000000000000ULL) ),
tx_cpu_usage_exceeded, fc_exception_message_contains("reached speculative executed adjusted trx max time") );
uint32_t time_left_in_block_us = config::default_max_block_cpu_usage - config::default_min_transaction_cpu_usage;
std::string dummy_string = "nonce";
uint32_t increment = config::default_max_transaction_cpu_usage / 3;
for( auto i = 0; time_left_in_block_us > 2*increment; ++i ) {
t.push_dummy( "testapi"_n, dummy_string + std::to_string(i), increment );
time_left_in_block_us -= increment;
}
BOOST_CHECK_EXCEPTION( call_test( t, test_api_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 200, 200, fc::raw::pack(10000000000000000000ULL) ),
block_cpu_usage_exceeded, is_block_cpu_usage_exceeded );
BOOST_REQUIRE_EQUAL( t.validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(checktime_pause_max_trx_cpu_extended_test) { try {
fc::temp_directory tempdir;
auto conf_genesis = tester::default_config( tempdir );
auto& cfg = conf_genesis.second.initial_configuration;
cfg.max_block_cpu_usage = 150'000;
cfg.max_transaction_cpu_usage = 24'999; // needs to be large enough for create_account and set_code
cfg.min_transaction_cpu_usage = 1;
tester t( conf_genesis.first, conf_genesis.second );
if( t.get_config().wasm_runtime == wasm_interface::vm_type::eos_vm_oc ) {
// eos_vm_oc wasm_runtime does not tier-up and completes compile before continuing execution.
// A completely different test with different constraints would be needed to test with eos_vm_oc.
// Since non-tier-up is not a normal valid nodeos runtime, just skip this test for eos_vm_oc.
return;
}
t.execute_setup_policy( setup_policy::full );
t.produce_blocks(2);
t.create_account( "pause"_n );
t.set_code( "pause"_n, test_contracts::test_api_wasm() );
t.produce_blocks(1);
int64_t ram_bytes; int64_t net; int64_t cpu;
auto& rl = t.control->get_resource_limits_manager();
rl.get_account_limits( "pause"_n, ram_bytes, net, cpu );
BOOST_CHECK_EQUAL( cpu, -1 );
auto cpu_limit = rl.get_block_cpu_limit();
idump(("cpu_limit")(cpu_limit));
BOOST_CHECK( cpu_limit <= 150'000 );
// 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.
auto before = fc::time_point::now();
BOOST_CHECK_EXCEPTION( call_test( t, test_pause_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 9999, 500, fc::raw::pack(10000000000000000000ULL), "pause"_n ),
tx_cpu_usage_exceeded, fc_exception_message_contains("reached on chain max_transaction_cpu_usage") );
auto after = fc::time_point::now();
// Test that it runs longer than specified limit of 24'999 to allow for wasm load time.
auto dur = (after - before).count();
dlog("elapsed ${e}us", ("e", dur) );
BOOST_CHECK( dur >= 24'999 ); // should never fail
BOOST_TEST( t.is_code_cached("pause"_n) );
// This assumes that loading the WASM takes at least 0.750 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 0.750 ms to load.
BOOST_CHECK_MESSAGE( dur > 25'750, "elapsed " << dur << "us" );
BOOST_CHECK_MESSAGE( dur < 150'000, "elapsed " << dur << "us" ); // Should not run to block_cpu_usage deadline
// Test hitting max_transaction_time throws tx_cpu_usage_exceeded
BOOST_CHECK_EXCEPTION( call_test( t, test_pause_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 5, 50, fc::raw::pack(10000000000000000000ULL), "pause"_n ),
tx_cpu_usage_exceeded, fc_exception_message_contains("reached node configured max-transaction-time") );
// Test hitting block deadline throws deadline_exception
BOOST_CHECK_EXCEPTION( call_test( t, test_pause_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 50, 5, fc::raw::pack(10000000000000000000ULL), "pause"_n ),
deadline_exception, is_deadline_exception );
BOOST_REQUIRE_EQUAL( t.validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(checktime_pause_max_trx_extended_test) { try {
fc::temp_directory tempdir;
auto conf_genesis = tester::default_config( tempdir );
auto& cfg = conf_genesis.second.initial_configuration;
cfg.max_block_cpu_usage = 350'000;
cfg.max_transaction_cpu_usage = 250'000; // needs to be large enough for create_account and set_code
cfg.min_transaction_cpu_usage = 1;
tester t( conf_genesis.first, conf_genesis.second );
if( t.get_config().wasm_runtime == wasm_interface::vm_type::eos_vm_oc ) {
// eos_vm_oc wasm_runtime does not tier-up and completes compile before continuing execution.
// A completely different test with different constraints would be needed to test with eos_vm_oc.
// Since non-tier-up is not a normal valid nodeos runtime, just skip this test for eos_vm_oc.
return;
}
t.execute_setup_policy( setup_policy::full );
t.produce_blocks(2);
t.create_account( "pause"_n );
t.set_code( "pause"_n, test_contracts::test_api_wasm() );
t.produce_blocks(1);
// 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.
auto before = fc::time_point::now();
BOOST_CHECK_EXCEPTION( call_test( t, test_pause_action<TEST_METHOD("test_checktime", "checktime_failure")>{},
0, 25, 500, fc::raw::pack(10000000000000000000ULL), "pause"_n ),
tx_cpu_usage_exceeded, fc_exception_message_contains("reached node configured max-transaction-time") );
auto after = fc::time_point::now();
// Test that it runs longer than specified limit of 24'999 to allow for wasm load time.
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 0.750 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 0.750 ms to load.
BOOST_CHECK_MESSAGE( dur > 25'750, "elapsed " << dur << "us" );
BOOST_CHECK_MESSAGE( dur < 250'000, "elapsed " << dur << "us" ); // Should not run to max_transaction_cpu_usage deadline
BOOST_REQUIRE_EQUAL( t.validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(checktime_pause_block_deadline_not_extended_test) { try {
fc::temp_directory tempdir;
auto conf_genesis = tester::default_config( tempdir );
auto& cfg = conf_genesis.second.initial_configuration;
cfg.max_block_cpu_usage = 350'000;
cfg.max_transaction_cpu_usage = 250'000; // needs to be large enough for create_account and set_code
cfg.min_transaction_cpu_usage = 1;
tester t( conf_genesis.first, conf_genesis.second );
if( t.get_config().wasm_runtime == wasm_interface::vm_type::eos_vm_oc ) {
// eos_vm_oc wasm_runtime does not tier-up and completes compile before continuing execution.
// A completely different test with different constraints would be needed to test with eos_vm_oc.
// Since non-tier-up is not a normal valid nodeos runtime, just skip this test for eos_vm_oc.
return;
}
t.execute_setup_policy( setup_policy::full );
t.produce_blocks(2);
t.create_account( "pause"_n );
t.set_code( "pause"_n, test_contracts::test_api_wasm() );
t.produce_blocks(1);
// 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")>{},
0, 150, 75, fc::raw::pack(10000000000000000000ULL), "pause"_n ),
deadline_exception, is_deadline_exception );
auto after = fc::time_point::now();
// WASM load times on my machine are around 35ms
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" );
BOOST_REQUIRE_EQUAL( t.validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(checktime_pause_block_deadline_not_extended_while_loading_test) { try {
fc::temp_directory tempdir;
auto conf_genesis = tester::default_config( tempdir );
auto& cfg = conf_genesis.second.initial_configuration;
cfg.max_block_cpu_usage = 350'000;
cfg.max_transaction_cpu_usage = 250'000; // needs to be large enough for create_account and set_code
cfg.min_transaction_cpu_usage = 1;
tester t( conf_genesis.first, conf_genesis.second );
if( t.get_config().wasm_runtime == wasm_interface::vm_type::eos_vm_oc ) {
// eos_vm_oc wasm_runtime does not tier-up and completes compile before continuing execution.
// A completely different test with different constraints would be needed to test with eos_vm_oc.
// Since non-tier-up is not a normal valid nodeos runtime, just skip this test for eos_vm_oc.
return;
}
t.execute_setup_policy( setup_policy::full );
t.produce_blocks(2);
t.create_account( "pause"_n );
t.set_code( "pause"_n, test_contracts::test_api_wasm() );
t.produce_blocks(1);
// Test block deadline is not extended when it is the limiting factor
// This test is different from the previous in that not enough time is provided to load the WASM.
// The block deadline will kick in once the timer is unpaused after loading the WASM.
// 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")>{},
0, 150, 5, fc::raw::pack(10000000000000000000ULL), "pause"_n ),
deadline_exception, is_deadline_exception );
auto after = fc::time_point::now();
// Test that it runs longer than specified limit of 10ms to allow for wasm load time.
// WASM load times on my machine are around 35ms
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
BOOST_CHECK_MESSAGE( dur < 50'000, "elapsed " << dur << "us" );
BOOST_REQUIRE_MESSAGE( dur < 150'000, "elapsed " << dur << "us" ); // should never fail
BOOST_REQUIRE_EQUAL( t.validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(checktime_intrinsic, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
produce_blocks(10);
std::stringstream ss;
ss << R"CONTRACT(
(module
(type $FUNCSIG$vij (func (param i32 i64)))
(type $FUNCSIG$j (func (result i64)))
(type $FUNCSIG$vjj (func (param i64 i64)))
(type $FUNCSIG$vii (func (param i32 i32)))
(type $FUNCSIG$i (func (result i32)))
(type $FUNCSIG$iii (func (param i32 i32) (result i32)))
(type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
(type $FUNCSIG$vi (func (param i32)))
(type $FUNCSIG$v (func ))
(type $_1 (func (param i64 i64 i64)))
(export "apply" (func $apply))
(import "env" "memmove" (func $memmove (param i32 i32 i32) (result i32)))
(import "env" "printui" (func $printui (param i64)))
(memory $0 1)
(func $apply (type $_1)
(param $0 i64)
(param $1 i64)
(param $2 i64)
(drop (grow_memory (i32.const 527)))
(call $printui (i64.const 11))
)CONTRACT";
for(unsigned int i = 0; i < 5000; ++i) {
ss << R"CONTRACT(
(drop (call $memmove
(i32.const 1)
(i32.const 9)
(i32.const 33554432)
))
)CONTRACT";
}
ss<< "))";
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 );
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")>{},
5000, 10, 10 ),
deadline_exception, is_deadline_exception );
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(checktime_grow_memory, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
produce_blocks(10);
std::stringstream ss;
ss << R"CONTRACT(
(module
(memory 1)
(func (export "apply") (param i64 i64 i64)
)CONTRACT";
for(unsigned int i = 0; i < 5000; ++i) {
ss << R"CONTRACT(
(drop (grow_memory (i32.const 527)))
(drop (grow_memory (i32.const -527)))
)CONTRACT";
}
ss<< "))";
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 );
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")>{},
5000, 10, 10 ),
deadline_exception, is_deadline_exception );
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(checktime_hashing_fail, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
produce_blocks(10);
set_code( "testapi"_n, test_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, 8, 8 ),
deadline_exception, is_deadline_exception );
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")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("test_checktime", "checktime_assert_sha1_failure")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("test_checktime", "checktime_sha256_failure")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("test_checktime", "checktime_assert_sha256_failure")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("test_checktime", "checktime_sha512_failure")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("test_checktime", "checktime_assert_sha512_failure")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("test_checktime", "checktime_ripemd160_failure")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("test_checktime", "checktime_assert_ripemd160_failure")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(checktime_start, validating_tester) try {
const char checktime_start_wast[] = R"=====(
(module
(func $start (loop (br 0)))
(func (export "apply") (param i64 i64 i64))
(start $start)
)
)=====";
produce_blocks(2);
create_account( "testapi"_n );
produce_blocks(10);
set_code( "testapi"_n, checktime_start_wast );
produce_blocks(1);
BOOST_CHECK_EXCEPTION( call_test( *this, test_api_action<TEST_METHOD("doesn't matter", "doesn't matter")>{},
5000, 3, 3 ),
deadline_exception, is_deadline_exception );
} FC_LOG_AND_RETHROW()
/*************************************************************************************
* transaction_tests common function
*************************************************************************************/
template<typename T>
void transaction_tests(T& chain) {
chain.produce_blocks(2);
chain.create_account( "testapi"_n );
chain.produce_blocks(100);
chain.set_code( "testapi"_n, test_contracts::test_api_wasm() );
chain.produce_blocks(1);
// test for zero auth
{
signed_transaction trx;
auto tm = test_api_action<TEST_METHOD("test_action", "require_auth")>{};
action act({}, tm);
trx.actions.push_back(act);
chain.set_transaction_headers(trx);
BOOST_CHECK_EXCEPTION(chain.push_transaction(trx), transaction_exception,
[](const fc::exception& e) {
return expect_assert_message(e, "transaction must have at least one authorization");
}
);
}
// test send_action
CALL_TEST_FUNCTION(chain, "test_transaction", "send_action", {});
// test send_action_empty
CALL_TEST_FUNCTION(chain, "test_transaction", "send_action_empty", {});
// test send_action_large (512k)
CALL_TEST_FUNCTION( chain, "test_transaction", "send_action_512k", {});
// test send_many_actions_512k (512k)
CALL_TEST_FUNCTION( chain, "test_transaction", "send_many_actions_512k", {});
// test send_action_large (512k + 1)
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION(chain, "test_transaction", "send_action_large", {}), inline_action_too_big,
[](const fc::exception& e) {
return expect_assert_message(e, "inline action too big");
}
);
// test send_action_inline_fail
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION(chain, "test_transaction", "send_action_inline_fail", {}),
eosio_assert_message_exception,
eosio_assert_message_is("test_action::assert_false") );
// test send_transaction
CALL_TEST_FUNCTION(chain, "test_transaction", "send_transaction", {});
if (std::is_same<T, validating_tester_no_disable_deferred_trx>::value) {
// test send_transaction_empty
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION(chain, "test_transaction", "send_transaction_empty", {}), tx_no_auths,
[](const fc::exception& e) {
return expect_assert_message(e, "transaction must have at least one authorization");
}
);
{
chain.produce_blocks(10);
transaction_trace_ptr trace;
auto c = chain.control->applied_transaction.connect([&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if (t && t->receipt && t->receipt->status != transaction_receipt::executed) { trace = t; }
} );
signed_block_ptr block;
auto c2 = chain.control->accepted_block.connect([&](block_signal_params t) {
const auto& [ b, id ] = t;
block = b; });
// test error handling on deferred transaction failure
auto test_trace = CALL_TEST_FUNCTION(chain, "test_transaction", "send_transaction_trigger_error_handler", {});
BOOST_REQUIRE(trace);
BOOST_CHECK_EQUAL(trace->receipt->status, transaction_receipt::soft_fail);
std::set<transaction_id_type> block_ids;
for( const auto& receipt : block->transactions ) {
transaction_id_type id;
if( std::holds_alternative<packed_transaction>(receipt.trx) ) {
const auto& pt = std::get<packed_transaction>(receipt.trx);
id = pt.id();
} else {
id = std::get<transaction_id_type>(receipt.trx);
}
block_ids.insert( id );
}
BOOST_CHECK_EQUAL(2, block_ids.size() ); // originating trx and deferred
BOOST_CHECK_EQUAL(1, block_ids.count( test_trace->id ) ); // originating
BOOST_CHECK( !test_trace->failed_dtrx_trace );
BOOST_CHECK_EQUAL(0, block_ids.count( trace->id ) ); // onerror id, not in block
BOOST_CHECK_EQUAL(1, block_ids.count( trace->failed_dtrx_trace->id ) ); // deferred id since trace moved to failed_dtrx_trace
BOOST_CHECK( trace->action_traces.at(0).act.name == "onerror"_n );
c.disconnect();
c2.disconnect();
}
}
// test test_transaction_size
CALL_TEST_FUNCTION(chain, "test_transaction", "test_transaction_size", fc::raw::pack(54) ); // TODO: Need a better way to test this.
// test test_read_transaction
// this is a bit rough, but I couldn't figure out a better way to compare the hashes
auto tx_trace = CALL_TEST_FUNCTION( chain, "test_transaction", "test_read_transaction", {} );
string sha_expect = tx_trace->id;
BOOST_TEST_MESSAGE( "tx_trace->action_traces.front().console: = " << tx_trace->action_traces.front().console );
BOOST_TEST_MESSAGE( "sha_expect = " << sha_expect );
BOOST_CHECK_EQUAL(tx_trace->action_traces.front().console == sha_expect, true);
// test test_tapos_block_num
CALL_TEST_FUNCTION(chain, "test_transaction", "test_tapos_block_num", fc::raw::pack(chain.control->head_block_num()) );
// test test_tapos_block_prefix
CALL_TEST_FUNCTION(chain, "test_transaction", "test_tapos_block_prefix", fc::raw::pack(chain.control->head_block_id()._hash[1]) );
// test send_action_recurse
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION(chain, "test_transaction", "send_action_recurse", {}), eosio::chain::transaction_exception,
[](const eosio::chain::transaction_exception& e) {
return expect_assert_message(e, "max inline action depth per transaction reached");
}
);
BOOST_REQUIRE_EQUAL( chain.validate(), true );
// test read_transaction only returns packed transaction
{
signed_transaction trx;
auto pl = vector<permission_level>{{"testapi"_n, config::active_name}};
action act( pl, test_api_action<TEST_METHOD( "test_transaction", "test_read_transaction" )>{} );
act.data = {};
act.authorization = {{"testapi"_n, config::active_name}};
trx.actions.push_back( act );
chain.set_transaction_headers( trx, chain.DEFAULT_EXPIRATION_DELTA );
auto sigs = trx.sign( chain.get_private_key( "testapi"_n, "active" ), chain.control->get_chain_id() );
auto time_limit = fc::microseconds::maximum();
auto ptrx = std::make_shared<packed_transaction>( signed_transaction(trx), packed_transaction::compression_type::none );
string sha_expect = ptrx->id();
auto packed = fc::raw::pack( static_cast<const transaction&>(ptrx->get_transaction()) );
packed.push_back('7'); packed.push_back('7'); // extra ignored
auto packed_copy = packed;
vector<signature_type> psigs = ptrx->get_signatures();
vector<bytes> pcfd = ptrx->get_context_free_data();
packed_transaction pkt( std::move(packed), std::move(psigs), std::move(pcfd), packed_transaction::compression_type::none );
BOOST_CHECK(pkt.get_packed_transaction() == packed_copy);
ptrx = std::make_shared<packed_transaction>( pkt );
auto fut = transaction_metadata::start_recover_keys( std::move( ptrx ), chain.control->get_thread_pool(), chain.control->get_chain_id(), time_limit, transaction_metadata::trx_type::input );
auto r = chain.control->push_transaction( fut.get(), fc::time_point::maximum(), fc::microseconds::maximum(), T::DEFAULT_BILLED_CPU_TIME_US, true, 0 );
if( r->except_ptr ) std::rethrow_exception( r->except_ptr );
if( r->except) throw *r->except;
tx_trace = r;
chain.produce_block();
BOOST_CHECK(tx_trace->action_traces.front().console == sha_expect);
}
}
/*************************************************************************************
* transaction tests for before disable_trxs_protocol_features are activated
*************************************************************************************/
BOOST_AUTO_TEST_CASE(transaction_tests_before_disable_trxs_protocol_features) { try {
validating_tester_no_disable_deferred_trx chain;
transaction_tests<validating_tester>(chain);
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* transaction tests after before disable_trxs_protocol_features are activated
*************************************************************************************/
BOOST_AUTO_TEST_CASE(transaction_tests_after_disable_trxs_protocol_features) { try {
validating_tester chain;
transaction_tests<validating_tester>(chain);
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* verify objective limit test case
*************************************************************************************/
BOOST_AUTO_TEST_CASE(inline_action_with_over_4k_limit) { try {
const uint32_t _4k = 4 * 1024;
tester chain(setup_policy::full, db_read_mode::HEAD, {_4k + 100});
tester chain2(setup_policy::full, db_read_mode::HEAD, {_4k + 100});
signed_block_ptr block;
for (int n=0; n < 2; ++n) {
block = chain.produce_block();
chain2.push_block(block);
}
chain.create_account( "testapi"_n );
for (int n=0; n < 2; ++n) {
block = chain.produce_block();
chain2.push_block(block);
}
chain.set_code( "testapi"_n, test_contracts::test_api_wasm() );
block = chain.produce_block();
chain2.push_block(block);
block = CALL_TEST_FUNCTION_WITH_BLOCK(chain, "test_transaction", "send_action_4k", {}).second;
chain2.push_block(block);
block = chain.produce_block();
chain2.push_block(block);
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* verify objective limit test case
*************************************************************************************/
BOOST_AUTO_TEST_CASE(inline_action_objective_limit) { try {
const uint32_t _4k = 4 * 1024;
tester chain(setup_policy::full, db_read_mode::HEAD, {_4k});
chain.produce_blocks(2);
chain.create_account( "testapi"_n );
chain.produce_blocks(100);
chain.set_code( "testapi"_n, test_contracts::test_api_wasm() );
chain.produce_block();
chain.push_action(config::system_account_name, "setpriv"_n, config::system_account_name, mutable_variant_object()
("account", "testapi")
("is_priv", 1));
chain.produce_block();
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION(chain, "test_transaction", "send_action_4k", {}), inline_action_too_big,
[](const fc::exception& e) {
return expect_assert_message(e, "inline action too big");
}
);
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(deferred_inline_action_limit) { try {
const uint32_t _4k = 4 * 1024;
tester chain(setup_policy::full_except_do_not_disable_deferred_trx, db_read_mode::HEAD, {_4k + 100});
tester chain2(setup_policy::full_except_do_not_disable_deferred_trx, db_read_mode::HEAD, {_4k + 100});
signed_block_ptr block;
for (int n=0; n < 2; ++n) {
block = chain.produce_block();
chain2.push_block(block);
}
chain.create_accounts( {"testapi"_n, "testapi2"_n, "alice"_n} );
chain.set_code( "testapi"_n, test_contracts::test_api_wasm() );
chain.set_code( "testapi2"_n, test_contracts::test_api_wasm() );
block = chain.produce_block();
chain2.push_block(block);
transaction_trace_ptr trace;
auto c = chain.control->applied_transaction.connect([&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if (t->scheduled) { trace = t; }
} );
block = CALL_TEST_FUNCTION_WITH_BLOCK(chain, "test_transaction", "send_deferred_transaction_4k_action", {} ).second;
chain2.push_block(block);
BOOST_CHECK(!trace);
block = chain.produce_block( fc::seconds(2) );
chain2.push_block(block);
//check that it gets executed afterwards
BOOST_REQUIRE(trace);
//confirm printed message
BOOST_TEST(!trace->action_traces.empty());
BOOST_TEST(trace->action_traces.back().console == "action size: 4096");
c.disconnect();
for (int n=0; n < 10; ++n) {
block = chain.produce_block();
chain2.push_block(block);
}
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, validating_tester_no_disable_deferred_trx) { try {
produce_blocks(2);
create_accounts( {"testapi"_n, "testapi2"_n, "alice"_n} );
set_code( "testapi"_n, test_contracts::test_api_wasm() );
set_code( "testapi2"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
//schedule
{
transaction_trace_ptr trace;
auto c = control->applied_transaction.connect([&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if (t->scheduled) { trace = t; }
} );
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {} );
BOOST_CHECK(!trace);
produce_block( fc::seconds(2) );
//check that it gets executed afterwards
BOOST_REQUIRE(trace);
//confirm printed message
BOOST_TEST(!trace->action_traces.empty());
BOOST_TEST(trace->action_traces.back().console == "deferred executed\n");
c.disconnect();
}
produce_blocks(10);
//schedule twice without replace_existing flag (second deferred transaction should replace first one)
{
transaction_trace_ptr trace;
uint32_t count = 0;
auto c = control->applied_transaction.connect([&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if (t && t->scheduled) { trace = t; ++count; }
} );
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {});
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {}), deferred_tx_duplicate);
produce_blocks( 3 );
//check that only one deferred transaction executed
auto dtrxs = get_scheduled_transactions();
BOOST_CHECK_EQUAL(dtrxs.size(), 1);
for (const auto& trx: dtrxs) {
control->push_scheduled_transaction(trx, fc::time_point::maximum(), fc::microseconds::maximum(), 0, false);
}
BOOST_CHECK_EQUAL(1, count);
BOOST_REQUIRE(trace);
BOOST_CHECK_EQUAL( 1, trace->action_traces.size() );
c.disconnect();
}
produce_blocks(10);
//schedule twice with replace_existing flag (second deferred transaction should replace first one)
{
transaction_trace_ptr trace;
uint32_t count = 0;
auto c = control->applied_transaction.connect([&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if (t && t->scheduled) { trace = t; ++count; }
} );
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction_replace", {});
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction_replace", {});
produce_blocks( 3 );
//check that only one deferred transaction executed
auto billed_cpu_time_us = control->get_global_properties().configuration.min_transaction_cpu_usage;
auto dtrxs = get_scheduled_transactions();
BOOST_CHECK_EQUAL(dtrxs.size(), 1);
for (const auto& trx: dtrxs) {
control->push_scheduled_transaction(trx, fc::time_point::maximum(), fc::microseconds::maximum(), billed_cpu_time_us, true);
}
BOOST_CHECK_EQUAL(1, count);
BOOST_CHECK(trace);
BOOST_CHECK_EQUAL( 1, trace->action_traces.size() );
c.disconnect();
}
produce_blocks(10);
//schedule and cancel
{
transaction_trace_ptr trace;
auto c = control->applied_transaction.connect([&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if (t && t->scheduled) { trace = t; }
} );
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {});
CALL_TEST_FUNCTION(*this, "test_transaction", "cancel_deferred_transaction_success", {});
produce_block( fc::seconds(2) );
BOOST_CHECK(!trace);
c.disconnect();
}
produce_blocks(10);
//cancel_deferred() return zero if no scheduled transaction found
{
CALL_TEST_FUNCTION(*this, "test_transaction", "cancel_deferred_transaction_not_found", {});
}
produce_blocks(10);
//repeated deferred transactions
{
vector<transaction_trace_ptr> traces;
auto c = control->applied_transaction.connect([&](std::tuple<const transaction_trace_ptr&, const packed_transaction_ptr&> x) {
auto& t = std::get<0>(x);
if (t && t->scheduled) {
traces.push_back( t );
}
} );
CALL_TEST_FUNCTION(*this, "test_transaction", "repeat_deferred_transaction", fc::raw::pack( (uint32_t)5 ) );
produce_block();
c.disconnect();
BOOST_CHECK_EQUAL( traces.size(), 5 );
}
produce_blocks(10);
{
// Trigger a tx which in turn sends a deferred tx with payer != receiver
// Payer is alice in this case, this tx should fail since we don't have the authorization of alice
dtt_action dtt_act1;
dtt_act1.payer = "alice"_n.to_uint64_t();
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act1)), action_validate_exception);
// Send a tx which in turn sends a deferred tx with the deferred tx's receiver != this tx receiver
// This will include the authorization of the receiver, and impose any related delay associated with the authority
// We set the authorization delay to be 10 sec here, and since the deferred tx delay is set to be 5 sec, so this tx should fail
dtt_action dtt_act2;
dtt_act2.deferred_account = "testapi2"_n.to_uint64_t();
dtt_act2.permission_name = "additional"_n.to_uint64_t();
dtt_act2.delay_sec = 5;
auto auth = authority(get_public_key(name("testapi"), name(dtt_act2.permission_name).to_string()), 10);
auth.accounts.push_back( permission_level_weight{{"testapi"_n, config::eosio_code_name}, 1} );
push_action(config::system_account_name, updateauth::get_name(), name("testapi"), fc::mutable_variant_object()
("account", "testapi")
("permission", name(dtt_act2.permission_name))
("parent", "active")
("auth", auth)
);
push_action(config::system_account_name, linkauth::get_name(), name("testapi"), fc::mutable_variant_object()
("account", "testapi")
("code", name(dtt_act2.deferred_account))
("type", name(dtt_act2.deferred_action))
("requirement", name(dtt_act2.permission_name)));
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2)), unsatisfied_authorization);
// But if the deferred transaction has a sufficient delay, then it should work.
dtt_act2.delay_sec = 10;
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2));
// If the deferred tx receiver == this tx receiver, the authorization checking would originally be bypassed.
// But not anymore. With the RESTRICT_ACTION_TO_SELF protocol feature activated, it should now objectively
// fail because testapi@additional permission is not unilaterally satisfied by testapi@eosio.code.
dtt_action dtt_act3;
dtt_act3.deferred_account = "testapi"_n.to_uint64_t();
dtt_act3.permission_name = "additional"_n.to_uint64_t();
push_action(config::system_account_name, linkauth::get_name(), name("testapi"), fc::mutable_variant_object()
("account", "testapi")
("code", name(dtt_act3.deferred_account))
("type", name(dtt_act3.deferred_action))
("requirement", name(dtt_act3.permission_name)));
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act3)), unsatisfied_authorization);
// But it should again work if the deferred transaction has a sufficient delay.
dtt_act3.delay_sec = 10;
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act3));
// If we make testapi account to be priviledged account:
// - the deferred transaction will work no matter who is the payer
// - the deferred transaction will not care about the delay of the authorization
push_action(config::system_account_name, "setpriv"_n, config::system_account_name, mutable_variant_object()
("account", "testapi")
("is_priv", 1));
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act1));
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act2));
}
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(more_deferred_transaction_tests) { try {
fc::temp_directory tempdir;
validating_tester chain( tempdir, true );
chain.execute_setup_policy( setup_policy::preactivate_feature_and_new_bios );
chain.preactivate_builtin_protocol_features( {builtin_protocol_feature_t::replace_deferred} );
chain.preactivate_builtin_protocol_features( {builtin_protocol_feature_t::crypto_primitives} );
chain.produce_block();
const auto& index = chain.control->db().get_index<generated_transaction_multi_index,by_id>();
auto print_deferred = [&index]() {
for( const auto& gto : index ) {
wlog("id = ${id}, trx_id = ${trx_id}", ("id", gto.id)("trx_id", gto.trx_id));
}
};
const auto& contract_account = account_name("tester");
const auto& test_account = account_name("alice");
chain.create_accounts( {contract_account, test_account} );
chain.set_code( contract_account, test_contracts::deferred_test_wasm() );
chain.set_abi( contract_account, test_contracts::deferred_test_abi() );
chain.produce_block();
BOOST_REQUIRE_EQUAL(0, index.size());
chain.push_action( contract_account, "delayedcall"_n, test_account, fc::mutable_variant_object()
("payer", test_account)
("sender_id", 0)
("contract", contract_account)
("payload", 42)
("delay_sec", 1000)
("replace_existing", false)
);
BOOST_REQUIRE_EQUAL(1, index.size());
print_deferred();
signed_transaction trx;
trx.actions.emplace_back(
chain.get_action( contract_account, "delayedcall"_n,
vector<permission_level>{{test_account, config::active_name}},
fc::mutable_variant_object()
("payer", test_account)
("sender_id", 0)
("contract", contract_account)
("payload", 13)
("delay_sec", 1000)
("replace_existing", true)
)
);
trx.actions.emplace_back(
chain.get_action( contract_account, "delayedcall"_n,
vector<permission_level>{{test_account, config::active_name}},
fc::mutable_variant_object()
("payer", test_account)
("sender_id", 1)
("contract", contract_account)
("payload", 42)
("delay_sec", 1000)
("replace_existing", false)
)
);
trx.actions.emplace_back(
chain.get_action( contract_account, "fail"_n,
vector<permission_level>{},
fc::mutable_variant_object()
)
);
chain.set_transaction_headers(trx);
trx.sign( chain.get_private_key( test_account, "active" ), chain.control->get_chain_id() );
BOOST_REQUIRE_EXCEPTION(
chain.push_transaction( trx ),
eosio_assert_message_exception,
eosio_assert_message_is("fail")
);
BOOST_REQUIRE_EQUAL(1, index.size());
print_deferred();
chain.produce_blocks(2);
chain.push_action( contract_account, "delayedcall"_n, test_account, fc::mutable_variant_object()
("payer", test_account)
("sender_id", 1)
("contract", contract_account)
("payload", 101)
("delay_sec", 1000)
("replace_existing", false)
);
chain.push_action( contract_account, "delayedcall"_n, test_account, fc::mutable_variant_object()
("payer", test_account)
("sender_id", 2)
("contract", contract_account)
("payload", 102)
("delay_sec", 1000)
("replace_existing", false)
);
BOOST_REQUIRE_EQUAL(3, index.size());
print_deferred();
BOOST_REQUIRE_THROW(
chain.push_action( contract_account, "delayedcall"_n, test_account, fc::mutable_variant_object()
("payer", test_account)
("sender_id", 2)
("contract", contract_account)
("payload", 101)
("delay_sec", 1000)
("replace_existing", true)
),
fc::exception
);
BOOST_REQUIRE_EQUAL(3, index.size());
print_deferred();
signed_transaction trx2;
trx2.actions.emplace_back(
chain.get_action( contract_account, "delayedcall"_n,
vector<permission_level>{{test_account, config::active_name}},
fc::mutable_variant_object()
("payer", test_account)
("sender_id", 1)
("contract", contract_account)
("payload", 100)
("delay_sec", 1000)
("replace_existing", true)
)
);
trx2.actions.emplace_back(
chain.get_action( contract_account, "delayedcall"_n,
vector<permission_level>{{test_account, config::active_name}},
fc::mutable_variant_object()
("payer", test_account)
("sender_id", 2)
("contract", contract_account)
("payload", 101)
("delay_sec", 1000)
("replace_existing", true)
)
);
trx2.actions.emplace_back(
chain.get_action( contract_account, "delayedcall"_n,
vector<permission_level>{{test_account, config::active_name}},
fc::mutable_variant_object()
("payer", test_account)
("sender_id", 1)
("contract", contract_account)
("payload", 102)
("delay_sec", 1000)
("replace_existing", true)
)
);
trx2.actions.emplace_back(
chain.get_action( contract_account, "fail"_n,
vector<permission_level>{},
fc::mutable_variant_object()
)
);
chain.set_transaction_headers(trx2);
trx2.sign( chain.get_private_key( test_account, "active" ), chain.control->get_chain_id() );
BOOST_REQUIRE_EXCEPTION(
chain.push_transaction( trx2 ),
eosio_assert_message_exception,
eosio_assert_message_is("fail")
);
BOOST_REQUIRE_EQUAL(3, index.size());
print_deferred();
BOOST_REQUIRE_EQUAL( chain.validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* chain_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(chain_tests, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
vector<account_name> producers = { "inita"_n,
"initb"_n,
"initc"_n,
"initd"_n,
"inite"_n,
"initf"_n,
"initg"_n,
"inith"_n,
"initi"_n,
"initj"_n,
"initk"_n,
"initl"_n,
"initm"_n,
"initn"_n,
"inito"_n,
"initp"_n,
"initq"_n,
"initr"_n,
"inits"_n,
"initt"_n,
"initu"_n
};
create_accounts( producers );
set_producers (producers );
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(100);
vector<account_name> prods( control->active_producers().producers.size() );
for ( uint32_t i = 0; i < prods.size(); i++ ) {
prods[i] = control->active_producers().producers[i].producer_name;
}
CALL_TEST_FUNCTION( *this, "test_chain", "test_activeprods", fc::raw::pack(prods) );
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* db_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(db_tests, validating_tester) { try {
produce_blocks(2);
create_account( "testapi"_n );
create_account( "testapi2"_n );
produce_blocks(10);
set_code( "testapi"_n, test_contracts::test_api_db_wasm() );
set_abi( "testapi"_n, test_contracts::test_api_db_abi() );
set_code( "testapi2"_n, test_contracts::test_api_db_wasm() );
set_abi( "testapi2"_n, test_contracts::test_api_db_abi() );
produce_blocks(1);
push_action( "testapi"_n, "pg"_n, "testapi"_n, mutable_variant_object() ); // primary_i64_general
push_action( "testapi"_n, "pl"_n, "testapi"_n, mutable_variant_object() ); // primary_i64_lowerbound
push_action( "testapi"_n, "pu"_n, "testapi"_n, mutable_variant_object() ); // primary_i64_upperbound
push_action( "testapi"_n, "s1g"_n, "testapi"_n, mutable_variant_object() ); // idx64_general
push_action( "testapi"_n, "s1l"_n, "testapi"_n, mutable_variant_object() ); // idx64_lowerbound
push_action( "testapi"_n, "s1u"_n, "testapi"_n, mutable_variant_object() ); // idx64_upperbound
// Store value in primary table
push_action( "testapi"_n, "tia"_n, "testapi"_n, mutable_variant_object() // test_invalid_access
("code", "testapi")
("val", 10)
("index", 0)
("store", true)
);
// Attempt to change the value stored in the primary table under the code of "testapi"_n
BOOST_CHECK_EXCEPTION( push_action( "testapi2"_n, "tia"_n, "testapi2"_n, mutable_variant_object()
("code", "testapi")
("val", "20")
("index", 0)
("store", true)
), table_access_violation,
fc_exception_message_is("db access violation")
);
// Verify that the value has not changed.
push_action( "testapi"_n, "tia"_n, "testapi"_n, mutable_variant_object()
("code", "testapi")
("val", 10)
("index", 0)
("store", false)
);
// Store value in secondary table
push_action( "testapi"_n, "tia"_n, "testapi"_n, mutable_variant_object() // test_invalid_access
("code", "testapi")
("val", 10)
("index", 1)
("store", true)
);
// Attempt to change the value stored in the secondary table under the code of "testapi"_n
BOOST_CHECK_EXCEPTION( push_action( "testapi2"_n, "tia"_n, "testapi2"_n, mutable_variant_object()
("code", "testapi")
("val", "20")
("index", 1)
("store", true)
), table_access_violation,
fc_exception_message_is("db access violation")
);
// Verify that the value has not changed.
push_action( "testapi"_n, "tia"_n, "testapi"_n, mutable_variant_object()
("code", "testapi")
("val", 10)
("index", 1)
("store", false)
);
// idx_double_nan_create_fail
BOOST_CHECK_EXCEPTION( push_action( "testapi"_n, "sdnancreate"_n, "testapi"_n, mutable_variant_object() ),
transaction_exception,
fc_exception_message_is("NaN is not an allowed value for a secondary key")
);
// idx_double_nan_modify_fail
BOOST_CHECK_EXCEPTION( push_action( "testapi"_n, "sdnanmodify"_n, "testapi"_n, mutable_variant_object() ),
transaction_exception,
fc_exception_message_is("NaN is not an allowed value for a secondary key")
);
// idx_double_nan_lookup_fail
BOOST_CHECK_EXCEPTION( push_action( "testapi"_n, "sdnanlookup"_n, "testapi"_n, mutable_variant_object()
("lookup_type", 0) // 0 for find
), transaction_exception,
fc_exception_message_is("NaN is not an allowed value for a secondary key")
);
BOOST_CHECK_EXCEPTION( push_action( "testapi"_n, "sdnanlookup"_n, "testapi"_n, mutable_variant_object()
("lookup_type", 1) // 1 for lower bound
), transaction_exception,
fc_exception_message_is("NaN is not an allowed value for a secondary key")
);
BOOST_CHECK_EXCEPTION( push_action( "testapi"_n, "sdnanlookup"_n, "testapi"_n, mutable_variant_object()
("lookup_type", 2) // 2 for upper bound
), transaction_exception,
fc_exception_message_is("NaN is not an allowed value for a secondary key")
);
push_action( "testapi"_n, "sk32align"_n, "testapi"_n, mutable_variant_object() ); // misaligned_secondary_key256_tests
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
// The multi_index iterator cache is preserved across notifications for the same action.
BOOST_FIXTURE_TEST_CASE(db_notify_tests, validating_tester) {
create_accounts( {"notifier"_n,"notified"_n } );
const char notifier[] = R"=====(
(module
(func $db_store_i64 (import "env" "db_store_i64") (param i64 i64 i64 i64 i32 i32) (result i32))
(func $db_find_i64 (import "env" "db_find_i64") (param i64 i64 i64 i64) (result i32))
(func $db_idx64_store (import "env" "db_idx64_store") (param i64 i64 i64 i64 i32) (result i32))
(func $db_idx64_find_primary (import "env" "db_idx64_find_primary") (param i64 i64 i64 i32 i64) (result i32))
(func $db_idx128_store (import "env" "db_idx128_store") (param i64 i64 i64 i64 i32) (result i32))
(func $db_idx128_find_primary (import "env" "db_idx128_find_primary") (param i64 i64 i64 i32 i64) (result i32))
(func $db_idx256_store (import "env" "db_idx256_store") (param i64 i64 i64 i64 i32 i32) (result i32))
(func $db_idx256_find_primary (import "env" "db_idx256_find_primary") (param i64 i64 i64 i32 i32 i64) (result i32))
(func $db_idx_double_store (import "env" "db_idx_double_store") (param i64 i64 i64 i64 i32) (result i32))
(func $db_idx_double_find_primary (import "env" "db_idx_double_find_primary") (param i64 i64 i64 i32 i64) (result i32))
(func $db_idx_long_double_store (import "env" "db_idx_long_double_store") (param i64 i64 i64 i64 i32) (result i32))
(func $db_idx_long_double_find_primary (import "env" "db_idx_long_double_find_primary") (param i64 i64 i64 i32 i64) (result i32))
(func $eosio_assert (import "env" "eosio_assert") (param i32 i32))
(func $require_recipient (import "env" "require_recipient") (param i64))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(local i32)
(set_local 3 (i64.ne (get_local 0) (get_local 1)))
(if (get_local 3) (then (i32.store8 (i32.const 7) (i32.const 100))))
(drop (call $db_store_i64 (i64.const 0) (i64.const 0) (get_local 0) (i64.const 0) (i32.const 0) (i32.const 0)))
(drop (call $db_idx64_store (i64.const 0) (i64.const 0) (get_local 0) (i64.const 0) (i32.const 256)))
(drop (call $db_idx128_store (i64.const 0) (i64.const 0) (get_local 0) (i64.const 0) (i32.const 256)))
(drop (call $db_idx256_store (i64.const 0) (i64.const 0) (get_local 0) (i64.const 0) (i32.const 256) (i32.const 2)))
(drop (call $db_idx_double_store (i64.const 0) (i64.const 0) (get_local 0) (i64.const 0) (i32.const 256)))
(drop (call $db_idx_long_double_store (i64.const 0) (i64.const 0) (get_local 0) (i64.const 0) (i32.const 256)))
(call $eosio_assert (i32.eq (call $db_find_i64 (get_local 0) (i64.const 0) (i64.const 0) (i64.const 0) ) (get_local 3)) (i32.const 0))
(call $eosio_assert (i32.eq (call $db_idx64_find_primary (get_local 0) (i64.const 0) (i64.const 0) (i32.const 256) (i64.const 0)) (get_local 3)) (i32.const 32))
(call $eosio_assert (i32.eq (call $db_idx128_find_primary (get_local 0) (i64.const 0) (i64.const 0) (i32.const 256) (i64.const 0)) (get_local 3)) (i32.const 64))
(call $eosio_assert (i32.eq (call $db_idx256_find_primary (get_local 0) (i64.const 0) (i64.const 0) (i32.const 256) (i32.const 2) (i64.const 0)) (get_local 3)) (i32.const 96))
(call $eosio_assert (i32.eq (call $db_idx_double_find_primary (get_local 0) (i64.const 0) (i64.const 0) (i32.const 256) (i64.const 0)) (get_local 3)) (i32.const 128))
(call $eosio_assert (i32.eq (call $db_idx_long_double_find_primary (get_local 0) (i64.const 0) (i64.const 0) (i32.const 256) (i64.const 0)) (get_local 3)) (i32.const 160))
(call $require_recipient (i64.const 11327368596746665984))
)
(data (i32.const 0) "notifier: primary")
(data (i32.const 32) "notifier: idx64")
(data (i32.const 64) "notifier: idx128")
(data (i32.const 96) "notifier: idx256")
(data (i32.const 128) "notifier: idx_double")
(data (i32.const 160) "notifier: idx_long_double")
)
)=====";
set_code("notifier"_n, notifier );
set_code("notified"_n, notifier );
BOOST_TEST_REQUIRE(push_action( action({},"notifier"_n, name(), {}),"notifier"_n.to_uint64_t() ) == "");
}
/*************************************************************************************
* multi_index_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(multi_index_tests, validating_tester) { try {
produce_blocks(1);
create_account( "testapi"_n );
produce_blocks(1);
set_code( "testapi"_n, test_contracts::test_api_multi_index_wasm() );
set_abi( "testapi"_n, test_contracts::test_api_multi_index_abi() );
produce_blocks(1);
auto check_failure = [this]( action_name a, const char* expected_error_msg ) {
BOOST_CHECK_EXCEPTION( push_action( "testapi"_n, a, "testapi"_n, {} ),
eosio_assert_message_exception,
eosio_assert_message_is( expected_error_msg )
);
};
push_action( "testapi"_n, "s1g"_n, "testapi"_n, {} ); // idx64_general
push_action( "testapi"_n, "s1store"_n, "testapi"_n, {} ); // idx64_store_only
push_action( "testapi"_n, "s1check"_n, "testapi"_n, {} ); // idx64_check_without_storing
push_action( "testapi"_n, "s2g"_n, "testapi"_n, {} ); // idx128_general
push_action( "testapi"_n, "s2store"_n, "testapi"_n, {} ); // idx128_store_only
push_action( "testapi"_n, "s2check"_n, "testapi"_n, {} ); // idx128_check_without_storing
push_action( "testapi"_n, "s2autoinc"_n, "testapi"_n, {} ); // idx128_autoincrement_test
push_action( "testapi"_n, "s2autoinc1"_n, "testapi"_n, {} ); // idx128_autoincrement_test_part1
push_action( "testapi"_n, "s2autoinc2"_n, "testapi"_n, {} ); // idx128_autoincrement_test_part2
push_action( "testapi"_n, "s3g"_n, "testapi"_n, {} ); // idx256_general
push_action( "testapi"_n, "sdg"_n, "testapi"_n, {} ); // idx_double_general
push_action( "testapi"_n, "sldg"_n, "testapi"_n, {} ); // idx_long_double_general
check_failure( "s1pkend"_n, "cannot increment end iterator" ); // idx64_pk_iterator_exceed_end
check_failure( "s1skend"_n, "cannot increment end iterator" ); // idx64_sk_iterator_exceed_end
check_failure( "s1pkbegin"_n, "cannot decrement iterator at beginning of table" ); // idx64_pk_iterator_exceed_begin
check_failure( "s1skbegin"_n, "cannot decrement iterator at beginning of index" ); // idx64_sk_iterator_exceed_begin
check_failure( "s1pkref"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_pk_ref_to_other_table
check_failure( "s1skref"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_sk_ref_to_other_table
check_failure( "s1pkitrto"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_pk_end_itr_to_iterator_to
check_failure( "s1pkmodify"_n, "cannot pass end iterator to modify" ); // idx64_pass_pk_end_itr_to_modify
check_failure( "s1pkerase"_n, "cannot pass end iterator to erase" ); // idx64_pass_pk_end_itr_to_erase
check_failure( "s1skitrto"_n, "object passed to iterator_to is not in multi_index" ); // idx64_pass_sk_end_itr_to_iterator_to
check_failure( "s1skmodify"_n, "cannot pass end iterator to modify" ); // idx64_pass_sk_end_itr_to_modify
check_failure( "s1skerase"_n, "cannot pass end iterator to erase" ); // idx64_pass_sk_end_itr_to_erase
check_failure( "s1modpk"_n, "updater cannot change primary key when modifying an object" ); // idx64_modify_primary_key
check_failure( "s1exhaustpk"_n, "next primary key in table is at autoincrement limit" ); // idx64_run_out_of_avl_pk
check_failure( "s1findfail1"_n, "unable to find key" ); // idx64_require_find_fail
check_failure( "s1findfail2"_n, "unable to find primary key in require_find" );// idx64_require_find_fail_with_msg
check_failure( "s1findfail3"_n, "unable to find secondary key" ); // idx64_require_find_sk_fail
check_failure( "s1findfail4"_n, "unable to find sec key" ); // idx64_require_find_sk_fail_with_msg
push_action( "testapi"_n, "s1skcache"_n, "testapi"_n, {} ); // idx64_sk_cache_pk_lookup
push_action( "testapi"_n, "s1pkcache"_n, "testapi"_n, {} ); // idx64_pk_cache_sk_lookup
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* crypto_tests test cases
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(crypto_tests, validating_tester) { try {
produce_block();
create_account("testapi"_n );
produce_block();
set_code("testapi"_n, test_contracts::test_api_wasm() );
produce_block();
{
signed_transaction trx;
auto pl = vector<permission_level>{{"testapi"_n, config::active_name}};
action act(pl, test_api_action<TEST_METHOD("test_crypto", "test_recover_key")>{});
const auto priv_key = get_private_key("testapi"_n, "active" );
const auto pub_key = priv_key.get_public_key();
auto hash = trx.sig_digest( control->get_chain_id() );
auto sig = priv_key.sign(hash);
auto pk = fc::raw::pack( pub_key );
auto sigs = fc::raw::pack( sig );
vector<char> payload(8192);
datastream<char*> payload_ds(payload.data(), payload.size());
fc::raw::pack(payload_ds, hash, (uint32_t)pk.size(), (uint32_t)sigs.size() );
payload_ds.write(pk.data(), pk.size() );
payload_ds.write(sigs.data(), sigs.size());
payload.resize(payload_ds.tellp());
//No Error Here
CALL_TEST_FUNCTION( *this, "test_crypto", "test_recover_key", payload );
// Error Here
CALL_TEST_FUNCTION( *this, "test_crypto", "test_recover_key_assert_true", payload );
payload[payload.size()-1] = 0;
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_crypto", "test_recover_key_assert_false", payload ),
crypto_api_exception, fc_exception_message_is("Error expected key different than recovered key") );
}
{
signed_transaction trx;
auto pl = vector<permission_level>{{"testapi"_n, config::active_name}};
action act(pl, test_api_action<TEST_METHOD("test_crypto", "test_recover_key_partial")>{});
// construct a mock WebAuthN pubkey and signature, as it is the only type that would be variable-sized
const auto priv_key = get_private_key<mock::webauthn_private_key>("testapi"_n, "active" );
const auto pub_key = priv_key.get_public_key();
auto hash = trx.sig_digest( control->get_chain_id() );
auto sig = priv_key.sign(hash);
auto pk = fc::raw::pack( pub_key );
auto sigs = fc::raw::pack( sig );
vector<char> payload(8192);
datastream<char*> payload_ds(payload.data(), payload.size());
fc::raw::pack(payload_ds, hash, (uint32_t)pk.size(), (uint32_t)sigs.size() );
payload_ds.write(pk.data(), pk.size() );
payload_ds.write(sigs.data(), sigs.size());
payload.resize(payload_ds.tellp());
//No Error Here
CALL_TEST_FUNCTION( *this, "test_crypto", "test_recover_key_partial", payload );
}
CALL_TEST_FUNCTION( *this, "test_crypto", "test_sha1", {} );
CALL_TEST_FUNCTION( *this, "test_crypto", "test_sha256", {} );
CALL_TEST_FUNCTION( *this, "test_crypto", "test_sha512", {} );
CALL_TEST_FUNCTION( *this, "test_crypto", "test_ripemd160", {} );
CALL_TEST_FUNCTION( *this, "test_crypto", "sha1_no_data", {} );
CALL_TEST_FUNCTION( *this, "test_crypto", "sha256_no_data", {} );
CALL_TEST_FUNCTION( *this, "test_crypto", "sha512_no_data", {} );
CALL_TEST_FUNCTION( *this, "test_crypto", "ripemd160_no_data", {} );
CALL_TEST_FUNCTION_AND_CHECK_EXCEPTION( *this, "test_crypto", "assert_sha256_false", {},
crypto_api_exception, "hash mismatch" );
CALL_TEST_FUNCTION( *this, "test_crypto", "assert_sha256_true", {} );
CALL_TEST_FUNCTION_AND_CHECK_EXCEPTION( *this, "test_crypto", "assert_sha1_false", {},
crypto_api_exception, "hash mismatch" );
CALL_TEST_FUNCTION( *this, "test_crypto", "assert_sha1_true", {} );
CALL_TEST_FUNCTION_AND_CHECK_EXCEPTION( *this, "test_crypto", "assert_sha512_false", {},
crypto_api_exception, "hash mismatch" );
CALL_TEST_FUNCTION( *this, "test_crypto", "assert_sha512_true", {} );
CALL_TEST_FUNCTION_AND_CHECK_EXCEPTION( *this, "test_crypto", "assert_ripemd160_false", {},
crypto_api_exception, "hash mismatch" );
CALL_TEST_FUNCTION( *this, "test_crypto", "assert_ripemd160_true", {} );
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* memory_tests test case
*************************************************************************************/
static const char memcpy_pass_wast[] = R"======(
(module
(import "env" "memcpy" (func $memcpy (param i32 i32 i32) (result i32)))
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(i64.store (i32.const 0) (i64.const 0x8877665544332211))
(call $eosio_assert (i32.eq (call $memcpy (i32.const 65535) (i32.const 0) (i32.const 1)) (i32.const 65535)) (i32.const 128))
(call $eosio_assert (i64.eq (i64.load (i32.const 65528)) (i64.const 0x1100000000000000)) (i32.const 256))
(drop (call $memcpy (i32.const 8) (i32.const 7) (i32.const 1)))
(drop (call $memcpy (i32.const 7) (i32.const 8) (i32.const 1)))
)
(data (i32.const 128) "expected memcpy to return 65535")
(data (i32.const 256) "expected memcpy to write one byte")
)
)======";
static const char memcpy_overlap_wast[] = R"======(
(module
(import "env" "memcpy" (func $memcpy (param i32 i32 i32) (result i32)))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(drop (call $memcpy (i32.const 16) (i32.wrap/i64 (get_local 2)) (i32.const 8)))
)
)
)======";
static const char memcpy_past_end_wast[] = R"======(
(module
(import "env" "memcpy" (func $memcpy (param i32 i32 i32) (result i32)))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(drop (call $memcpy (i32.const 65535) (i32.const 0) (i32.const 2)))
)
)
)======";
static const char memmove_pass_wast[] = R"======(
(module
(import "env" "memmove" (func $memmove (param i32 i32 i32) (result i32)))
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(memory 1)
(func $fillmem (param i32 i32)
(loop
(i32.store8 (get_local 0) (get_local 1))
(set_local 1 (i32.sub (get_local 1) (i32.const 1)))
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
(br_if 0 (get_local 1))
)
)
(func $checkmem (param i32 i32 i32)
(loop
(call $eosio_assert (i32.eq (i32.load8_u (get_local 0)) (get_local 1)) (get_local 2))
(set_local 1 (i32.sub (get_local 1) (i32.const 1)))
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
(br_if 0 (get_local 1))
)
)
(func (export "apply") (param i64 i64 i64)
(i64.store (i32.const 0) (i64.const 0x8877665544332211))
(call $eosio_assert (i32.eq (call $memmove (i32.const 65535) (i32.const 0) (i32.const 1)) (i32.const 65535)) (i32.const 128))
(call $eosio_assert (i64.eq (i64.load (i32.const 65528)) (i64.const 0x1100000000000000)) (i32.const 256))
(call $fillmem (i32.const 8) (i32.const 128))
(drop (call $memmove (i32.const 64) (i32.const 8) (i32.const 128)))
(call $checkmem (i32.const 64) (i32.const 128) (i32.const 384))
(call $fillmem (i32.const 8) (i32.const 128))
(drop (call $memmove (i32.const 8) (i32.const 8) (i32.const 128)))
(call $checkmem (i32.const 8) (i32.const 128) (i32.const 512))
(call $fillmem (i32.const 64) (i32.const 128))
(drop (call $memmove (i32.const 8) (i32.const 64) (i32.const 128)))
(call $checkmem (i32.const 8) (i32.const 128) (i32.const 640))
)
(data (i32.const 128) "expected memmove to return 65535")
(data (i32.const 256) "expected memmove to write one byte")
(data (i32.const 384) "memmove overlap dest above src")
(data (i32.const 512) "memmove overlap exact")
(data (i32.const 640) "memmove overlap src above dst")
)
)======";
static const char memcmp_pass_wast[] = R"======(
(module
(import "env" "memcmp" (func $memcmp (param i32 i32 i32) (result i32)))
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(call $eosio_assert (i32.eq (call $memcmp (i32.const 65535) (i32.const 65535) (i32.const 1)) (i32.const 0)) (i32.const 128))
(call $eosio_assert (i32.eq (call $memcmp (i32.const 0) (i32.const 2) (i32.const 3)) (i32.const 0)) (i32.const 256))
(call $eosio_assert (i32.eq (call $memcmp (i32.const 0) (i32.const 2) (i32.const 6)) (i32.const -1)) (i32.const 384))
(call $eosio_assert (i32.eq (call $memcmp (i32.const 2) (i32.const 0) (i32.const 6)) (i32.const 1)) (i32.const 512))
)
(data (i32.const 0) "abababcdcdcd")
(data (i32.const 128) "memcmp at end of memory")
(data (i32.const 256) "memcmp overlap eq1")
(data (i32.const 384) "memcmp overlap <")
(data (i32.const 512) "memcmp overlap >")
)
)======";
static const char memset_pass_wast[] = R"======(
(module
(import "env" "memset" (func $memset (param i32 i32 i32) (result i32)))
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(call $eosio_assert (i32.eq (call $memset (i32.const 65535) (i32.const 0xCC) (i32.const 1)) (i32.const 65535)) (i32.const 128))
(call $eosio_assert (i64.eq (i64.load (i32.const 65528)) (i64.const 0xCC00000000000000)) (i32.const 256))
)
(data (i32.const 128) "expected memset to return 65535")
(data (i32.const 256) "expected memset to write one byte")
)
)======";
BOOST_FIXTURE_TEST_CASE(memory_tests, validating_tester) {
produce_block();
create_accounts( { "memcpy"_n, "memcpy2"_n, "memcpy3"_n, "memmove"_n, "memcmp"_n, "memset"_n } );
set_code( "memcpy"_n, memcpy_pass_wast );
set_code( "memcpy2"_n, memcpy_overlap_wast );
set_code( "memcpy3"_n, memcpy_past_end_wast );
set_code( "memmove"_n, memmove_pass_wast );
set_code( "memcmp"_n, memcmp_pass_wast );
set_code( "memset"_n, memset_pass_wast );
auto pushit = [&](name acct, name act) {
signed_transaction trx;
trx.actions.push_back({ { {acct, config::active_name} }, acct, act, bytes()});
set_transaction_headers(trx);
trx.sign(get_private_key(acct, "active"), control->get_chain_id());
push_transaction(trx);
};
pushit("memcpy"_n, name());
pushit("memcpy2"_n, name(0));
pushit("memcpy2"_n, name(8));
BOOST_CHECK_THROW(pushit("memcpy2"_n, name(12)), overlapping_memory_error);
BOOST_CHECK_THROW(pushit("memcpy2"_n, name(16)), overlapping_memory_error);
BOOST_CHECK_THROW(pushit("memcpy2"_n, name(20)), overlapping_memory_error);
BOOST_CHECK_THROW(pushit("memcpy3"_n, name()), wasm_execution_error);
pushit("memcpy2"_n, name(24));
pushit("memmove"_n, name());
pushit("memcmp"_n, name());
pushit("memset"_n, name());
}
static const char cstr_wast[] = R"======(
(module
(import "env" "eosio_assert" (func $eosio_assert (param i32 i32)))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(call $eosio_assert (i32.const 1) (i32.const 65534))
)
(data (i32.const 65535) "x")
)
)======";
BOOST_FIXTURE_TEST_CASE(cstr_tests, validating_tester) {
produce_block();
create_accounts( { "cstr"_n } );
set_code( "cstr"_n, cstr_wast );
auto pushit = [&](name acct, name act) {
signed_transaction trx;
trx.actions.push_back({ { {acct, config::active_name} }, acct, act, bytes()});
set_transaction_headers(trx);
trx.sign(get_private_key(acct, "active"), control->get_chain_id());
push_transaction(trx);
};
pushit("cstr"_n, name());
}
/*************************************************************************************
* print_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(print_tests, validating_tester) { try {
produce_blocks(2);
create_account("testapi"_n );
produce_blocks(1000);
set_code("testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1000);
string captured = "";
// test prints
auto tx1_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_prints", {} );
auto tx1_act_cnsl = tx1_trace->action_traces.front().console;
BOOST_CHECK_EQUAL(tx1_act_cnsl == "abcefg", true);
// test prints_l
auto tx2_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_prints_l", {} );
auto tx2_act_cnsl = tx2_trace->action_traces.front().console;
BOOST_CHECK_EQUAL(tx2_act_cnsl == "abatest", true);
// test printi
auto tx3_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printi", {} );
auto tx3_act_cnsl = tx3_trace->action_traces.front().console;
BOOST_CHECK_EQUAL( tx3_act_cnsl.substr(0,1), I64Str(0) );
BOOST_CHECK_EQUAL( tx3_act_cnsl.substr(1,6), I64Str(556644) );
BOOST_CHECK_EQUAL( tx3_act_cnsl.substr(7, std::string::npos), I64Str(-1) );
// test printui
auto tx4_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printui", {} );
auto tx4_act_cnsl = tx4_trace->action_traces.front().console;
BOOST_CHECK_EQUAL( tx4_act_cnsl.substr(0,1), U64Str(0) );
BOOST_CHECK_EQUAL( tx4_act_cnsl.substr(1,6), U64Str(556644) );
BOOST_CHECK_EQUAL( tx4_act_cnsl.substr(7, std::string::npos), U64Str(-1) ); // "18446744073709551615"
// test printn
auto tx5_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printn", {} );
auto tx5_act_cnsl = tx5_trace->action_traces.front().console;
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(0,1), "1" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(1,1), "5" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(2,1), "a" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(3,1), "z" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(4,3), "abc" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(7,3), "123" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(10,7), "abc.123" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(17,7), "123.abc" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(24,13), "12345abcdefgj" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(37,13), "ijklmnopqrstj" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(50,13), "vwxyz.12345aj" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(63, 13), "111111111111j" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(76, 13), "555555555555j" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(89, 13), "aaaaaaaaaaaaj" );
BOOST_CHECK_EQUAL( tx5_act_cnsl.substr(102,13), "zzzzzzzzzzzzj" );
// test printi128
auto tx6_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printi128", {} );
auto tx6_act_cnsl = tx6_trace->action_traces.front().console;
size_t start = 0;
size_t end = tx6_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx6_act_cnsl.substr(start, end-start), U128Str(1) );
start = end + 1; end = tx6_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx6_act_cnsl.substr(start, end-start), U128Str(0) );
start = end + 1; end = tx6_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx6_act_cnsl.substr(start, end-start), "-" + U128Str(static_cast<unsigned __int128>(std::numeric_limits<__int128>::lowest())) );
start = end + 1; end = tx6_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx6_act_cnsl.substr(start, end-start), "-" + U128Str(87654323456) );
// test printui128
auto tx7_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printui128", {} );
auto tx7_act_cnsl = tx7_trace->action_traces.front().console;
start = 0; end = tx7_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx7_act_cnsl.substr(start, end-start), U128Str(std::numeric_limits<unsigned __int128>::max()) );
start = end + 1; end = tx7_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx7_act_cnsl.substr(start, end-start), U128Str(0) );
start = end + 1; end = tx7_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx7_act_cnsl.substr(start, end-start), U128Str(87654323456) );
// test printsf
auto tx8_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printsf", {} );
auto tx8_act_cnsl = tx8_trace->action_traces.front().console;
start = 0; end = tx8_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx8_act_cnsl.substr(start, end-start), "5.000000e-01" );
start = end + 1; end = tx8_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx8_act_cnsl.substr(start, end-start), "-3.750000e+00" );
start = end + 1; end = tx8_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx8_act_cnsl.substr(start, end-start), "6.666667e-07" );
// test printdf
auto tx9_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printdf", {} );
auto tx9_act_cnsl = tx9_trace->action_traces.front().console;
start = 0; end = tx9_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx9_act_cnsl.substr(start, end-start), "5.000000000000000e-01" );
start = end + 1; end = tx9_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx9_act_cnsl.substr(start, end-start), "-3.750000000000000e+00" );
start = end + 1; end = tx9_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx9_act_cnsl.substr(start, end-start), "6.666666666666666e-07" );
// test printqf
#ifdef __x86_64__
std::string expect1 = "5.000000000000000000e-01";
std::string expect2 = "-3.750000000000000000e+00";
std::string expect3 = "6.666666666666666667e-07";
#else
std::string expect1 = "5.000000000000000e-01";
std::string expect2 = "-3.750000000000000e+00";
std::string expect3 = "6.666666666666667e-07";
#endif
auto tx10_trace = CALL_TEST_FUNCTION( *this, "test_print", "test_printqf", {} );
auto tx10_act_cnsl = tx10_trace->action_traces.front().console;
start = 0; end = tx10_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx10_act_cnsl.substr(start, end-start), expect1 );
start = end + 1; end = tx10_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx10_act_cnsl.substr(start, end-start), expect2 );
start = end + 1; end = tx10_act_cnsl.find('\n', start);
BOOST_CHECK_EQUAL( tx10_act_cnsl.substr(start, end-start), expect3 );
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* types_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(types_tests, validating_tester) { try {
produce_blocks(1000);
create_account( "testapi"_n );
produce_blocks(1000);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1000);
CALL_TEST_FUNCTION( *this, "test_types", "types_size", {});
CALL_TEST_FUNCTION( *this, "test_types", "char_to_symbol", {});
CALL_TEST_FUNCTION( *this, "test_types", "string_to_name", {});
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* permission_tests test case
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(permission_tests, validating_tester) { try {
produce_blocks(1);
create_account( "testapi"_n );
produce_blocks(1);
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
auto get_result_int64 = [&]() -> int64_t {
const auto& db = control->db();
const auto* t_id = db.find<table_id_object, by_code_scope_table>(boost::make_tuple("testapi"_n, "testapi"_n, "testapi"_n));
FC_ASSERT(t_id != 0, "Table id not found");
const auto& idx = db.get_index<key_value_index, by_scope_primary>();
auto itr = idx.lower_bound(boost::make_tuple(t_id->id));
FC_ASSERT( itr != idx.end() && itr->t_id == t_id->id, "lower_bound failed");
FC_ASSERT( 0 != itr->value.size(), "unexpected result size");
return *reinterpret_cast<const int64_t *>(itr->value.data());
};
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = "testapi"_n,
.permission = "active"_n,
.pubkeys = {
get_public_key("testapi"_n, "active")
}
})
);
BOOST_CHECK_EQUAL( int64_t(1), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = "testapi"_n,
.permission = "active"_n,
.pubkeys = {
public_key_type(string("EOS7GfRtyDWWgxV88a5TRaYY59XmHptyfjsFmHHfioGNJtPjpSmGX"))
}
})
);
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = "testapi"_n,
.permission = "active"_n,
.pubkeys = {
get_public_key("testapi"_n, "active"),
public_key_type(string("EOS7GfRtyDWWgxV88a5TRaYY59XmHptyfjsFmHHfioGNJtPjpSmGX"))
}
})
);
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() ); // Failure due to irrelevant signatures
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = "noname"_n,
.permission = "active"_n,
.pubkeys = {
get_public_key("testapi"_n, "active")
}
})
);
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = "testapi"_n,
.permission = "active"_n,
.pubkeys = {}
})
);
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
CALL_TEST_FUNCTION( *this, "test_permission", "check_authorization",
fc::raw::pack( check_auth {
.account = "testapi"_n,
.permission = "noname"_n,
.pubkeys = {
get_public_key("testapi"_n, "active")
}
})
);
BOOST_CHECK_EQUAL( int64_t(0), get_result_int64() );
} FC_LOG_AND_RETHROW() }
static const char resource_limits_wast[] = R"=====(
(module
(func $set_resource_limits (import "env" "set_resource_limits") (param i64 i64 i64 i64))
(func $get_resource_limits (import "env" "get_resource_limits") (param i64 i32 i32 i32))
(func $eosio_assert (import "env" "eosio_assert") (param i32 i32))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(call $set_resource_limits (get_local 2) (i64.const 2788) (i64.const 11) (i64.const 12))
(call $get_resource_limits (get_local 2) (i32.const 0x100) (i32.const 0x108) (i32.const 0x110))
(call $eosio_assert (i64.eq (i64.const 2788) (i64.load (i32.const 0x100))) (i32.const 8))
(call $eosio_assert (i64.eq (i64.const 11) (i64.load (i32.const 0x108))) (i32.const 32))
(call $eosio_assert (i64.eq (i64.const 12) (i64.load (i32.const 0x110))) (i32.const 64))
;; Aligned overlap
(call $get_resource_limits (get_local 2) (i32.const 0x100) (i32.const 0x100) (i32.const 0x110))
(call $eosio_assert (i64.eq (i64.const 11) (i64.load (i32.const 0x100))) (i32.const 96))
(call $get_resource_limits (get_local 2) (i32.const 0x100) (i32.const 0x110) (i32.const 0x110))
(call $eosio_assert (i64.eq (i64.const 12) (i64.load (i32.const 0x110))) (i32.const 128))
;; Unaligned beats aligned
(call $get_resource_limits (get_local 2) (i32.const 0x101) (i32.const 0x108) (i32.const 0x100))
(call $eosio_assert (i64.eq (i64.const 2788) (i64.load (i32.const 0x101))) (i32.const 160))
;; Unaligned overlap
(call $get_resource_limits (get_local 2) (i32.const 0x101) (i32.const 0x101) (i32.const 0x110))
(call $eosio_assert (i64.eq (i64.const 11) (i64.load (i32.const 0x101))) (i32.const 192))
(call $get_resource_limits (get_local 2) (i32.const 0x100) (i32.const 0x111) (i32.const 0x111))
(call $eosio_assert (i64.eq (i64.const 12) (i64.load (i32.const 0x111))) (i32.const 224))
)
(data (i32.const 8) "expected ram 2788")
(data (i32.const 32) "expected net 11")
(data (i32.const 64) "expected cpu 12")
(data (i32.const 96) "expected net to overwrite ram")
(data (i32.const 128) "expected cpu to overwrite net")
(data (i32.const 160) "expected unaligned")
(data (i32.const 192) "expected unet to overwrite uram")
(data (i32.const 224) "expected ucpu to overwrite unet")
)
)=====";
static const char get_resource_limits_null_ram_wast[] = R"=====(
(module
(func $get_resource_limits (import "env" "get_resource_limits") (param i64 i32 i32 i32))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(call $get_resource_limits (get_local 2) (i32.const 0) (i32.const 0x10) (i32.const 0x10))
)
)
)=====";
static const char get_resource_limits_null_net_wast[] = R"=====(
(module
(func $get_resource_limits (import "env" "get_resource_limits") (param i64 i32 i32 i32))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(call $get_resource_limits (get_local 2) (i32.const 0x10) (i32.const 0) (i32.const 0x10))
)
)
)=====";
static const char get_resource_limits_null_cpu_wast[] = R"=====(
(module
(func $get_resource_limits (import "env" "get_resource_limits") (param i64 i32 i32 i32))
(memory 1)
(func (export "apply") (param i64 i64 i64)
(call $get_resource_limits (get_local 2) (i32.const 0x10) (i32.const 0x10) (i32.const 0))
)
)
)=====";
BOOST_FIXTURE_TEST_CASE(resource_limits_tests, validating_tester) {
create_accounts( { "rlimits"_n, "testacnt"_n } );
set_code("rlimits"_n, resource_limits_wast);
push_action( "eosio"_n, "setpriv"_n, "eosio"_n, mutable_variant_object()("account", "rlimits"_n)("is_priv", 1));
produce_block();
auto pushit = [&]{
signed_transaction trx;
trx.actions.push_back({ { { "rlimits"_n, config::active_name } }, "rlimits"_n, "testacnt"_n, bytes{}});
set_transaction_headers(trx);
trx.sign(get_private_key( "rlimits"_n, "active" ), control->get_chain_id());
push_transaction(trx);
};
pushit();
produce_block();
set_code("rlimits"_n, get_resource_limits_null_ram_wast);
BOOST_CHECK_THROW(pushit(), wasm_exception);
set_code("rlimits"_n, get_resource_limits_null_net_wast);
BOOST_CHECK_THROW(pushit(), wasm_exception);
set_code("rlimits"_n, get_resource_limits_null_cpu_wast);
BOOST_CHECK_THROW(pushit(), wasm_exception);
}
BOOST_AUTO_TEST_CASE( set_producers_legacy ) { try {
fc::temp_directory tempdir;
validating_tester t( tempdir, true );
t.execute_setup_policy( setup_policy::preactivate_feature_and_new_bios );
vector<account_name> prods = {
"inita"_n,
"initb"_n,
"initc"_n,
"initd"_n,
"inite"_n,
"initf"_n,
"initg"_n,
"inith"_n,
"initi"_n,
"initj"_n,
"initk"_n,
"initl"_n,
"initm"_n,
"initn"_n,
"inito"_n,
"initp"_n,
"initq"_n,
"initr"_n,
"inits"_n,
"initt"_n,
"initu"_n
};
t.create_accounts( prods );
t.produce_block();
auto trace = t.set_producers_legacy(prods);
BOOST_REQUIRE(trace && trace->receipt);
BOOST_CHECK_EQUAL(trace->receipt->status, transaction_receipt::executed);
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* real_tests test cases
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(datastream_tests, validating_tester) { try {
produce_blocks(1000);
create_account("testapi"_n );
produce_blocks(1000);
set_code("testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1000);
CALL_TEST_FUNCTION( *this, "test_datastream", "test_basic", {} );
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* permission_usage_tests test cases
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(permission_usage_tests, validating_tester) { try {
produce_block();
create_accounts( {"testapi"_n, "alice"_n, "bob"_n} );
produce_block();
set_code("testapi"_n, test_contracts::test_api_wasm() );
produce_block();
push_reqauth( "alice"_n, {{"alice"_n, config::active_name}}, {get_private_key("alice"_n, "active")} );
CALL_TEST_FUNCTION( *this, "test_permission", "test_permission_last_used",
fc::raw::pack(test_permission_last_used_action{
"alice"_n, config::active_name,
control->pending_block_time()
})
);
// Fails because the last used time is updated after the transaction executes.
BOOST_CHECK_THROW( CALL_TEST_FUNCTION( *this, "test_permission", "test_permission_last_used",
fc::raw::pack(test_permission_last_used_action{
"testapi"_n, config::active_name,
control->head_block_time() + fc::milliseconds(config::block_interval_ms)
})
), eosio_assert_message_exception );
produce_blocks(5);
set_authority( "bob"_n, "perm1"_n, authority( get_private_key("bob"_n, "perm1").get_public_key() ) );
push_action(config::system_account_name, linkauth::get_name(), "bob"_n, fc::mutable_variant_object()
("account", "bob")
("code", "eosio")
("type", "reqauth")
("requirement", "perm1")
);
auto permission_creation_time = control->pending_block_time();
produce_blocks(5);
CALL_TEST_FUNCTION( *this, "test_permission", "test_permission_last_used",
fc::raw::pack(test_permission_last_used_action{
"bob"_n, "perm1"_n,
permission_creation_time
})
);
produce_blocks(5);
push_reqauth( "bob"_n, {{"bob"_n, "perm1"_n}}, {get_private_key("bob"_n, "perm1")} );
auto perm1_last_used_time = control->pending_block_time();
CALL_TEST_FUNCTION( *this, "test_permission", "test_permission_last_used",
fc::raw::pack(test_permission_last_used_action{
"bob"_n, config::active_name,
permission_creation_time
})
);
BOOST_CHECK_THROW( CALL_TEST_FUNCTION( *this, "test_permission", "test_permission_last_used",
fc::raw::pack(test_permission_last_used_action{
"bob"_n, "perm1"_n,
permission_creation_time
})
), eosio_assert_message_exception );
CALL_TEST_FUNCTION( *this, "test_permission", "test_permission_last_used",
fc::raw::pack(test_permission_last_used_action{
"bob"_n, "perm1"_n,
perm1_last_used_time
})
);
produce_block();
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* account_creation_time_tests test cases
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(account_creation_time_tests, validating_tester) { try {
produce_block();
create_account( "testapi"_n );
produce_block();
set_code("testapi"_n, test_contracts::test_api_wasm() );
produce_block();
create_account( "alice"_n );
auto alice_creation_time = control->pending_block_time();
produce_blocks(10);
CALL_TEST_FUNCTION( *this, "test_permission", "test_account_creation_time",
fc::raw::pack(test_permission_last_used_action{
"alice"_n, config::active_name,
alice_creation_time
})
);
produce_block();
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* extended_symbol_api_tests test cases
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(extended_symbol_api_tests, validating_tester) { try {
name n0{"1"};
name n1{"5"};
name n2{"a"};
name n3{"z"};
name n4{"111111111111j"};
name n5{"555555555555j"};
name n6{"zzzzzzzzzzzzj"};
symbol s0{4, ""};
symbol s1{5, "Z"};
symbol s2{10, "AAAAA"};
symbol s3{10, "ZZZZZ"};
// Test comparison operators
BOOST_REQUIRE( (extended_symbol{s0, n0} == extended_symbol{s0, n0}) );
BOOST_REQUIRE( (extended_symbol{s1, n3} == extended_symbol{s1, n3}) );
BOOST_REQUIRE( (extended_symbol{s2, n4} == extended_symbol{s2, n4}) );
BOOST_REQUIRE( (extended_symbol{s3, n6} == extended_symbol{s3, n6}) );
BOOST_REQUIRE( (extended_symbol{s0, n0} != extended_symbol{s1, n0}) );
BOOST_REQUIRE( (extended_symbol{s0, n0} != extended_symbol{s0, n1}) );
BOOST_REQUIRE( (extended_symbol{s1, n1} != extended_symbol{s2, n2}) );
BOOST_REQUIRE( (extended_symbol{s0, n0} < extended_symbol{s1, n0}) );
BOOST_REQUIRE( (extended_symbol{s0, n0} < extended_symbol{s0, n1}) );
BOOST_REQUIRE( (extended_symbol{s0, n5} < extended_symbol{s0, n3}) );
BOOST_REQUIRE( (extended_symbol{s2, n0} < extended_symbol{s3, n0}) );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
* eosio_assert_code_tests test cases
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(eosio_assert_code_tests, validating_tester) { try {
produce_block();
create_account( "testapi"_n );
produce_block();
set_code("testapi"_n, test_contracts::test_api_wasm() );
const char* abi_string = R"=====(
{
"version": "eosio::abi/1.0",
"types": [],
"structs": [],
"actions": [],
"tables": [],
"ricardian_clauses": [],
"error_messages": [
{"error_code": 1, "error_msg": "standard error message" },
{"error_code": 42, "error_msg": "The answer to life, the universe, and everything."}
]
"abi_extensions": []
}
)=====";
set_abi( "testapi"_n, abi_string );
auto var = fc::json::from_string(abi_string);
abi_serializer abis(var.as<abi_def>(), abi_serializer::create_yield_function( abi_serializer_max_time ));
produce_blocks(10);
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_action", "test_assert_code", fc::raw::pack((uint64_t)42) ),
eosio_assert_code_exception, eosio_assert_code_is(42) );
auto trace = CALL_TEST_FUNCTION_NO_THROW( *this, "test_action", "test_assert_code", fc::raw::pack((uint64_t)42) );
BOOST_REQUIRE( trace );
BOOST_REQUIRE( trace->except );
BOOST_REQUIRE( trace->error_code );
BOOST_REQUIRE_EQUAL( *trace->error_code, 42 );
BOOST_REQUIRE_EQUAL( trace->action_traces.size(), 1 );
BOOST_REQUIRE( trace->action_traces[0].except );
BOOST_REQUIRE( trace->action_traces[0].error_code );
BOOST_REQUIRE_EQUAL( *trace->action_traces[0].error_code, 42 );
produce_block();
auto omsg1 = abis.get_error_message(1);
BOOST_REQUIRE_EQUAL( omsg1.has_value(), true );
BOOST_CHECK_EQUAL( *omsg1, "standard error message" );
auto omsg2 = abis.get_error_message(2);
BOOST_CHECK_EQUAL( omsg2.has_value(), false );
auto omsg3 = abis.get_error_message(42);
BOOST_REQUIRE_EQUAL( omsg3.has_value(), true );
BOOST_CHECK_EQUAL( *omsg3, "The answer to life, the universe, and everything." );
produce_block();
auto trace2 = CALL_TEST_FUNCTION_NO_THROW(
*this, "test_action", "test_assert_code",
fc::raw::pack( static_cast<uint64_t>(system_error_code::generic_system_error) )
);
BOOST_REQUIRE( trace2 );
BOOST_REQUIRE( trace2->except );
BOOST_REQUIRE( trace2->error_code );
BOOST_REQUIRE_EQUAL( *trace2->error_code, static_cast<uint64_t>(system_error_code::contract_restricted_error_code) );
BOOST_REQUIRE_EQUAL( trace2->action_traces.size(), 1 );
BOOST_REQUIRE( trace2->action_traces[0].except );
BOOST_REQUIRE( trace2->action_traces[0].error_code );
BOOST_REQUIRE_EQUAL( *trace2->action_traces[0].error_code, static_cast<uint64_t>(system_error_code::contract_restricted_error_code) );
produce_block();
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
+ * action_ordinal_test test cases
+ *************************************************************************************/
BOOST_FIXTURE_TEST_CASE(action_ordinal_test, validating_tester) { try {
produce_blocks(1);
create_account("testapi"_n );
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("bob"_n );
set_code( "bob"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("charlie"_n );
set_code( "charlie"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("david"_n );
set_code( "david"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("erin"_n );
set_code( "erin"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
// prove act digest
auto pad = [](const digest_type& expected_act_digest, const action& act, const vector<char>& act_output)
{
std::vector<char> buf1;
buf1.resize(64);
datastream<char*> ds(buf1.data(), buf1.size());
{
std::vector<char> buf2;
const action_base* act_base = &act;
buf2.resize(fc::raw::pack_size(*act_base));
datastream<char*> ds2(buf2.data(), buf2.size());
fc::raw::pack(ds2, *act_base);
fc::raw::pack(ds, sha256::hash(buf2.data(), buf2.size()));
}
{
std::vector<char> buf2;
buf2.resize(fc::raw::pack_size(act.data) + fc::raw::pack_size(act_output));
datastream<char*> ds2(buf2.data(), buf2.size());
fc::raw::pack(ds2, act.data);
fc::raw::pack(ds2, act_output);
fc::raw::pack(ds, sha256::hash(buf2.data(), buf2.size()));
}
digest_type computed_act_digest = sha256::hash(buf1.data(), ds.tellp());
return expected_act_digest == computed_act_digest;
};
transaction_trace_ptr txn_trace = CALL_TEST_FUNCTION_SCOPE( *this, "test_action", "test_action_ordinal1",
{}, vector<account_name>{ "testapi"_n});
BOOST_REQUIRE_EQUAL( validate(), true );
BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true);
BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 11);
auto &atrace = txn_trace->action_traces;
BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0);
BOOST_REQUIRE_EQUAL((int)atrace[0].closest_unnotified_ancestor_action_ordinal, 0);
BOOST_REQUIRE_EQUAL(atrace[0].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[0].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<unsigned_int>(atrace[0].return_value), unsigned_int(1) );
BOOST_REQUIRE(pad(atrace[0].receipt->act_digest, atrace[0].act, atrace[0].return_value));
int start_gseq = atrace[0].receipt->global_sequence;
BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2);
BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[1].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[1].receiver, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[1].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<std::string>(atrace[1].return_value), "bob" );
BOOST_REQUIRE(pad(atrace[1].receipt->act_digest, atrace[1].act, atrace[1].return_value));
BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1);
BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[2].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[2].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[2].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<name>(atrace[2].return_value), name("five") );
BOOST_REQUIRE(pad(atrace[2].receipt->act_digest, atrace[2].act, atrace[2].return_value));
BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4);
BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4);
BOOST_REQUIRE_EQUAL((int)atrace[3].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[3].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[3].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[3].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[3].act.name, TEST_METHOD("test_action", "test_action_ordinal3"));
BOOST_REQUIRE_EQUAL(atrace[3].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<unsigned_int>(atrace[3].return_value), unsigned_int(9) );
BOOST_REQUIRE(pad(atrace[3].receipt->act_digest, atrace[3].act, atrace[3].return_value));
BOOST_REQUIRE_EQUAL(atrace[3].receipt->global_sequence, start_gseq + 8);
BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5);
BOOST_REQUIRE_EQUAL((int)atrace[4].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[4].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[4].receiver, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[4].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[4].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[4].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<std::string>(atrace[4].return_value), "charlie" );
BOOST_REQUIRE(pad(atrace[4].receipt->act_digest, atrace[4].act, atrace[4].return_value));
BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2);
BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6);
BOOST_REQUIRE_EQUAL((int)atrace[5].creator_action_ordinal, 2);
BOOST_REQUIRE_EQUAL((int)atrace[5].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[5].receiver, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[5].act.account, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[5].act.name, TEST_METHOD("test_action", "test_action_ordinal_foo"));
BOOST_REQUIRE_EQUAL(atrace[5].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<double>(atrace[5].return_value), 13.23 );
BOOST_REQUIRE(pad(atrace[5].receipt->act_digest, atrace[5].act, atrace[5].return_value));
BOOST_REQUIRE_EQUAL(atrace[5].receipt->global_sequence, start_gseq + 9);
BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7);
BOOST_REQUIRE_EQUAL((int)atrace[6].creator_action_ordinal,2);
BOOST_REQUIRE_EQUAL((int)atrace[6].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[6].receiver, "david"_n);
BOOST_REQUIRE_EQUAL(atrace[6].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[6].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[6].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<std::string>(atrace[6].return_value), "david" );
BOOST_REQUIRE(pad(atrace[6].receipt->act_digest, atrace[6].act, atrace[6].return_value));
BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3);
BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8);
BOOST_REQUIRE_EQUAL((int)atrace[7].creator_action_ordinal, 5);
BOOST_REQUIRE_EQUAL((int)atrace[7].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[7].receiver, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[7].act.account, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[7].act.name, TEST_METHOD("test_action", "test_action_ordinal_bar"));
BOOST_REQUIRE_EQUAL(atrace[7].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<float>(atrace[7].return_value), 11.42f );
BOOST_REQUIRE(pad(atrace[7].receipt->act_digest, atrace[7].act, atrace[7].return_value));
BOOST_REQUIRE_EQUAL(atrace[7].receipt->global_sequence, start_gseq + 10);
BOOST_REQUIRE_EQUAL((int)atrace[8].action_ordinal, 9);
BOOST_REQUIRE_EQUAL((int)atrace[8].creator_action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[8].closest_unnotified_ancestor_action_ordinal, 3);
BOOST_REQUIRE_EQUAL(atrace[8].receiver, "david"_n);
BOOST_REQUIRE_EQUAL(atrace[8].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[8].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[8].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<bool>(atrace[8].return_value), true );
BOOST_REQUIRE(pad(atrace[8].receipt->act_digest, atrace[8].act, atrace[8].return_value));
BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5);
BOOST_REQUIRE_EQUAL((int)atrace[9].action_ordinal, 10);
BOOST_REQUIRE_EQUAL((int)atrace[9].creator_action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[9].closest_unnotified_ancestor_action_ordinal, 3);
BOOST_REQUIRE_EQUAL(atrace[9].receiver, "erin"_n);
BOOST_REQUIRE_EQUAL(atrace[9].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[9].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[9].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<signed_int>(atrace[9].return_value), signed_int(7) );
BOOST_REQUIRE(pad(atrace[9].receipt->act_digest, atrace[9].act, atrace[9].return_value));
BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6);
BOOST_REQUIRE_EQUAL((int)atrace[10].action_ordinal, 11);
BOOST_REQUIRE_EQUAL((int)atrace[10].creator_action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[10].closest_unnotified_ancestor_action_ordinal, 3);
BOOST_REQUIRE_EQUAL(atrace[10].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[10].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[10].act.name, TEST_METHOD("test_action", "test_action_ordinal4"));
BOOST_REQUIRE_EQUAL(atrace[10].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(atrace[10].return_value.size(), 0 );
BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7);
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
+ * action_ordinal_failtest1 test cases
+ *************************************************************************************/
BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest1, validating_tester) { try {
produce_blocks(1);
create_account("testapi"_n );
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("bob"_n );
set_code( "bob"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("charlie"_n );
set_code( "charlie"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("david"_n );
set_code( "david"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("erin"_n );
set_code( "erin"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("fail1"_n ); // <- make first action fails in the middle
produce_blocks(1);
transaction_trace_ptr txn_trace =
CALL_TEST_FUNCTION_NO_THROW( *this, "test_action", "test_action_ordinal1", {});
BOOST_REQUIRE_EQUAL( validate(), true );
BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true);
BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 3);
auto &atrace = txn_trace->action_traces;
// fails here after creating one notify action and one inline action
BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0);
BOOST_REQUIRE_EQUAL((int)atrace[0].closest_unnotified_ancestor_action_ordinal, 0);
BOOST_REQUIRE_EQUAL(atrace[0].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[0].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[0].except.has_value(), true);
BOOST_REQUIRE_EQUAL(atrace[0].except->code(), 3050003);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal, 2);
BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[1].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[1].receiver, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[1].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[1].except.has_value(), false);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[2].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[2].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[2].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[2].except.has_value(), false);
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
+ * action_ordinal_failtest2 test cases
+ *************************************************************************************/
BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest2, validating_tester) { try {
produce_blocks(1);
create_account("testapi"_n );
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("bob"_n );
set_code( "bob"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("charlie"_n );
set_code( "charlie"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("david"_n );
set_code( "david"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("erin"_n );
set_code( "erin"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("fail3"_n ); // <- make action 3 fails in the middle
produce_blocks(1);
transaction_trace_ptr txn_trace =
CALL_TEST_FUNCTION_NO_THROW( *this, "test_action", "test_action_ordinal1", {});
BOOST_REQUIRE_EQUAL( validate(), true );
BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true);
BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 8);
auto &atrace = txn_trace->action_traces;
// executed
BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0);
BOOST_REQUIRE_EQUAL((int)atrace[0].closest_unnotified_ancestor_action_ordinal, 0);
BOOST_REQUIRE_EQUAL(atrace[0].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[0].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<unsigned_int>(atrace[0].return_value), unsigned_int(1) );
BOOST_REQUIRE_EQUAL(atrace[0].except.has_value(), false);
int start_gseq = atrace[0].receipt->global_sequence;
// executed
BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2);
BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[1].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[1].receiver, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[1].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<std::string>(atrace[1].return_value), "bob" );
BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[2].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[2].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[2].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[2].except.has_value(), false);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4);
BOOST_REQUIRE_EQUAL((int)atrace[3].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[3].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[3].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[3].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[3].act.name, TEST_METHOD("test_action", "test_action_ordinal3"));
BOOST_REQUIRE_EQUAL(atrace[3].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[3].except.has_value(), false);
// hey exception is here
BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5);
BOOST_REQUIRE_EQUAL((int)atrace[4].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[4].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[4].receiver, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[4].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[4].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[4].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[4].except.has_value(), true);
BOOST_REQUIRE_EQUAL(atrace[4].except->code(), 3050003);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6);
BOOST_REQUIRE_EQUAL((int)atrace[5].creator_action_ordinal, 2);
BOOST_REQUIRE_EQUAL((int)atrace[5].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[5].receiver, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[5].act.account, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[5].act.name, TEST_METHOD("test_action", "test_action_ordinal_foo"));
BOOST_REQUIRE_EQUAL(atrace[5].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[5].except.has_value(), false);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7);
BOOST_REQUIRE_EQUAL((int)atrace[6].creator_action_ordinal,2);
BOOST_REQUIRE_EQUAL((int)atrace[6].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[6].receiver, "david"_n);
BOOST_REQUIRE_EQUAL(atrace[6].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[6].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[6].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[6].except.has_value(), false);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8);
BOOST_REQUIRE_EQUAL((int)atrace[7].creator_action_ordinal, 5);
BOOST_REQUIRE_EQUAL((int)atrace[7].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[7].receiver, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[7].act.account, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[7].act.name, TEST_METHOD("test_action", "test_action_ordinal_bar"));
BOOST_REQUIRE_EQUAL(atrace[7].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[7].except.has_value(), false);
} FC_LOG_AND_RETHROW() }
/*************************************************************************************
+ * action_ordinal_failtest3 test cases
+ *************************************************************************************/
BOOST_FIXTURE_TEST_CASE(action_ordinal_failtest3, validating_tester) { try {
produce_blocks(1);
create_account("testapi"_n );
set_code( "testapi"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("bob"_n );
set_code( "bob"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("charlie"_n );
set_code( "charlie"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("david"_n );
set_code( "david"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("erin"_n );
set_code( "erin"_n, test_contracts::test_api_wasm() );
produce_blocks(1);
create_account("failnine"_n ); // <- make action 9 fails in the middle
produce_blocks(1);
transaction_trace_ptr txn_trace =
CALL_TEST_FUNCTION_NO_THROW( *this, "test_action", "test_action_ordinal1", {});
BOOST_REQUIRE_EQUAL( validate(), true );
BOOST_REQUIRE_EQUAL( txn_trace != nullptr, true);
BOOST_REQUIRE_EQUAL( txn_trace->action_traces.size(), 11);
auto &atrace = txn_trace->action_traces;
// executed
BOOST_REQUIRE_EQUAL((int)atrace[0].action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[0].creator_action_ordinal, 0);
BOOST_REQUIRE_EQUAL((int)atrace[0].closest_unnotified_ancestor_action_ordinal, 0);
BOOST_REQUIRE_EQUAL(atrace[0].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[0].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[0].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<unsigned_int>(atrace[0].return_value), unsigned_int(1) );
BOOST_REQUIRE_EQUAL(atrace[0].except.has_value(), false);
int start_gseq = atrace[0].receipt->global_sequence;
// executed
BOOST_REQUIRE_EQUAL((int)atrace[1].action_ordinal,2);
BOOST_REQUIRE_EQUAL((int)atrace[1].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[1].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[1].receiver, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[1].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[1].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<std::string>(atrace[1].return_value), "bob" );
BOOST_REQUIRE_EQUAL(atrace[1].receipt->global_sequence, start_gseq + 1);
// executed
BOOST_REQUIRE_EQUAL((int)atrace[2].action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[2].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[2].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[2].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[2].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[2].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<name>(atrace[2].return_value), name("five") );
BOOST_REQUIRE_EQUAL(atrace[2].receipt->global_sequence, start_gseq + 4);
// fails here
BOOST_REQUIRE_EQUAL((int)atrace[3].action_ordinal, 4);
BOOST_REQUIRE_EQUAL((int)atrace[3].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[3].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[3].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[3].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[3].act.name, TEST_METHOD("test_action", "test_action_ordinal3"));
BOOST_REQUIRE_EQUAL(atrace[3].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[3].except.has_value(), true);
BOOST_REQUIRE_EQUAL(atrace[3].except->code(), 3050003);
// executed
BOOST_REQUIRE_EQUAL((int)atrace[4].action_ordinal, 5);
BOOST_REQUIRE_EQUAL((int)atrace[4].creator_action_ordinal, 1);
BOOST_REQUIRE_EQUAL((int)atrace[4].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[4].receiver, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[4].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[4].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[4].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<std::string>(atrace[4].return_value), "charlie" );
BOOST_REQUIRE_EQUAL(atrace[4].receipt->global_sequence, start_gseq + 2);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[5].action_ordinal, 6);
BOOST_REQUIRE_EQUAL((int)atrace[5].creator_action_ordinal, 2);
BOOST_REQUIRE_EQUAL((int)atrace[5].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[5].receiver, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[5].act.account, "bob"_n);
BOOST_REQUIRE_EQUAL(atrace[5].act.name, TEST_METHOD("test_action", "test_action_ordinal_foo"));
BOOST_REQUIRE_EQUAL(atrace[5].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[5].except.has_value(), false);
// executed
BOOST_REQUIRE_EQUAL((int)atrace[6].action_ordinal, 7);
BOOST_REQUIRE_EQUAL((int)atrace[6].creator_action_ordinal,2);
BOOST_REQUIRE_EQUAL((int)atrace[6].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[6].receiver, "david"_n);
BOOST_REQUIRE_EQUAL(atrace[6].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[6].act.name, TEST_METHOD("test_action", "test_action_ordinal1"));
BOOST_REQUIRE_EQUAL(atrace[6].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<std::string>(atrace[6].return_value), "david" );
BOOST_REQUIRE_EQUAL(atrace[6].receipt->global_sequence, start_gseq + 3);
// not executed
BOOST_REQUIRE_EQUAL((int)atrace[7].action_ordinal, 8);
BOOST_REQUIRE_EQUAL((int)atrace[7].creator_action_ordinal, 5);
BOOST_REQUIRE_EQUAL((int)atrace[7].closest_unnotified_ancestor_action_ordinal, 1);
BOOST_REQUIRE_EQUAL(atrace[7].receiver, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[7].act.account, "charlie"_n);
BOOST_REQUIRE_EQUAL(atrace[7].act.name, TEST_METHOD("test_action", "test_action_ordinal_bar"));
BOOST_REQUIRE_EQUAL(atrace[7].receipt.has_value(), false);
BOOST_REQUIRE_EQUAL(atrace[7].except.has_value(), false);
// executed
BOOST_REQUIRE_EQUAL((int)atrace[8].action_ordinal, 9);
BOOST_REQUIRE_EQUAL((int)atrace[8].creator_action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[8].closest_unnotified_ancestor_action_ordinal, 3);
BOOST_REQUIRE_EQUAL(atrace[8].receiver, "david"_n);
BOOST_REQUIRE_EQUAL(atrace[8].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[8].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[8].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<bool>(atrace[8].return_value), true );
BOOST_REQUIRE_EQUAL(atrace[8].receipt->global_sequence, start_gseq + 5);
// executed
BOOST_REQUIRE_EQUAL((int)atrace[9].action_ordinal, 10);
BOOST_REQUIRE_EQUAL((int)atrace[9].creator_action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[9].closest_unnotified_ancestor_action_ordinal, 3);
BOOST_REQUIRE_EQUAL(atrace[9].receiver, "erin"_n);
BOOST_REQUIRE_EQUAL(atrace[9].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[9].act.name, TEST_METHOD("test_action", "test_action_ordinal2"));
BOOST_REQUIRE_EQUAL(atrace[9].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(fc::raw::unpack<signed_int>(atrace[9].return_value), signed_int(7) );
BOOST_REQUIRE_EQUAL(atrace[9].receipt->global_sequence, start_gseq + 6);
// executed
BOOST_REQUIRE_EQUAL((int)atrace[10].action_ordinal, 11);
BOOST_REQUIRE_EQUAL((int)atrace[10].creator_action_ordinal, 3);
BOOST_REQUIRE_EQUAL((int)atrace[10].closest_unnotified_ancestor_action_ordinal, 3);
BOOST_REQUIRE_EQUAL(atrace[10].receiver, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[10].act.account, "testapi"_n);
BOOST_REQUIRE_EQUAL(atrace[10].act.name, TEST_METHOD("test_action", "test_action_ordinal4"));
BOOST_REQUIRE_EQUAL(atrace[10].receipt.has_value(), true);
BOOST_REQUIRE_EQUAL(atrace[10].return_value.size(), 0 );
BOOST_REQUIRE_EQUAL(atrace[10].receipt->global_sequence, start_gseq + 7);
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE(action_results_tests) { try {
validating_tester t;
t.produce_blocks(2);
t.create_account( "test"_n );
t.set_code( "test"_n, test_contracts::action_results_wasm() );
t.produce_blocks(1);
auto call_autoresret_and_check = [&]( account_name contract, account_name signer, action_name action, auto&& checker ) {
signed_transaction trx;
trx.actions.emplace_back( vector<permission_level>{{signer, config::active_name}}, contract, action, bytes{} );
t.set_transaction_headers( trx, t.DEFAULT_EXPIRATION_DELTA );
trx.sign( t.get_private_key(signer, "active"), t.control->get_chain_id() );
auto res = t.push_transaction(trx);
checker( res );
};
call_autoresret_and_check( "test"_n, "test"_n, "actionresret"_n, [&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );
auto &atrace = res->action_traces;
BOOST_REQUIRE_EQUAL( atrace[0].receipt.has_value(), true );
BOOST_REQUIRE_EQUAL( atrace[0].return_value.size(), 4 );
BOOST_REQUIRE_EQUAL( fc::raw::unpack<int>(atrace[0].return_value), 10 );
} );
t.produce_blocks(1);
call_autoresret_and_check( "test"_n, "test"_n, "retlim"_n, [&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );
auto &atrace = res->action_traces;
BOOST_REQUIRE_EQUAL( atrace[0].receipt.has_value(), true );
BOOST_REQUIRE_EQUAL( atrace[0].return_value.size(), 256 );
vector<char> expected_vec(256 - 2, '0');//2 bytes for size of type unsigned_int
vector<char> ret_vec = fc::raw::unpack<vector<char>>(atrace[0].return_value);
BOOST_REQUIRE_EQUAL_COLLECTIONS( ret_vec.begin(),
ret_vec.end(),
expected_vec.begin(),
expected_vec.end() );
} );
t.produce_blocks(1);
BOOST_REQUIRE_THROW(call_autoresret_and_check( "test"_n, "test"_n, "retoverlim"_n, [&]( auto res ) {}),
action_return_value_exception);
t.produce_blocks(1);
BOOST_REQUIRE_THROW(call_autoresret_and_check( "test"_n, "test"_n, "ret1overlim"_n, [&]( auto res ) {}),
action_return_value_exception);
t.produce_blocks(1);
t.set_code( config::system_account_name, test_contracts::action_results_wasm() );
t.produce_blocks(1);
call_autoresret_and_check( config::system_account_name,
config::system_account_name,
"retmaxlim"_n,
[&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );
auto &atrace = res->action_traces;
BOOST_REQUIRE_EQUAL( atrace[0].receipt.has_value(), true );
BOOST_REQUIRE_EQUAL( atrace[0].return_value.size(), 20*1024*1024 );
vector<char> expected_vec(20*1024*1024, '1');
BOOST_REQUIRE_EQUAL_COLLECTIONS( atrace[0].return_value.begin(),
atrace[0].return_value.end(),
expected_vec.begin(),
expected_vec.end() );
} );
t.produce_blocks(1);
BOOST_REQUIRE_THROW(call_autoresret_and_check( config::system_account_name,
config::system_account_name,
"setliminv"_n,
[&]( auto res ) {}),
action_validate_exception);
} FC_LOG_AND_RETHROW() }
static const char get_code_hash_wast[] = R"=====(
(module
(import "env" "get_code_hash" (func $get_code_hash (param i64 i32 i32 i32) (result i32)))
(import "env" "prints_l" (func $prints_l (param i32 i32)))
(import "env" "printui" (func $printui (param i64)))
(import "env" "printhex" (func $printhex (param i32 i32)))
(memory $0 32)
(data (i32.const 4) ":")
(data (i32.const 8) "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64) (param $2 i64)
(call $printui (i64.extend_u/i32
(call $get_code_hash
(get_local $2)
(i32.const 0)
(i32.const 8)
(i32.const 43)
)
))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load8_u offset=8 (i32.const 0)))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load offset=9 (i32.const 0)))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printhex (i32.const 17) (i32.const 32))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load8_u offset=49 (i32.const 0)))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load8_u offset=50 (i32.const 0)))
)
)
)=====";
BOOST_AUTO_TEST_CASE(get_code_hash_tests) { try {
validating_tester t;
t.produce_blocks(2);
t.create_account("gethash"_n);
t.create_account("test"_n);
t.set_code("gethash"_n, get_code_hash_wast);
t.produce_blocks(1);
auto check = [&](account_name acc, uint64_t expected_seq) {
fc::sha256 expected_code_hash;
auto obj = t.control->db().find<account_metadata_object,by_name>(acc);
if(obj)
expected_code_hash = obj->code_hash;
auto expected = "43:0:" + std::to_string(expected_seq) +
":" + expected_code_hash.str() + ":0:0";
signed_transaction trx;
trx.actions.emplace_back(vector<permission_level>{{"gethash"_n, config::active_name}}, "gethash"_n, acc, bytes{});
t.set_transaction_headers(trx, t.DEFAULT_EXPIRATION_DELTA);
trx.sign(t.get_private_key("gethash"_n, "active"), t.control->get_chain_id());
auto tx_trace = t.push_transaction(trx);
BOOST_CHECK_EQUAL(tx_trace->receipt->status, transaction_receipt::executed);
BOOST_REQUIRE(tx_trace->action_traces.front().console == expected);
t.produce_block();
};
check("gethash"_n, 1);
check("nonexisting"_n, 0);
check("test"_n, 0);
t.set_code("test"_n, test_contracts::test_api_wasm());
check("test"_n, 1);
t.set_code("test"_n, get_code_hash_wast);
check("test"_n, 2);
t.set_code("test"_n, std::vector<uint8_t>{});
check("test"_n, 3);
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END()