From 07f830ffadcd695574a039860f682eb35a2cb4a9 Mon Sep 17 00:00:00 2001 From: Jonathan Giszczak Date: Fri, 17 Jul 2020 09:23:12 -0600 Subject: [PATCH 1/2] Convert msig to inline execution from deferred transactions --- CMakeLists.txt | 5 +- build.sh | 6 + contracts/eosio.msig/src/eosio.msig.cpp | 10 +- scripts/helper.sh | 55 +++- tests/CMakeLists.txt | 4 +- tests/contracts.hpp.in | 2 + tests/eosio.msig_tests.cpp | 298 +++++++++++++++--- tests/eosio.system_tests.cpp | 14 +- tests/eosio.wrap_tests.cpp | 104 +++--- tests/test_contracts/CMakeLists.txt | 2 + .../test_contracts/sendinline/CMakeLists.txt | 4 + .../sendinline/src/sendinline.cpp | 25 ++ 12 files changed, 435 insertions(+), 94 deletions(-) create mode 100644 tests/test_contracts/CMakeLists.txt create mode 100644 tests/test_contracts/sendinline/CMakeLists.txt create mode 100644 tests/test_contracts/sendinline/src/sendinline.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5783e8319..ce72ba4df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,12 +16,13 @@ endif() include(ExternalProject) find_package(eosio.cdt) +message(STATUS "Using eosio.cdt in ${EOSIO_CDT_ROOT}") message(STATUS "Building eosio.contracts v${VERSION_FULL}") set(EOSIO_CDT_VERSION_MIN "1.6") set(EOSIO_CDT_VERSION_SOFT_MAX "1.6") -#set(EOSIO_CDT_VERSION_HARD_MAX "") +set(EOSIO_CDT_VERSION_HARD_MAX "1.6") ### Check the version of eosio.cdt set(VERSION_MATCH_ERROR_MSG "") @@ -82,7 +83,7 @@ if(BUILD_TESTS) ExternalProject_Add( contracts_unit_tests LIST_SEPARATOR | # Use the alternate list separator - CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DCMAKE_PREFIX_PATH=${TEST_PREFIX_PATH} -DCMAKE_FRAMEWORK_PATH=${TEST_FRAMEWORK_PATH} -DCMAKE_MODULE_PATH=${TEST_MODULE_PATH} -DEOSIO_ROOT=${EOSIO_ROOT} -DLLVM_DIR=${LLVM_DIR} -DBOOST_ROOT=${BOOST_ROOT} + CMAKE_ARGS -Deosio.cdt_DIR=${eosio.cdt_DIR} -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DCMAKE_PREFIX_PATH=${TEST_PREFIX_PATH} -DCMAKE_FRAMEWORK_PATH=${TEST_FRAMEWORK_PATH} -DCMAKE_MODULE_PATH=${TEST_MODULE_PATH} -DEOSIO_ROOT=${EOSIO_ROOT} -DLLVM_DIR=${LLVM_DIR} -DBOOST_ROOT=${BOOST_ROOT} SOURCE_DIR ${CMAKE_SOURCE_DIR}/tests BINARY_DIR ${CMAKE_BINARY_DIR}/tests BUILD_ALWAYS 1 diff --git a/build.sh b/build.sh index 558736ccc..43a2c30b0 100755 --- a/build.sh +++ b/build.sh @@ -12,6 +12,8 @@ function usage() { exit 1 } +EOSIO_MAX_VERSION_MAJOR=1 +EOSIO_MAX_VERSION_MINOR=8 BUILD_TESTS=false if [ $# -ne 0 ]; then @@ -55,6 +57,9 @@ fi if [[ ${BUILD_TESTS} == true ]]; then # Prompt user for location of eosio. eosio-directory-prompt + + default-boost-directories; + echo "Using Boost installation at: ${BOOST_INSTALL_DIR}" fi # Prompt user for location of eosio.cdt. @@ -71,6 +76,7 @@ if [[ ${BUILD_TESTS} == true ]]; then # Include EOSIO_INSTALL_DIR in CMAKE_FRAMEWORK_PATH echo "Using EOSIO installation at: $EOSIO_INSTALL_DIR" export CMAKE_FRAMEWORK_PATH="${EOSIO_INSTALL_DIR}:${CMAKE_FRAMEWORK_PATH}" + export CMAKE_PREFIX_PATH="${BOOST_INSTALL_DIR}" fi printf "\t=========== Building eosio.contracts ===========\n\n" diff --git a/contracts/eosio.msig/src/eosio.msig.cpp b/contracts/eosio.msig/src/eosio.msig.cpp index 8fd75213d..16cf117bc 100644 --- a/contracts/eosio.msig/src/eosio.msig.cpp +++ b/contracts/eosio.msig/src/eosio.msig.cpp @@ -146,9 +146,14 @@ void multisig::exec( name proposer, name proposal_name, name executer ) { proposals proptable( get_self(), proposer.value ); auto& prop = proptable.get( proposal_name.value, "proposal not found" ); transaction_header trx_header; + std::vector context_free_actions; + std::vector actions; datastream ds( prop.packed_transaction.data(), prop.packed_transaction.size() ); ds >> trx_header; check( trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); + ds >> context_free_actions; + check( context_free_actions.empty(), "not allowed to `exec` a transaction with context-free actions" ); + ds >> actions; approvals apptable( get_self(), proposer.value ); auto apps_it = apptable.find( proposal_name.value ); @@ -184,8 +189,9 @@ void multisig::exec( name proposer, name proposal_name, name executer ) { check( res > 0, "transaction authorization failed" ); - send_deferred( (uint128_t(proposer.value) << 64) | proposal_name.value, executer, - prop.packed_transaction.data(), prop.packed_transaction.size() ); + for (const auto& act : actions) { + act.send(); + } proptable.erase(prop); } diff --git a/scripts/helper.sh b/scripts/helper.sh index 6ef760b30..39d610618 100755 --- a/scripts/helper.sh +++ b/scripts/helper.sh @@ -22,6 +22,37 @@ function check-version-numbers() { exit 0 } +# Ensures passed in boost version values are supported. +function check-boost-version-numbers() { + CHECK_VERSION_MAJOR=$1 + CHECK_VERSION_MINOR=$2 + + if [[ ${BOOST_MIN_VERSION_MAJOR} && ${BOOST_MIN_VERSION_MAJOR} != "" ]]; then + if [[ $CHECK_VERSION_MAJOR -lt $BOOST_MIN_VERSION_MAJOR ]]; then + exit 1 + fi + fi + if [[ ${BOOST_MAX_VERSION_MAJOR} && ${BOOST_MAX_VERSION_MAJOR} != "" ]]; then + if [[ $CHECK_VERSION_MAJOR -gt $BOOST_MAX_VERSION_MAJOR ]]; then + exit 1 + fi + fi + if [[ $CHECK_VERSION_MAJOR -eq $BOOST_MIN_VERSION_MAJOR ]]; then + if [[ ${BOOST_MIN_VERSION_MINOR} && ${BOOST_MIN_VERSION_MINOR} != "" ]]; then + if [[ $CHECK_VERSION_MINOR -lt $BOOST_MIN_VERSION_MINOR ]]; then + exit 1 + fi + fi + fi + if [[ $CHECK_VERSION_MAJOR -eq $BOOST_MAX_VERSION_MAJOR ]]; then + if [[ ${BOOST_MAX_VERSION_MINOR} && ${BOOST_MAX_VERSION_MINOR} != "" ]]; then + if [[ $CHECK_VERSION_MINOR -gt $BOOST_MAX_VERSION_MINOR ]]; then + exit 1 + fi + fi + fi + exit 0 +} # Handles choosing which EOSIO directory to select when the default location is used. function default-eosio-directories() { @@ -46,7 +77,6 @@ function default-eosio-directories() { done } - # Prompts or sets default behavior for choosing EOSIO directory. function eosio-directory-prompt() { if [[ -z $EOSIO_DIR_PROMPT ]]; then @@ -83,6 +113,29 @@ function eosio-directory-prompt() { export EOSIO_INSTALL_DIR="${EOSIO_DIR_PROMPT:-${HOME}/eosio/${EOSIO_VERSION}}" } +# Handles choosing which Boost directory to select. +function default-boost-directories() { + REGEX='boost_[0-9]+([_][0-9]+)?+([_][0-9]+)?$' + ALL_BOOST_SUBDIRS=() + if [[ -d ${HOME}/eosio ]]; then + ALL_BOOST_SUBDIRS=($(ls ${EOSIO_INSTALL_DIR}/src | sort -V)) + fi + for ITEM in "${ALL_BOOST_SUBDIRS[@]}"; do + if [[ "$ITEM" =~ $REGEX ]]; then + DIR_MAJOR=$(echo $ITEM | cut -f2 -d '_') + DIR_MINOR=$(echo $ITEM | cut -f3 -d '_') + if $(check-boost-version-numbers $DIR_MAJOR $DIR_MINOR); then + PROMPT_BOOST_DIRS+=($ITEM) + fi + fi + done + for ITEM in "${PROMPT_BOOST_DIRS[@]}"; do + if [[ "$ITEM" =~ $REGEX ]]; then + BOOST_VERSION=$ITEM + fi + done + export BOOST_INSTALL_DIR=${EOSIO_INSTALL_DIR}/src/${BOOST_VERSION} +} # Prompts or default behavior for choosing EOSIO.CDT directory. function cdt-directory-prompt() { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a14236482..77a0845b3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,10 @@ cmake_minimum_required( VERSION 3.5 ) +add_subdirectory(test_contracts) + set(EOSIO_VERSION_MIN "1.8") set(EOSIO_VERSION_SOFT_MAX "1.8") -#set(EOSIO_VERSION_HARD_MAX "") +set(EOSIO_VERSION_HARD_MAX "1.8") find_package(eosio) diff --git a/tests/contracts.hpp.in b/tests/contracts.hpp.in index f7780d3e3..253a2079d 100644 --- a/tests/contracts.hpp.in +++ b/tests/contracts.hpp.in @@ -22,6 +22,8 @@ struct contracts { static std::vector system_abi_old() { return read_abi("${CMAKE_SOURCE_DIR}/test_contracts/eosio.system.old/eosio.system.abi"); } static std::vector msig_wasm_old() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/eosio.msig.old/eosio.msig.wasm"); } static std::vector msig_abi_old() { return read_abi("${CMAKE_SOURCE_DIR}/test_contracts/eosio.msig.old/eosio.msig.abi"); } + static std::vector sendinline_wasm() {return read_wasm("${CMAKE_BINARY_DIR}/test_contracts/sendinline/sendinline.wasm"); } + static std::vector sendinline_abi() {return read_abi("${CMAKE_BINARY_DIR}/test_contracts/sendinline/sendinline.abi"); } }; }; }} //ns eosio::testing diff --git a/tests/eosio.msig_tests.cpp b/tests/eosio.msig_tests.cpp index e28e229c0..27c8a1eeb 100644 --- a/tests/eosio.msig_tests.cpp +++ b/tests/eosio.msig_tests.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -210,8 +211,7 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_execute, eosio_msig_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); push_action( N(alice), N(exec), mvo() ("proposer", "alice") @@ -296,8 +296,7 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_by_two, eosio_msig_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); push_action( N(alice), N(exec), mvo() @@ -329,6 +328,14 @@ BOOST_FIXTURE_TEST_CASE( propose_with_wrong_requested_auth, eosio_msig_tester ) BOOST_FIXTURE_TEST_CASE( big_transaction, eosio_msig_tester ) try { + //change `default_max_inline_action_size` to 512 KB + eosio::chain::chain_config params = control->get_global_properties().configuration; + params.max_inline_action_size = 512 * 1024; + base_tester::push_action( config::system_account_name, N(setparams), config::system_account_name, mutable_variant_object() + ("params", params) ); + + produce_blocks(); + vector perm = { { N(alice), config::active_name }, { N(bob), config::active_name } }; auto wasm = contracts::util::exchange_wasm(); @@ -379,8 +386,7 @@ BOOST_FIXTURE_TEST_CASE( big_transaction, eosio_msig_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); push_action( N(alice), N(exec), mvo() @@ -496,22 +502,36 @@ BOOST_FIXTURE_TEST_CASE( update_system_contract_all_approve, eosio_msig_tester ) ("level", permission_level{ N(carol), config::active_name }) ); // execute by alice to replace the eosio system contract - transaction_trace_ptr trace; - control->applied_transaction.connect( - [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } - } ); - - push_action( N(alice), N(exec), mvo() - ("proposer", "alice") - ("proposal_name", "first") - ("executer", "alice") + transaction_trace_ptr trx_trace; + trx_trace = push_action( N(alice), N(exec), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("executer", "alice") ); - BOOST_REQUIRE( bool(trace) ); - BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() ); - BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status ); + BOOST_REQUIRE( bool(trx_trace) ); + BOOST_REQUIRE( trx_trace->receipt.valid() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trx_trace->receipt->status ); + BOOST_REQUIRE_EQUAL( 2, trx_trace->action_traces.size() ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(0).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).closest_unnotified_ancestor_action_ordinal ); + // EOSIO 1.8 N() macro returns a uint64_t rather than a struct name + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, action_name{trx_trace->action_traces.at(0).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, name{trx_trace->action_traces.at(0).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(exec)}, name{trx_trace->action_traces.at(0).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(alice)}, name{trx_trace->action_traces.at(0).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(0).act.authorization[0].permission} ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{2}, trx_trace->action_traces.at(1).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).closest_unnotified_ancestor_action_ordinal ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, action_name{trx_trace->action_traces.at(1).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, name{trx_trace->action_traces.at(1).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(setcode)}, name{trx_trace->action_traces.at(1).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, name{trx_trace->action_traces.at(1).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(1).act.authorization[0].permission} ); // can't create account because system contract was replaced by the reject_all contract @@ -625,24 +645,38 @@ BOOST_FIXTURE_TEST_CASE( update_system_contract_major_approve, eosio_msig_tester ("proposal_name", "first") ("level", permission_level{ N(apple), config::active_name }) ); - // execute by alice to replace the eosio system contract - transaction_trace_ptr trace; - control->applied_transaction.connect( - [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } - } ); // execute by another producer different from proposer - push_action( N(apple), N(exec), mvo() - ("proposer", "alice") - ("proposal_name", "first") - ("executer", "apple") + transaction_trace_ptr trx_trace; + trx_trace = push_action( N(apple), N(exec), mvo() + ("proposer", "alice") + ("proposal_name", "first") + ("executer", "apple") ); - BOOST_REQUIRE( bool(trace) ); - BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() ); - BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status ); + BOOST_REQUIRE( bool(trx_trace) ); + BOOST_REQUIRE( trx_trace->receipt.valid() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trx_trace->receipt->status ); + BOOST_REQUIRE_EQUAL( 2, trx_trace->action_traces.size() ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(0).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).closest_unnotified_ancestor_action_ordinal ); + // EOSIO 1.8 N() macro returns a uint64_t rather than a struct name + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, action_name{trx_trace->action_traces.at(0).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, name{trx_trace->action_traces.at(0).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(exec)}, name{trx_trace->action_traces.at(0).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(apple)}, name{trx_trace->action_traces.at(0).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(0).act.authorization[0].permission} ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{2}, trx_trace->action_traces.at(1).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).closest_unnotified_ancestor_action_ordinal ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, action_name{trx_trace->action_traces.at(1).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, name{trx_trace->action_traces.at(1).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(setcode)}, name{trx_trace->action_traces.at(1).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, name{trx_trace->action_traces.at(1).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(1).act.authorization[0].permission} ); // can't create account because system contract was replaced by the reject_all contract @@ -731,8 +765,7 @@ BOOST_FIXTURE_TEST_CASE( propose_invalidate_approve, eosio_msig_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); push_action( N(bob), N(exec), mvo() @@ -774,8 +807,7 @@ BOOST_FIXTURE_TEST_CASE( approve_execute_old, eosio_msig_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); push_action( N(alice), N(exec), mvo() @@ -878,8 +910,7 @@ BOOST_FIXTURE_TEST_CASE( approve_by_two_old, eosio_msig_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); push_action( N(alice), N(exec), mvo() @@ -928,8 +959,7 @@ BOOST_FIXTURE_TEST_CASE( approve_with_hash, eosio_msig_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); push_action( N(alice), N(exec), mvo() @@ -985,4 +1015,188 @@ BOOST_FIXTURE_TEST_CASE( switch_proposal_and_fail_approve_with_hash, eosio_msig_ ); } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( sendinline, eosio_msig_tester ) try { + create_accounts( {N(sendinline)} ); + set_code( N(sendinline), contracts::util::sendinline_wasm() ); + set_abi( N(sendinline), contracts::util::sendinline_abi().data() ); + + create_accounts( {N(wrongcon)} ); + set_code( N(wrongcon), contracts::util::sendinline_wasm() ); + set_abi( N(wrongcon), contracts::util::sendinline_abi().data() ); + produce_blocks(); + + action act = get_action( config::system_account_name, N(reqauth), {}, mvo()("from", "alice")); + + BOOST_REQUIRE_EXCEPTION( base_tester::push_action( N(sendinline), N(send), N(bob), mvo() + ("contract", "eosio") + ("action_name", "reqauth") + ("auths", std::vector{ {N(alice), config::active_name} }) + ("payload", act.data) + ), + unsatisfied_authorization, + fc_exception_message_starts_with("transaction declares authority") + ); + + base_tester::push_action(config::system_account_name, N(updateauth), N(alice), mvo() + ("account", "alice") + ("permission", "perm") + ("parent", "active") + ("auth", authority{ 1, {}, {permission_level_weight{ {N(sendinline), config::eosio_code_name}, 1}}, {} }) + ); + produce_blocks(); + + base_tester::push_action( config::system_account_name, N(linkauth), N(alice), mvo() + ("account", "alice") + ("code", "eosio") + ("type", "reqauth") + ("requirement", "perm") + ); + produce_blocks(); + + transaction_trace_ptr trx_trace; + trx_trace = base_tester::push_action( N(sendinline), N(send), N(bob), mvo() + ("contract", "eosio") + ("action_name", "reqauth") + ("auths", std::vector{ {N(alice), N(perm)} }) + ("payload", act.data) + ); + produce_blocks(); + + BOOST_REQUIRE( bool(trx_trace) ); + BOOST_REQUIRE( trx_trace->receipt.valid() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trx_trace->receipt->status ); + BOOST_REQUIRE_EQUAL( 2, trx_trace->action_traces.size() ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(0).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).closest_unnotified_ancestor_action_ordinal ); + // EOSIO 1.8 N() macro returns a uint64_t rather than a struct name + BOOST_REQUIRE_EQUAL( name{N(sendinline)}, action_name{trx_trace->action_traces.at(0).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(sendinline)}, name{trx_trace->action_traces.at(0).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(send)}, name{trx_trace->action_traces.at(0).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(bob)}, name{trx_trace->action_traces.at(0).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(0).act.authorization[0].permission} ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{2}, trx_trace->action_traces.at(1).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).closest_unnotified_ancestor_action_ordinal ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, action_name{trx_trace->action_traces.at(1).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, name{trx_trace->action_traces.at(1).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(reqauth)}, name{trx_trace->action_traces.at(1).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(alice)}, name{trx_trace->action_traces.at(1).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(perm)}, name{trx_trace->action_traces.at(1).act.authorization[0].permission} ); + + + base_tester::push_action(config::system_account_name, updateauth::get_name(), N(sendinline), mvo() + ("account", "sendinline") + ("permission", name(config::active_name)) + ("parent", name(config::owner_name)) + ("auth", authority(1, {key_weight{get_public_key("sendinline", "active"), 1}}, { + permission_level_weight{{"sendinline", config::eosio_code_name}, 1} + } + )) + ); + produce_blocks(); + + action approve_act = get_action(N(eosio.msig), N(approve), {}, mvo() + ("proposer", "bob") + ("proposal_name", "first") + ("level", permission_level{N(sendinline), N(eosio.code)}) + ); + + action unapprove_act = get_action(N(eosio.msig), N(unapprove), {}, mvo() + ("proposer", "bob") + ("proposal_name", "first") + ("level", permission_level{N(sendinline), N(eosio.code)}) + ); + + transaction trx = reqauth( N(alice), {permission_level{N(alice), N(perm)}}, abi_serializer_max_time ); + + base_tester::push_action( N(eosio.msig), N(propose), N(bob), mvo() + ("proposer", "bob") + ("proposal_name", "first") + ("trx", trx) + ("requested", std::vector{{ N(sendinline), N(eosio.code) }}) + ); + produce_blocks(); +#if 0 + // `approve` shall fail when being sent from the wrong contract + BOOST_REQUIRE_EXCEPTION( base_tester::push_action( N(wrongcon), N(send), N(bob), mvo() + ("contract", "eosio.msig") + ("action_name", "approve") + ("auths", std::vector{{N(bob), config::active_name}}) + ("payload", approve_act.data) + ), + eosio_assert_message_exception, + eosio_assert_message_is("wrong contract sent `approve` action for eosio.code permmission") + ); + // `approve` shall succeed when being sent from the correct contract + base_tester::push_action( N(sendinline), N(send), N(bob), mvo() + ("contract", "eosio.msig") + ("action_name", "approve") + ("auths", std::vector{{N(bob), config::eosio_code_name}}) + ("payload", approve_act.data) + ); + produce_blocks(); + + // `unapprove` shall fail when being sent from the wrong contract + BOOST_REQUIRE_EXCEPTION( base_tester::push_action( N(wrongcon), N(send), N(bob), mvo() + ("contract", N(eosio.msig)) + ("action_name", "unapprove") + ("auths", std::vector{}) + ("payload", unapprove_act.data) + ), + eosio_assert_message_exception, + eosio_assert_message_is("wrong contract sent `unapprove` action for eosio.code permmission") + ); + + // `unapprove` shall succeed when being sent from the correct contract + base_tester::push_action( N(sendinline), N(send), N(bob), mvo() + ("contract", N(eosio.msig)) + ("action_name", "unapprove") + ("auths", std::vector{}) + ("payload", unapprove_act.data) + ); + produce_blocks(); +#endif + // `approve` to get back into a state ready for `exec` + base_tester::push_action( N(sendinline), N(send), N(bob), mvo() + ("contract", "eosio.msig") + ("action_name", "approve") + ("auths", std::vector{{N(sendinline), config::active_name}}) + ("payload", approve_act.data) + ); + produce_blocks(); + + trx_trace = base_tester::push_action( N(eosio.msig), N(exec), N(bob), mvo() + ("proposer", "bob") + ("proposal_name", "first") + ("executer", "bob") + ); + + BOOST_REQUIRE( bool(trx_trace) ); + BOOST_REQUIRE( trx_trace->receipt.valid() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trx_trace->receipt->status ); + BOOST_REQUIRE_EQUAL( 2, trx_trace->action_traces.size() ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(0).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).closest_unnotified_ancestor_action_ordinal ); + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, action_name{trx_trace->action_traces.at(0).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, name{trx_trace->action_traces.at(0).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(exec)}, name{trx_trace->action_traces.at(0).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(bob)}, name{trx_trace->action_traces.at(0).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(0).act.authorization[0].permission} ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{2}, trx_trace->action_traces.at(1).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).closest_unnotified_ancestor_action_ordinal ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, action_name{trx_trace->action_traces.at(1).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio)}, name{trx_trace->action_traces.at(1).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(reqauth)}, name{trx_trace->action_traces.at(1).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(alice)}, name{trx_trace->action_traces.at(1).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(perm)}, name{trx_trace->action_traces.at(1).act.authorization[0].permission} ); + +} FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/eosio.system_tests.cpp b/tests/eosio.system_tests.cpp index 7021e927b..c43f106a2 100644 --- a/tests/eosio.system_tests.cpp +++ b/tests/eosio.system_tests.cpp @@ -2533,6 +2533,14 @@ BOOST_FIXTURE_TEST_CASE(producers_upgrade_system_contract, eosio_system_tester) abi_serializer msig_abi_ser = initialize_multisig(); auto producer_names = active_and_vote_producers(); + //change `default_max_inline_action_size` to 512 KB + eosio::chain::chain_config params = control->get_global_properties().configuration; + params.max_inline_action_size = 512 * 1024; + base_tester::push_action( config::system_account_name, N(setparams), config::system_account_name, mutable_variant_object() + ("params", params) ); + + produce_blocks(); + //helper function auto push_action_msig = [&]( const account_name& signer, const action_name &name, const variant_object &data, bool auth = true ) -> action_result { string action_type_name = msig_abi_ser.get_action_type(name); @@ -2620,8 +2628,7 @@ BOOST_FIXTURE_TEST_CASE(producers_upgrade_system_contract, eosio_system_tester) transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); BOOST_REQUIRE_EQUAL(success(), push_action_msig( N(alice1111111), N(exec), mvo() @@ -3382,8 +3389,7 @@ BOOST_FIXTURE_TEST_CASE( setparams, eosio_system_tester ) try { transaction_trace_ptr trace; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { trace = t; } + trace = std::get<0>(p); } ); BOOST_REQUIRE_EQUAL(success(), push_action_msig( N(alice1111111), N(exec), mvo() diff --git a/tests/eosio.wrap_tests.cpp b/tests/eosio.wrap_tests.cpp index 4e24a48ff..4983a348d 100644 --- a/tests/eosio.wrap_tests.cpp +++ b/tests/eosio.wrap_tests.cpp @@ -220,32 +220,42 @@ BOOST_FIXTURE_TEST_CASE( wrap_with_msig, eosio_wrap_tester ) try { vector traces; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { - traces.push_back( t ); - } + traces.push_back( std::get<0>(p) ); } ); // Now the proposal should be ready to execute - push_action( N(eosio.msig), N(exec), N(alice), mvo() - ("proposer", "carol") - ("proposal_name", "first") - ("executer", "alice") + transaction_trace_ptr trx_trace; + trx_trace = push_action( N(eosio.msig), N(exec), N(alice), mvo() + ("proposer", "carol") + ("proposal_name", "first") + ("executer", "alice") ); - produce_block(); - - BOOST_REQUIRE_EQUAL( 2, traces.size() ); - - BOOST_REQUIRE_EQUAL( 1, traces[0]->action_traces.size() ); - BOOST_REQUIRE_EQUAL( "eosio.wrap", name{traces[0]->action_traces[0].act.account} ); - BOOST_REQUIRE_EQUAL( "exec", name{traces[0]->action_traces[0].act.name} ); - BOOST_REQUIRE_EQUAL( transaction_receipt::executed, traces[0]->receipt->status ); - - BOOST_REQUIRE_EQUAL( 1, traces[1]->action_traces.size() ); - BOOST_REQUIRE_EQUAL( "eosio", name{traces[1]->action_traces[0].act.account} ); - BOOST_REQUIRE_EQUAL( "reqauth", name{traces[1]->action_traces[0].act.name} ); - BOOST_REQUIRE_EQUAL( transaction_receipt::executed, traces[1]->receipt->status ); + BOOST_REQUIRE( bool(trx_trace) ); + BOOST_REQUIRE( trx_trace->receipt.valid() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trx_trace->receipt->status ); + BOOST_REQUIRE_EQUAL( 2, trx_trace->action_traces.size() ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(0).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).closest_unnotified_ancestor_action_ordinal ); + // EOSIO 1.8 N() macro returns a uint64_t rather than a struct name + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, action_name{trx_trace->action_traces.at(0).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, name{trx_trace->action_traces.at(0).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(exec)}, name{trx_trace->action_traces.at(0).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(alice)}, name{trx_trace->action_traces.at(0).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(0).act.authorization[0].permission} ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{2}, trx_trace->action_traces.at(1).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).closest_unnotified_ancestor_action_ordinal ); + BOOST_REQUIRE_EQUAL( name{N(eosio.wrap)}, action_name{trx_trace->action_traces.at(1).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.wrap)}, name{trx_trace->action_traces.at(1).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(exec)}, name{trx_trace->action_traces.at(1).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(alice)}, name{trx_trace->action_traces.at(1).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(1).act.authorization[0].permission} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.wrap)}, name{trx_trace->action_traces.at(1).act.authorization[1].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(1).act.authorization[1].permission} ); } FC_LOG_AND_RETHROW() @@ -336,32 +346,42 @@ BOOST_FIXTURE_TEST_CASE( wrap_with_msig_producers_change, eosio_wrap_tester ) tr vector traces; control->applied_transaction.connect( [&]( std::tuple p ) { - const auto& t = std::get<0>(p); - if( t->scheduled ) { - traces.push_back( t ); - } + traces.push_back( std::get<0>(p) ); } ); // Now the proposal should be ready to execute - push_action( N(eosio.msig), N(exec), N(alice), mvo() - ("proposer", "carol") - ("proposal_name", "first") - ("executer", "alice") + transaction_trace_ptr trx_trace; + trx_trace = push_action( N(eosio.msig), N(exec), N(alice), mvo() + ("proposer", "carol") + ("proposal_name", "first") + ("executer", "alice") ); - produce_block(); - - BOOST_REQUIRE_EQUAL( 2, traces.size() ); - - BOOST_REQUIRE_EQUAL( 1, traces[0]->action_traces.size() ); - BOOST_REQUIRE_EQUAL( "eosio.wrap", name{traces[0]->action_traces[0].act.account} ); - BOOST_REQUIRE_EQUAL( "exec", name{traces[0]->action_traces[0].act.name} ); - BOOST_REQUIRE_EQUAL( transaction_receipt::executed, traces[0]->receipt->status ); - - BOOST_REQUIRE_EQUAL( 1, traces[1]->action_traces.size() ); - BOOST_REQUIRE_EQUAL( "eosio", name{traces[1]->action_traces[0].act.account} ); - BOOST_REQUIRE_EQUAL( "reqauth", name{traces[1]->action_traces[0].act.name} ); - BOOST_REQUIRE_EQUAL( transaction_receipt::executed, traces[1]->receipt->status ); + BOOST_REQUIRE( bool(trx_trace) ); + BOOST_REQUIRE( trx_trace->receipt.valid() ); + BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trx_trace->receipt->status ); + BOOST_REQUIRE_EQUAL( 2, trx_trace->action_traces.size() ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(0).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{0}, trx_trace->action_traces.at(0).closest_unnotified_ancestor_action_ordinal ); + // EOSIO 1.8 N() macro returns a uint64_t rather than a struct name + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, action_name{trx_trace->action_traces.at(0).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.msig)}, name{trx_trace->action_traces.at(0).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(exec)}, name{trx_trace->action_traces.at(0).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(alice)}, name{trx_trace->action_traces.at(0).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(0).act.authorization[0].permission} ); + + BOOST_REQUIRE_EQUAL( fc::unsigned_int{2}, trx_trace->action_traces.at(1).action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).creator_action_ordinal ); + BOOST_REQUIRE_EQUAL( fc::unsigned_int{1}, trx_trace->action_traces.at(1).closest_unnotified_ancestor_action_ordinal ); + BOOST_REQUIRE_EQUAL( name{N(eosio.wrap)}, action_name{trx_trace->action_traces.at(1).receiver} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.wrap)}, name{trx_trace->action_traces.at(1).act.account} ); + BOOST_REQUIRE_EQUAL( name{N(exec)}, name{trx_trace->action_traces.at(1).act.name} ); + BOOST_REQUIRE_EQUAL( name{N(alice)}, name{trx_trace->action_traces.at(1).act.authorization[0].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(1).act.authorization[0].permission} ); + BOOST_REQUIRE_EQUAL( name{N(eosio.wrap)}, name{trx_trace->action_traces.at(1).act.authorization[1].actor} ); + BOOST_REQUIRE_EQUAL( name{N(active)}, name{trx_trace->action_traces.at(1).act.authorization[1].permission} ); } FC_LOG_AND_RETHROW() diff --git a/tests/test_contracts/CMakeLists.txt b/tests/test_contracts/CMakeLists.txt new file mode 100644 index 000000000..87c42896a --- /dev/null +++ b/tests/test_contracts/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(sendinline) + diff --git a/tests/test_contracts/sendinline/CMakeLists.txt b/tests/test_contracts/sendinline/CMakeLists.txt new file mode 100644 index 000000000..9c846f4c0 --- /dev/null +++ b/tests/test_contracts/sendinline/CMakeLists.txt @@ -0,0 +1,4 @@ +find_package(eosio.cdt) +add_contract(sendinline sendinline ${CMAKE_CURRENT_SOURCE_DIR}/src/sendinline.cpp) +target_include_directories(sendinline.wasm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +set_target_properties(sendinline.wasm PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/tests/test_contracts/sendinline/src/sendinline.cpp b/tests/test_contracts/sendinline/src/sendinline.cpp new file mode 100644 index 000000000..88c0c01e1 --- /dev/null +++ b/tests/test_contracts/sendinline/src/sendinline.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include + +/// `eosio.code` is a virtual permission (there is no private or public +/// key associated with it). Therefore, this test tests how `eosio.msig` +/// contract reacts to a smart contract submitting a proposal and +/// approving/unnapproving itself. +class [[eosio::contract]] +sendinline : public eosio::contract { +public: + using contract::contract; + + [[eosio::action]] + void send( eosio::name contract, eosio::name action_name, std::vector auths, std::vector payload) { + eosio::action act; + act.account = contract; + act.name = action_name; + act.authorization = auths; + act.data = std::move(payload); + act.send(); + } +}; From 37efeea0e63dcde07ff85b5cff3d4eb3d21cd94f Mon Sep 17 00:00:00 2001 From: Jonathan Giszczak Date: Fri, 17 Jul 2020 11:25:48 -0600 Subject: [PATCH 2/2] Switch test to active permission from code and removed unused code --- tests/eosio.msig_tests.cpp | 52 +++----------------------------------- 1 file changed, 3 insertions(+), 49 deletions(-) diff --git a/tests/eosio.msig_tests.cpp b/tests/eosio.msig_tests.cpp index 27c8a1eeb..2eaaee9dc 100644 --- a/tests/eosio.msig_tests.cpp +++ b/tests/eosio.msig_tests.cpp @@ -1041,7 +1041,7 @@ BOOST_FIXTURE_TEST_CASE( sendinline, eosio_msig_tester ) try { ("account", "alice") ("permission", "perm") ("parent", "active") - ("auth", authority{ 1, {}, {permission_level_weight{ {N(sendinline), config::eosio_code_name}, 1}}, {} }) + ("auth", authority{ 1, {}, {permission_level_weight{ {N(sendinline), config::active_name}, 1}}, {} }) ); produce_blocks(); @@ -1101,13 +1101,7 @@ BOOST_FIXTURE_TEST_CASE( sendinline, eosio_msig_tester ) try { action approve_act = get_action(N(eosio.msig), N(approve), {}, mvo() ("proposer", "bob") ("proposal_name", "first") - ("level", permission_level{N(sendinline), N(eosio.code)}) - ); - - action unapprove_act = get_action(N(eosio.msig), N(unapprove), {}, mvo() - ("proposer", "bob") - ("proposal_name", "first") - ("level", permission_level{N(sendinline), N(eosio.code)}) + ("level", permission_level{N(sendinline), config::active_name}) ); transaction trx = reqauth( N(alice), {permission_level{N(alice), N(perm)}}, abi_serializer_max_time ); @@ -1116,50 +1110,10 @@ BOOST_FIXTURE_TEST_CASE( sendinline, eosio_msig_tester ) try { ("proposer", "bob") ("proposal_name", "first") ("trx", trx) - ("requested", std::vector{{ N(sendinline), N(eosio.code) }}) - ); - produce_blocks(); -#if 0 - // `approve` shall fail when being sent from the wrong contract - BOOST_REQUIRE_EXCEPTION( base_tester::push_action( N(wrongcon), N(send), N(bob), mvo() - ("contract", "eosio.msig") - ("action_name", "approve") - ("auths", std::vector{{N(bob), config::active_name}}) - ("payload", approve_act.data) - ), - eosio_assert_message_exception, - eosio_assert_message_is("wrong contract sent `approve` action for eosio.code permmission") - ); - // `approve` shall succeed when being sent from the correct contract - base_tester::push_action( N(sendinline), N(send), N(bob), mvo() - ("contract", "eosio.msig") - ("action_name", "approve") - ("auths", std::vector{{N(bob), config::eosio_code_name}}) - ("payload", approve_act.data) + ("requested", std::vector{{ N(sendinline), config::active_name }}) ); produce_blocks(); - // `unapprove` shall fail when being sent from the wrong contract - BOOST_REQUIRE_EXCEPTION( base_tester::push_action( N(wrongcon), N(send), N(bob), mvo() - ("contract", N(eosio.msig)) - ("action_name", "unapprove") - ("auths", std::vector{}) - ("payload", unapprove_act.data) - ), - eosio_assert_message_exception, - eosio_assert_message_is("wrong contract sent `unapprove` action for eosio.code permmission") - ); - - // `unapprove` shall succeed when being sent from the correct contract - base_tester::push_action( N(sendinline), N(send), N(bob), mvo() - ("contract", N(eosio.msig)) - ("action_name", "unapprove") - ("auths", std::vector{}) - ("payload", unapprove_act.data) - ); - produce_blocks(); -#endif - // `approve` to get back into a state ready for `exec` base_tester::push_action( N(sendinline), N(send), N(bob), mvo() ("contract", "eosio.msig") ("action_name", "approve")