Compare commits
28 Commits
v5.0.0-dev1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 69599db279 | |||
| d81cb903ad | |||
| 06a3c518fe | |||
| 7a2c49262f | |||
| 7479f2be35 | |||
| acfb0a7cc2 | |||
| d658c90b6d | |||
| 2ae2708fd8 | |||
| 36f2da87c3 | |||
| fef5731b58 | |||
| 92945e4572 | |||
| c32f4e4f7a | |||
| c8535689e2 | |||
| 8b76fa3f7c | |||
| a762ebdbab | |||
| d244ead4d8 | |||
| 4153ba41f7 | |||
| 62f04bd94d | |||
| d607a92712 | |||
| 425fc6c3c2 | |||
| 00cb315396 | |||
| a53fc34ce3 | |||
| a294a08ab7 | |||
| bad7b81900 | |||
| db42ab457f | |||
| b086797420 | |||
| 09b234bf35 | |||
| b9f47e0119 |
+1
-1
Submodule cdt-llvm updated: b919709985...34c0de5404
@@ -29,6 +29,12 @@ namespace eosio {
|
||||
*/
|
||||
class contract {
|
||||
public:
|
||||
enum class exec_type_t : uint8_t {
|
||||
action,
|
||||
call,
|
||||
unknown
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a new contract given the contract name
|
||||
*
|
||||
@@ -75,7 +81,27 @@ class contract {
|
||||
*/
|
||||
inline const datastream<const char*>& get_datastream()const { return _ds; }
|
||||
|
||||
/**
|
||||
* Whether this contract is for a sync call
|
||||
*
|
||||
* @return bool - Whether this contract is for a sync call
|
||||
*/
|
||||
inline bool is_sync_call()const {
|
||||
check(_exec_type != exec_type_t::unknown, "too early to call is_sync_call(). _exec_type has not been set yet");
|
||||
return (_exec_type == exec_type_t::call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exectution type.
|
||||
*
|
||||
* @param type - The exectution type to be set.
|
||||
*/
|
||||
inline void set_exec_type(exec_type_t type) {
|
||||
_exec_type = type;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The name of the account this contract is deployed on.
|
||||
*/
|
||||
@@ -90,5 +116,10 @@ class contract {
|
||||
* The datastream for this contract
|
||||
*/
|
||||
datastream<const char*> _ds = datastream<const char*>(nullptr, 0);
|
||||
|
||||
/**
|
||||
* The execution type: action or sync call
|
||||
*/
|
||||
exec_type_t _exec_type = exec_type_t::unknown;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright defined in eos/LICENSE
|
||||
*/
|
||||
#pragma once
|
||||
#include "system.hpp"
|
||||
#include "transaction.hpp"
|
||||
#include "../../core/eosio/serialize.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace eosio {
|
||||
namespace internal_use_do_not_use {
|
||||
extern "C" {
|
||||
__attribute__((eosio_wasm_import))
|
||||
void send_deferred(const uint128_t&, uint64_t, const char*, size_t, uint32_t);
|
||||
|
||||
__attribute__((eosio_wasm_import))
|
||||
int cancel_deferred(const uint128_t&);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup deferred_transaction Transaction
|
||||
* @ingroup contracts
|
||||
* @brief Type-safe C++ wrappers for transaction C API
|
||||
*
|
||||
* deferred_transaction is no longer supported on Vaulta. This class is provided to
|
||||
* support legacy test contracts that use deferred transactions.
|
||||
*
|
||||
* @details An inline message allows one contract to send another contract a message
|
||||
* which is processed immediately after the current message's processing
|
||||
* ends such that the success or failure of the parent transaction is
|
||||
* dependent on the success of the message. If an inline message fails in
|
||||
* processing then the whole tree of transactions and actions rooted in the
|
||||
* block will me marked as failing and none of effects on the database will
|
||||
* persist.
|
||||
*
|
||||
* Inline actions and Deferred transactions must adhere to the permissions
|
||||
* available to the parent transaction or, in the future, delegated to the
|
||||
* contract account for future use.
|
||||
*
|
||||
* @note There are some methods from the @ref transactioncapi that can be used directly from C++
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class transaction contains the actions, context_free_actions and extensions type for a transaction
|
||||
*
|
||||
* @ingroup transaction
|
||||
*/
|
||||
class deferred_transaction : public transaction {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a new deferred_transaction with an expiration of now + 60 seconds.
|
||||
*/
|
||||
deferred_transaction(time_point_sec exp = time_point_sec(current_time_point()) + 60) : transaction( exp ) {}
|
||||
|
||||
/**
|
||||
* Sends this transaction, packs the transaction then sends it as a deferred transaction
|
||||
*
|
||||
* @details Writes the symbol_code as a string to the provided char buffer
|
||||
* @param sender_id - ID of sender
|
||||
* @param payer - Account paying for RAM
|
||||
* @param replace_existing - Defaults to false, if this is `0`/false then if the provided sender_id is already in use by an in-flight transaction from this contract, which will be a failing assert. If `1` then transaction will atomically cancel/replace the inflight transaction
|
||||
*/
|
||||
void send(const uint128_t& sender_id, name payer, bool replace_existing = false) const {
|
||||
auto serialize = pack(*static_cast<const transaction*>(this));
|
||||
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialize.data(), serialize.size(), replace_existing);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct onerror contains and sender id and packed transaction
|
||||
*
|
||||
* @ingroup transaction
|
||||
*/
|
||||
struct onerror {
|
||||
uint128_t sender_id;
|
||||
std::vector<char> sent_trx;
|
||||
|
||||
/**
|
||||
* from_current_action unpacks and returns a onerror struct
|
||||
*
|
||||
* @ingroup transaction
|
||||
*/
|
||||
static onerror from_current_action() {
|
||||
return unpack_action_data<onerror>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks and returns a transaction
|
||||
*/
|
||||
transaction unpack_sent_trx() const {
|
||||
return unpack<transaction>(sent_trx);
|
||||
}
|
||||
|
||||
EOSLIB_SERIALIZE( onerror, (sender_id)(sent_trx) )
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a deferred transaction
|
||||
*
|
||||
* @ingroup transaction
|
||||
* @param sender_id - Account name of the sender of this deferred transaction
|
||||
* @param payer - Account name responsible for paying the RAM for this deferred transaction
|
||||
* @param serialized_transaction - The packed transaction to be deferred
|
||||
* @param size - The size of the packed transaction, required for persistence.
|
||||
* @param replace - If true, will replace an existing transaction.
|
||||
*/
|
||||
inline void send_deferred(const uint128_t& sender_id, name payer, const char* serialized_transaction, size_t size, bool replace = false) {
|
||||
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialized_transaction, size, replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a deferred transaction.
|
||||
*
|
||||
* @ingroup transaction
|
||||
* @param sender_id - The id of the sender
|
||||
*
|
||||
* @pre The deferred transaction ID exists.
|
||||
* @pre The deferred transaction ID has not yet been published.
|
||||
* @post Deferred transaction canceled.
|
||||
*
|
||||
* @return 1 if transaction was canceled, 0 if transaction was not found
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
* id = 0xffffffffffffffff
|
||||
* cancel_deferred( id );
|
||||
* @endcode
|
||||
*/
|
||||
inline int cancel_deferred(const uint128_t& sender_id) {
|
||||
return internal_use_do_not_use::cancel_deferred(sender_id);
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,11 @@ namespace eosio { namespace detail {
|
||||
template <typename T>
|
||||
struct is_same<T,bool> { static constexpr bool value = std::is_integral<T>::value; };
|
||||
|
||||
// Full specialization to resolve ambiguity introduced by partial specializations
|
||||
// of is_same<bool,U> and is_same<T,bool>
|
||||
template <>
|
||||
struct is_same<bool, bool> { static constexpr bool value = true; };
|
||||
|
||||
template <size_t N, size_t I, auto Arg, auto... Args>
|
||||
struct get_nth_impl { static constexpr auto value = get_nth_impl<N,I+1,Args...>::value; };
|
||||
|
||||
|
||||
@@ -13,12 +13,6 @@
|
||||
namespace eosio {
|
||||
namespace internal_use_do_not_use {
|
||||
extern "C" {
|
||||
__attribute__((eosio_wasm_import))
|
||||
void send_deferred(const uint128_t&, uint64_t, const char*, size_t, uint32_t);
|
||||
|
||||
__attribute__((eosio_wasm_import))
|
||||
int cancel_deferred(const uint128_t&);
|
||||
|
||||
__attribute__((eosio_wasm_import))
|
||||
size_t read_transaction(char*, size_t);
|
||||
|
||||
@@ -47,17 +41,7 @@ namespace eosio {
|
||||
* @ingroup contracts
|
||||
* @brief Type-safe C++ wrappers for transaction C API
|
||||
*
|
||||
* @details An inline message allows one contract to send another contract a message
|
||||
* which is processed immediately after the current message's processing
|
||||
* ends such that the success or failure of the parent transaction is
|
||||
* dependent on the success of the message. If an inline message fails in
|
||||
* processing then the whole tree of transactions and actions rooted in the
|
||||
* block will me marked as failing and none of effects on the database will
|
||||
* persist.
|
||||
*
|
||||
* Inline actions and Deferred transactions must adhere to the permissions
|
||||
* available to the parent transaction or, in the future, delegated to the
|
||||
* contract account for future use.
|
||||
* @details See action for the ability to send an inline action.
|
||||
*
|
||||
* @note There are some methods from the @ref transactioncapi that can be used directly from C++
|
||||
*/
|
||||
@@ -83,17 +67,17 @@ namespace eosio {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a new transaction_header with an expiration of now + 60 seconds.
|
||||
* Construct a new transaction_header
|
||||
*
|
||||
* @brief Construct a new transaction_header object initialising the transaction header expiration to now + 60 seconds
|
||||
* @brief Construct a new transaction_header object
|
||||
*/
|
||||
transaction_header( time_point_sec exp = time_point_sec(current_time_point()) + 60)
|
||||
transaction_header( time_point_sec exp = time_point_sec{} )
|
||||
:expiration(exp)
|
||||
{}
|
||||
|
||||
time_point_sec expiration;
|
||||
uint16_t ref_block_num;
|
||||
uint32_t ref_block_prefix;
|
||||
uint16_t ref_block_num = 0UL;
|
||||
uint32_t ref_block_prefix = 0UL;
|
||||
unsigned_int max_net_usage_words = 0UL; /// number of 8 byte words this transaction can serialize into after compressions
|
||||
uint8_t max_cpu_usage_ms = 0UL; /// number of CPU usage units to bill transaction for
|
||||
unsigned_int delay_sec = 0UL; /// number of seconds to delay transaction, default: 0
|
||||
@@ -110,22 +94,9 @@ namespace eosio {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a new transaction with an expiration of now + 60 seconds.
|
||||
* Construct a new transaction
|
||||
*/
|
||||
transaction(time_point_sec exp = time_point_sec(current_time_point()) + 60) : transaction_header( exp ) {}
|
||||
|
||||
/**
|
||||
* Sends this transaction, packs the transaction then sends it as a deferred transaction
|
||||
*
|
||||
* @details Writes the symbol_code as a string to the provided char buffer
|
||||
* @param sender_id - ID of sender
|
||||
* @param payer - Account paying for RAM
|
||||
* @param replace_existing - Defaults to false, if this is `0`/false then if the provided sender_id is already in use by an in-flight transaction from this contract, which will be a failing assert. If `1` then transaction will atomically cancel/replace the inflight transaction
|
||||
*/
|
||||
void send(const uint128_t& sender_id, name payer, bool replace_existing = false) const {
|
||||
auto serialize = pack(*this);
|
||||
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialize.data(), serialize.size(), replace_existing);
|
||||
}
|
||||
transaction(time_point_sec exp = time_point_sec{}) : transaction_header( exp ) {}
|
||||
|
||||
std::vector<action> context_free_actions;
|
||||
std::vector<action> actions;
|
||||
@@ -134,47 +105,6 @@ namespace eosio {
|
||||
EOSLIB_SERIALIZE_DERIVED( transaction, transaction_header, (context_free_actions)(actions)(transaction_extensions) )
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct onerror contains and sender id and packed transaction
|
||||
*
|
||||
* @ingroup transaction
|
||||
*/
|
||||
struct onerror {
|
||||
uint128_t sender_id;
|
||||
std::vector<char> sent_trx;
|
||||
|
||||
/**
|
||||
* from_current_action unpacks and returns a onerror struct
|
||||
*
|
||||
* @ingroup transaction
|
||||
*/
|
||||
static onerror from_current_action() {
|
||||
return unpack_action_data<onerror>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks and returns a transaction
|
||||
*/
|
||||
transaction unpack_sent_trx() const {
|
||||
return unpack<transaction>(sent_trx);
|
||||
}
|
||||
|
||||
EOSLIB_SERIALIZE( onerror, (sender_id)(sent_trx) )
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a deferred transaction
|
||||
*
|
||||
* @ingroup transaction
|
||||
* @param sender_id - Account name of the sender of this deferred transaction
|
||||
* @param payer - Account name responsible for paying the RAM for this deferred transaction
|
||||
* @param serialized_transaction - The packed transaction to be deferred
|
||||
* @param size - The size of the packed transaction, required for persistence.
|
||||
* @param replace - If true, will replace an existing transaction.
|
||||
*/
|
||||
inline void send_deferred(const uint128_t& sender_id, name payer, const char* serialized_transaction, size_t size, bool replace = false) {
|
||||
internal_use_do_not_use::send_deferred(sender_id, payer.value, serialized_transaction, size, replace);
|
||||
}
|
||||
/**
|
||||
* Retrieve the indicated action from the active transaction.
|
||||
*
|
||||
@@ -204,29 +134,6 @@ namespace eosio {
|
||||
return internal_use_do_not_use::read_transaction( ptr, sz );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a deferred transaction.
|
||||
*
|
||||
* @ingroup transaction
|
||||
* @param sender_id - The id of the sender
|
||||
*
|
||||
* @pre The deferred transaction ID exists.
|
||||
* @pre The deferred transaction ID has not yet been published.
|
||||
* @post Deferred transaction canceled.
|
||||
*
|
||||
* @return 1 if transaction was canceled, 0 if transaction was not found
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
* id = 0xffffffffffffffff
|
||||
* cancel_deferred( id );
|
||||
* @endcode
|
||||
*/
|
||||
inline int cancel_deferred(const uint128_t& sender_id) {
|
||||
return internal_use_do_not_use::cancel_deferred(sender_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the currently executing transaction.
|
||||
*
|
||||
|
||||
@@ -140,12 +140,11 @@ BOOST_AUTO_TEST_CASE(mixed_action_call_tags_test) { try {
|
||||
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,
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("callee"_n, "sum"_n, "callee"_n,
|
||||
mvo()
|
||||
("a", 1)
|
||||
("b", 2)
|
||||
("c", 3)); //);
|
||||
("c", 3)));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify the receiver contract with only one sync call function works
|
||||
@@ -234,4 +233,43 @@ BOOST_AUTO_TEST_CASE(addr_book_tests) { try {
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "get"_n, "caller"_n, mvo() ("user", "alice")));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// For a function tagged as both `action` and `call`, verify is_sync_call()
|
||||
// returns true if tagged as `call` and false if tagged as `action`.
|
||||
BOOST_AUTO_TEST_CASE(is_sync_call_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
// issynccall() is tagged as both `action` and `call`, and returns
|
||||
// is_sync_call(). makesynccall() calls issynccall() as a sync call
|
||||
// and returns its result. So the current action return value must be
|
||||
// true.
|
||||
auto trx_trace = t.push_action("caller"_n, "makesynccall"_n, "caller"_n, {});
|
||||
auto& action_trace = trx_trace->action_traces[0];
|
||||
bool return_value = fc::raw::unpack<bool>(action_trace.return_value);
|
||||
BOOST_REQUIRE(return_value == true);
|
||||
|
||||
// Call issynccall() directly as an action. Since issynccall()
|
||||
// returns is_sync_call(), the current action return value must be false.
|
||||
trx_trace = t.push_action("callee"_n, "issynccall"_n, "callee"_n, {});
|
||||
auto& action_trace1 = trx_trace->action_traces[0];
|
||||
return_value = fc::raw::unpack<bool>(action_trace1.return_value);
|
||||
BOOST_REQUIRE(return_value == false);
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify customized sync call entry function, without sync call tag
|
||||
BOOST_AUTO_TEST_CASE(customized_call_entry_func_test1) { try {
|
||||
call_tester t({{"receiver"_n, contracts::cust_entry_wasm(), contracts::cust_entry_abi().data()}});
|
||||
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("receiver"_n, "cusentrytst1"_n, "receiver"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify customized sync call entry function, with sync call tag
|
||||
BOOST_AUTO_TEST_CASE(customized_call_entry_func_test2) { try {
|
||||
call_tester t({{"receiver"_n, contracts::cust_entry_wasm(), contracts::cust_entry_abi().data()}});
|
||||
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("receiver"_n, "cusentrytst2"_n, "receiver"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -54,5 +54,7 @@ namespace eosio::testing {
|
||||
static std::vector<char> addr_book_callee_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_addr_book_callee.abi"); }
|
||||
static std::vector<uint8_t> addr_book_caller_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_addr_book_caller.wasm"); }
|
||||
static std::vector<char> addr_book_caller_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_addr_book_caller.abi"); }
|
||||
static std::vector<uint8_t> cust_entry_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_cust_entry.wasm"); }
|
||||
static std::vector<char> cust_entry_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_cust_entry.abi"); }
|
||||
};
|
||||
} //ns eosio::testing
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
class [[eosio::contract]] separate_cpp_hpp : eosio::contract {
|
||||
class [[eosio::contract]] separate_cpp_hpp : public eosio::contract {
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ add_contract(sync_call_not_supported sync_call_not_supported sync_call_not_suppo
|
||||
add_contract(sync_call_single_func sync_call_single_func sync_call_single_func.cpp)
|
||||
add_contract(sync_call_addr_book_callee sync_call_addr_book_callee sync_call_addr_book_callee.cpp)
|
||||
add_contract(sync_call_addr_book_caller sync_call_addr_book_caller sync_call_addr_book_caller.cpp)
|
||||
add_contract(sync_call_cust_entry sync_call_cust_entry sync_call_cust_entry.cpp)
|
||||
add_contract(capi_tests capi_tests capi/capi.c capi/action.c capi/chain.c capi/crypto.c capi/db.c capi/permission.c
|
||||
capi/print.c capi/privileged.c capi/system.c capi/transaction.c capi/call.c)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <eosio/eosio.hpp>
|
||||
#include <eosio/transaction.hpp>
|
||||
#include <eosio/deferred_transaction.hpp>
|
||||
#include <eosio/bitset.hpp>
|
||||
|
||||
#include "transfer.hpp"
|
||||
@@ -52,7 +52,7 @@ class [[eosio::contract]] simple_tests : public contract {
|
||||
|
||||
[[eosio::action]]
|
||||
void testd(name nm) {
|
||||
transaction t;
|
||||
deferred_transaction t;
|
||||
action act;
|
||||
act.account = "other"_n;
|
||||
act.name = "testc"_n;
|
||||
|
||||
@@ -31,3 +31,6 @@ struct1_t sync_call_callee::pass_multi_structs(struct1_t s1, int32_t m, struct2_
|
||||
return { .a = s1.a * m + s2.c, .b = s1.b * m + s2.d };
|
||||
}
|
||||
|
||||
bool sync_call_callee::issynccall() {
|
||||
return is_sync_call();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ public:
|
||||
[[eosio::call]]
|
||||
struct1_t pass_multi_structs(struct1_t s1, int32_t m, struct2_t s2);
|
||||
|
||||
// return is_sync_call()
|
||||
[[eosio::action, eosio::call]]
|
||||
bool issynccall();
|
||||
using issynccall_func = eosio::call_wrapper<"issynccall"_i, &sync_call_callee::issynccall>;
|
||||
|
||||
using return_ten_func = eosio::call_wrapper<"return_ten"_i, &sync_call_callee::return_ten>;
|
||||
using echo_input_func = eosio::call_wrapper<"echo_input"_i, &sync_call_callee::echo_input>;
|
||||
using void_func_func = eosio::call_wrapper<"void_func"_i, &sync_call_callee::void_func>;
|
||||
|
||||
@@ -172,4 +172,11 @@ public:
|
||||
status = eosio::call("callee"_n, 0, bad_version_data.data(), bad_version_data.size());
|
||||
eosio::check(status == -10000, "call did not return -10000 for invalid version");
|
||||
}
|
||||
|
||||
// Call issynccall as a sync call and return its return value
|
||||
[[eosio::action]]
|
||||
bool makesynccall() {
|
||||
sync_call_callee::issynccall_func is_sync_call_func{ "callee"_n };
|
||||
return is_sync_call_func();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This contract is used to verify customized sync_call entry function works.
|
||||
* It verifies customized sync_call entry is exported, and `call()` host function
|
||||
* can reach it.
|
||||
*/
|
||||
|
||||
#include <eosio/eosio.hpp>
|
||||
#include <eosio/call.hpp>
|
||||
|
||||
namespace eosio {
|
||||
|
||||
class [[eosio::contract]] sync_call_cust_entry : public contract {
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
[[eosio::action]]
|
||||
void cusentrytst1() {
|
||||
// Make a sync call to "receiver"_n account, pass function ID in `data`,
|
||||
// go to `sync_call()` entry function, and dispatch the call to `get_10()`
|
||||
// based on the function ID.
|
||||
const std::vector<char> data{ eosio::pack(1) }; // function ID 1
|
||||
eosio::call("receiver"_n, 0, data.data(), data.size());
|
||||
|
||||
// Retrieve return value and unpack it
|
||||
std::vector<char> return_value(sizeof(uint32_t));
|
||||
eosio::get_call_return_value(return_value.data(), return_value.size());
|
||||
|
||||
// Verify it
|
||||
eosio::check(eosio::unpack<uint32_t>(return_value) == 10, "return value is not 10");
|
||||
}
|
||||
|
||||
[[eosio::action]]
|
||||
void cusentrytst2() {
|
||||
// Make a sync call to "receiver"_n account, pass function ID in `data`,
|
||||
// go to `sync_call()` entry function, and dispatch the call to `get_20()`
|
||||
// based on the function ID.
|
||||
const std::vector<char> data{ eosio::pack(2) }; // function ID 2
|
||||
eosio::call("receiver"_n, 0, data.data(), data.size());
|
||||
|
||||
// Retrieve return value and unpack it
|
||||
std::vector<char> return_value(sizeof(uint32_t));
|
||||
eosio::get_call_return_value(return_value.data(), return_value.size());
|
||||
|
||||
// Verify it
|
||||
eosio::check(eosio::unpack<uint32_t>(return_value) == 20, "return value is not 20");
|
||||
}
|
||||
|
||||
// `eosio::call` tag is optional
|
||||
uint32_t get_10() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t get_20() {
|
||||
return 20;
|
||||
}
|
||||
};
|
||||
} /// namespace eosio
|
||||
|
||||
extern "C" {
|
||||
[[eosio::wasm_entry]]
|
||||
int64_t sync_call(uint64_t sender, uint64_t receiver, uint32_t data_size) {
|
||||
eosio::datastream<const char*> ds(nullptr, 0); // for testing, not used
|
||||
eosio::sync_call_cust_entry obj(eosio::name{receiver}, eosio::name{receiver}, ds);
|
||||
|
||||
std::vector<char> data(sizeof(uint32_t));
|
||||
eosio::get_call_data(data.data(), data.size());
|
||||
auto func_id = eosio::unpack<uint32_t>(data);
|
||||
|
||||
// Dispatch sync calls
|
||||
uint32_t rv = 0;
|
||||
switch (func_id) {
|
||||
case 1: rv = obj.get_10(); break;
|
||||
case 2: rv = obj.get_20(); break;
|
||||
default: eosio::check(false, "wrong function ID");
|
||||
}
|
||||
|
||||
// set return value
|
||||
eosio::set_call_return_value(&rv, sizeof(uint32_t));
|
||||
|
||||
return 0; // return 0 to indicate success
|
||||
}
|
||||
}
|
||||
@@ -146,11 +146,28 @@ namespace eosio { namespace cdt {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Return `true` if the method `decl`'s base class if `eosio::contract`
|
||||
bool base_is_eosio_contract_class(const clang::CXXMethodDecl* decl) {
|
||||
auto cxx_decl = decl->getParent();
|
||||
// on this point it could be just an attribute so let's check base classes
|
||||
for (const auto& base : cxx_decl->bases()) {
|
||||
if (const clang::Type *base_type = base.getType().getTypePtrOrNull()) {
|
||||
if (const auto* cur_cxx_decl = base_type->getAsCXXRecordDecl()) {
|
||||
if (cur_cxx_decl->getQualifiedNameAsString() == "eosio::contract") {
|
||||
return true;;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void create_dispatch(const std::string& attr, const std::string& func_name, F&& get_str, CXXMethodDecl* decl) {
|
||||
constexpr static uint32_t max_stack_size = 512;
|
||||
codegen& cg = codegen::get();
|
||||
std::string nm = decl->getNameAsString()+"_"+decl->getParent()->getNameAsString();
|
||||
|
||||
if (cg.is_eosio_contract(decl, cg.contract_name)) {
|
||||
ss << "\n\n#include <eosio/datastream.hpp>\n";
|
||||
ss << "#include <eosio/name.hpp>\n";
|
||||
@@ -190,8 +207,22 @@ namespace eosio { namespace cdt {
|
||||
ss << tn << " arg" << i << "; ds >> arg" << i << ";\n";
|
||||
i++;
|
||||
}
|
||||
|
||||
// Create contract object
|
||||
ss << decl->getParent()->getQualifiedNameAsString()
|
||||
<< " obj {eosio::name{r},eosio::name{c},ds};\n";
|
||||
|
||||
// Call `set_exec_type()` only for contracts dervied from `eosio::contract`.
|
||||
// A contract class can have only `eosio::contract` attribute
|
||||
// but does not inherit from the `eosio::contract` class;
|
||||
// it may not have `set_exec_type()`. We need to make sure the class derives
|
||||
// from `eosio::contract` before calling set_exec_type().
|
||||
if (base_is_eosio_contract_class(decl)) {
|
||||
ss << "obj.set_exec_type(eosio::contract::exec_type_t::action);\n";
|
||||
}
|
||||
|
||||
const auto& call_action = [&]() {
|
||||
ss << decl->getParent()->getQualifiedNameAsString() << "{eosio::name{r},eosio::name{c},ds}." << decl->getNameAsString() << "(";
|
||||
ss << "obj." << decl->getNameAsString() << "(";
|
||||
for (int i=0; i < decl->parameters().size(); i++) {
|
||||
ss << "arg" << i;
|
||||
if (i < decl->parameters().size()-1)
|
||||
@@ -260,8 +291,22 @@ namespace eosio { namespace cdt {
|
||||
ss << tn << " arg" << i << "; ds >> arg" << i << ";\n";
|
||||
i++;
|
||||
}
|
||||
|
||||
// Create contract object
|
||||
ss << decl->getParent()->getQualifiedNameAsString()
|
||||
<< " obj {eosio::name{receiver},eosio::name{receiver},ds};\n";
|
||||
|
||||
// Call `set_exec_type()` only for contracts dervied from `eosio::contract`.
|
||||
// A contract class can have only `eosio::contract` attribute
|
||||
// but does not inherit from the `eosio::contract` class;
|
||||
// it may not have `set_exec_type()`. We need to make sure the class derives
|
||||
// from `eosio::contract` before calling set_exec_type().
|
||||
if (base_is_eosio_contract_class(decl)) {
|
||||
ss << "obj.set_exec_type(eosio::contract::exec_type_t::call);\n";
|
||||
}
|
||||
|
||||
const auto& call_function = [&]() {
|
||||
ss << decl->getParent()->getQualifiedNameAsString() << "{eosio::name{receiver},eosio::name{receiver},ds}." << decl->getNameAsString() << "(";
|
||||
ss << "obj." << decl->getNameAsString() << "(";
|
||||
for (int i=0; i < decl->parameters().size(); i++) {
|
||||
ss << "arg" << i;
|
||||
if (i < decl->parameters().size()-1)
|
||||
|
||||
@@ -170,7 +170,6 @@ public:
|
||||
{
|
||||
using std::swap;
|
||||
swap(lhs.it_,rhs.it_);
|
||||
swap(lhs.empty_,rhs.empty_);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -248,7 +247,6 @@ public:
|
||||
{
|
||||
using std::swap;
|
||||
swap(lhs.it_,rhs.it_);
|
||||
swap(lhs.empty_,rhs.empty_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user