Merge pull request #363 from AntelopeIO/consolidate_abi

SC: Consolidate all sync call related fields into a single call section in ABI and allow a function to be tagged both as an action and a call
This commit is contained in:
Lin Huang
2025-07-14 16:22:24 -04:00
committed by GitHub
14 changed files with 43 additions and 199 deletions
+22
View File
@@ -126,6 +126,28 @@ BOOST_AUTO_TEST_CASE(void_func_test) { try {
check(trx_trace);
} FC_LOG_AND_RETHROW() }
// Verify a function tagged as both `action` and `call` works
BOOST_AUTO_TEST_CASE(mixed_action_call_tags_test) { try {
call_tester t({
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
});
// `sum` in `callee` contract is tagged as `action` and `call`
// Make sure we can make a sync call to `sum` (`mulparamtest` in `caller` does
// a sync call to `sum`)
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "hstmulprmtst"_n, "caller"_n, {}));
// Make sure we can push an action using `sum`.
//BOOST_REQUIRE_NO_THROW(t.push_action("callee"_n, "sum"_n, "callee"_n,
t.push_action("callee"_n, "sum"_n, "callee"_n,
mvo()
("a", 1)
("b", 2)
("c", 3)); //);
} FC_LOG_AND_RETHROW() }
// Verify the receiver contract with only one sync call function works
// (for testing the sync_call entry point dispatcher)
BOOST_AUTO_TEST_CASE(single_function_test) { try {
+9 -51
View File
@@ -3,38 +3,15 @@
"version": "eosio::abi/1.3",
"types": [],
"structs": [
{
"name": "call_data_header",
"base": "",
"fields": [
{
"name": "version",
"type": "uint32"
},
{
"name": "func_name",
"type": "uint64"
}
]
},
{
"name": "noparam",
"base": "",
"fields": [
{
"name": "header",
"type": "call_data_header"
}
]
"fields": []
},
{
"name": "sum",
"base": "",
"fields": [
{
"name": "header",
"type": "call_data_header"
},
{
"name": "a",
"type": "uint32"
@@ -52,21 +29,12 @@
{
"name": "voidfunc",
"base": "",
"fields": [
{
"name": "header",
"type": "call_data_header"
}
]
"fields": []
},
{
"name": "withparam",
"base": "",
"fields": [
{
"name": "header",
"type": "call_data_header"
},
{
"name": "in",
"type": "uint32"
@@ -83,35 +51,25 @@
{
"name": "noparam",
"type": "noparam",
"id": 229476383600947
"id": 229476383600947,
"result_type": "uint32"
},
{
"name": "sum",
"type": "sum",
"id": 193506202
"id": 193506202,
"result_type": "uint32"
},
{
"name": "voidfunc",
"type": "voidfunc",
"id": 7573061335575747
"id": 7573061335575747,
"result_type": ""
},
{
"name": "withparam",
"type": "withparam",
"id": 249912189145794130
}
],
"call_results": [
{
"name": "noparam",
"result_type": "uint32"
},
{
"name": "sum",
"result_type": "uint32"
},
{
"name": "withparam",
"id": 249912189145794130,
"result_type": "uint32"
}
]
@@ -1,19 +0,0 @@
#include <eosio/call.hpp>
#include <eosio/eosio.hpp>
// Test the validation that action and call tags cannot be mixed.
class [[eosio::contract]] invalid_call_tags : public eosio::contract{
public:
using contract::contract;
[[eosio::action, eosio::call]]
uint32_t act1() {
return 0;
}
[[eosio::call, eosio::action]]
uint32_t act2() {
return 0;
}
};
@@ -1,11 +0,0 @@
{
"tests" : [
{
"compile_flags": [],
"expected" : {
"exit-code": 255,
"stderr": "codegen error (cannot be tagged as both action and call)"
}
}
]
}
@@ -1,17 +0,0 @@
#include <eosio/call.hpp>
#include <eosio/eosio.hpp>
// Test the validation that action and call tags cannot be mixed.
class [[eosio::contract]] invalid_call_tags : public eosio::contract{
public:
using contract::contract;
[[eosio::action]]
uint32_t act();
};
[[eosio::call]]
uint32_t invalid_call_tags::act() {
return 0;
}
@@ -1,11 +0,0 @@
{
"tests" : [
{
"compile_flags": [],
"expected" : {
"exit-code": 255,
"stderr": "codegen error (cannot be tagged as both action and call)"
}
}
]
}
@@ -1,17 +0,0 @@
#include <eosio/call.hpp>
#include <eosio/eosio.hpp>
// Test the validation that action and call tags cannot be mixed.
class [[eosio::contract]] invalid_call_tags : public eosio::contract{
public:
using contract::contract;
[[eosio::call]]
uint32_t act();
};
[[eosio::action]]
uint32_t invalid_call_tags::act() {
return 0;
}
@@ -1,11 +0,0 @@
{
"tests" : [
{
"compile_flags": [],
"expected" : {
"exit-code": 255,
"stderr": "codegen error (cannot be tagged as both action and call)"
}
}
]
}
@@ -16,7 +16,7 @@ void sync_call_callee::void_func() {
eosio::print("I am a void function");
}
[[eosio::call]]
[[eosio::action, eosio::call]]
uint32_t sync_call_callee::sum(uint32_t a, uint32_t b, uint32_t c) {
return a + b + c;
}
@@ -26,7 +26,7 @@ public:
[[eosio::call]]
void void_func();
[[eosio::call]]
[[eosio::action, eosio::call]]
uint32_t sum(uint32_t a, uint32_t b, uint32_t c);
// pass in a struct and return it
+1 -7
View File
@@ -34,6 +34,7 @@ struct abi_call {
std::string name;
std::string type;
uint64_t id = 0; // internal short ID of `name`
std::string result_type;
bool operator<(const abi_call& s) const { return name < s.name; }
};
@@ -68,12 +69,6 @@ struct abi_action_result {
bool operator<(const abi_action_result& ar) const { return name < ar.name; }
};
struct abi_call_result {
std::string name;
std::string type;
bool operator<(const abi_call_result& ar) const { return name < ar.name; }
};
// The version when sync call was first introduced.
constexpr int abi_call_version_major = 1;
constexpr int abi_call_version_minor = 3;
@@ -93,7 +88,6 @@ struct abi {
std::vector<abi_ricardian_clause_pair> ricardian_clauses;
std::vector<abi_error_message> error_messages;
std::set<abi_action_result> action_results;
std::set<abi_call_result> call_results;
};
inline void dump( const abi& abi ) {
+5 -36
View File
@@ -163,11 +163,12 @@ namespace eosio { namespace cdt {
}
ret.type = decl->getNameAsString();
ret.id = to_hash_id(ret.name);
_abi.calls.insert(ret);
if (translate_type(decl->getReturnType()) != "void") {
auto result_type = translate_type(decl->getReturnType());
if (result_type != "void") {
add_type(decl->getReturnType());
_abi.call_results.insert({get_call_name(decl), translate_type(decl->getReturnType())});
ret.result_type = result_type;
}
_abi.calls.insert(ret);
}
void add_tuple(const clang::QualType& type) {
@@ -256,20 +257,6 @@ namespace eosio { namespace cdt {
abi_struct new_struct;
new_struct.name = decl->getNameAsString();
if (decl->isEosioCall()) {
// Add call_data_header definition to structs set
abi_struct data_header;
data_header.name = "call_data_header";
if (_abi.structs.count(data_header) == 0) {
data_header.fields.push_back({"version", "uint32"});
data_header.fields.push_back({"func_name", "uint64"});
_abi.structs.insert(data_header);
}
// Add header field as the first field to the method struct
new_struct.fields.push_back({"header", "call_data_header"});
}
for (auto param : decl->parameters() ) {
auto param_type = param->getType().getNonReferenceType().getUnqualifiedType();
new_struct.fields.push_back({param->getNameAsString(), get_type(param_type)});
@@ -610,6 +597,7 @@ namespace eosio { namespace cdt {
o["name"] = c.name;
o["type"] = c.type;
o["id"] = c.id;
o["result_type"] = c.result_type;
return o;
}
@@ -637,13 +625,6 @@ namespace eosio { namespace cdt {
return o;
}
ojson call_result_to_json( const abi_call_result& result ) {
ojson o;
o["name"] = result.name;
o["result_type"] = result.type;
return o;
}
bool is_empty() {
std::set<abi_table> set_of_tables;
for ( auto t : ctables ) {
@@ -743,10 +724,6 @@ namespace eosio { namespace cdt {
if (as.name == _translate_type(ar.type))
return true;
}
for( auto ar : _abi.call_results ) {
if (as.name == _translate_type(ar.type))
return true;
}
return false;
};
@@ -782,9 +759,6 @@ namespace eosio { namespace cdt {
if ( ar.type == td.new_type_name )
return true;
}
for ( auto ar : _abi.call_results )
if ( ar.type == td.new_type_name )
return true;
return false;
};
@@ -808,11 +782,6 @@ namespace eosio { namespace cdt {
for ( auto a : _abi.calls ) {
o["calls"].push_back(call_to_json( a ));
}
o["call_results"] = ojson::array();
for ( auto ar : _abi.call_results ) {
o["call_results"].push_back(call_result_to_json( ar ));
}
}
o["tables"] = ojson::array();
for ( auto t : set_of_tables ) {
-12
View File
@@ -39,7 +39,6 @@ class ABIMerger {
}
if (std::stod(vers.substr(vers.size()-3))*10 >= abi_call_version) {
ret["calls"] = merge_calls(other);
ret["call_results"] = merge_call_results(other);
}
return ret;
}
@@ -122,11 +121,6 @@ class ABIMerger {
a["result_type"] == b["result_type"];
}
static bool call_result_is_same(ojson a, ojson b) {
return a["name"] == b["name"] &&
a["result_type"] == b["result_type"];
}
template <typename F>
void add_object_to_array(ojson& ret, ojson a, ojson b, std::string type, std::string id, F&& is_same_func) {
for (auto obj_a : a[type].array_range()) {
@@ -218,12 +212,6 @@ class ABIMerger {
return res;
}
ojson merge_call_results(ojson b) {
ojson res = ojson::array();
add_object_to_array(res, abi, b, "call_results", "name", call_result_is_same);
return res;
}
ojson abi;
};
#pragma GCC diagnostic pop
+4 -5
View File
@@ -318,10 +318,6 @@ namespace eosio { namespace cdt {
static std::set<std::string> _notify_set; //used for validations
static std::set<std::string> _call_set; //used for validations
if (decl->isEosioAction() && decl->isEosioCall()) {
CDT_ERROR("codegen_error", decl->getLocation(), "cannot be tagged as both action and call");
}
if (decl->isEosioAction()) {
name = generation_utils::get_action_name(decl);
validate_name(name, [&](auto s) {
@@ -368,7 +364,10 @@ namespace eosio { namespace cdt {
create_notify_dispatch(decl);
cg.notify_handlers.insert(full_notify_name); // insert the method action, so we don't create the dispatcher twice
}
} else if (decl->isEosioCall()) {
}
// We allow a method to be tagged as both `action` and `call`
if (decl->isEosioCall()) {
static std::unordered_map<uint64_t, std::string> _call_id_map;
name = generation_utils::get_call_name(decl);