-
Notifications
You must be signed in to change notification settings - Fork 32
SC: Implement new sync call host functions #346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
defff56
b97d563
21fa60d
fa72f82
fde97b1
1dcd192
b0b2e8e
2853bc1
b307b2e
8909962
f27981b
cfec3f1
617bfe6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"antelope-spring-dev":{ | ||
"target":"main", | ||
"target":"sync_call", | ||
"prerelease":false | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#pragma once | ||
#include "types.h" | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
/** | ||
* @addtogroup call_c Call C API | ||
* @ingroup c_api | ||
* @brief Defines API for querying call object and making a sync call | ||
*/ | ||
|
||
/** | ||
* Make a sync call in the context of this call's parent action or parent call | ||
* | ||
* @param receiver - the name of the account that the sync call is made to | ||
* @param flags - flags (bits) representing blockchain level requirements about | ||
* the call. Currently LSB bit indicates read-only. All other bits | ||
* are reserved to be 0 | ||
* @param data - the data of the sync call, which may include function name, arguments, and other information | ||
* @return -1 if the receiver contract does not have sync call entry point or the entry point's signature is invalid, otherwise | ||
* @return the number of bytes of the return value of the call. If the function is `void`, rturn `0` | ||
heifner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
*/ | ||
__attribute__((eosio_wasm_import)) | ||
int64_t call(capi_name receiver, uint64_t flags, const char* data, size_t data_size); | ||
|
||
/** | ||
* Copy up to `len` bytes of the return value of the most recent call to `mem`, | ||
* in the context of this call's parent action or parent call | ||
* | ||
* @brief Copy the return value of the most recent call to the specified location | ||
* @param mem - a pointer where up to `len` bytes of the return value will be copied | ||
* @param len - length of the return value to be copied | ||
heifner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @return the number of bytes of the return value that can be retrieved (the number of total bytes of return value) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So success is Note There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@arhag ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In addition, the new host functions are designed such that the user knows call data length from the argument of the entry point, and the return value of the sync call. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. The way The approach we are taking here (and I hope we take going forward for all similar host functions that need to read data of unknown size) is to provide more useful information always so that the optimistic approach can be more efficient. If it works, only one call to the host function was needed (same case with Additionally, if a contract didn't want to guess the size to pre-allocate and always wanted to get it done in two host function calls (get size, allocate, retrieve data) but with the first call being as light weight as possible (no copying of data required) as it can currently be done with It is unfortunate that all of our unknown sized data retrieval host functions don't work the same way (and in particular this new way). But I don't want to keep sticking with an inferior way for consistency reasons. For consistency reasons, we can adopt the new way going forward and accept that the legacy methods will remain inconsistent. |
||
*/ | ||
__attribute__((eosio_wasm_import)) | ||
uint32_t get_call_return_value( void* mem, uint32_t len ); | ||
|
||
/** | ||
* Copy up to `len` bytes of the current call data to `mem` | ||
* | ||
* @brief Copy current call data to the specified location | ||
* @param mem - a pointer where up to `len` bytes of the current call data will be copied | ||
* @param len - length of the current call data to be copied | ||
* @return the number of bytes of the data that can be retrieved (the number of total bytes of the data) | ||
heifner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
__attribute__((eosio_wasm_import)) | ||
uint32_t get_call_data( void* mem, uint32_t len ); | ||
|
||
/** | ||
* Set the return value from `mem` with `len` bytes. This is to be retrieved by parent action | ||
* or parent call using `get_call_return_value` | ||
* | ||
* @brief Copy the return value from the specified location | ||
* @param mem - a pointer where `len` bytes of the return value will be copied from | ||
*/ | ||
__attribute__((eosio_wasm_import)) | ||
void set_call_return_value( void* mem, uint32_t len ); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
/// @} call |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/** | ||
* @file | ||
*/ | ||
#pragma once | ||
#include <cstdlib> | ||
#include <type_traits> | ||
|
||
#include "../../core/eosio/serialize.hpp" | ||
#include "../../core/eosio/datastream.hpp" | ||
#include "../../core/eosio/name.hpp" | ||
|
||
namespace eosio { | ||
|
||
namespace internal_use_do_not_use { | ||
extern "C" { | ||
__attribute__((eosio_wasm_import)) | ||
int64_t call(uint64_t receiver, uint64_t flags, const char* data, size_t data_size); | ||
|
||
__attribute__((eosio_wasm_import)) | ||
uint32_t get_call_return_value( void* mem, uint32_t len ); | ||
|
||
__attribute__((eosio_wasm_import)) | ||
uint32_t get_call_data( void* mem, uint32_t len ); | ||
|
||
__attribute__((eosio_wasm_import)) | ||
void set_call_return_value( void* mem, uint32_t len ); | ||
} | ||
}; | ||
|
||
/** | ||
* @defgroup call call | ||
* @ingroup contracts | ||
* @brief Defines type-safe C++ wrappers for querying call and sending call | ||
* @note There are some methods from the @ref call that can be used directly from C++ | ||
*/ | ||
|
||
inline uint32_t get_call_return_value( void* mem, uint32_t len ) { | ||
return internal_use_do_not_use::get_call_return_value(mem, len); | ||
} | ||
|
||
inline uint32_t get_call_data( void* mem, uint32_t len ) { | ||
return internal_use_do_not_use::get_call_data(mem, len); | ||
} | ||
|
||
inline void set_call_return_value( void* mem, uint32_t len ) { | ||
internal_use_do_not_use::set_call_return_value(mem, len); | ||
} | ||
|
||
/** | ||
* This is the packed representation of a call | ||
* | ||
* @ingroup call | ||
*/ | ||
struct call { | ||
/** | ||
* Name of the account the call is intended for | ||
*/ | ||
const name receiver{}; | ||
|
||
/** | ||
* indicating if the call is read only or not | ||
*/ | ||
const bool read_only = false; | ||
|
||
/** | ||
* if the receiver contract does not have sync_call entry point or its signature | ||
* is invalid, when no_op_if_receiver_not_support_sync_call is set to true, | ||
* the sync call is no op, otherwise the call is aborted and an exception is raised. | ||
*/ | ||
const bool no_op_if_receiver_not_support_sync_call = false; | ||
|
||
/** | ||
* Payload data | ||
*/ | ||
const std::vector<char> data{}; | ||
|
||
/** | ||
* Construct a new call object with receiver, name, and payload data | ||
* | ||
* @tparam T - Type of call data, must be serializable by `pack(...)` | ||
heifner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @param receiver - The name of the account this call is intended for | ||
* @param flags - The flags | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Although, since this is suppose to be an easy to use wrapper. I think this should just be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think about it more and agree with you. I have changed to take in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the comment please. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have changed the bools to enums in #351. I have updated the comment there. |
||
* @param payload - The call data that will be serialized via pack into data | ||
*/ | ||
template<typename T> | ||
call( struct name receiver, T&& payload, bool read_only = false, bool no_op = false ) | ||
: receiver(receiver) | ||
, read_only(read_only) | ||
, no_op_if_receiver_not_support_sync_call(no_op) | ||
, data(pack(std::forward<T>(payload))) {} | ||
|
||
/// @cond INTERNAL | ||
EOSLIB_SERIALIZE( call, (receiver)(read_only)(no_op_if_receiver_not_support_sync_call)(data) ) | ||
/// @endcond | ||
|
||
/** | ||
* Make a call using the functor operator | ||
*/ | ||
int64_t operator()() const { | ||
heifner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint64_t flags = read_only ? 0x01 : 0x00; // last bit indicating read only | ||
auto retval = internal_use_do_not_use::call(receiver.value, flags, data.data(), data.size()); | ||
|
||
if (retval == -1) { // sync call is not supported by the receiver contract | ||
check(no_op_if_receiver_not_support_sync_call, "receiver does not support sync call but no_op_if_receiver_not_support_sync_call flag is not set"); | ||
} | ||
return retval; | ||
} | ||
}; | ||
|
||
} // namespace eosio |
Uh oh!
There was an error while loading. Please reload this page.