Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
404 changes: 404 additions & 0 deletions unittests/action_ordering_tests.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions unittests/test-contracts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ add_subdirectory( settlewns )
add_subdirectory( snapshot_test )
add_subdirectory( test_api )
add_subdirectory( test_ram_limit )
add_subdirectory( action_order_test )
add_subdirectory( action_results )
add_subdirectory( wasm_config_bios )
add_subdirectory( params_test )
Expand Down
6 changes: 6 additions & 0 deletions unittests/test-contracts/action_order_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
if( BUILD_TEST_CONTRACTS )
add_contract( action_order_test action_order_test action_order_test.cpp )
else()
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/action_order_test.wasm ${CMAKE_CURRENT_BINARY_DIR}/action_order_test.wasm COPYONLY )
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/action_order_test.abi ${CMAKE_CURRENT_BINARY_DIR}/action_order_test.abi COPYONLY )
endif()
133 changes: 133 additions & 0 deletions unittests/test-contracts/action_order_test/action_order_test.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{
"____comment": "This file was generated with sysio-abigen. DO NOT EDIT ",
"version": "sysio::abi/1.2",
"types": [],
"structs": [
{
"name": "inlnotify",
"base": "",
"fields": [
{
"name": "obs",
"type": "name"
},
{
"name": "inlt",
"type": "name"
}
]
},
{
"name": "mixedord",
"base": "",
"fields": [
{
"name": "obs",
"type": "name"
},
{
"name": "inlt",
"type": "name"
},
{
"name": "cfat",
"type": "name"
}
]
},
{
"name": "nestedcall",
"base": "",
"fields": [
{
"name": "obs",
"type": "name"
},
{
"name": "inlt",
"type": "name"
}
]
},
{
"name": "noop",
"base": "",
"fields": []
},
{
"name": "notiffire",
"base": "",
"fields": [
{
"name": "obs",
"type": "name"
}
]
},
{
"name": "notifyinl",
"base": "",
"fields": [
{
"name": "obs",
"type": "name"
},
{
"name": "inlt",
"type": "name"
}
]
},
{
"name": "notifyme",
"base": "",
"fields": [
{
"name": "obs",
"type": "name"
}
]
}
],
"actions": [
{
"name": "inlnotify",
"type": "inlnotify",
"ricardian_contract": ""
},
{
"name": "mixedord",
"type": "mixedord",
"ricardian_contract": ""
},
{
"name": "nestedcall",
"type": "nestedcall",
"ricardian_contract": ""
},
{
"name": "noop",
"type": "noop",
"ricardian_contract": ""
},
{
"name": "notiffire",
"type": "notiffire",
"ricardian_contract": ""
},
{
"name": "notifyinl",
"type": "notifyinl",
"ricardian_contract": ""
},
{
"name": "notifyme",
"type": "notifyme",
"ricardian_contract": ""
}
],
"tables": [],
"ricardian_clauses": [],
"variants": [],
"action_results": []
}
93 changes: 93 additions & 0 deletions unittests/test-contracts/action_order_test/action_order_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "action_order_test.hpp"

using namespace sysio;

namespace {
/**
* @brief Build an inline action_wrapper instance with no authorizations.
*
* Inline actions normally carry the sender's permission_level. For these
* ordering tests we only care about the trace shape, not auth checks, so
* we pass an empty permission vector - the inline-action code path
* accepts empty authorization (check_authorization has nothing to verify)
* and the resulting trace is identical in shape.
*/
template <typename Wrapper>
Wrapper make_unauthed_wrapper(name code) {
return Wrapper(code, std::vector<permission_level>{});
}
} // namespace

void action_order_test::noop() {
// Intentionally empty. The trace's action_receipt is still produced.
}

void action_order_test::notifyme(name obs) {
require_recipient(obs);
}

void action_order_test::notiffire(name obs) {
// Single host call: enqueue obs as a notification recipient. The
// matching on_notify handler below queues a follow-on inline.
require_recipient(obs);
}

void action_order_test::on_notiffire([[maybe_unused]] name obs) {
// We are running inside the notification phase of the SHARED
// apply_context belonging to the originating notiffire action.
// send_inline(noop) lands in that shared _inline_actions queue and
// will execute only AFTER every sibling notification finishes. The
// inline targets get_self() (the notified receiver), so this trace
// shows the inline executing on the notification recipient, not the
// originator - matching the "B observes A and then acts" pattern.
auto act = make_unauthed_wrapper<noop_action>(get_self());
act.send();
}

void action_order_test::inlnotify(name obs, name inlt) {
// HEADLINE: schedule the inline FIRST, then the notification.
// Inline is queued in _inline_actions (regular inline phase, phase 4).
// Notification is queued in _notified (notification phase, phase 2).
// Result: notification runs first at execution time, but the action_trace
// for the inline appears EARLIER in the action_traces vector because it
// was scheduled (assigned its ordinal) first.
auto inline_act = make_unauthed_wrapper<noop_action>(inlt);
inline_act.send();
require_recipient(obs);
}

void action_order_test::notifyinl(name obs, name inlt) {
// Natural ordering: notification scheduled first, inline second.
// Here ordinal order and global_sequence order agree, providing the
// contrast case for the inlnotify divergence test.
require_recipient(obs);
auto inline_act = make_unauthed_wrapper<noop_action>(inlt);
inline_act.send();
}

void action_order_test::nestedcall(name obs, name inlt) {
// Outer scheduling: notification, then inline.
// The inline itself does require_recipient(obs) inside notifyme; that
// notification belongs to the INLINE'S apply_context, not this one,
// so it shows up as an additional trace inside the inline's subtree.
require_recipient(obs);
auto inline_act = make_unauthed_wrapper<notifyme_action>(inlt);
inline_act.send(obs);
}

void action_order_test::mixedord(name obs, name inlt, name cfat) {
// Three host calls in this scheduling order:
// 1. regular inline noop on inlt
// 2. CFA inline noop on cfat
// 3. require_recipient(obs)
//
// Despite that, execution runs phases in fixed order:
// self -> notifications -> CFA inlines -> regular inlines.
// So the obs notification (last scheduled) runs FIRST after self,
// the CFA noop runs second, the regular noop runs LAST.
auto regular_inline = make_unauthed_wrapper<noop_action>(inlt);
regular_inline.send();
auto cfa_inline = make_unauthed_wrapper<noop_action>(cfat);
cfa_inline.send_context_free();
require_recipient(obs);
}
Loading