diff --git a/unittests/contracts.hpp.in b/unittests/contracts.hpp.in index bc61854d403..35c01501436 100644 --- a/unittests/contracts.hpp.in +++ b/unittests/contracts.hpp.in @@ -44,6 +44,7 @@ namespace eosio { MAKE_READ_WASM_ABI(noop, noop, test-contracts) MAKE_READ_WASM_ABI(payloadless, payloadless, test-contracts) MAKE_READ_WASM_ABI(proxy, proxy, test-contracts) + MAKE_READ_WASM_ABI(restrict_action_test, restrict_action_test, test-contracts) MAKE_READ_WASM_ABI(snapshot_test, snapshot_test, test-contracts) MAKE_READ_WASM_ABI(test_api, test_api, test-contracts) MAKE_READ_WASM_ABI(test_api_db, test_api_db, test-contracts) diff --git a/unittests/protocol_feature_tests.cpp b/unittests/protocol_feature_tests.cpp index c510904864a..121c6e17354 100644 --- a/unittests/protocol_feature_tests.cpp +++ b/unittests/protocol_feature_tests.cpp @@ -607,4 +607,64 @@ BOOST_AUTO_TEST_CASE( disallow_empty_producer_schedule_test ) { try { } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( restrict_action_to_self_test ) { try { + tester c( setup_policy::preactivate_feature_and_new_bios ); + + const auto& pfm = c.control->get_protocol_feature_manager(); + const auto& d = pfm.get_builtin_digest( builtin_protocol_feature_t::restrict_action_to_self ); + BOOST_REQUIRE( d ); + + c.create_accounts( {N(testacc), N(acctonotify), N(alice)} ); + c.set_code( N(testacc), contracts::restrict_action_test_wasm() ); + c.set_abi( N(testacc), contracts::restrict_action_test_abi().data() ); + + c.set_code( N(acctonotify), contracts::restrict_action_test_wasm() ); + c.set_abi( N(acctonotify), contracts::restrict_action_test_abi().data() ); + + // Before the protocol feature is preactivated + // - Sending inline action to self = no problem + // - Sending deferred trx to self = throw subjective exception + // - Sending inline action to self from notification = throw subjective exception + // - Sending deferred trx to self from notification = throw subjective exception + BOOST_CHECK_NO_THROW( c.push_action( N(testacc), N(sendinline), N(alice), mutable_variant_object()("authorizer", "alice")) ); + BOOST_REQUIRE_EXCEPTION( c.push_action( N(testacc), N(senddefer), N(alice), + mutable_variant_object()("authorizer", "alice")("senderid", 0)), + subjective_block_production_exception, + fc_exception_message_starts_with( "Authorization failure with sent deferred transaction" ) ); + + BOOST_REQUIRE_EXCEPTION( c.push_action( N(testacc), N(notifyinline), N(alice), + mutable_variant_object()("acctonotify", "acctonotify")("authorizer", "alice")), + subjective_block_production_exception, + fc_exception_message_starts_with( "Authorization failure with inline action sent to self" ) ); + + BOOST_REQUIRE_EXCEPTION( c.push_action( N(testacc), N(notifydefer), N(alice), + mutable_variant_object()("acctonotify", "acctonotify")("authorizer", "alice")("senderid", 1)), + subjective_block_production_exception, + fc_exception_message_starts_with( "Authorization failure with sent deferred transaction" ) ); + + c.preactivate_protocol_features( {*d} ); + c.produce_block(); + + // After the protocol feature is preactivated, all the 4 cases will throw an objective unsatisfied_authorization exception + BOOST_REQUIRE_EXCEPTION( c.push_action( N(testacc), N(sendinline), N(alice), mutable_variant_object()("authorizer", "alice") ), + unsatisfied_authorization, + fc_exception_message_starts_with( "transaction declares authority" ) ); + + BOOST_REQUIRE_EXCEPTION( c.push_action( N(testacc), N(senddefer), N(alice), + mutable_variant_object()("authorizer", "alice")("senderid", 3)), + unsatisfied_authorization, + fc_exception_message_starts_with( "transaction declares authority" ) ); + + BOOST_REQUIRE_EXCEPTION( c.push_action( N(testacc), N(notifyinline), N(alice), + mutable_variant_object()("acctonotify", "acctonotify")("authorizer", "alice") ), + unsatisfied_authorization, + fc_exception_message_starts_with( "transaction declares authority" ) ); + + BOOST_REQUIRE_EXCEPTION( c.push_action( N(testacc), N(notifydefer), N(alice), + mutable_variant_object()("acctonotify", "acctonotify")("authorizer", "alice")("senderid", 4)), + unsatisfied_authorization, + fc_exception_message_starts_with( "transaction declares authority" ) ); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/test-contracts/CMakeLists.txt b/unittests/test-contracts/CMakeLists.txt index 59f4ec0c28d..749b0471349 100644 --- a/unittests/test-contracts/CMakeLists.txt +++ b/unittests/test-contracts/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory( integration_test ) add_subdirectory( noop ) add_subdirectory( payloadless ) add_subdirectory( proxy ) +add_subdirectory( restrict_action_test ) add_subdirectory( snapshot_test ) add_subdirectory( test_api ) add_subdirectory( test_api_db ) diff --git a/unittests/test-contracts/restrict_action_test/CMakeLists.txt b/unittests/test-contracts/restrict_action_test/CMakeLists.txt new file mode 100644 index 00000000000..5ffe32ae7da --- /dev/null +++ b/unittests/test-contracts/restrict_action_test/CMakeLists.txt @@ -0,0 +1,6 @@ +if( EOSIO_COMPILE_TEST_CONTRACTS ) + add_contract( restrict_action_test restrict_action_test restrict_action_test.cpp ) +else() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/restrict_action_test.wasm ${CMAKE_CURRENT_BINARY_DIR}/restrict_action_test.wasm COPYONLY ) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/restrict_action_test.abi ${CMAKE_CURRENT_BINARY_DIR}/restrict_action_test.abi COPYONLY ) +endif() diff --git a/unittests/test-contracts/restrict_action_test/restrict_action_test.abi b/unittests/test-contracts/restrict_action_test/restrict_action_test.abi new file mode 100644 index 00000000000..37db4926071 --- /dev/null +++ b/unittests/test-contracts/restrict_action_test/restrict_action_test.abi @@ -0,0 +1,98 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.1", + "types": [], + "structs": [ + { + "name": "noop", + "base": "", + "fields": [] + }, + { + "name": "notifydefer", + "base": "", + "fields": [ + { + "name": "acctonotify", + "type": "name" + }, + { + "name": "authorizer", + "type": "name" + }, + { + "name": "senderid", + "type": "uint32" + } + ] + }, + { + "name": "notifyinline", + "base": "", + "fields": [ + { + "name": "acctonotify", + "type": "name" + }, + { + "name": "authorizer", + "type": "name" + } + ] + }, + { + "name": "senddefer", + "base": "", + "fields": [ + { + "name": "authorizer", + "type": "name" + }, + { + "name": "senderid", + "type": "uint32" + } + ] + }, + { + "name": "sendinline", + "base": "", + "fields": [ + { + "name": "authorizer", + "type": "name" + } + ] + } + ], + "actions": [ + { + "name": "noop", + "type": "noop", + "ricardian_contract": "" + }, + { + "name": "notifydefer", + "type": "notifydefer", + "ricardian_contract": "" + }, + { + "name": "notifyinline", + "type": "notifyinline", + "ricardian_contract": "" + }, + { + "name": "senddefer", + "type": "senddefer", + "ricardian_contract": "" + }, + { + "name": "sendinline", + "type": "sendinline", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [] +} \ No newline at end of file diff --git a/unittests/test-contracts/restrict_action_test/restrict_action_test.cpp b/unittests/test-contracts/restrict_action_test/restrict_action_test.cpp new file mode 100644 index 00000000000..5c8f2b596b5 --- /dev/null +++ b/unittests/test-contracts/restrict_action_test/restrict_action_test.cpp @@ -0,0 +1,48 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#include "restrict_action_test.hpp" +#include + +using namespace eosio; + +void restrict_action_test::noop( ) { + +} + +void restrict_action_test::sendinline( name authorizer ) { + action( + permission_level{authorizer,"active"_n}, + get_self(), + "noop"_n, + std::make_tuple() + ).send(); +} + +void restrict_action_test::senddefer( name authorizer, uint32_t senderid ) { + transaction trx; + trx.actions.emplace_back( + permission_level{authorizer,"active"_n}, + get_self(), + "noop"_n, + std::make_tuple() + ); + trx.send(senderid, get_self()); +} + +void restrict_action_test::notifyinline( name acctonotify, name authorizer ) { + require_recipient(acctonotify); +} + +void restrict_action_test::notifydefer( name acctonotify, name authorizer, uint32_t senderid ) { + require_recipient(acctonotify); +} + +void restrict_action_test::on_notify_inline( name acctonotify, name authorizer ) { + sendinline(authorizer); +} + +void restrict_action_test::on_notify_defer( name acctonotify, name authorizer, uint32_t senderid ) { + senddefer(authorizer, senderid); +} diff --git a/unittests/test-contracts/restrict_action_test/restrict_action_test.hpp b/unittests/test-contracts/restrict_action_test/restrict_action_test.hpp new file mode 100644 index 00000000000..f5ab48e385b --- /dev/null +++ b/unittests/test-contracts/restrict_action_test/restrict_action_test.hpp @@ -0,0 +1,34 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#pragma once + +#include + +class [[eosio::contract]] restrict_action_test : public eosio::contract { +public: + using eosio::contract::contract; + + [[eosio::action]] + void noop( ); + + [[eosio::action]] + void sendinline( eosio::name authorizer ); + + [[eosio::action]] + void senddefer( eosio::name authorizer, uint32_t senderid ); + + + [[eosio::action]] + void notifyinline( eosio::name acctonotify, eosio::name authorizer ); + + [[eosio::action]] + void notifydefer( eosio::name acctonotify, eosio::name authorizer, uint32_t senderid ); + + [[eosio::on_notify("testacc::notifyinline")]] + void on_notify_inline( eosio::name acctonotify, eosio::name authorizer ); + + [[eosio::on_notify("testacc::notifydefer")]] + void on_notify_defer( eosio::name acctonotify, eosio::name authorizer, uint32_t senderid ); +}; diff --git a/unittests/test-contracts/restrict_action_test/restrict_action_test.wasm b/unittests/test-contracts/restrict_action_test/restrict_action_test.wasm new file mode 100755 index 00000000000..31acb952b19 Binary files /dev/null and b/unittests/test-contracts/restrict_action_test/restrict_action_test.wasm differ