From 07f830ffadcd695574a039860f682eb35a2cb4a9 Mon Sep 17 00:00:00 2001 From: Jonathan Giszczak Date: Fri, 17 Jul 2020 09:23:12 -0600 Subject: [PATCH 1/5] 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/5] 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") From fa3cc8ac21e3c4765fde4911249d819b98e9770f Mon Sep 17 00:00:00 2001 From: johndebord Date: Mon, 14 Sep 2020 09:32:27 +0000 Subject: [PATCH 3/5] Patch of OOB Patch to v1.8.3 based on #504 --- .../include/eosio.msig/eosio.msig.hpp | 314 ++++++++++++++---- contracts/eosio.msig/src/eosio.msig.cpp | 192 +++++++---- 2 files changed, 374 insertions(+), 132 deletions(-) diff --git a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp index 465433ec0..2ceee166e 100644 --- a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp +++ b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp @@ -6,21 +6,219 @@ #include namespace eosio { - /** - * @defgroup eosiomsig eosio.msig - * @ingroup eosiocontracts - * eosio.msig contract defines the structures and actions needed to manage the proposals and approvals on blockchain. - * @{ + * Clone of `eosio::binary_extension` that includes `operator=` to avoid + * bumping the `eosio.cdt` dependency version of the v1.8.x patch release of + * `eosio.contracts`. + */ + template + class eosio_msig_binary_extension { + public: + using value_type = T; + + constexpr eosio_msig_binary_extension() {} + constexpr eosio_msig_binary_extension( const T& ext ) + :_has_value(true) + { + ::new (&_data) T(ext); + } + constexpr eosio_msig_binary_extension( T&& ext ) + :_has_value(true) + { + ::new (&_data) T(std::move(ext)); + } + /** construct contained type in place */ + template + constexpr eosio_msig_binary_extension( std::in_place_t, Args&&... args ) + :_has_value(true) + { + ::new (&_data) T(std::forward(args)...); + } + + constexpr eosio_msig_binary_extension( const eosio_msig_binary_extension& other ) + :_has_value(other._has_value) + { + if( other._has_value ) ::new (&_data) T( *other ); + } + + constexpr eosio_msig_binary_extension( eosio_msig_binary_extension&& other ) + :_has_value(other._has_value) + { + if( other._has_value ) { + ::new (&_data) T( *std::move(other) ); + other._has_value = false; + } + } + + /// @cond INTERNAL + ~eosio_msig_binary_extension() { reset(); } + + /// @cond INTERNAL + constexpr eosio_msig_binary_extension& operator= (const eosio_msig_binary_extension& other) { + if (has_value()) + reset(); + + if (other.has_value()) { + ::new (&_data) T(*other); + _has_value = true; + } + return *this; + } + + /// @cond INTERNAL + constexpr eosio_msig_binary_extension& operator= (eosio_msig_binary_extension&& other) { + if (has_value()) + reset(); + + if (other.has_value()) { + ::new (&_data) T(*other); + _has_value = true; + other._has_value = false; + } + return *this; + } + /** test if container is holding a value */ + constexpr explicit operator bool()const { return _has_value; } + /** test if container is holding a value */ + constexpr bool has_value()const { return _has_value; } + + /** get the contained value */ + constexpr T& value()& { + if (!_has_value) { + check(false, "cannot get value of empty eosio_msig_binary_extension"); + } + return _get(); + } + + /// @cond INTERNAL + + /** get the contained value */ + constexpr const T& value()const & { + if (!_has_value) { + check(false, "cannot get value of empty eosio_msig_binary_extension"); + } + return _get(); + } + + /** get the contained value or a user specified default + * @pre def should be convertible to type T + * */ + template + constexpr auto value_or( U&& def ) -> std::enable_if_t::value, T&>& { + if (_has_value) + return _get(); + return def; + } + + constexpr T value_or() const { return (-_has_value) ? _get() : T{}; } + + constexpr T* operator->() { + return &_get(); + } + constexpr const T* operator->()const { + return &_get(); + } + + constexpr T& operator*()& { + return _get(); + } + constexpr const T& operator*()const& { + return _get(); + } + constexpr const T&& operator*()const&& { + return std::move(_get()); + } + constexpr T&& operator*()&& { + return std::move(_get()); + } + + template + T& emplace(Args&& ... args)& { + if (_has_value) { + reset(); + } + + ::new (&_data) T( std::forward(args)... ); + _has_value = true; + + return _get(); + } + + void reset() { + if( _has_value ) { + _get().~value_type(); + _has_value = false; + } + } + + /// @endcond + + private: + bool _has_value = false; + typename std::aligned_storage::type _data; + + constexpr T& _get() { + return *reinterpret_cast(&_data); + } + + constexpr const T& _get()const { + return *reinterpret_cast(&_data); + } + }; + + /// @cond IMPLEMENTATIONS + + /** + * Serialize a eosio_msig_binary_extension into a stream + * + * @ingroup eosio_msig_binary_extension + * @brief Serialize a eosio_msig_binary_extension + * @param ds - The stream to write + * @param opt - The value to serialize + * @tparam DataStream - Type of datastream buffer + * @return DataStream& - Reference to the datastream + */ + template + inline DataStream& operator<<(DataStream& ds, const eosio::eosio_msig_binary_extension& be) { + ds << be.value_or(); + return ds; + } + + /** + * Deserialize a eosio_msig_binary_extension from a stream + * + * @ingroup eosio_msig_binary_extension + * @brief Deserialize a eosio_msig_binary_extension + * @param ds - The stream to read + * @param opt - The destination for deserialized value + * @tparam DataStream - Type of datastream buffer + * @return DataStream& - Reference to the datastream + */ + template + inline DataStream& operator>>(DataStream& ds, eosio::eosio_msig_binary_extension& be) { + if( ds.remaining() ) { + T val; + ds >> val; + be.emplace(val); + } + return ds; + } + + /** + * The `eosio.msig` system contract allows for creation of proposed transactions which require authorization from a list of accounts, approval of the proposed transactions by those accounts required to approve it, and finally, it also allows the execution of the approved transactions on the blockchain. + * + * In short, the workflow to propose, review, approve and then executed a transaction it can be described by the following: + * - first you create a transaction json file, + * - then you submit this proposal to the `eosio.msig` contract, and you also insert the account permissions required to approve this proposal into the command that submits the proposal to the blockchain, + * - the proposal then gets stored on the blockchain by the `eosio.msig` contract, and is accessible for review and approval to those accounts required to approve it, + * - after each of the appointed accounts required to approve the proposed transactions reviews and approves it, you can execute the proposed transaction. The `eosio.msig` contract will execute it automatically, but not before validating that the transaction has not expired, it is not cancelled, and it has been signed by all the permissions in the initial proposal's required permission list. */ class [[eosio::contract("eosio.msig")]] multisig : public contract { public: using contract::contract; /** - * Create proposal - * - * @details Creates a proposal containing one transaction. + * Propose action, creates a proposal containing one transaction. * Allows an account `proposer` to make a proposal `proposal_name` which has `requested` * permission levels expected to approve the proposal, and if approved by all expected * permission levels then `trx` transaction can we executed by this proposal. @@ -36,13 +234,10 @@ namespace eosio { * @param trx - Proposed transaction */ [[eosio::action]] - void propose(ignore proposer, ignore proposal_name, - ignore> requested, ignore trx); + void propose(name proposer, name proposal_name, + std::vector requested, ignore trx); /** - * Approve proposal - * - * @details Approves an existing proposal - * Allows an account, the owner of `level` permission, to approve a proposal `proposal_name` + * Approve action approves an existing proposal. Allows an account, the owner of `level` permission, to approve a proposal `proposal_name` * proposed by `proposer`. If the proposal's requested approval list contains the `level` * permission then the `level` permission is moved from internal `requested_approvals` list to * internal `provided_approvals` list of the proposal, thus persisting the approval for @@ -57,10 +252,7 @@ namespace eosio { void approve( name proposer, name proposal_name, permission_level level, const eosio::binary_extension& proposal_hash ); /** - * Revoke proposal - * - * @details Revokes an existing proposal - * This action is the reverse of the `approve` action: if all validations pass + * Unapprove action revokes an existing proposal. This action is the reverse of the `approve` action: if all validations pass * the `level` permission is erased from internal `provided_approvals` and added to the internal * `requested_approvals` list, and thus un-approve or revoke the proposal. * @@ -71,9 +263,7 @@ namespace eosio { [[eosio::action]] void unapprove( name proposer, name proposal_name, permission_level level ); /** - * Cancel proposal - * - * @details Cancels an existing proposal + * Cancel action cancels an existing proposal. * * @param proposer - The account proposing a transaction * @param proposal_name - The name of the proposal (should be an existing proposal) @@ -86,9 +276,7 @@ namespace eosio { [[eosio::action]] void cancel( name proposer, name proposal_name, name canceler ); /** - * Execute proposal - * - * @details Allows an `executer` account to execute a proposal. + * Exec action allows an `executer` account to execute a proposal. * * Preconditions: * - `executer` has authorization, @@ -107,9 +295,7 @@ namespace eosio { [[eosio::action]] void exec( name proposer, name proposal_name, name executer ); /** - * Invalidate proposal - * - * @details Allows an `account` to invalidate itself, that is, its name is added to + * Invalidate action allows an `account` to invalidate itself, that is, its name is added to * the invalidations table and this table will be cross referenced when exec is performed. * * @param account - The account invalidating the transaction @@ -123,52 +309,50 @@ namespace eosio { using cancel_action = eosio::action_wrapper<"cancel"_n, &multisig::cancel>; using exec_action = eosio::action_wrapper<"exec"_n, &multisig::exec>; using invalidate_action = eosio::action_wrapper<"invalidate"_n, &multisig::invalidate>; + }; - private: - struct [[eosio::table]] proposal { - name proposal_name; - std::vector packed_transaction; - - uint64_t primary_key()const { return proposal_name.value; } - }; - - typedef eosio::multi_index< "proposal"_n, proposal > proposals; + struct [[eosio::table]] proposal { + name proposal_name; + std::vector packed_transaction; + eosio::eosio_msig_binary_extension< std::optional > earliest_exec_time; - struct [[eosio::table]] old_approvals_info { - name proposal_name; - std::vector requested_approvals; - std::vector provided_approvals; + uint64_t primary_key()const { return proposal_name.value; } + }; + typedef eosio::multi_index< "proposal"_n, proposal > proposals; - uint64_t primary_key()const { return proposal_name.value; } - }; - typedef eosio::multi_index< "approvals"_n, old_approvals_info > old_approvals; + struct [[eosio::table]] old_approvals_info { + name proposal_name; + std::vector requested_approvals; + std::vector provided_approvals; - struct approval { - permission_level level; - time_point time; - }; + uint64_t primary_key()const { return proposal_name.value; } + }; + typedef eosio::multi_index< "approvals"_n, old_approvals_info > old_approvals; - struct [[eosio::table]] approvals_info { - uint8_t version = 1; - name proposal_name; - //requested approval doesn't need to cointain time, but we want requested approval - //to be of exact the same size ad provided approval, in this case approve/unapprove - //doesn't change serialized data size. So, we use the same type. - std::vector requested_approvals; - std::vector provided_approvals; + struct approval { + permission_level level; + time_point time; + }; - uint64_t primary_key()const { return proposal_name.value; } - }; - typedef eosio::multi_index< "approvals2"_n, approvals_info > approvals; + struct [[eosio::table]] approvals_info { + uint8_t version = 1; + name proposal_name; + //requested approval doesn't need to contain time, but we want requested approval + //to be of exactly the same size as provided approval, in this case approve/unapprove + //doesn't change serialized data size. So, we use the same type. + std::vector requested_approvals; + std::vector provided_approvals; - struct [[eosio::table]] invalidation { - name account; - time_point last_invalidation_time; + uint64_t primary_key()const { return proposal_name.value; } + }; + typedef eosio::multi_index< "approvals2"_n, approvals_info > approvals; - uint64_t primary_key() const { return account.value; } - }; + struct [[eosio::table]] invalidation { + name account; + time_point last_invalidation_time; - typedef eosio::multi_index< "invals"_n, invalidation > invalidations; + uint64_t primary_key() const { return account.value; } }; - /** @}*/ // end of @defgroup eosiomsig eosio.msig + typedef eosio::multi_index< "invals"_n, invalidation > invalidations; + } /// namespace eosio diff --git a/contracts/eosio.msig/src/eosio.msig.cpp b/contracts/eosio.msig/src/eosio.msig.cpp index 16cf117bc..2795ea6cd 100644 --- a/contracts/eosio.msig/src/eosio.msig.cpp +++ b/contracts/eosio.msig/src/eosio.msig.cpp @@ -6,32 +6,62 @@ namespace eosio { -void multisig::propose( ignore proposer, - ignore proposal_name, - ignore> requested, +transaction_header get_trx_header(const char* ptr, size_t sz); +bool trx_is_authorized(const std::vector& approvals, const std::vector& packed_trx); + +template +std::vector get_approvals_and_adjust_table(name self, name proposer, name proposal_name, Function&& table_op) { + approvals approval_table( self, proposer.value ); + auto approval_table_iter = approval_table.find( proposal_name.value ); + std::vector approvals_vector; + invalidations invalidations_table( self, self.value ); + + if ( approval_table_iter != approval_table.end() ) { + approvals_vector.reserve( approval_table_iter->provided_approvals.size() ); + for ( const auto& permission : approval_table_iter->provided_approvals ) { + auto iter = invalidations_table.find( permission.level.actor.value ); + if ( iter == invalidations_table.end() || iter->last_invalidation_time < permission.time ) { + approvals_vector.push_back(permission.level); + } + } + table_op( approval_table, approval_table_iter ); + } else { + old_approvals old_approval_table( self, proposer.value ); + const auto& old_approvals_obj = old_approval_table.get( proposal_name.value, "proposal not found" ); + for ( const auto& permission : old_approvals_obj.provided_approvals ) { + auto iter = invalidations_table.find( permission.actor.value ); + if ( iter == invalidations_table.end() ) { + approvals_vector.push_back( permission ); + } + } + table_op( old_approval_table, old_approvals_obj ); + } + return approvals_vector; +} + +void multisig::propose( name proposer, + name proposal_name, + std::vector requested, ignore trx ) { - name _proposer; - name _proposal_name; - std::vector _requested; - transaction_header _trx_header; + require_auth( proposer ); + auto& ds = get_datastream(); - _ds >> _proposer >> _proposal_name >> _requested; + const char* trx_pos = ds.pos(); + size_t size = ds.remaining(); - const char* trx_pos = _ds.pos(); - size_t size = _ds.remaining(); - _ds >> _trx_header; - - require_auth( _proposer ); - check( _trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); - //check( trx_header.actions.size() > 0, "transaction must have at least one action" ); + transaction_header trx_header; + std::vector context_free_actions; + 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 `propose` a transaction with context-free actions" ); - proposals proptable( get_self(), _proposer.value ); - check( proptable.find( _proposal_name.value ) == proptable.end(), "proposal with the same name exists" ); + proposals proptable( get_self(), proposer.value ); + check( proptable.find( proposal_name.value ) == proptable.end(), "proposal with the same name exists" ); - auto packed_requested = pack(_requested); - // TODO: Remove internal_use_do_not_use namespace after minimum eosio.cdt dependency becomes 1.7.x - auto res = internal_use_do_not_use::check_transaction_authorization( + auto packed_requested = pack(requested); + auto res = check_transaction_authorization( trx_pos, size, (const char*)0, 0, packed_requested.data(), packed_requested.size() @@ -42,19 +72,21 @@ void multisig::propose( ignore proposer, std::vector pkd_trans; pkd_trans.resize(size); memcpy((char*)pkd_trans.data(), trx_pos, size); - proptable.emplace( _proposer, [&]( auto& prop ) { - prop.proposal_name = _proposal_name; - prop.packed_transaction = pkd_trans; - }); - - approvals apptable( get_self(), _proposer.value ); - apptable.emplace( _proposer, [&]( auto& a ) { - a.proposal_name = _proposal_name; - a.requested_approvals.reserve( _requested.size() ); - for ( auto& level : _requested ) { - a.requested_approvals.push_back( approval{ level, time_point{ microseconds{0} } } ); - } - }); + + proptable.emplace( proposer, [&]( auto& prop ) { + prop.proposal_name = proposal_name; + prop.packed_transaction = pkd_trans; + prop.earliest_exec_time = std::optional{}; + }); + + approvals apptable( get_self(), proposer.value ); + apptable.emplace( proposer, [&]( auto& a ) { + a.proposal_name = proposal_name; + a.requested_approvals.reserve( requested.size() ); + for ( auto& level : requested ) { + a.requested_approvals.push_back( approval{ level, time_point{ microseconds{0} } } ); + } + }); } void multisig::approve( name proposer, name proposal_name, permission_level level, @@ -62,9 +94,10 @@ void multisig::approve( name proposer, name proposal_name, permission_level leve { require_auth( level ); + proposals proptable( get_self(), proposer.value ); + auto& prop = proptable.get( proposal_name.value, "proposal not found" ); + if( proposal_hash ) { - proposals proptable( get_self(), proposer.value ); - auto& prop = proptable.get( proposal_name.value, "proposal not found" ); assert_sha256( prop.packed_transaction.data(), prop.packed_transaction.size(), *proposal_hash ); } @@ -90,6 +123,21 @@ void multisig::approve( name proposer, name proposal_name, permission_level leve a.requested_approvals.erase( itr ); }); } + + transaction_header trx_header = get_trx_header(prop.packed_transaction.data(), prop.packed_transaction.size()); + + if( prop.earliest_exec_time.has_value() ) { + if( !prop.earliest_exec_time->has_value() ) { + auto table_op = [](auto&&, auto&&){}; + if( trx_is_authorized(get_approvals_and_adjust_table(get_self(), proposer, proposal_name, table_op), prop.packed_transaction) ) { + proptable.modify( prop, proposer, [&]( auto& p ) { + p.earliest_exec_time = std::optional{ current_time_point() + eosio::seconds(trx_header.delay_sec.value)}; + }); + } + } + } else { + check( trx_header.delay_sec.value == 0, "old proposals are not allowed to have non-zero `delay_sec`; cancel and retry" ); + } } void multisig::unapprove( name proposer, name proposal_name, permission_level level ) { @@ -114,6 +162,23 @@ void multisig::unapprove( name proposer, name proposal_name, permission_level le a.provided_approvals.erase( itr ); }); } + + proposals proptable( get_self(), proposer.value ); + auto& prop = proptable.get( proposal_name.value, "proposal not found" ); + + if( prop.earliest_exec_time.has_value() ) { + if( prop.earliest_exec_time->has_value() ) { + auto table_op = [](auto&&, auto&&){}; + if( !trx_is_authorized(get_approvals_and_adjust_table(get_self(), proposer, proposal_name, table_op), prop.packed_transaction) ) { + proptable.modify( prop, proposer, [&]( auto& p ) { + p.earliest_exec_time = std::optional{}; + }); + } + } + } else { + transaction_header trx_header = get_trx_header(prop.packed_transaction.data(), prop.packed_transaction.size()); + check( trx_header.delay_sec.value == 0, "old proposals are not allowed to have non-zero `delay_sec`; cancel and retry" ); + } } void multisig::cancel( name proposer, name proposal_name, name canceler ) { @@ -145,49 +210,26 @@ 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" ); + + datastream ds = {prop.packed_transaction.data(), prop.packed_transaction.size()}; 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 ); - std::vector approvals; - invalidations inv_table( get_self(), get_self().value ); - if ( apps_it != apptable.end() ) { - approvals.reserve( apps_it->provided_approvals.size() ); - for ( auto& p : apps_it->provided_approvals ) { - auto it = inv_table.find( p.level.actor.value ); - if ( it == inv_table.end() || it->last_invalidation_time < p.time ) { - approvals.push_back(p.level); - } - } - apptable.erase(apps_it); + auto table_op = [](auto&& table, auto&& table_iter) { table.erase(table_iter); }; + bool ok = trx_is_authorized(get_approvals_and_adjust_table(get_self(), proposer, proposal_name, table_op), prop.packed_transaction); + check( ok, "transaction authorization failed" ); + + if ( prop.earliest_exec_time.has_value() && prop.earliest_exec_time->has_value() ) { + check( **prop.earliest_exec_time <= current_time_point(), "too early to execute" ); } else { - old_approvals old_apptable( get_self(), proposer.value ); - auto& apps = old_apptable.get( proposal_name.value, "proposal not found" ); - for ( auto& level : apps.provided_approvals ) { - auto it = inv_table.find( level.actor.value ); - if ( it == inv_table.end() ) { - approvals.push_back( level ); - } - } - old_apptable.erase(apps); + check( trx_header.delay_sec.value == 0, "old proposals are not allowed to have non-zero `delay_sec`; cancel and retry" ); } - auto packed_provided_approvals = pack(approvals); - // TODO: Remove internal_use_do_not_use namespace after minimum eosio.cdt dependency becomes 1.7.x - auto res = internal_use_do_not_use::check_transaction_authorization( - prop.packed_transaction.data(), prop.packed_transaction.size(), - (const char*)0, 0, - packed_provided_approvals.data(), packed_provided_approvals.size() - ); - - check( res > 0, "transaction authorization failed" ); for (const auto& act : actions) { act.send(); @@ -212,4 +254,20 @@ void multisig::invalidate( name account ) { } } +transaction_header get_trx_header(const char* ptr, size_t sz) { + datastream ds = {ptr, sz}; + transaction_header trx_header; + ds >> trx_header; + return trx_header; +} + +bool trx_is_authorized(const std::vector& approvals, const std::vector& packed_trx) { + auto packed_approvals = pack(approvals); + return check_transaction_authorization( + packed_trx.data(), packed_trx.size(), + (const char*)0, 0, + packed_approvals.data(), packed_approvals.size() + ); +} + } /// namespace eosio From 301c9017df9a50239037c2053700233497c78eb0 Mon Sep 17 00:00:00 2001 From: johndebord Date: Mon, 14 Sep 2020 15:36:57 +0000 Subject: [PATCH 4/5] Fix typo --- contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp index 2ceee166e..4bca57554 100644 --- a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp +++ b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp @@ -110,7 +110,7 @@ namespace eosio { return def; } - constexpr T value_or() const { return (-_has_value) ? _get() : T{}; } + constexpr T value_or() const { return (_has_value) ? _get() : T{}; } constexpr T* operator->() { return &_get(); From 605fe7df114e85a9d7cdef8a3e5f789c45a560a8 Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Wed, 23 Sep 2020 14:38:47 -0400 Subject: [PATCH 5/5] added appropriate annotations and downgraded binary extension to the one present in 1.6.x series CDTs --- .../include/eosio.msig/eosio.msig.hpp | 206 +----------------- contracts/eosio.msig/src/eosio.msig.cpp | 6 +- 2 files changed, 8 insertions(+), 204 deletions(-) diff --git a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp index 4bca57554..189ef8bb9 100644 --- a/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp +++ b/contracts/eosio.msig/include/eosio.msig/eosio.msig.hpp @@ -6,204 +6,8 @@ #include namespace eosio { - /** - * Clone of `eosio::binary_extension` that includes `operator=` to avoid - * bumping the `eosio.cdt` dependency version of the v1.8.x patch release of - * `eosio.contracts`. - */ - template - class eosio_msig_binary_extension { - public: - using value_type = T; - - constexpr eosio_msig_binary_extension() {} - constexpr eosio_msig_binary_extension( const T& ext ) - :_has_value(true) - { - ::new (&_data) T(ext); - } - constexpr eosio_msig_binary_extension( T&& ext ) - :_has_value(true) - { - ::new (&_data) T(std::move(ext)); - } - /** construct contained type in place */ - template - constexpr eosio_msig_binary_extension( std::in_place_t, Args&&... args ) - :_has_value(true) - { - ::new (&_data) T(std::forward(args)...); - } - - constexpr eosio_msig_binary_extension( const eosio_msig_binary_extension& other ) - :_has_value(other._has_value) - { - if( other._has_value ) ::new (&_data) T( *other ); - } - - constexpr eosio_msig_binary_extension( eosio_msig_binary_extension&& other ) - :_has_value(other._has_value) - { - if( other._has_value ) { - ::new (&_data) T( *std::move(other) ); - other._has_value = false; - } - } - - /// @cond INTERNAL - ~eosio_msig_binary_extension() { reset(); } - - /// @cond INTERNAL - constexpr eosio_msig_binary_extension& operator= (const eosio_msig_binary_extension& other) { - if (has_value()) - reset(); - - if (other.has_value()) { - ::new (&_data) T(*other); - _has_value = true; - } - return *this; - } - - /// @cond INTERNAL - constexpr eosio_msig_binary_extension& operator= (eosio_msig_binary_extension&& other) { - if (has_value()) - reset(); - - if (other.has_value()) { - ::new (&_data) T(*other); - _has_value = true; - other._has_value = false; - } - return *this; - } - /** test if container is holding a value */ - constexpr explicit operator bool()const { return _has_value; } - /** test if container is holding a value */ - constexpr bool has_value()const { return _has_value; } - - /** get the contained value */ - constexpr T& value()& { - if (!_has_value) { - check(false, "cannot get value of empty eosio_msig_binary_extension"); - } - return _get(); - } - - /// @cond INTERNAL - - /** get the contained value */ - constexpr const T& value()const & { - if (!_has_value) { - check(false, "cannot get value of empty eosio_msig_binary_extension"); - } - return _get(); - } - - /** get the contained value or a user specified default - * @pre def should be convertible to type T - * */ - template - constexpr auto value_or( U&& def ) -> std::enable_if_t::value, T&>& { - if (_has_value) - return _get(); - return def; - } - - constexpr T value_or() const { return (_has_value) ? _get() : T{}; } - - constexpr T* operator->() { - return &_get(); - } - constexpr const T* operator->()const { - return &_get(); - } - - constexpr T& operator*()& { - return _get(); - } - constexpr const T& operator*()const& { - return _get(); - } - constexpr const T&& operator*()const&& { - return std::move(_get()); - } - constexpr T&& operator*()&& { - return std::move(_get()); - } - - template - T& emplace(Args&& ... args)& { - if (_has_value) { - reset(); - } - - ::new (&_data) T( std::forward(args)... ); - _has_value = true; - - return _get(); - } - - void reset() { - if( _has_value ) { - _get().~value_type(); - _has_value = false; - } - } - - /// @endcond - - private: - bool _has_value = false; - typename std::aligned_storage::type _data; - - constexpr T& _get() { - return *reinterpret_cast(&_data); - } - - constexpr const T& _get()const { - return *reinterpret_cast(&_data); - } - }; - /// @cond IMPLEMENTATIONS - /** - * Serialize a eosio_msig_binary_extension into a stream - * - * @ingroup eosio_msig_binary_extension - * @brief Serialize a eosio_msig_binary_extension - * @param ds - The stream to write - * @param opt - The value to serialize - * @tparam DataStream - Type of datastream buffer - * @return DataStream& - Reference to the datastream - */ - template - inline DataStream& operator<<(DataStream& ds, const eosio::eosio_msig_binary_extension& be) { - ds << be.value_or(); - return ds; - } - - /** - * Deserialize a eosio_msig_binary_extension from a stream - * - * @ingroup eosio_msig_binary_extension - * @brief Deserialize a eosio_msig_binary_extension - * @param ds - The stream to read - * @param opt - The destination for deserialized value - * @tparam DataStream - Type of datastream buffer - * @return DataStream& - Reference to the datastream - */ - template - inline DataStream& operator>>(DataStream& ds, eosio::eosio_msig_binary_extension& be) { - if( ds.remaining() ) { - T val; - ds >> val; - be.emplace(val); - } - return ds; - } - /** * The `eosio.msig` system contract allows for creation of proposed transactions which require authorization from a list of accounts, approval of the proposed transactions by those accounts required to approve it, and finally, it also allows the execution of the approved transactions on the blockchain. * @@ -311,16 +115,16 @@ namespace eosio { using invalidate_action = eosio::action_wrapper<"invalidate"_n, &multisig::invalidate>; }; - struct [[eosio::table]] proposal { + struct [[eosio::table, eosio::contract("eosio.msig")]] proposal { name proposal_name; std::vector packed_transaction; - eosio::eosio_msig_binary_extension< std::optional > earliest_exec_time; + eosio::binary_extension< std::optional > earliest_exec_time; uint64_t primary_key()const { return proposal_name.value; } }; typedef eosio::multi_index< "proposal"_n, proposal > proposals; - struct [[eosio::table]] old_approvals_info { + struct [[eosio::table, eosio::contract("eosio.msig")]] old_approvals_info { name proposal_name; std::vector requested_approvals; std::vector provided_approvals; @@ -334,7 +138,7 @@ namespace eosio { time_point time; }; - struct [[eosio::table]] approvals_info { + struct [[eosio::table, eosio::contract("eosio.msig")]] approvals_info { uint8_t version = 1; name proposal_name; //requested approval doesn't need to contain time, but we want requested approval @@ -347,7 +151,7 @@ namespace eosio { }; typedef eosio::multi_index< "approvals2"_n, approvals_info > approvals; - struct [[eosio::table]] invalidation { + struct [[eosio::table, eosio::contract("eosio.msig")]] invalidation { name account; time_point last_invalidation_time; diff --git a/contracts/eosio.msig/src/eosio.msig.cpp b/contracts/eosio.msig/src/eosio.msig.cpp index 2795ea6cd..99d706d1e 100644 --- a/contracts/eosio.msig/src/eosio.msig.cpp +++ b/contracts/eosio.msig/src/eosio.msig.cpp @@ -76,7 +76,7 @@ void multisig::propose( name proposer, proptable.emplace( proposer, [&]( auto& prop ) { prop.proposal_name = proposal_name; prop.packed_transaction = pkd_trans; - prop.earliest_exec_time = std::optional{}; + prop.earliest_exec_time.emplace(); }); approvals apptable( get_self(), proposer.value ); @@ -131,7 +131,7 @@ void multisig::approve( name proposer, name proposal_name, permission_level leve auto table_op = [](auto&&, auto&&){}; if( trx_is_authorized(get_approvals_and_adjust_table(get_self(), proposer, proposal_name, table_op), prop.packed_transaction) ) { proptable.modify( prop, proposer, [&]( auto& p ) { - p.earliest_exec_time = std::optional{ current_time_point() + eosio::seconds(trx_header.delay_sec.value)}; + p.earliest_exec_time.emplace(time_point{ current_time_point() + eosio::seconds(trx_header.delay_sec.value)}); }); } } @@ -171,7 +171,7 @@ void multisig::unapprove( name proposer, name proposal_name, permission_level le auto table_op = [](auto&&, auto&&){}; if( !trx_is_authorized(get_approvals_and_adjust_table(get_self(), proposer, proposal_name, table_op), prop.packed_transaction) ) { proptable.modify( prop, proposer, [&]( auto& p ) { - p.earliest_exec_time = std::optional{}; + p.earliest_exec_time.emplace(); }); } }