Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d0889dbc9 | |||
| 4caf3ed614 | |||
| 4d324084e8 | |||
| eae8cd24f4 | |||
| 0529c28563 | |||
| b35ebffb22 | |||
| 9cd7fcb51e | |||
| 3e7f4e7254 | |||
| 3be71635ac | |||
| e6c412e2c3 | |||
| 960076f4ce | |||
| 7d12b93e7e |
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"antelope-spring-dev":{
|
||||
"target":"release/2.0",
|
||||
"target":"main",
|
||||
"prerelease":false
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
[submodule "cdt-llvm"]
|
||||
path = cdt-llvm
|
||||
url = https://github.com/AntelopeIO/cdt-llvm
|
||||
url = https://git.coopenomics.world/C9S/cdt-llvm.git
|
||||
[submodule "libraries/libc/cdt-musl"]
|
||||
path = libraries/libc/cdt-musl
|
||||
url = https://github.com/AntelopeIO/cdt-musl
|
||||
url = https://git.coopenomics.world/C9S/cdt-musl.git
|
||||
[submodule "libraries/libc++/cdt-libcxx"]
|
||||
path = libraries/libc++/cdt-libcxx
|
||||
url = https://github.com/AntelopeIO/cdt-libcxx
|
||||
url = https://git.coopenomics.world/C9S/cdt-libcxx.git
|
||||
[submodule "libraries/native/softfloat"]
|
||||
path = libraries/native/softfloat
|
||||
url = https://github.com/AntelopeIO/berkeley-softfloat-3
|
||||
url = https://git.coopenomics.world/C9S/berkeley-softfloat-3.git
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5...4.0)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Sanity check our source directory to make sure that we are not trying to
|
||||
# generate an in-source build, and to make
|
||||
@@ -13,10 +13,10 @@ endif()
|
||||
|
||||
project(cdt)
|
||||
|
||||
set(VERSION_MAJOR 5)
|
||||
set(VERSION_MINOR 0)
|
||||
set(VERSION_MAJOR 4)
|
||||
set(VERSION_MINOR 3)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_SUFFIX "dev1")
|
||||
# set(VERSION_SUFFIX "rc1")
|
||||
|
||||
if (VERSION_SUFFIX)
|
||||
set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_SUFFIX}")
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
WORKDIR /workdir
|
||||
|
||||
COPY build/cdt_*.deb /workdir
|
||||
|
||||
RUN apt-get update && apt-get install -y wget cmake build-essential g++ libboost-all-dev libz3-dev && \
|
||||
apt install ./cdt_*.deb -y && \
|
||||
rm cdt_*.deb
|
||||
+1
-1
Submodule cdt-llvm updated: 34c0de5404...6650e8c6b4
@@ -1,65 +0,0 @@
|
||||
#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`, return `0`
|
||||
*
|
||||
*/
|
||||
__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
|
||||
* @return the number of bytes of the return value that can be retrieved (the number of total bytes of return value)
|
||||
*/
|
||||
__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)
|
||||
*/
|
||||
__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
|
||||
@@ -232,6 +232,41 @@ int recover_key( const struct capi_checksum256* digest, const char* sig, size_t
|
||||
__attribute__((eosio_wasm_import))
|
||||
void assert_recover_key( const struct capi_checksum256* digest, const char* sig, size_t siglen, const char* pub, size_t publen );
|
||||
|
||||
/**
|
||||
* Tests a signature against a hash, verifies the recovered public key matches the expected one,
|
||||
* and checks that this public key belongs to the specified account permission.
|
||||
*
|
||||
* @ingroup crypto
|
||||
* @param digest - Pointer to the digest/hash of the message that was signed
|
||||
* @param sig - Pointer to the signature
|
||||
* @param siglen - Length of the signature
|
||||
* @param pub - Pointer to the expected public key
|
||||
* @param publen - Length of the expected public key
|
||||
* @param account - The account name to verify key ownership
|
||||
* @param permission - The permission name to check (e.g., active, owner)
|
||||
*
|
||||
* @pre `digest` is a valid pointer to a `capi_checksum256` struct
|
||||
* @pre `sig` is a valid pointer to a buffer containing the signature
|
||||
* @pre `pub` is a valid pointer to a buffer containing the expected public key
|
||||
*
|
||||
* @post Throws if signature is invalid, key doesn't match, or key doesn't belong to account permission
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* capi_checksum256 digest;
|
||||
* char sig[65];
|
||||
* size_t siglen = 65;
|
||||
* char pub[34];
|
||||
* size_t publen = 34;
|
||||
* capi_name account = N(myaccount);
|
||||
* capi_name permission = N(active);
|
||||
* assert_recover_key_account( &digest, sig, siglen, pub, publen, account, permission );
|
||||
* // If all checks pass, code continues here
|
||||
* @endcode
|
||||
*/
|
||||
__attribute__((eosio_wasm_import))
|
||||
void assert_recover_key_account( const struct capi_checksum256* digest, const char* sig, size_t siglen, const char* pub, size_t publen, uint64_t account, uint64_t permission );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,16 @@ extern "C" {
|
||||
* @brief Defines %C Privileged API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the RAM usage an account
|
||||
*
|
||||
* @param account - name of the account whose resource limit to get
|
||||
* @param used_ram_bytes - pointer to `int64_t` to hold retrieved ram usage in absolute bytes
|
||||
*/
|
||||
__attribute__((eosio_wasm_import))
|
||||
void get_account_ram_usage( capi_name account, int64_t* used_ram_bytes );
|
||||
|
||||
|
||||
/**
|
||||
* Get the resource limits of an account
|
||||
*
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Propose new participants to the security group.
|
||||
*
|
||||
* @param data - the buffer containing the packed participants.
|
||||
* @param datalen - size of the packed participants
|
||||
* @pre `data` is a valid pointer to a range of memory at least `datalen` bytes long that contains packed participants data
|
||||
*
|
||||
* @return -1 if proposing a new security group was unsuccessful, otherwise returns 0.
|
||||
*/
|
||||
__attribute__((eosio_wasm_import))
|
||||
int64_t add_security_group_participants(const char* data, uint32_t datalen);
|
||||
|
||||
/**
|
||||
* Propose to remove participants from the security group.
|
||||
*
|
||||
* @param data - the buffer containing the packed participants.
|
||||
* @param datalen - size of the packed participants
|
||||
* @pre `data` is a valid pointer to a range of memory at least `datalen` bytes long that contains packed participants data
|
||||
*
|
||||
* @return -1 if proposing a new security group was unsuccessful, otherwise returns 0.
|
||||
*/
|
||||
__attribute__((eosio_wasm_import))
|
||||
int64_t remove_security_group_participants(const char* data, uint32_t datalen);
|
||||
|
||||
/**
|
||||
* Check if the specified accounts are all in the active security group.
|
||||
*
|
||||
* @param data - the buffer containing the packed participants.
|
||||
* @param datalen - size of the packed participants
|
||||
*
|
||||
* @return Returns true if the specified accounts are all in the active security group.
|
||||
*/
|
||||
__attribute__((eosio_wasm_import))
|
||||
bool in_active_security_group(const char* data, uint32_t datalen);
|
||||
|
||||
/**
|
||||
* Gets the active security group
|
||||
*
|
||||
* @param[out] data - the output buffer containing the packed security group.
|
||||
* @param datalen - size of the `data` buffer
|
||||
*
|
||||
* @return Returns the size required in the buffer (if the buffer is too small, nothing is written).
|
||||
*
|
||||
*/
|
||||
__attribute__((eosio_wasm_import))
|
||||
uint32_t get_active_security_group(char* data, uint32_t datalen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -6,11 +6,11 @@
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
#include "detail.hpp"
|
||||
#include "../../core/eosio/serialize.hpp"
|
||||
#include "../../core/eosio/datastream.hpp"
|
||||
#include "../../core/eosio/name.hpp"
|
||||
#include "../../core/eosio/fixed_bytes.hpp"
|
||||
#include "../../core/eosio/ignore.hpp"
|
||||
#include "../../core/eosio/time.hpp"
|
||||
|
||||
namespace eosio {
|
||||
@@ -409,6 +409,89 @@ namespace eosio {
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// @cond INTERNAL
|
||||
|
||||
template <typename T>
|
||||
struct unwrap { typedef T type; };
|
||||
|
||||
template <typename T>
|
||||
struct unwrap<ignore<T>> { typedef T type; };
|
||||
|
||||
template <typename R, typename Act, typename... Args>
|
||||
auto get_args(R(Act::*p)(Args...)) {
|
||||
return std::tuple<std::decay_t<typename unwrap<Args>::type>...>{};
|
||||
}
|
||||
|
||||
template <typename R, typename Act, typename... Args>
|
||||
auto get_args_nounwrap(R(Act::*p)(Args...)) {
|
||||
return std::tuple<std::decay_t<Args>...>{};
|
||||
}
|
||||
|
||||
template <auto Action>
|
||||
using deduced = decltype(get_args(Action));
|
||||
|
||||
template <auto Action>
|
||||
using deduced_nounwrap = decltype(get_args_nounwrap(Action));
|
||||
|
||||
template <typename T>
|
||||
struct convert { typedef T type; };
|
||||
|
||||
template <>
|
||||
struct convert<const char*> { typedef std::string type; };
|
||||
|
||||
template <>
|
||||
struct convert<char*> { typedef std::string type; };
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_same { static constexpr bool value = std::is_convertible<T,U>::value; };
|
||||
|
||||
template <typename U>
|
||||
struct is_same<bool,U> { static constexpr bool value = std::is_integral<U>::value; };
|
||||
|
||||
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; };
|
||||
|
||||
template <size_t N, auto Arg, auto... Args>
|
||||
struct get_nth_impl<N, N, Arg, Args...> { static constexpr auto value = Arg; };
|
||||
|
||||
template <size_t N, auto... Args>
|
||||
struct get_nth { static constexpr auto value = get_nth_impl<N,0,Args...>::value; };
|
||||
|
||||
template <auto Action, size_t I, typename T, typename... Rest>
|
||||
struct check_types {
|
||||
static_assert(detail::is_same<typename convert<T>::type, typename convert<typename std::tuple_element<I, deduced<Action>>::type>::type>::value);
|
||||
using type = check_types<Action, I+1, Rest...>;
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <auto Action, size_t I, typename T>
|
||||
struct check_types<Action, I, T> {
|
||||
static_assert(detail::is_same<typename convert<T>::type, typename convert<typename std::tuple_element<I, deduced<Action>>::type>::type>::value);
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template <auto Action, typename... Ts>
|
||||
constexpr bool type_check() {
|
||||
static_assert(sizeof...(Ts) == std::tuple_size<deduced<Action>>::value);
|
||||
if constexpr (sizeof...(Ts) != 0)
|
||||
return check_types<Action, 0, Ts...>::value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for an action object.
|
||||
*
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*/
|
||||
#pragma once
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
#include "detail.hpp"
|
||||
#include "../../core/eosio/serialize.hpp"
|
||||
#include "../../core/eosio/datastream.hpp"
|
||||
#include "../../core/eosio/name.hpp"
|
||||
#include "../../core/eosio/hash_id.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 int64_t call(eosio::name receiver, uint64_t flags, const char* data, size_t data_size) {
|
||||
return internal_use_do_not_use::call(receiver.value, flags, data, data_size);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Indicate whether a sync call is read_write or read_only. Default is read_write
|
||||
enum class access_mode { read_write = 0, read_only = 1 };
|
||||
|
||||
// Indicate the action to take if the receiver does not support sync calls.
|
||||
// Default is abort_op
|
||||
enum class support_mode { abort_op = 0, no_op = 1 };
|
||||
|
||||
// For a void function, when support_mode is set to no_op, the call_wrapper.
|
||||
// returns `std::optional<void_call>`. If the optional has no value, it indicates
|
||||
// the call was op-op; if the optional has a value of `void_call`, the call
|
||||
// executed successfully.
|
||||
struct void_call {
|
||||
};
|
||||
|
||||
struct call_data_header {
|
||||
uint32_t version = 0;
|
||||
uint64_t func_name = 0; // At WASM level, function name is a short ID of uint64_t.
|
||||
|
||||
EOSLIB_SERIALIZE(call_data_header, (version)(func_name))
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for simplifying making a sync call
|
||||
*
|
||||
* @brief Used to wrap a particular sync call to simplify the process of other contracts making sync calls to the "wrapped" call.
|
||||
* Example:
|
||||
* @code
|
||||
* // defined by contract writer of the sync call functions
|
||||
* using get_func = call_wrapper<"get"_i, &callee::get>;
|
||||
* // usage by different contract writer
|
||||
* get_func{"callee"_n}();
|
||||
* // or
|
||||
* get_func get{"callee"_n};
|
||||
* get();
|
||||
* @endcode
|
||||
*/
|
||||
template <eosio::hash_id::raw Func_Name, auto Func_Ref, access_mode Access_Mode=access_mode::read_write, support_mode Support_Mode = support_mode::abort_op>
|
||||
struct call_wrapper {
|
||||
template <typename Receiver>
|
||||
constexpr call_wrapper(Receiver&& receiver)
|
||||
: receiver(std::forward<Receiver>(receiver))
|
||||
{}
|
||||
|
||||
static constexpr eosio::hash_id function_name = eosio::hash_id(Func_Name);
|
||||
eosio::name receiver {};
|
||||
|
||||
using orig_ret_type = typename detail::function_traits<decltype(Func_Ref)>::return_type;
|
||||
|
||||
using return_type = std::conditional_t<
|
||||
Support_Mode == support_mode::abort_op,// if Support_Mode is abort_op
|
||||
orig_ret_type, // use the original return type
|
||||
std::conditional_t< // else
|
||||
std::is_void<orig_ret_type>::value, // original return type is void
|
||||
std::optional<void_call>, // use optional of empty struct
|
||||
std::optional<orig_ret_type> // use optional of original return type
|
||||
>
|
||||
>;
|
||||
|
||||
template <typename... Args>
|
||||
return_type operator()(Args&&... args)const {
|
||||
static_assert(detail::type_check<Func_Ref, Args...>());
|
||||
|
||||
uint64_t flags = 0x00;
|
||||
if constexpr (Access_Mode == access_mode::read_only) {
|
||||
flags = 0x01;
|
||||
}
|
||||
|
||||
call_data_header header{ .version = 0,
|
||||
.func_name = function_name.id };
|
||||
|
||||
const std::vector<char> data{ pack(std::forward_as_tuple(header, detail::deduced<Func_Ref>{std::forward<Args>(args)...})) };
|
||||
|
||||
auto ret_val_size = internal_use_do_not_use::call(receiver.value, flags, data.data(), data.size());
|
||||
|
||||
if (ret_val_size < 0) { // the receiver does not support sync calls
|
||||
if constexpr (Support_Mode == support_mode::abort_op) {
|
||||
check(false, "receiver does not support sync call but support_mode is set to abort_op");
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// The sync call has been executed by the receiver
|
||||
if constexpr (std::is_void<return_type>::value) {
|
||||
return;
|
||||
} else {
|
||||
if constexpr (Support_Mode == support_mode::no_op && std::is_void<orig_ret_type>::value) {
|
||||
return void_call{};
|
||||
} else {
|
||||
constexpr size_t max_stack_buffer_size = 512;
|
||||
char* buffer = (char*)(max_stack_buffer_size < ret_val_size ? malloc(ret_val_size) : alloca(ret_val_size)); // intentionally no `free()` is called. the memory will be freed at the end of callers wasm execution.
|
||||
internal_use_do_not_use::get_call_return_value(buffer, ret_val_size);
|
||||
orig_ret_type ret_val = unpack<orig_ret_type>(buffer, ret_val_size);
|
||||
|
||||
if constexpr (Support_Mode == support_mode::no_op) {
|
||||
return std::make_optional(ret_val);
|
||||
} else {
|
||||
return ret_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace eosio
|
||||
@@ -29,12 +29,6 @@ namespace eosio {
|
||||
*/
|
||||
class contract {
|
||||
public:
|
||||
enum class exec_type_t : uint8_t {
|
||||
action,
|
||||
call,
|
||||
unknown
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a new contract given the contract name
|
||||
*
|
||||
@@ -81,27 +75,7 @@ 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.
|
||||
*/
|
||||
@@ -116,10 +90,5 @@ 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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../core/eosio/ignore.hpp"
|
||||
|
||||
namespace eosio { namespace detail {
|
||||
|
||||
/// @cond INTERNAL
|
||||
|
||||
template <typename T>
|
||||
struct unwrap { typedef T type; };
|
||||
|
||||
template <typename T>
|
||||
struct unwrap<ignore<T>> { typedef T type; };
|
||||
|
||||
template <typename R, typename Act, typename... Args>
|
||||
auto get_args(R(Act::*p)(Args...)) {
|
||||
return std::tuple<std::decay_t<typename unwrap<Args>::type>...>{};
|
||||
}
|
||||
|
||||
template <typename R, typename Act, typename... Args>
|
||||
auto get_args_nounwrap(R(Act::*p)(Args...)) {
|
||||
return std::tuple<std::decay_t<Args>...>{};
|
||||
}
|
||||
|
||||
template <auto Function>
|
||||
using deduced = decltype(get_args(Function));
|
||||
|
||||
template <auto Function>
|
||||
using deduced_nounwrap = decltype(get_args_nounwrap(Function));
|
||||
|
||||
template <typename T>
|
||||
struct convert { typedef T type; };
|
||||
|
||||
template <>
|
||||
struct convert<const char*> { typedef std::string type; };
|
||||
|
||||
template <>
|
||||
struct convert<char*> { typedef std::string type; };
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_same { static constexpr bool value = std::is_convertible<T,U>::value; };
|
||||
|
||||
template <typename U>
|
||||
struct is_same<bool,U> { static constexpr bool value = std::is_integral<U>::value; };
|
||||
|
||||
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; };
|
||||
|
||||
template <size_t N, auto Arg, auto... Args>
|
||||
struct get_nth_impl<N, N, Arg, Args...> { static constexpr auto value = Arg; };
|
||||
|
||||
template <size_t N, auto... Args>
|
||||
struct get_nth { static constexpr auto value = get_nth_impl<N,0,Args...>::value; };
|
||||
|
||||
template <auto Function, size_t I, typename T, typename... Rest>
|
||||
struct check_types {
|
||||
static_assert(detail::is_same<typename convert<T>::type, typename convert<typename std::tuple_element<I, deduced<Function>>::type>::type>::value);
|
||||
using type = check_types<Function, I+1, Rest...>;
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
template <auto Function, size_t I, typename T>
|
||||
struct check_types<Function, I, T> {
|
||||
static_assert(detail::is_same<typename convert<T>::type, typename convert<typename std::tuple_element<I, deduced<Function>>::type>::type>::value);
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template <auto Function, typename... Ts>
|
||||
constexpr bool type_check() {
|
||||
static_assert(sizeof...(Ts) == std::tuple_size<deduced<Function>>::value);
|
||||
if constexpr (sizeof...(Ts) != 0)
|
||||
return check_types<Function, 0, Ts...>::value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// For non-function-pointers (function_traits is undefined)
|
||||
template <typename T>
|
||||
struct function_traits;
|
||||
|
||||
// For non-const member function
|
||||
template <typename Class, typename Ret, typename... Args>
|
||||
struct function_traits<Ret (Class::*)(Args...)> {
|
||||
using return_type = Ret;
|
||||
};
|
||||
|
||||
// For const member function
|
||||
template <typename Class, typename Ret, typename... Args>
|
||||
struct function_traits<Ret (Class::*)(Args...) const> {
|
||||
using return_type = Ret;
|
||||
};
|
||||
/// @endcond
|
||||
}} // eosio detail
|
||||
@@ -12,6 +12,9 @@ namespace eosio {
|
||||
__attribute__((eosio_wasm_import))
|
||||
bool is_privileged( uint64_t account );
|
||||
|
||||
__attribute__((eosio_wasm_import))
|
||||
void get_account_ram_usage( uint64_t account, int64_t* used_ram_bytes );
|
||||
|
||||
__attribute__((eosio_wasm_import))
|
||||
void get_resource_limits( uint64_t account, int64_t* ram_bytes, int64_t* net_weight, int64_t* cpu_weight );
|
||||
|
||||
@@ -182,6 +185,19 @@ namespace eosio {
|
||||
*/
|
||||
void get_blockchain_parameters(eosio::blockchain_parameters& params);
|
||||
|
||||
|
||||
/**
|
||||
* Get the ram usage of an account
|
||||
*
|
||||
* @ingroup privileged
|
||||
* @param account - name of the account whose resource limit to get
|
||||
* @param used_ram_bytes - output to hold retrieved ram usage in absolute bytes
|
||||
*/
|
||||
inline void get_account_ram_usage( name account, int64_t& used_ram_bytes ) {
|
||||
internal_use_do_not_use::get_account_ram_usage( account.value, &used_ram_bytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the resource limits of an account
|
||||
*
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include <set>
|
||||
#include "../../core/eosio/name.hpp"
|
||||
#include "../../core/eosio/serialize.hpp"
|
||||
|
||||
namespace eosio {
|
||||
|
||||
namespace internal_use_do_not_use {
|
||||
extern "C" {
|
||||
__attribute__((eosio_wasm_import)) int64_t add_security_group_participants(const char* data, uint32_t datalen);
|
||||
|
||||
__attribute__((eosio_wasm_import)) int64_t remove_security_group_participants(const char* data, uint32_t datalen);
|
||||
|
||||
__attribute__((eosio_wasm_import)) bool in_active_security_group(const char* data, uint32_t datalen);
|
||||
|
||||
__attribute__((eosio_wasm_import)) uint32_t get_active_security_group(char* data, uint32_t datalen);
|
||||
}
|
||||
} // namespace internal_use_do_not_use
|
||||
|
||||
/**
|
||||
* @defgroup security_group Security Group
|
||||
* @ingroup contracts
|
||||
* @brief Defines C++ security group API
|
||||
*/
|
||||
|
||||
struct security_group {
|
||||
uint32_t version;
|
||||
std::set<name> participants;
|
||||
CDT_REFLECT(version, participants);
|
||||
};
|
||||
|
||||
/**
|
||||
* Propose new participants to the security group.
|
||||
*
|
||||
* @ingroup security_group
|
||||
* @param participants - the participants.
|
||||
*
|
||||
* @return -1 if proposing a new security group was unsuccessful, otherwise returns 0.
|
||||
*/
|
||||
inline int64_t add_security_group_participants(const std::set<name>& participants) {
|
||||
auto packed_participants = eosio::pack( participants );
|
||||
return internal_use_do_not_use::add_security_group_participants( packed_participants.data(), packed_participants.size() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Propose to remove participants from the security group.
|
||||
*å
|
||||
* @ingroup security_group
|
||||
* @param participants - the participants.
|
||||
*å
|
||||
* @return -1 if proposing a new security group was unsuccessful, otherwise returns 0.
|
||||
*/
|
||||
inline int64_t remove_security_group_participants(const std::set<name>& participants){
|
||||
auto packed_participants = eosio::pack( participants );
|
||||
return internal_use_do_not_use::remove_security_group_participants( packed_participants.data(), packed_participants.size() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the specified accounts are all in the active security group.
|
||||
*
|
||||
* @ingroup security_group
|
||||
* @param participants - the participants.
|
||||
*
|
||||
* @return Returns true if the specified accounts are all in the active security group.
|
||||
*/
|
||||
inline bool in_active_security_group(const std::set<name>& participants){
|
||||
auto packed_participants = eosio::pack( participants );
|
||||
return internal_use_do_not_use::in_active_security_group( packed_participants.data(), packed_participants.size() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active security group
|
||||
*
|
||||
* @ingroup security_group
|
||||
* @param[out] packed_security_group - the buffer containing the packed security_group.
|
||||
*
|
||||
* @return Returns the size required in the buffer (if the buffer is too small, nothing is written).
|
||||
*
|
||||
*/
|
||||
inline security_group get_active_security_group() {
|
||||
size_t buffer_size = internal_use_do_not_use::get_active_security_group(0, 0);
|
||||
std::vector<char> buffer(buffer_size);
|
||||
internal_use_do_not_use::get_active_security_group(buffer.data(), buffer_size);
|
||||
return eosio::unpack<security_group>(buffer);
|
||||
}
|
||||
} // namespace eosio
|
||||
@@ -62,7 +62,7 @@ namespace eosio {
|
||||
* @return true - if exists
|
||||
* @return false - otherwise
|
||||
*/
|
||||
bool exists() const {
|
||||
bool exists() {
|
||||
return _t.find( pk_value ) != _t.end();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace eosio {
|
||||
* @brief Get the value stored inside the singleton table
|
||||
* @return T - The value stored
|
||||
*/
|
||||
T get() const {
|
||||
T get() {
|
||||
auto itr = _t.find( pk_value );
|
||||
eosio::check( itr != _t.end(), "singleton does not exist" );
|
||||
return itr->value;
|
||||
@@ -84,7 +84,7 @@ namespace eosio {
|
||||
* @param def - The default value to be returned in case the data doesn't exist
|
||||
* @return T - The value stored
|
||||
*/
|
||||
T get_or_default( const T& def = T() ) const {
|
||||
T get_or_default( const T& def = T() ) {
|
||||
auto itr = _t.find( pk_value );
|
||||
return itr != _t.end() ? itr->value : def;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
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);
|
||||
|
||||
@@ -41,7 +47,17 @@ namespace eosio {
|
||||
* @ingroup contracts
|
||||
* @brief Type-safe C++ wrappers for transaction C API
|
||||
*
|
||||
* @details See action for the ability to send an inline action.
|
||||
* @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++
|
||||
*/
|
||||
@@ -67,17 +83,17 @@ namespace eosio {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a new transaction_header
|
||||
* Construct a new transaction_header with an expiration of now + 60 seconds.
|
||||
*
|
||||
* @brief Construct a new transaction_header object
|
||||
* @brief Construct a new transaction_header object initialising the transaction header expiration to now + 60 seconds
|
||||
*/
|
||||
transaction_header( time_point_sec exp = time_point_sec{} )
|
||||
transaction_header( time_point_sec exp = time_point_sec(current_time_point()) + 60)
|
||||
:expiration(exp)
|
||||
{}
|
||||
|
||||
time_point_sec expiration;
|
||||
uint16_t ref_block_num = 0UL;
|
||||
uint32_t ref_block_prefix = 0UL;
|
||||
uint16_t ref_block_num;
|
||||
uint32_t ref_block_prefix;
|
||||
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
|
||||
@@ -94,9 +110,22 @@ namespace eosio {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a new transaction
|
||||
* Construct a new transaction with an expiration of now + 60 seconds.
|
||||
*/
|
||||
transaction(time_point_sec exp = time_point_sec{}) : transaction_header( exp ) {}
|
||||
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);
|
||||
}
|
||||
|
||||
std::vector<action> context_free_actions;
|
||||
std::vector<action> actions;
|
||||
@@ -105,6 +134,47 @@ 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.
|
||||
*
|
||||
@@ -134,6 +204,29 @@ 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.
|
||||
*
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "serialize.hpp"
|
||||
#include "print.hpp"
|
||||
#include "check.hpp"
|
||||
#include "varint.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace eosio {
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// see https://github.com/AntelopeIO/spring/wiki/ABI-1.3:-bitset-type
|
||||
// -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// stores a bitset in a std::vector<uint8_t>
|
||||
//
|
||||
// - bits 0-7 in first byte, 8-15 in second, ...
|
||||
// - least significant bit of byte 0 is bit 0 of bitset.
|
||||
// - unused bits must be zero.
|
||||
// ---------------------------------------------------------------------------------
|
||||
struct bitset {
|
||||
using buffer_type = std::vector<uint8_t>;
|
||||
using size_type = uint32_t;
|
||||
static constexpr size_type bits_per_block = 8;
|
||||
static constexpr size_type npos = static_cast<size_type>(-1);
|
||||
|
||||
static constexpr size_type calc_num_blocks(size_type num_bits) {
|
||||
return (num_bits + bits_per_block - 1) / bits_per_block;
|
||||
}
|
||||
|
||||
static size_type block_index(size_type pos) noexcept { return pos / bits_per_block; }
|
||||
static uint8_t bit_index(size_type pos) noexcept { return static_cast<uint8_t>(pos % bits_per_block); }
|
||||
static uint8_t bit_mask(size_type pos) noexcept { return uint8_t(1) << bit_index(pos); }
|
||||
|
||||
size_type size() const { return m_num_bits; }
|
||||
|
||||
size_type num_blocks() const {
|
||||
assert(m_bits.size() == calc_num_blocks(m_num_bits));
|
||||
return m_bits.size();
|
||||
}
|
||||
|
||||
void resize(size_type num_bits) {
|
||||
m_bits.resize(calc_num_blocks(num_bits), 0);
|
||||
m_num_bits = num_bits;
|
||||
zero_unused_bits();
|
||||
}
|
||||
|
||||
void set(size_type pos) {
|
||||
assert(pos < m_num_bits);
|
||||
m_bits[block_index(pos)] |= bit_mask(pos);
|
||||
}
|
||||
|
||||
void clear(size_type pos) {
|
||||
assert(pos < m_num_bits);
|
||||
m_bits[block_index(pos)] &= ~bit_mask(pos);
|
||||
}
|
||||
|
||||
bool test(size_type pos) const {
|
||||
return (*this)[pos];
|
||||
}
|
||||
|
||||
bool operator[](size_type pos) const {
|
||||
assert(pos < m_num_bits);
|
||||
return !!(m_bits[block_index(pos)] & bit_mask(pos));
|
||||
}
|
||||
|
||||
void flip(size_type pos) {
|
||||
assert(pos < m_num_bits);
|
||||
if (test(pos))
|
||||
clear(pos);
|
||||
else
|
||||
set(pos);
|
||||
}
|
||||
|
||||
void flip() {
|
||||
for (auto& byte : m_bits)
|
||||
byte = ~byte;
|
||||
zero_unused_bits();
|
||||
}
|
||||
|
||||
bool all() const {
|
||||
auto sz = size();
|
||||
for (size_t i=0; i<sz; ++i)
|
||||
if (!test(i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool none() const {
|
||||
for (auto& byte : m_bits)
|
||||
if (byte)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void zero_all_bits() {
|
||||
for (auto& byte : m_bits)
|
||||
byte = 0;
|
||||
}
|
||||
|
||||
bitset operator|=(const bitset& o) {
|
||||
assert(size() == o.size());
|
||||
for (size_t i=0; i<m_bits.size(); ++i)
|
||||
m_bits[i] |= o.m_bits[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
void zero_unused_bits() {
|
||||
assert (m_bits.size() == calc_num_blocks(m_num_bits));
|
||||
|
||||
// if != 0 this is the number of bits used in the last block
|
||||
const size_type extra_bits = bit_index(size());
|
||||
|
||||
if (extra_bits != 0)
|
||||
m_bits.back() &= (uint8_t(1) << extra_bits) - 1;
|
||||
}
|
||||
|
||||
bool unused_bits_zeroed() const {
|
||||
// if != 0 this is the number of bits used in the last block
|
||||
const size_type extra_bits = bit_index(size());
|
||||
return extra_bits == 0 || (m_bits.back() & ~((uint8_t(1) << extra_bits) - 1)) == 0;
|
||||
}
|
||||
|
||||
friend auto operator<(const bitset& a, const bitset& b) {
|
||||
return std::tuple(a.m_num_bits, a.m_bits) < std::tuple(b.m_num_bits, b.m_bits);
|
||||
}
|
||||
|
||||
friend bool operator==(const bitset& a, const bitset& b) {
|
||||
return std::tuple(a.m_num_bits, a.m_bits) == std::tuple(b.m_num_bits, b.m_bits);
|
||||
}
|
||||
|
||||
uint8_t& byte(size_t i) {
|
||||
assert(i < m_bits.size());
|
||||
return m_bits[i];
|
||||
}
|
||||
|
||||
const uint8_t& byte(size_t i) const {
|
||||
assert(i < m_bits.size());
|
||||
return m_bits[i];
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
std::string res;
|
||||
res.resize(size());
|
||||
size_t idx = 0;
|
||||
for (auto i = size(); i-- > 0;)
|
||||
res[idx++] = (*this)[i] ? '1' : '0';
|
||||
return res;
|
||||
}
|
||||
|
||||
static bitset from_string(std::string_view s) {
|
||||
bitset bs;
|
||||
auto num_bits = s.size();
|
||||
bs.resize(num_bits);
|
||||
|
||||
for (size_t i = 0; i < num_bits; ++i) {
|
||||
switch (s[i]) {
|
||||
case '0':
|
||||
break; // nothing to do, all bits initially 0
|
||||
case '1':
|
||||
bs.set(num_bits - i - 1); // high bitset indexes come first in the JSON representation
|
||||
break;
|
||||
default:
|
||||
eosio::check(false, "unexpected character in bitset string representation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(bs.unused_bits_zeroed());
|
||||
return bs;
|
||||
}
|
||||
|
||||
void print() const {
|
||||
auto s = to_string();
|
||||
if (!s.empty())
|
||||
printl(s.data(), s.size());
|
||||
}
|
||||
|
||||
// binary representation
|
||||
// ---------------------
|
||||
// The bitset first encodes the number of bits it contains as a varint, then encodes
|
||||
// (size+8-1)/8 bytes into the stream. The first byte represents bits 0-7, the next 8-15,
|
||||
// and so on; i.e. LSB first.
|
||||
// Within a byte, the least significant bit stores the smaller bitset index.
|
||||
// Unused bits should be written as 0.
|
||||
//
|
||||
// This matches the storage scheme of bitset above
|
||||
// ---------------------------------------------------------------------------------------
|
||||
template <typename DataStream>
|
||||
friend DataStream& operator>>(DataStream& stream, bitset& obj) {
|
||||
unsigned_int num_bits(0);
|
||||
stream >> num_bits;
|
||||
obj.resize(num_bits.value);
|
||||
if (obj.size() > 0) {
|
||||
auto num_blocks = bitset::calc_num_blocks(obj.size());
|
||||
for (size_t i = 0; i < num_blocks; ++i)
|
||||
stream >> obj.byte(i);
|
||||
obj.zero_unused_bits();
|
||||
assert(obj.unused_bits_zeroed());
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template <typename DataStream>
|
||||
friend DataStream& operator<<(DataStream& stream, const bitset& obj) {
|
||||
unsigned_int num_bits(obj.size());
|
||||
stream << num_bits;
|
||||
if (obj.size() > 0) {
|
||||
auto num_blocks = bitset::calc_num_blocks(obj.size());
|
||||
assert(num_blocks >= 1);
|
||||
for (size_t i = 0; i < num_blocks; ++i)
|
||||
stream << obj.byte(i);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
private:
|
||||
size_type m_num_bits{0}; // members order matters for comparison operators
|
||||
buffer_type m_bits; // must be after `m_num_bits`
|
||||
};
|
||||
|
||||
constexpr const char* get_type_name(bitset*) { return "bitset"; }
|
||||
|
||||
} // namespace eosio
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "fixed_bytes.hpp"
|
||||
#include "varint.hpp"
|
||||
#include "serialize.hpp"
|
||||
#include "name.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
@@ -336,4 +337,24 @@ namespace eosio {
|
||||
* @param pubkey - Public key
|
||||
*/
|
||||
void assert_recover_key( const eosio::checksum256& digest, const eosio::signature& sig, const eosio::public_key& pubkey );
|
||||
|
||||
/**
|
||||
* Tests a signature against a hash, verifies the recovered public key matches the expected one,
|
||||
* and checks that this public key belongs to the specified account permission.
|
||||
*
|
||||
* @ingroup crypto
|
||||
* @param digest - Digest of the message that was signed
|
||||
* @param sig - Signature
|
||||
* @param pubkey - Expected public key
|
||||
* @param account - The account name to verify key ownership
|
||||
* @param permission - The permission name to check (e.g., "active"_n, "owner"_n)
|
||||
*
|
||||
* @throw eosio::check will fail if:
|
||||
* - The signature is invalid
|
||||
* - The recovered key doesn't match the expected public key
|
||||
* - The account doesn't exist
|
||||
* - The permission doesn't exist for the account
|
||||
* - The public key doesn't belong to the specified account permission
|
||||
*/
|
||||
void assert_recover_key_account( const eosio::checksum256& digest, const eosio::signature& sig, const eosio::public_key& pubkey, eosio::name account, eosio::name permission );
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ inline datastream<Stream>& operator>>(datastream<Stream>& ds, std::optional<T>&
|
||||
T val;
|
||||
ds >> val;
|
||||
opt = val;
|
||||
} else { opt.reset(); }
|
||||
}
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "check.hpp"
|
||||
#include "serialize.hpp"
|
||||
#include "reflect.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace eosio {
|
||||
struct hash_id {
|
||||
public:
|
||||
static constexpr uint32_t max_length = 128;
|
||||
|
||||
enum class raw : uint64_t {};
|
||||
|
||||
constexpr explicit hash_id( hash_id::raw r )
|
||||
:id(static_cast<uint64_t>(r)) {}
|
||||
|
||||
constexpr hash_id() : id(0) {}
|
||||
|
||||
constexpr explicit hash_id( std::string_view s )
|
||||
: id(djbh_hash(s))
|
||||
{
|
||||
validate(s);
|
||||
}
|
||||
|
||||
constexpr void validate(std::string_view str) {
|
||||
if (str.empty()) {
|
||||
eosio::check(false, "string cannot be empty to be an hash_id");
|
||||
}
|
||||
|
||||
if (str.length() > max_length) {
|
||||
eosio::check(false, "string is too long be a valid hash_id. must be less than or equal to " + std::to_string(max_length));
|
||||
}
|
||||
|
||||
// cannot use std::isalpha in constexpr function
|
||||
if (! ((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z') || str[0] == '_') ) {
|
||||
eosio::check(false, "string must start with a letter or _ to be a valid hash_id.");
|
||||
}
|
||||
|
||||
for (char c : str.substr(1)) {
|
||||
// cannot use std::isalnum in constexpr function
|
||||
if (! ((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z') || (str[0] >= '0' && str[0] <= '9') || str[0] == '_') ) {
|
||||
eosio::check(false, "string contains a character " + std::string{c} + " that is not a letter, number, or _");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint64_t djbh_hash(std::string_view s) {
|
||||
uint64_t hash = 5381;
|
||||
for (char c : s) {
|
||||
hash = ((hash << 5) + hash) + static_cast<uint8_t>(c); // hash * 33 + c
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
constexpr operator raw()const { return raw(id); }
|
||||
|
||||
uint64_t id = 0;
|
||||
|
||||
CDT_REFLECT(id);
|
||||
EOSLIB_SERIALIZE( hash_id, (id) )
|
||||
}; /// namespace hash_id
|
||||
} /// namespace eosio
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template"
|
||||
template <typename T, T... Str>
|
||||
inline constexpr eosio::hash_id operator""_i() {
|
||||
constexpr auto x = eosio::hash_id{std::string_view{eosio::detail::to_const_char_arr<Str...>::value, sizeof...(Str)}};
|
||||
return x;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
@@ -42,6 +42,11 @@ extern "C" {
|
||||
__attribute__((eosio_wasm_import))
|
||||
void assert_recover_key( const capi_checksum256* digest, const char* sig,
|
||||
size_t siglen, const char* pub, size_t publen );
|
||||
|
||||
__attribute__((eosio_wasm_import))
|
||||
void assert_recover_key_account( const capi_checksum256* digest, const char* sig,
|
||||
size_t siglen, const char* pub, size_t publen,
|
||||
uint64_t account, uint64_t permission );
|
||||
}
|
||||
|
||||
namespace eosio {
|
||||
@@ -131,4 +136,16 @@ namespace eosio {
|
||||
sig_data.data(), sig_data.size(),
|
||||
pubkey_data.data(), pubkey_data.size() );
|
||||
}
|
||||
|
||||
void assert_recover_key_account( const eosio::checksum256& digest, const eosio::signature& sig, const eosio::public_key& pubkey, eosio::name account, eosio::name permission ) {
|
||||
auto digest_data = digest.extract_as_byte_array();
|
||||
|
||||
auto sig_data = eosio::pack(sig);
|
||||
auto pubkey_data = eosio::pack(pubkey);
|
||||
|
||||
::assert_recover_key_account( reinterpret_cast<const capi_checksum256*>(digest_data.data()),
|
||||
sig_data.data(), sig_data.size(),
|
||||
pubkey_data.data(), pubkey_data.size(),
|
||||
account.value, permission.value );
|
||||
}
|
||||
}
|
||||
|
||||
+159
-149
@@ -16,347 +16,341 @@
|
||||
// Boilerplate
|
||||
using namespace eosio::native;
|
||||
extern "C" {
|
||||
void get_account_ram_usage( capi_name account, int64_t* used_ram_bytes ) {
|
||||
return intrinsics::get().call<intrinsics::get_account_ram_usage>(account, used_ram_bytes);
|
||||
}
|
||||
void get_resource_limits( capi_name account, int64_t* ram_bytes, int64_t* net_weight, int64_t* cpu_weight ) {
|
||||
return intrinsics::get().exec<intrinsics::get_resource_limits>(account, ram_bytes, net_weight, cpu_weight);
|
||||
return intrinsics::get().call<intrinsics::get_resource_limits>(account, ram_bytes, net_weight, cpu_weight);
|
||||
}
|
||||
void set_resource_limits( capi_name account, int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight ) {
|
||||
return intrinsics::get().exec<intrinsics::set_resource_limits>(account, ram_bytes, net_weight, cpu_weight);
|
||||
return intrinsics::get().call<intrinsics::set_resource_limits>(account, ram_bytes, net_weight, cpu_weight);
|
||||
}
|
||||
int64_t set_proposed_producers( char *producer_data, uint32_t producer_data_size ) {
|
||||
return intrinsics::get().exec<intrinsics::set_proposed_producers>(producer_data, producer_data_size);
|
||||
return intrinsics::get().call<intrinsics::set_proposed_producers>(producer_data, producer_data_size);
|
||||
}
|
||||
int64_t set_proposed_producers_ex( uint64_t producer_data_format, char *producer_data, uint32_t producer_data_size ) {
|
||||
return intrinsics::get().exec<intrinsics::set_proposed_producers_ex>(producer_data_format, producer_data, producer_data_size);
|
||||
return intrinsics::get().call<intrinsics::set_proposed_producers_ex>(producer_data_format, producer_data, producer_data_size);
|
||||
}
|
||||
uint32_t get_blockchain_parameters_packed( char* data, uint32_t datalen ) {
|
||||
return intrinsics::get().exec<intrinsics::get_blockchain_parameters_packed>(data, datalen);
|
||||
return intrinsics::get().call<intrinsics::get_blockchain_parameters_packed>(data, datalen);
|
||||
}
|
||||
void set_blockchain_parameters_packed( char* data, uint32_t datalen ) {
|
||||
return intrinsics::get().exec<intrinsics::set_blockchain_parameters_packed>(data, datalen);
|
||||
return intrinsics::get().call<intrinsics::set_blockchain_parameters_packed>(data, datalen);
|
||||
}
|
||||
bool is_privileged( capi_name account ) {
|
||||
return intrinsics::get().exec<intrinsics::is_privileged>(account);
|
||||
return intrinsics::get().call<intrinsics::is_privileged>(account);
|
||||
}
|
||||
void set_privileged( capi_name account, bool is_priv ) {
|
||||
return intrinsics::get().exec<intrinsics::set_privileged>(account, is_priv);
|
||||
return intrinsics::get().call<intrinsics::set_privileged>(account, is_priv);
|
||||
}
|
||||
bool is_feature_activated( const capi_checksum256* feature_digest ) {
|
||||
return intrinsics::get().exec<intrinsics::is_feature_activated>(feature_digest);
|
||||
return intrinsics::get().call<intrinsics::is_feature_activated>(feature_digest);
|
||||
}
|
||||
void preactivate_feature( const capi_checksum256* feature_digest ) {
|
||||
return intrinsics::get().exec<intrinsics::preactivate_feature>(feature_digest);
|
||||
return intrinsics::get().call<intrinsics::preactivate_feature>(feature_digest);
|
||||
}
|
||||
uint32_t get_active_producers( capi_name* producers, uint32_t datalen ) {
|
||||
return intrinsics::get().exec<intrinsics::get_active_producers>(producers, datalen);
|
||||
return intrinsics::get().call<intrinsics::get_active_producers>(producers, datalen);
|
||||
}
|
||||
int32_t db_idx64_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const uint64_t* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_store>(scope, table, payer, id, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_store>(scope, table, payer, id, secondary);
|
||||
}
|
||||
void db_idx64_remove(int32_t iterator) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_remove>(iterator);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_remove>(iterator);
|
||||
}
|
||||
void db_idx64_update(int32_t iterator, capi_name payer, const uint64_t* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_update>(iterator, payer, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_update>(iterator, payer, secondary);
|
||||
}
|
||||
int32_t db_idx64_find_primary(capi_name code, uint64_t scope, capi_name table, uint64_t* secondary, uint64_t primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_find_primary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_find_primary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx64_find_secondary(capi_name code, uint64_t scope, capi_name table, const uint64_t* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_find_secondary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_find_secondary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx64_lowerbound(capi_name code, uint64_t scope, capi_name table, uint64_t* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_lowerbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_lowerbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx64_upperbound(capi_name code, uint64_t scope, capi_name table, uint64_t* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_upperbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_upperbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx64_end(capi_name code, uint64_t scope, capi_name table) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_end>(code, scope, table);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_end>(code, scope, table);
|
||||
}
|
||||
int32_t db_idx64_next(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_next>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_next>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx64_previous(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx64_previous>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx64_previous>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx128_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const uint128_t* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_store>(scope, table, payer, id, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_store>(scope, table, payer, id, secondary);
|
||||
}
|
||||
void db_idx128_remove(int32_t iterator) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_remove>(iterator);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_remove>(iterator);
|
||||
}
|
||||
void db_idx128_update(int32_t iterator, capi_name payer, const uint128_t* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_update>(iterator, payer, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_update>(iterator, payer, secondary);
|
||||
}
|
||||
int32_t db_idx128_find_primary(capi_name code, uint64_t scope, capi_name table, uint128_t* secondary, uint64_t primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_find_primary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_find_primary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx128_find_secondary(capi_name code, uint64_t scope, capi_name table, const uint128_t* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_find_secondary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_find_secondary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx128_lowerbound(capi_name code, uint64_t scope, capi_name table, uint128_t* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_lowerbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_lowerbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx128_upperbound(capi_name code, uint64_t scope, capi_name table, uint128_t* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_upperbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_upperbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx128_end(capi_name code, uint64_t scope, capi_name table) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_end>(code, scope, table);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_end>(code, scope, table);
|
||||
}
|
||||
int32_t db_idx128_next(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_next>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_next>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx128_previous(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx128_previous>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx128_previous>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx256_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const uint128_t* data, uint32_t datalen) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_store>(scope, table, payer, id, data, datalen);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_store>(scope, table, payer, id, data, datalen);
|
||||
}
|
||||
void db_idx256_remove(int32_t iterator) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_remove>(iterator);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_remove>(iterator);
|
||||
}
|
||||
void db_idx256_update(int32_t iterator, capi_name payer, const uint128_t* data, uint32_t datalen) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_update>(iterator, payer, data, datalen);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_update>(iterator, payer, data, datalen);
|
||||
}
|
||||
int32_t db_idx256_find_primary(capi_name code, uint64_t scope, capi_name table, uint128_t* data, uint32_t datalen, uint64_t primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_find_primary>(code, scope, table, data, datalen, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_find_primary>(code, scope, table, data, datalen, primary);
|
||||
}
|
||||
int32_t db_idx256_find_secondary(capi_name code, uint64_t scope, capi_name table, const uint128_t* data, uint32_t datalen, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_find_secondary>(code, scope, table, data, datalen, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_find_secondary>(code, scope, table, data, datalen, primary);
|
||||
}
|
||||
int32_t db_idx256_lowerbound(capi_name code, uint64_t scope, capi_name table, uint128_t* data, uint32_t datalen, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_lowerbound>(code, scope, table, data, datalen, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_lowerbound>(code, scope, table, data, datalen, primary);
|
||||
}
|
||||
int32_t db_idx256_upperbound(capi_name code, uint64_t scope, capi_name table, uint128_t* data, uint32_t datalen, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_upperbound>(code, scope, table, data, datalen, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_upperbound>(code, scope, table, data, datalen, primary);
|
||||
}
|
||||
int32_t db_idx256_end(capi_name code, uint64_t scope, capi_name table) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_end>(code, scope, table);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_end>(code, scope, table);
|
||||
}
|
||||
int32_t db_idx256_next(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_next>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_next>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx256_previous(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx256_previous>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx256_previous>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx_double_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const double* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_store>(scope, table, payer, id, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_store>(scope, table, payer, id, secondary);
|
||||
}
|
||||
void db_idx_double_remove(int32_t iterator) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_remove>(iterator);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_remove>(iterator);
|
||||
}
|
||||
void db_idx_double_update(int32_t iterator, capi_name payer, const double* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_update>(iterator, payer, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_update>(iterator, payer, secondary);
|
||||
}
|
||||
int32_t db_idx_double_find_primary(capi_name code, uint64_t scope, capi_name table, double* secondary, uint64_t primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_find_primary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_find_primary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_double_find_secondary(capi_name code, uint64_t scope, capi_name table, const double* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_find_secondary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_find_secondary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_double_lowerbound(capi_name code, uint64_t scope, capi_name table, double* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_lowerbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_lowerbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_double_upperbound(capi_name code, uint64_t scope, capi_name table, double* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_upperbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_upperbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_double_end(capi_name code, uint64_t scope, capi_name table) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_end>(code, scope, table);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_end>(code, scope, table);
|
||||
}
|
||||
int32_t db_idx_double_next(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_next>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_next>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx_double_previous(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_double_previous>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_double_previous>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx_long_double_store(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const long double* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_store>(scope, table, payer, id, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_store>(scope, table, payer, id, secondary);
|
||||
}
|
||||
void db_idx_long_double_remove(int32_t iterator) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_remove>(iterator);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_remove>(iterator);
|
||||
}
|
||||
void db_idx_long_double_update(int32_t iterator, capi_name payer, const long double* secondary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_update>(iterator, payer, secondary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_update>(iterator, payer, secondary);
|
||||
}
|
||||
int32_t db_idx_long_double_find_primary(capi_name code, uint64_t scope, capi_name table, long double* secondary, uint64_t primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_find_primary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_find_primary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_long_double_find_secondary(capi_name code, uint64_t scope, capi_name table, const long double* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_find_secondary>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_find_secondary>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_long_double_lowerbound(capi_name code, uint64_t scope, capi_name table, long double* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_lowerbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_lowerbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_long_double_upperbound(capi_name code, uint64_t scope, capi_name table, long double* secondary, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_upperbound>(code, scope, table, secondary, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_upperbound>(code, scope, table, secondary, primary);
|
||||
}
|
||||
int32_t db_idx_long_double_end(capi_name code, uint64_t scope, capi_name table) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_end>(code, scope, table);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_end>(code, scope, table);
|
||||
}
|
||||
int32_t db_idx_long_double_next(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_next>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_next>(iterator, primary);
|
||||
}
|
||||
int32_t db_idx_long_double_previous(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_idx_long_double_previous>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_idx_long_double_previous>(iterator, primary);
|
||||
}
|
||||
int32_t db_store_i64(uint64_t scope, capi_name table, capi_name payer, uint64_t id, const void* data, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::db_store_i64>(scope, table, payer, id, data, len);
|
||||
return intrinsics::get().call<intrinsics::db_store_i64>(scope, table, payer, id, data, len);
|
||||
}
|
||||
void db_update_i64(int32_t iterator, capi_name payer, const void* data, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::db_update_i64>(iterator, payer, data, len);
|
||||
return intrinsics::get().call<intrinsics::db_update_i64>(iterator, payer, data, len);
|
||||
}
|
||||
void db_remove_i64(int32_t iterator) {
|
||||
return intrinsics::get().exec<intrinsics::db_remove_i64>(iterator);
|
||||
return intrinsics::get().call<intrinsics::db_remove_i64>(iterator);
|
||||
}
|
||||
int32_t db_get_i64(int32_t iterator, const void* data, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::db_get_i64>(iterator, data, len);
|
||||
return intrinsics::get().call<intrinsics::db_get_i64>(iterator, data, len);
|
||||
}
|
||||
int32_t db_next_i64(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_next_i64>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_next_i64>(iterator, primary);
|
||||
}
|
||||
int32_t db_previous_i64(int32_t iterator, uint64_t* primary) {
|
||||
return intrinsics::get().exec<intrinsics::db_previous_i64>(iterator, primary);
|
||||
return intrinsics::get().call<intrinsics::db_previous_i64>(iterator, primary);
|
||||
}
|
||||
int32_t db_find_i64(capi_name code, uint64_t scope, capi_name table, uint64_t id) {
|
||||
return intrinsics::get().exec<intrinsics::db_find_i64>(code, scope, table, id);
|
||||
return intrinsics::get().call<intrinsics::db_find_i64>(code, scope, table, id);
|
||||
}
|
||||
int32_t db_lowerbound_i64(capi_name code, uint64_t scope, capi_name table, uint64_t id) {
|
||||
return intrinsics::get().exec<intrinsics::db_lowerbound_i64>(code, scope, table, id);
|
||||
return intrinsics::get().call<intrinsics::db_lowerbound_i64>(code, scope, table, id);
|
||||
}
|
||||
int32_t db_upperbound_i64(capi_name code, uint64_t scope, capi_name table, uint64_t id) {
|
||||
return intrinsics::get().exec<intrinsics::db_upperbound_i64>(code, scope, table, id);
|
||||
return intrinsics::get().call<intrinsics::db_upperbound_i64>(code, scope, table, id);
|
||||
}
|
||||
int32_t db_end_i64(capi_name code, uint64_t scope, capi_name table) {
|
||||
return intrinsics::get().exec<intrinsics::db_end_i64>(code, scope, table);
|
||||
return intrinsics::get().call<intrinsics::db_end_i64>(code, scope, table);
|
||||
}
|
||||
void assert_recover_key( const capi_checksum256* digest, const char* sig, size_t siglen, const char* pub, size_t publen ) {
|
||||
return intrinsics::get().exec<intrinsics::assert_recover_key>(digest, sig, siglen, pub, publen);
|
||||
return intrinsics::get().call<intrinsics::assert_recover_key>(digest, sig, siglen, pub, publen);
|
||||
}
|
||||
int recover_key( const capi_checksum256* digest, const char* sig, size_t siglen, char* pub, size_t publen ) {
|
||||
return intrinsics::get().exec<intrinsics::recover_key>(digest, sig, siglen, pub, publen);
|
||||
return intrinsics::get().call<intrinsics::recover_key>(digest, sig, siglen, pub, publen);
|
||||
}
|
||||
void assert_recover_key_account( const capi_checksum256* digest, const char* sig, size_t siglen, const char* pub, size_t publen, uint64_t account, uint64_t permission ) {
|
||||
return intrinsics::get().call<intrinsics::assert_recover_key_account>(digest, sig, siglen, pub, publen, account, permission);
|
||||
}
|
||||
void assert_sha256( const char* data, uint32_t length, const capi_checksum256* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::assert_sha256>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::assert_sha256>(data, length, hash);
|
||||
}
|
||||
void assert_sha1( const char* data, uint32_t length, const capi_checksum160* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::assert_sha1>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::assert_sha1>(data, length, hash);
|
||||
}
|
||||
void assert_sha512( const char* data, uint32_t length, const capi_checksum512* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::assert_sha512>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::assert_sha512>(data, length, hash);
|
||||
}
|
||||
void assert_ripemd160( const char* data, uint32_t length, const capi_checksum160* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::assert_ripemd160>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::assert_ripemd160>(data, length, hash);
|
||||
}
|
||||
void sha256( const char* data, uint32_t length, capi_checksum256* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::sha256>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::sha256>(data, length, hash);
|
||||
}
|
||||
void sha1( const char* data, uint32_t length, capi_checksum160* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::sha1>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::sha1>(data, length, hash);
|
||||
}
|
||||
void sha512( const char* data, uint32_t length, capi_checksum512* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::sha512>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::sha512>(data, length, hash);
|
||||
}
|
||||
void ripemd160( const char* data, uint32_t length, capi_checksum160* hash ) {
|
||||
return intrinsics::get().exec<intrinsics::ripemd160>(data, length, hash);
|
||||
return intrinsics::get().call<intrinsics::ripemd160>(data, length, hash);
|
||||
}
|
||||
int32_t check_transaction_authorization( const char* trx_data, uint32_t trx_size,
|
||||
const char* pubkeys_data, uint32_t pubkeys_size,
|
||||
const char* perms_data, uint32_t perms_size
|
||||
) {
|
||||
return intrinsics::get().exec<intrinsics::check_transaction_authorization>(trx_data, trx_size, pubkeys_data, pubkeys_size, perms_data, perms_size);
|
||||
return intrinsics::get().call<intrinsics::check_transaction_authorization>(trx_data, trx_size, pubkeys_data, pubkeys_size, perms_data, perms_size);
|
||||
}
|
||||
int32_t check_permission_authorization( capi_name account, capi_name permission,
|
||||
const char* pubkeys_data, uint32_t pubkeys_size,
|
||||
const char* perms_data, uint32_t perms_size, uint64_t delay_us
|
||||
) {
|
||||
return intrinsics::get().exec<intrinsics::check_permission_authorization>(account, permission, pubkeys_data, pubkeys_size, perms_data, perms_size, delay_us);
|
||||
return intrinsics::get().call<intrinsics::check_permission_authorization>(account, permission, pubkeys_data, pubkeys_size, perms_data, perms_size, delay_us);
|
||||
}
|
||||
int64_t get_permission_last_used( capi_name account, capi_name permission ) {
|
||||
return intrinsics::get().exec<intrinsics::get_permission_last_used>(account, permission);
|
||||
return intrinsics::get().call<intrinsics::get_permission_last_used>(account, permission);
|
||||
}
|
||||
int64_t get_account_creation_time( capi_name account ) {
|
||||
return intrinsics::get().exec<intrinsics::get_account_creation_time>(account);
|
||||
return intrinsics::get().call<intrinsics::get_account_creation_time>(account);
|
||||
}
|
||||
uint64_t current_time() {
|
||||
return intrinsics::get().exec<intrinsics::current_time>();
|
||||
return intrinsics::get().call<intrinsics::current_time>();
|
||||
}
|
||||
uint64_t publication_time() {
|
||||
return intrinsics::get().exec<intrinsics::publication_time>();
|
||||
return intrinsics::get().call<intrinsics::publication_time>();
|
||||
}
|
||||
uint32_t read_action_data( void* msg, uint32_t len ) {
|
||||
return intrinsics::get().exec<intrinsics::read_action_data>(msg, len);
|
||||
return intrinsics::get().call<intrinsics::read_action_data>(msg, len);
|
||||
}
|
||||
uint32_t action_data_size() {
|
||||
return intrinsics::get().exec<intrinsics::action_data_size>();
|
||||
return intrinsics::get().call<intrinsics::action_data_size>();
|
||||
}
|
||||
capi_name current_receiver() {
|
||||
return intrinsics::get().exec<intrinsics::current_receiver>();
|
||||
return intrinsics::get().call<intrinsics::current_receiver>();
|
||||
}
|
||||
void set_action_return_value( void* rv, size_t len ) {
|
||||
intrinsics::get().exec<intrinsics::set_action_return_value>(rv, len);
|
||||
intrinsics::get().call<intrinsics::set_action_return_value>(rv, len);
|
||||
}
|
||||
void require_recipient( capi_name name ) {
|
||||
return intrinsics::get().exec<intrinsics::require_recipient>(name);
|
||||
return intrinsics::get().call<intrinsics::require_recipient>(name);
|
||||
}
|
||||
void require_auth( capi_name name ) {
|
||||
return intrinsics::get().exec<intrinsics::require_auth>(name);
|
||||
return intrinsics::get().call<intrinsics::require_auth>(name);
|
||||
}
|
||||
void require_auth2( capi_name name, capi_name permission ) {
|
||||
return intrinsics::get().exec<intrinsics::require_auth2>(name, permission);
|
||||
return intrinsics::get().call<intrinsics::require_auth2>(name, permission);
|
||||
}
|
||||
bool has_auth( capi_name name ) {
|
||||
return intrinsics::get().exec<intrinsics::has_auth>(name);
|
||||
return intrinsics::get().call<intrinsics::has_auth>(name);
|
||||
}
|
||||
bool is_account( capi_name name ) {
|
||||
return intrinsics::get().exec<intrinsics::is_account>(name);
|
||||
return intrinsics::get().call<intrinsics::is_account>(name);
|
||||
}
|
||||
size_t read_transaction(char *buffer, size_t size) {
|
||||
return intrinsics::get().exec<intrinsics::read_transaction>(buffer, size);
|
||||
return intrinsics::get().call<intrinsics::read_transaction>(buffer, size);
|
||||
}
|
||||
size_t transaction_size() {
|
||||
return intrinsics::get().exec<intrinsics::transaction_size>();
|
||||
return intrinsics::get().call<intrinsics::transaction_size>();
|
||||
}
|
||||
uint32_t expiration() {
|
||||
return intrinsics::get().exec<intrinsics::expiration>();
|
||||
return intrinsics::get().call<intrinsics::expiration>();
|
||||
}
|
||||
int tapos_block_prefix() {
|
||||
return intrinsics::get().exec<intrinsics::tapos_block_prefix>();
|
||||
return intrinsics::get().call<intrinsics::tapos_block_prefix>();
|
||||
}
|
||||
int tapos_block_num() {
|
||||
return intrinsics::get().exec<intrinsics::tapos_block_num>();
|
||||
return intrinsics::get().call<intrinsics::tapos_block_num>();
|
||||
}
|
||||
int get_action( uint32_t type, uint32_t index, char* buff, size_t size ) {
|
||||
return intrinsics::get().exec<intrinsics::get_action>(type, index, buff, size);
|
||||
return intrinsics::get().call<intrinsics::get_action>(type, index, buff, size);
|
||||
}
|
||||
void send_inline(char *serialized_action, size_t size) {
|
||||
return intrinsics::get().exec<intrinsics::send_inline>(serialized_action, size);
|
||||
return intrinsics::get().call<intrinsics::send_inline>(serialized_action, size);
|
||||
}
|
||||
void send_context_free_inline(char *serialized_action, size_t size) {
|
||||
return intrinsics::get().exec<intrinsics::send_context_free_inline>(serialized_action, size);
|
||||
return intrinsics::get().call<intrinsics::send_context_free_inline>(serialized_action, size);
|
||||
}
|
||||
void send_deferred(const uint128_t* sender_id, capi_name payer, const char *serialized_transaction, size_t size, uint32_t replace_existing) {
|
||||
return intrinsics::get().exec<intrinsics::send_deferred>(sender_id, payer, serialized_transaction, size, replace_existing);
|
||||
return intrinsics::get().call<intrinsics::send_deferred>(sender_id, payer, serialized_transaction, size, replace_existing);
|
||||
}
|
||||
int cancel_deferred(const uint128_t* sender_id) {
|
||||
return intrinsics::get().exec<intrinsics::cancel_deferred>(sender_id);
|
||||
return intrinsics::get().call<intrinsics::cancel_deferred>(sender_id);
|
||||
}
|
||||
int get_context_free_data( uint32_t index, char* buff, size_t size ) {
|
||||
return intrinsics::get().exec<intrinsics::get_context_free_data>(index, buff, size);
|
||||
return intrinsics::get().call<intrinsics::get_context_free_data>(index, buff, size);
|
||||
}
|
||||
capi_name get_sender() {
|
||||
return intrinsics::get().exec<intrinsics::get_sender>();
|
||||
}
|
||||
int64_t call(capi_name receiver, uint64_t flags, const char* data, size_t data_size) {
|
||||
return intrinsics::get().exec<intrinsics::call>(receiver, flags, data, data_size);
|
||||
}
|
||||
uint32_t get_call_return_value(void* mem, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::get_call_return_value>(mem, len);
|
||||
}
|
||||
uint32_t get_call_data(void* mem, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::get_call_data>(mem, len);
|
||||
}
|
||||
void set_call_return_value(void* mem, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::set_call_return_value>(mem, len);
|
||||
return intrinsics::get().call<intrinsics::get_sender>();
|
||||
}
|
||||
|
||||
// softfloat
|
||||
@@ -798,47 +792,47 @@ extern "C" {
|
||||
}
|
||||
|
||||
void prints_l(const char* cstr, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::prints_l>(cstr, len);
|
||||
return intrinsics::get().call<intrinsics::prints_l>(cstr, len);
|
||||
}
|
||||
|
||||
void prints(const char* cstr) {
|
||||
return intrinsics::get().exec<intrinsics::prints>(cstr);
|
||||
return intrinsics::get().call<intrinsics::prints>(cstr);
|
||||
}
|
||||
|
||||
void printi(int64_t value) {
|
||||
return intrinsics::get().exec<intrinsics::printi>(value);
|
||||
return intrinsics::get().call<intrinsics::printi>(value);
|
||||
}
|
||||
|
||||
void printui(uint64_t value) {
|
||||
return intrinsics::get().exec<intrinsics::printui>(value);
|
||||
return intrinsics::get().call<intrinsics::printui>(value);
|
||||
}
|
||||
|
||||
void printi128(const int128_t* value) {
|
||||
return intrinsics::get().exec<intrinsics::printi128>(value);
|
||||
return intrinsics::get().call<intrinsics::printi128>(value);
|
||||
}
|
||||
|
||||
void printui128(const uint128_t* value) {
|
||||
return intrinsics::get().exec<intrinsics::printui128>(value);
|
||||
return intrinsics::get().call<intrinsics::printui128>(value);
|
||||
}
|
||||
|
||||
void printsf(float value) {
|
||||
return intrinsics::get().exec<intrinsics::printsf>(value);
|
||||
return intrinsics::get().call<intrinsics::printsf>(value);
|
||||
}
|
||||
|
||||
void printdf(double value) {
|
||||
return intrinsics::get().exec<intrinsics::printdf>(value);
|
||||
return intrinsics::get().call<intrinsics::printdf>(value);
|
||||
}
|
||||
|
||||
void printqf(const long double* value) {
|
||||
return intrinsics::get().exec<intrinsics::printqf>(value);
|
||||
return intrinsics::get().call<intrinsics::printqf>(value);
|
||||
}
|
||||
|
||||
void printn(uint64_t nm) {
|
||||
return intrinsics::get().exec<intrinsics::printn>(nm);
|
||||
return intrinsics::get().call<intrinsics::printn>(nm);
|
||||
}
|
||||
|
||||
void printhex(const void* data, uint32_t len) {
|
||||
return intrinsics::get().exec<intrinsics::printhex>(data, len);
|
||||
return intrinsics::get().call<intrinsics::printhex>(data, len);
|
||||
}
|
||||
|
||||
void* memset ( void* ptr, int value, size_t num ) {
|
||||
@@ -902,87 +896,103 @@ extern "C" {
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
int64_t add_security_group_participants(const char* data, uint32_t datalen) {
|
||||
return intrinsics::get().call<intrinsics::add_security_group_participants>(data, datalen);
|
||||
}
|
||||
|
||||
int64_t remove_security_group_participants(const char* data, uint32_t datalen){
|
||||
return intrinsics::get().call<intrinsics::remove_security_group_participants>(data, datalen);
|
||||
}
|
||||
|
||||
bool in_active_security_group(const char* data, uint32_t datalen){
|
||||
return intrinsics::get().call<intrinsics::in_active_security_group>(data, datalen);
|
||||
}
|
||||
|
||||
uint32_t get_active_security_group(char* data, uint32_t datalen){
|
||||
return intrinsics::get().call<intrinsics::get_active_security_group>(data, datalen);
|
||||
}
|
||||
|
||||
void set_finalizers(uint64_t packed_finalizer_format, const char* data, uint32_t len) {
|
||||
intrinsics::get().exec<intrinsics::set_finalizers>(packed_finalizer_format, data, len);
|
||||
intrinsics::get().call<intrinsics::set_finalizers>(packed_finalizer_format, data, len);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
int32_t blake2_f( uint32_t rounds, const char* state, uint32_t state_len, const char* msg, uint32_t msg_len,
|
||||
const char* t0_offset, uint32_t t0_len, const char* t1_offset, uint32_t t1_len, int32_t final, char* result, uint32_t result_len) {
|
||||
return intrinsics::get().exec<intrinsics::blake2_f>(rounds, state, state_len, msg, msg_len, t0_offset, t0_len, t1_offset, t1_len, final, result, result_len);
|
||||
return intrinsics::get().call<intrinsics::blake2_f>(rounds, state, state_len, msg, msg_len, t0_offset, t0_len, t1_offset, t1_len, final, result, result_len);
|
||||
}
|
||||
|
||||
int32_t k1_recover( const char* sig, uint32_t sig_len, const char* dig, uint32_t dig_len, char* pub, uint32_t pub_len) {
|
||||
return intrinsics::get().exec<intrinsics::k1_recover>(sig, sig_len, dig, dig_len, pub, pub_len);
|
||||
return intrinsics::get().call<intrinsics::k1_recover>(sig, sig_len, dig, dig_len, pub, pub_len);
|
||||
}
|
||||
|
||||
int32_t alt_bn128_add( const char* op1, uint32_t op1_len, const char* op2, uint32_t op2_len, char* result, uint32_t result_len) {
|
||||
return intrinsics::get().exec<intrinsics::alt_bn128_add>(op1, op1_len, op2, op2_len, result, result_len);
|
||||
return intrinsics::get().call<intrinsics::alt_bn128_add>(op1, op1_len, op2, op2_len, result, result_len);
|
||||
}
|
||||
|
||||
int32_t alt_bn128_mul( const char* g1, uint32_t g1_len, const char* scalar, uint32_t scalar_len, char* result, uint32_t result_len) {
|
||||
return intrinsics::get().exec<intrinsics::alt_bn128_mul>(g1, g1_len, scalar, scalar_len, result, result_len);
|
||||
return intrinsics::get().call<intrinsics::alt_bn128_mul>(g1, g1_len, scalar, scalar_len, result, result_len);
|
||||
}
|
||||
|
||||
int32_t alt_bn128_pair( const char* pairs, uint32_t pairs_len) {
|
||||
return intrinsics::get().exec<intrinsics::alt_bn128_pair>(pairs, pairs_len);
|
||||
return intrinsics::get().call<intrinsics::alt_bn128_pair>(pairs, pairs_len);
|
||||
}
|
||||
|
||||
int32_t mod_exp( const char* base, uint32_t base_len, const char* exp, uint32_t exp_len, const char* mod, uint32_t mod_len, char* result, uint32_t result_len) {
|
||||
return intrinsics::get().exec<intrinsics::mod_exp>(base, base_len, exp, exp_len, mod, mod_len, result, result_len);
|
||||
return intrinsics::get().call<intrinsics::mod_exp>(base, base_len, exp, exp_len, mod, mod_len, result, result_len);
|
||||
}
|
||||
|
||||
void sha3( const char* data, uint32_t data_len, char* hash, uint32_t hash_len, int32_t keccak ) {
|
||||
intrinsics::get().exec<intrinsics::sha3>(data, data_len, hash, hash_len, keccak);
|
||||
intrinsics::get().call<intrinsics::sha3>(data, data_len, hash, hash_len, keccak);
|
||||
}
|
||||
|
||||
int32_t bls_g1_add(const char* op1, uint32_t op1_len, const char* op2, uint32_t op2_len, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_g1_add>(op1, op1_len, op2, op2_len, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_g1_add>(op1, op1_len, op2, op2_len, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_g2_add(const char* op1, uint32_t op1_len, const char* op2, uint32_t op2_len, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_g2_add>(op1, op1_len, op2, op2_len, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_g2_add>(op1, op1_len, op2, op2_len, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_g1_weighted_sum(const char* points, uint32_t points_len, const char* scalars, uint32_t scalars_len, uint32_t n, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_g1_weighted_sum>(points, points_len, scalars, scalars_len, n, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_g1_weighted_sum>(points, points_len, scalars, scalars_len, n, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_g2_weighted_sum(const char* points, uint32_t points_len, const char* scalars, uint32_t scalars_len, uint32_t n, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_g2_weighted_sum>(points, points_len, scalars, scalars_len, n, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_g2_weighted_sum>(points, points_len, scalars, scalars_len, n, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_pairing(const char* g1_points, uint32_t g1_points_len, const char* g2_points, uint32_t g2_points_len, uint32_t n, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_pairing>(g1_points, g1_points_len, g1_points, g1_points_len, n, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_pairing>(g1_points, g1_points_len, g1_points, g1_points_len, n, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_g1_map(const char* e, uint32_t e_len, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_g1_map>(e, e_len, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_g1_map>(e, e_len, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_g2_map(const char* e, uint32_t e_len, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_g2_map>(e, e_len, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_g2_map>(e, e_len, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_fp_mod(const char* s, uint32_t s_len, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_fp_mod>(s, s_len, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_fp_mod>(s, s_len, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_fp_mul(const char* op1, uint32_t op1_len, const char* op2, uint32_t op2_len, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_fp_mul>(op1, op1_len, op2, op2_len, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_fp_mul>(op1, op1_len, op2, op2_len, res, res_len);
|
||||
}
|
||||
|
||||
int32_t bls_fp_exp(const char* base, uint32_t base_len, const char* exp, uint32_t exp_len, char* res, uint32_t res_len)
|
||||
{
|
||||
return intrinsics::get().exec<intrinsics::bls_fp_exp>(base, base_len, exp, exp_len, res, res_len);
|
||||
return intrinsics::get().call<intrinsics::bls_fp_exp>(base, base_len, exp, exp_len, res, res_len);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace eosio { namespace native {
|
||||
};
|
||||
|
||||
template <intrinsic_name IN, typename... Args>
|
||||
auto exec(Args... args) -> decltype(std::get<IN>(intrinsics::get().funcs)(args...)) {
|
||||
auto call(Args... args) -> decltype(std::get<IN>(intrinsics::get().funcs)(args...)) {
|
||||
return std::get<IN>(intrinsics::get().funcs)(args...);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#include <eosio/crypto_bls_ext.h>
|
||||
#include <eosio/db.h>
|
||||
#include <eosio/instant_finality.h>
|
||||
#include <eosio/call.h>
|
||||
#include <eosio/permission.h>
|
||||
#include <eosio/print.h>
|
||||
#include <eosio/privileged.h>
|
||||
#include <eosio/system.h>
|
||||
#include <eosio/transaction.h>
|
||||
#include <eosio/types.h>
|
||||
#include <eosio/security_group.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
@@ -45,6 +45,7 @@ namespace eosio { namespace native {
|
||||
}
|
||||
|
||||
#define INTRINSICS(intrinsic_macro) \
|
||||
intrinsic_macro(get_account_ram_usage) \
|
||||
intrinsic_macro(get_resource_limits) \
|
||||
intrinsic_macro(set_resource_limits) \
|
||||
intrinsic_macro(set_proposed_producers) \
|
||||
@@ -118,6 +119,7 @@ intrinsic_macro(db_upperbound_i64) \
|
||||
intrinsic_macro(db_end_i64) \
|
||||
intrinsic_macro(assert_recover_key) \
|
||||
intrinsic_macro(recover_key) \
|
||||
intrinsic_macro(assert_recover_key_account) \
|
||||
intrinsic_macro(assert_sha256) \
|
||||
intrinsic_macro(assert_sha1) \
|
||||
intrinsic_macro(assert_sha512) \
|
||||
@@ -164,6 +166,10 @@ intrinsic_macro(cancel_deferred) \
|
||||
intrinsic_macro(get_context_free_data) \
|
||||
intrinsic_macro(get_sender) \
|
||||
intrinsic_macro(set_action_return_value) \
|
||||
intrinsic_macro(add_security_group_participants) \
|
||||
intrinsic_macro(remove_security_group_participants) \
|
||||
intrinsic_macro(in_active_security_group) \
|
||||
intrinsic_macro(get_active_security_group) \
|
||||
intrinsic_macro(blake2_f) \
|
||||
intrinsic_macro(sha3) \
|
||||
intrinsic_macro(k1_recover) \
|
||||
@@ -181,11 +187,7 @@ intrinsic_macro(bls_g2_map) \
|
||||
intrinsic_macro(bls_fp_mod) \
|
||||
intrinsic_macro(bls_fp_mul) \
|
||||
intrinsic_macro(bls_fp_exp) \
|
||||
intrinsic_macro(set_finalizers) \
|
||||
intrinsic_macro(call) \
|
||||
intrinsic_macro(get_call_return_value) \
|
||||
intrinsic_macro(get_call_data) \
|
||||
intrinsic_macro(set_call_return_value)
|
||||
intrinsic_macro(set_finalizers)
|
||||
|
||||
#define CREATE_ENUM(name) \
|
||||
name,
|
||||
|
||||
@@ -32,7 +32,7 @@ if (spring_FOUND)
|
||||
CDTIntegrationTests
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/tests/integration"
|
||||
BINARY_DIR "${CMAKE_BINARY_DIR}/tests/integration"
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_FRAMEWORK_PATH=${TEST_FRAMEWORK_PATH} -DCMAKE_MODULE_PATH=${TEST_MODULE_PATH} -Dspring_DIR=${spring_DIR} -Deosio_DIR=${eosio_DIR} -DLLVM_DIR=${LLVM_DIR} -DBOOST_ROOT=${BOOST_ROOT}
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_FRAMEWORK_PATH=${TEST_FRAMEWORK_PATH} -DCMAKE_MODULE_PATH=${TEST_MODULE_PATH} -Deosio_DIR=${eosio_DIR} -DLLVM_DIR=${LLVM_DIR} -DBOOST_ROOT=${BOOST_ROOT}
|
||||
UPDATE_COMMAND ""
|
||||
PATCH_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <eosio/testing/tester.hpp>
|
||||
#include <eosio/chain/abi_serializer.hpp>
|
||||
|
||||
#include <fc/variant_object.hpp>
|
||||
|
||||
#include <contracts.hpp>
|
||||
|
||||
using namespace eosio;
|
||||
using namespace eosio::testing;
|
||||
using namespace eosio::chain;
|
||||
using namespace eosio::testing;
|
||||
using namespace fc;
|
||||
|
||||
using mvo = fc::mutable_variant_object;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(array_tests)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( std_array_param, tester ) try {
|
||||
/* ----------- testpa action tests --------------------------------------------------
|
||||
[[eosio::action]]
|
||||
void testpa(std::array<int,4> input){
|
||||
std::array<int,4> arr = input;
|
||||
for(int i = 0; i < 4; ++i){
|
||||
eosio::cout << arr[i] << " ";
|
||||
}
|
||||
eosio::cout << "\n";
|
||||
}
|
||||
-------------------------------------------------------------------------------------- */
|
||||
create_accounts( { "test"_n } );
|
||||
produce_block();
|
||||
set_code( "test"_n, contracts::array_tests_wasm() );
|
||||
set_abi( "test"_n, contracts::array_tests_abi().data() );
|
||||
produce_blocks();
|
||||
|
||||
auto trace = push_action("test"_n, "testpa"_n, "test"_n, mvo()("input", {1,2,3,4}));
|
||||
auto& con = trace->action_traces[0].console;
|
||||
BOOST_REQUIRE_EQUAL(con, std::string("1 2 3 4 \n"));
|
||||
produce_block();
|
||||
|
||||
// size should be correct
|
||||
// ----------------------
|
||||
BOOST_CHECK_EXCEPTION( push_action("test"_n, "testpa"_n, "test"_n, mvo()("input", {1,2,3})),
|
||||
pack_exception,
|
||||
fc_exception_message_starts_with("Incorrect number of values provided (4) for fixed-size (3) array type"));
|
||||
|
||||
produce_block();
|
||||
} FC_LOG_AND_RETHROW()
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( std_array_return_value, tester ) try {
|
||||
/* ----------- testre action tests --------------------------------------------------
|
||||
[[eosio::action]]
|
||||
std::array<int,4> testre(std::array<int,4> input){
|
||||
std::array<int,4> arr = input;
|
||||
for(auto & v : arr) v += 1;
|
||||
return arr;
|
||||
}
|
||||
-------------------------------------------------------------------------------------- */
|
||||
create_accounts( { "test"_n } );
|
||||
produce_block();
|
||||
set_code( "test"_n, contracts::array_tests_wasm() );
|
||||
set_abi( "test"_n, contracts::array_tests_abi().data() );
|
||||
produce_blocks();
|
||||
|
||||
auto trace = push_action("test"_n, "testre"_n, "test"_n, mvo()("input", {1, 2, 3, 4}));
|
||||
auto& rv = trace->action_traces[0].return_value;
|
||||
auto actual = fc::raw::unpack<std::array<int, 4>>(rv);
|
||||
auto expected = std::array<int, 4>{2, 3, 4, 5};
|
||||
BOOST_REQUIRE(actual == expected);
|
||||
} FC_LOG_AND_RETHROW()
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( std_vector_return_value, tester ) try {
|
||||
/* ----------- testrev action tests --------------------------------------------------
|
||||
[[eosio::action]]
|
||||
std::vector<int> testrev(std::vector<int> input){
|
||||
std::vector<int> vec = input;
|
||||
for(auto & v : vec) v += 1;
|
||||
return vec;
|
||||
}
|
||||
-------------------------------------------------------------------------------------- */
|
||||
create_accounts( { "test"_n } );
|
||||
produce_block();
|
||||
set_code( "test"_n, contracts::array_tests_wasm() );
|
||||
set_abi( "test"_n, contracts::array_tests_abi().data() );
|
||||
produce_blocks();
|
||||
|
||||
auto trace = push_action("test"_n, "testrev"_n, "test"_n, mvo()("input", {1, 2, 3, 4}));
|
||||
auto& rv = trace->action_traces[0].return_value;
|
||||
auto actual = fc::raw::unpack<std::vector<int>>(rv);
|
||||
auto expected = std::vector<int>{2, 3, 4, 5};
|
||||
BOOST_REQUIRE(actual == expected);
|
||||
} FC_LOG_AND_RETHROW()
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -1,47 +0,0 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <eosio/testing/tester.hpp>
|
||||
#include <eosio/chain/abi_serializer.hpp>
|
||||
|
||||
#include <fc/variant_object.hpp>
|
||||
|
||||
#include <contracts.hpp>
|
||||
#include "test_utils.hpp"
|
||||
|
||||
using namespace eosio;
|
||||
using namespace eosio::testing;
|
||||
using namespace eosio::chain;
|
||||
using namespace eosio::testing;
|
||||
using namespace fc;
|
||||
|
||||
using mvo = fc::mutable_variant_object;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(bitset_tests)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( bitset_test, tester ) try {
|
||||
create_accounts( { "test"_n } );
|
||||
produce_block();
|
||||
set_code( "test"_n, contracts::simple_wasm() );
|
||||
set_abi( "test"_n, contracts::simple_abi().data() );
|
||||
produce_blocks();
|
||||
|
||||
auto trx_trace = push_action("test"_n, "testbs"_n, "test"_n, mvo()("b", "0010"));
|
||||
auto& act_trace = trx_trace->action_traces[0];
|
||||
BOOST_REQUIRE_EQUAL(fc::raw::unpack<fc::bitset>(act_trace.return_value), fc::bitset{"1101"});
|
||||
|
||||
{
|
||||
// because contract `simple_tests.cpp` uses the `bitset` type, the abi version should be 1.3 or greater
|
||||
auto abi_version = extract_version_from_json_abi(contracts::simple_abi());
|
||||
BOOST_REQUIRE(abi_version.is_valid());
|
||||
BOOST_REQUIRE_LT(version_t(1,2), abi_version);
|
||||
}
|
||||
|
||||
{
|
||||
// check that abi version of minimal contract is still 1.2.
|
||||
auto abi_version = extract_version_from_json_abi(contracts::malloc_tests_abi());
|
||||
BOOST_REQUIRE(abi_version.is_valid());
|
||||
BOOST_REQUIRE_EQUAL(version_t(1,2), abi_version);
|
||||
}
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -1,275 +0,0 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <eosio/testing/tester.hpp>
|
||||
#include <eosio/chain/abi_serializer.hpp>
|
||||
#include <fc/variant_object.hpp>
|
||||
|
||||
#include <contracts.hpp>
|
||||
|
||||
using namespace eosio;
|
||||
using namespace eosio::testing;
|
||||
using namespace fc;
|
||||
|
||||
using mvo = fc::mutable_variant_object;
|
||||
|
||||
struct acct_and_code {
|
||||
account_name acct;
|
||||
std::vector<uint8_t> wasm;
|
||||
char* abi = nullptr;
|
||||
};
|
||||
|
||||
// The first account in the accounts vector is the action initiating the
|
||||
// first sync call
|
||||
struct call_tester: tester {
|
||||
call_tester(const std::vector<acct_and_code>& accounts) {
|
||||
for (auto i = 0u; i < accounts.size(); ++i) {
|
||||
create_account(accounts[i].acct);
|
||||
set_code(accounts[i].acct, accounts[i].wasm);
|
||||
set_abi(accounts[i].acct, accounts[i].abi);
|
||||
}
|
||||
|
||||
produce_block();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(sync_call_tests)
|
||||
|
||||
// Verify a sync call returns value correctly
|
||||
BOOST_AUTO_TEST_CASE(return_value_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
// Using host function directly
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "hstretvaltst"_n, "caller"_n, {}));
|
||||
|
||||
// Using call_wrapper
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "wrpretvaltst"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify one parameter passing works correctly
|
||||
BOOST_AUTO_TEST_CASE(param_basic_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
// Using host function directly
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "hstoneprmtst"_n, "caller"_n, {}));
|
||||
|
||||
// Using call_wrapper
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "wrponeprmtst"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify multiple parameters passing works correctly
|
||||
BOOST_AUTO_TEST_CASE(multiple_params_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
// Using host function directly
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "hstmulprmtst"_n, "caller"_n, {}));
|
||||
|
||||
// Using call_wrapper
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "wrpmulprmtst"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify passing a struct parameter works correctly
|
||||
BOOST_AUTO_TEST_CASE(struct_param_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "structtest"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify passing a mix of structs and integer works correctly
|
||||
BOOST_AUTO_TEST_CASE(mix_struct_int_params_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "structinttst"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify a sync call to a void function works properly.
|
||||
BOOST_AUTO_TEST_CASE(void_func_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
auto check = [] (const transaction_trace_ptr& trx_trace) {
|
||||
auto& atrace = trx_trace->action_traces;
|
||||
|
||||
auto& call_traces = atrace[0].call_traces;
|
||||
BOOST_REQUIRE_EQUAL(call_traces.size(), 1u);
|
||||
|
||||
// Verify the print from the void function is correct.
|
||||
// The test contract checks the return value size is 0.
|
||||
auto& call_trace = call_traces[0];
|
||||
BOOST_REQUIRE_EQUAL(call_trace.call_ordinal, 1u);
|
||||
BOOST_REQUIRE_EQUAL(call_trace.sender_ordinal, 0u);
|
||||
BOOST_REQUIRE_EQUAL(call_trace.console, "I am a void function");
|
||||
};
|
||||
|
||||
// Using host function directly
|
||||
auto trx_trace = t.push_action("caller"_n, "hstvodfuntst"_n, "caller"_n, {});
|
||||
check(trx_trace);
|
||||
|
||||
// Using call_wrapper
|
||||
trx_trace = t.push_action("caller"_n, "wrpvodfuntst"_n, "caller"_n, {});
|
||||
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,
|
||||
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 {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::single_func_wasm(), contracts::single_func_abi().data()}
|
||||
});
|
||||
|
||||
// The single_func_wasm contains only one function and the caller contract
|
||||
// hooks up with it
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "hstretvaltst"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify support_mode for void and non-void sync calls if calls are a failure
|
||||
BOOST_AUTO_TEST_CASE(sync_call_support_mode_failure_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::not_supported_wasm(), contracts::not_supported_abi().data()}
|
||||
});
|
||||
|
||||
// voidfncnoop uses support_mode::no_op
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "voidfncnoop"_n, "caller"_n, {}));
|
||||
|
||||
// voidfncabort uses default support_mode::abort
|
||||
BOOST_CHECK_EXCEPTION(t.push_action("caller"_n, "voidfncabort"_n, "caller"_n, {}),
|
||||
eosio_assert_message_exception,
|
||||
fc_exception_message_contains("receiver does not support sync call but support_mode is set to abort"));
|
||||
|
||||
// intfuncnoop uses support_mode::no_op
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "intfuncnoop"_n, "caller"_n, {}));
|
||||
|
||||
// intfuncabort uses default support_mode::abort
|
||||
BOOST_CHECK_EXCEPTION(t.push_action("caller"_n, "intfuncabort"_n, "caller"_n, {}),
|
||||
eosio_assert_message_exception,
|
||||
fc_exception_message_contains("receiver does not support sync call but support_mode is set to abort"));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify support_mode for void and non-void sync calls if call is successful
|
||||
BOOST_AUTO_TEST_CASE(sync_call_support_mode_success_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
// voidnoopsucc uses support_mode::no_op
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "voidnoopsucc"_n, "caller"_n, {}));
|
||||
|
||||
// sumnoopsucc uses support_mode::no_op
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "sumnoopsucc"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify header validation
|
||||
BOOST_AUTO_TEST_CASE(unknown_function_test) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::caller_wasm(), contracts::caller_abi().data()},
|
||||
{"callee"_n, contracts::callee_wasm(), contracts::callee_abi().data()}
|
||||
});
|
||||
|
||||
BOOST_REQUIRE_NO_THROW(t.push_action("caller"_n, "hdrvaltest"_n, "caller"_n, {}));
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
// Verify adding/reading entries to/from a table, and read-only enforcement work
|
||||
BOOST_AUTO_TEST_CASE(addr_book_tests) { try {
|
||||
call_tester t({
|
||||
{"caller"_n, contracts::addr_book_caller_wasm(), contracts::addr_book_caller_abi().data()},
|
||||
{"callee"_n, contracts::addr_book_callee_wasm(), contracts::addr_book_callee_abi().data()}
|
||||
});
|
||||
|
||||
// Try to add an entry using a read-only sync call
|
||||
BOOST_CHECK_EXCEPTION(t.push_action("caller"_n, "upsertrdonly"_n, "caller"_n, mvo()
|
||||
("user", "alice")
|
||||
("first_name", "alice")
|
||||
("street", "123 Main St.")),
|
||||
unaccessible_api,
|
||||
fc_exception_message_contains("this API is not allowed in read only action/call"));
|
||||
|
||||
// Add an entry using a read-write sync call
|
||||
t.push_action("caller"_n, "upsert"_n, "caller"_n, mvo()
|
||||
("user", "alice")
|
||||
("first_name", "alice")
|
||||
("street", "123 Main St."));
|
||||
|
||||
// Read the inserted entry. "get"_n action will check the return value from the sync call
|
||||
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()
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
namespace eosio::testing {
|
||||
struct contracts {
|
||||
static std::vector<uint8_t> array_tests_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/array_tests.wasm"); }
|
||||
static std::vector<char> array_tests_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/array_tests.abi"); }
|
||||
static std::vector<uint8_t> malloc_tests_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/malloc_tests.wasm"); }
|
||||
static std::vector<char> malloc_tests_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/malloc_tests.abi"); }
|
||||
static std::vector<uint8_t> old_malloc_tests_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/old_malloc_tests.wasm"); }
|
||||
@@ -41,20 +39,5 @@ namespace eosio::testing {
|
||||
|
||||
static std::vector<uint8_t> test_multi_index_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/test_multi_index.wasm"); }
|
||||
static std::vector<char> test_multi_index_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/test_multi_index.abi"); }
|
||||
|
||||
static std::vector<uint8_t> caller_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_caller.wasm"); }
|
||||
static std::vector<char> caller_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_caller.abi"); }
|
||||
static std::vector<uint8_t> callee_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_callee.wasm"); }
|
||||
static std::vector<char> callee_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_callee.abi"); }
|
||||
static std::vector<uint8_t> not_supported_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_not_supported.wasm"); }
|
||||
static std::vector<char> not_supported_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_not_supported.abi"); }
|
||||
static std::vector<uint8_t> single_func_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_single_func.wasm"); }
|
||||
static std::vector<char> single_func_abi() { return read_abi("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_single_func.abi"); }
|
||||
static std::vector<uint8_t> addr_book_callee_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../unit/test_contracts/sync_call_addr_book_callee.wasm"); }
|
||||
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,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <charconv>
|
||||
|
||||
inline eosio::chain::version_t extract_version_from_json_abi(std::vector<char> abi) {
|
||||
std::string_view sv(abi.data(), abi.size());
|
||||
|
||||
constexpr const char* pattern = R"("version": "eosio::abi/)";
|
||||
if (auto pos = sv.find(pattern); pos != std::string_view::npos) {
|
||||
return eosio::chain::version_t{sv.substr(pos + strlen(pattern))};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected" : {
|
||||
"abi-file" : "action_results_test.abi"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"tests": [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected": {
|
||||
"abi-file": "aliased_type_variant_template_arg.abi"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected" : {
|
||||
"abi-file" : "nested_container.abi"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected" : {
|
||||
"abi-file" : "singleton_contract.abi"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"tests": [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected": {
|
||||
"abi-file": "struct_base_typedefd.abi"
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"____comment": "This file was generated with eosio-abigen. DO NOT EDIT ",
|
||||
"version": "eosio::abi/1.3",
|
||||
"types": [],
|
||||
"structs": [
|
||||
{
|
||||
"name": "noparam",
|
||||
"base": "",
|
||||
"fields": []
|
||||
},
|
||||
{
|
||||
"name": "sum",
|
||||
"base": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint32"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": "uint32"
|
||||
},
|
||||
{
|
||||
"name": "c",
|
||||
"type": "uint32"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "voidfunc",
|
||||
"base": "",
|
||||
"fields": []
|
||||
},
|
||||
{
|
||||
"name": "withparam",
|
||||
"base": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "in",
|
||||
"type": "uint32"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [],
|
||||
"tables": [],
|
||||
"ricardian_clauses": [],
|
||||
"variants": [],
|
||||
"action_results": [],
|
||||
"calls": [
|
||||
{
|
||||
"name": "noparam",
|
||||
"type": "noparam",
|
||||
"id": 229476383600947,
|
||||
"result_type": "uint32"
|
||||
},
|
||||
{
|
||||
"name": "sum",
|
||||
"type": "sum",
|
||||
"id": 193506202,
|
||||
"result_type": "uint32"
|
||||
},
|
||||
{
|
||||
"name": "voidfunc",
|
||||
"type": "voidfunc",
|
||||
"id": 7573061335575747,
|
||||
"result_type": ""
|
||||
},
|
||||
{
|
||||
"name": "withparam",
|
||||
"type": "withparam",
|
||||
"id": 249912189145794130,
|
||||
"result_type": "uint32"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#include <eosio/call.hpp>
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
class [[eosio::contract]] sync_call_test : public eosio::contract {
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t noparam() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t withparam(uint32_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
void voidfunc() {
|
||||
int i = 10;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t sum(uint32_t a, uint32_t b, uint32_t c) {
|
||||
return a + b + c;
|
||||
}
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"expected" : {
|
||||
"abi-file" : "sync_call_test.abi"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected" : {
|
||||
"abi-file" : "tagged_number_test.abi"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected" : {
|
||||
"abi-file" : "using_std_array.abi"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
class [[eosio::contract]] separate_cpp_hpp : public eosio::contract {
|
||||
class [[eosio::contract]] separate_cpp_hpp : eosio::contract {
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#include <eosio/call.hpp>
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
// Test the validation of the number of arguments passed in call_wrapper.
|
||||
// Expected error:
|
||||
// .../build/bin/../include/eosiolib/contracts/eosio/detail.hpp:72:7: error: static_assert failed due to requirement 'sizeof...(Ts) == std::tuple_size<std::__1::tuple<unsigned int, unsigned int, unsigned int>>::value'
|
||||
// static_assert(sizeof...(Ts) == std::tuple_size<deduced<Function>>::value);
|
||||
|
||||
class [[eosio::contract]] sync_call_invalid_arg_nums : public eosio::contract{
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t sum(uint32_t a, uint32_t b, uint32_t c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
using sum_func = eosio::call_wrapper<"sum"_i, &sync_call_invalid_arg_nums::sum>;
|
||||
|
||||
// Fewer number of arguments
|
||||
[[eosio::action]]
|
||||
void fewerargs() {
|
||||
sum_func{"callee"_n}(1, 2);
|
||||
}
|
||||
|
||||
// More number of arguments
|
||||
[[eosio::action]]
|
||||
void moreargs() {
|
||||
sum_func{"callee"_n}(1, 2, 3, 4);
|
||||
}
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected" : {
|
||||
"exit-code": 255,
|
||||
"stderr": "error: static_assert failed due to requirement 'sizeof"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#include <eosio/call.hpp>
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
// Test the validation of the types of arguments passed in call_wrapper.
|
||||
// Expected error:
|
||||
// build/bin/../include/eosiolib/contracts/eosio/detail.hpp:60:7: error: static_assert failed due to requirement 'detail::is_same<sync_call_invalid_arg_nums::empty &, unsigned int>::value'
|
||||
// static_assert(detail::is_same<typename convert<T>::type, typename convert<typename std::tuple_element<I, deduced<Function>>::type>::type>::value);
|
||||
class [[eosio::contract]] sync_call_invalid_arg_nums : public eosio::contract{
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t sum(uint32_t a, uint32_t b, uint32_t c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
using sum_func = eosio::call_wrapper<"sum"_i, &sync_call_invalid_arg_nums::sum>;
|
||||
|
||||
struct empty {
|
||||
};
|
||||
|
||||
// Invalid first argument
|
||||
[[eosio::action]]
|
||||
void wrongrettype() {
|
||||
empty bad;
|
||||
sum_func{"callee"_n}(bad, 2, 3);
|
||||
}
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"tests" : [
|
||||
{
|
||||
"compile_flags": [],
|
||||
"expected" : {
|
||||
"exit-code": 255,
|
||||
"stderr": "error: static_assert failed due to requirement 'detail::is_same"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -351,13 +351,6 @@ EOSIO_TEST_BEGIN(datastream_stream_test)
|
||||
ds.seekp(0);
|
||||
ds >> o;
|
||||
CHECK_EQUAL( co, o )
|
||||
// test upacking an empty std::optional to a non-empty works
|
||||
static const optional<char> co1{}; // empty
|
||||
ds.seekp(0);
|
||||
ds << co1;
|
||||
ds.seekp(0);
|
||||
ds >> o; // o had value
|
||||
CHECK_EQUAL( co1, o )
|
||||
|
||||
// ---------
|
||||
// std::pair
|
||||
|
||||
@@ -13,15 +13,8 @@ add_contract(get_code_hash_tests get_code_hash_write get_code_hash_write.cpp)
|
||||
add_contract(get_code_hash_tests get_code_hash_read get_code_hash_read.cpp)
|
||||
add_contract(name_pk_tests name_pk_tests name_pk_tests.cpp)
|
||||
add_contract(test_multi_index test_multi_index multi_index_tests.cpp)
|
||||
add_contract(sync_call_caller sync_call_caller sync_call_caller.cpp)
|
||||
add_contract(sync_call_callee sync_call_callee sync_call_callee.cpp)
|
||||
add_contract(sync_call_not_supported sync_call_not_supported sync_call_not_supported.cpp)
|
||||
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)
|
||||
capi/print.c capi/privileged.c capi/system.c capi/transaction.c)
|
||||
|
||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/simple_wrong.abi ${CMAKE_CURRENT_BINARY_DIR}/simple_wrong.abi COPYONLY )
|
||||
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/capi/capi_tests.abi ${CMAKE_CURRENT_BINARY_DIR}/capi_tests.abi COPYONLY )
|
||||
|
||||
@@ -65,7 +65,8 @@ class [[eosio::contract]] array_tests : public contract {
|
||||
}
|
||||
}
|
||||
|
||||
// test parameter using std::array
|
||||
// test parameter using std::array
|
||||
// not supported so far
|
||||
[[eosio::action]]
|
||||
void testpa(std::array<int,4> input){
|
||||
std::array<int,4> arr = input;
|
||||
@@ -75,7 +76,7 @@ class [[eosio::contract]] array_tests : public contract {
|
||||
eosio::cout << "\n";
|
||||
}
|
||||
|
||||
// test parameter and return value using std::array
|
||||
// test return using std::array, not supported so far
|
||||
[[eosio::action]]
|
||||
// cleos -v push action eosio testre '[[1,2,3,4]]' -p eosio@active
|
||||
std::array<int,4> testre(std::array<int,4> input){
|
||||
@@ -84,15 +85,6 @@ class [[eosio::contract]] array_tests : public contract {
|
||||
return arr;
|
||||
}
|
||||
|
||||
// test return value using std::array
|
||||
[[eosio::action]]
|
||||
// cleos -v push action eosio testre2 '[[1,2,3,4]]' -p eosio@active
|
||||
std::array<int,4> testre2(std::vector<int> input){
|
||||
std::array<int,4> arr;
|
||||
for(size_t i=0; i<4; ++i) arr[i] = input[i+1];
|
||||
return arr;
|
||||
}
|
||||
|
||||
// test return using std::vector
|
||||
[[eosio::action]]
|
||||
// cleos -v push action eosio testrev '[[1,2,3,4]]' -p eosio@active
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <eosio/call.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void test_call( void ) {
|
||||
call(0, 0, NULL, 0);
|
||||
get_call_return_value(NULL, 0);
|
||||
get_call_data(NULL, 0);
|
||||
set_call_return_value(NULL, 0);
|
||||
}
|
||||
@@ -9,7 +9,6 @@ void test_print( void );
|
||||
void test_privileged( void );
|
||||
void test_system( void );
|
||||
void test_transaction( void );
|
||||
void test_call( void );
|
||||
|
||||
void apply( uint64_t a, uint64_t b, uint64_t c ) {
|
||||
if(a != 0xed234054a367196eull) return; // random value that should not be used
|
||||
@@ -22,5 +21,4 @@ void apply( uint64_t a, uint64_t b, uint64_t c ) {
|
||||
test_privileged();
|
||||
test_system();
|
||||
test_transaction();
|
||||
test_call();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <eosio/eosio.hpp>
|
||||
#include <eosio/deferred_transaction.hpp>
|
||||
#include <eosio/bitset.hpp>
|
||||
#include <eosio/transaction.hpp>
|
||||
|
||||
#include "transfer.hpp"
|
||||
|
||||
@@ -52,7 +51,7 @@ class [[eosio::contract]] simple_tests : public contract {
|
||||
|
||||
[[eosio::action]]
|
||||
void testd(name nm) {
|
||||
deferred_transaction t;
|
||||
transaction t;
|
||||
action act;
|
||||
act.account = "other"_n;
|
||||
act.name = "testc"_n;
|
||||
@@ -62,17 +61,6 @@ class [[eosio::contract]] simple_tests : public contract {
|
||||
t.send(nm.value, get_self());
|
||||
}
|
||||
|
||||
[[eosio::action]]
|
||||
eosio::bitset testbs(eosio::bitset b) {
|
||||
for (size_t i=0; i<b.size(); ++i) {
|
||||
if (b[i])
|
||||
b.clear(i);
|
||||
else
|
||||
b.set(i);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
[[eosio::on_notify("eosio.token::transfer")]]
|
||||
void on_transfer(name from, name to, asset quant, std::string memo) {
|
||||
check(get_first_receiver() == "eosio.token"_n, "should be eosio.token");
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "sync_call_addr_book_callee.hpp"
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
using namespace eosio;
|
||||
|
||||
void sync_call_addr_book_callee::upsert(name user, std::string first_name, std::string street) {
|
||||
// Intentionally leave out require_auth(user) to test upsert cannot be called as a read_only
|
||||
// sync call
|
||||
|
||||
address_index addresses(get_first_receiver(), get_first_receiver().value);
|
||||
auto iterator = addresses.find(user.value);
|
||||
if( iterator == addresses.end() )
|
||||
{
|
||||
addresses.emplace(user, [&]( auto& row ) {
|
||||
row.key = user;
|
||||
row.first_name = first_name;
|
||||
row.street = street;
|
||||
});
|
||||
}
|
||||
else {
|
||||
addresses.modify(iterator, user, [&]( auto& row ) {
|
||||
row.key = user;
|
||||
row.first_name = first_name;
|
||||
row.street = street;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
person_info sync_call_addr_book_callee::get(name user) {
|
||||
address_index addresses(get_first_receiver(), get_first_receiver().value);
|
||||
|
||||
auto iterator = addresses.find(user.value);
|
||||
check(iterator != addresses.end(), "Record does not exist");
|
||||
|
||||
return person_info{ .first_name = iterator->first_name,
|
||||
.street = iterator->street };
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#include <eosio/call.hpp>
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
struct person_info {
|
||||
std::string first_name;
|
||||
std::string street;
|
||||
};
|
||||
|
||||
class [[eosio::contract]] sync_call_addr_book_callee : public eosio::contract {
|
||||
public:
|
||||
sync_call_addr_book_callee(eosio::name receiver, eosio::name code, eosio::datastream<const char*> ds): contract(receiver, code, ds) {}
|
||||
|
||||
[[eosio::call]]
|
||||
void upsert(eosio::name user, std::string first_name, std::string street);
|
||||
|
||||
[[eosio::call]]
|
||||
person_info get(eosio::name user);
|
||||
|
||||
using upsert_read_only_func = eosio::call_wrapper<"upsert"_i, &sync_call_addr_book_callee::upsert, eosio::access_mode::read_only>;
|
||||
using upsert_func = eosio::call_wrapper<"upsert"_i, &sync_call_addr_book_callee::upsert>;
|
||||
using get_func = eosio::call_wrapper<"get"_i, &sync_call_addr_book_callee::get>;
|
||||
|
||||
private:
|
||||
struct [[eosio::table]] person {
|
||||
eosio::name key;
|
||||
std::string first_name;
|
||||
std::string street;
|
||||
|
||||
uint64_t primary_key() const { return key.value; }
|
||||
};
|
||||
|
||||
using address_index = eosio::multi_index<"people"_n, person>;
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "sync_call_addr_book_callee.hpp"
|
||||
|
||||
#include <eosio/eosio.hpp>
|
||||
#include <eosio/call.hpp>
|
||||
|
||||
class [[eosio::contract]] sync_call_addr_book_caller : public eosio::contract{
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
// Insert an entry using read_only, which will fail
|
||||
[[eosio::action]]
|
||||
void upsertrdonly(eosio::name user, std::string first_name, std::string street) {
|
||||
sync_call_addr_book_callee::upsert_read_only_func{"callee"_n}(user, first_name, street);
|
||||
}
|
||||
|
||||
// Insert an entry
|
||||
[[eosio::action]]
|
||||
void upsert(eosio::name user, std::string first_name, std::string street) {
|
||||
sync_call_addr_book_callee::upsert_func{"callee"_n}(user, first_name, street);
|
||||
}
|
||||
|
||||
// Read an entry
|
||||
[[eosio::action]]
|
||||
person_info get(eosio::name user) {
|
||||
auto user_info = sync_call_addr_book_callee::get_func{"callee"_n}(user);
|
||||
eosio::check(user_info.first_name == "alice", "first name not alice");
|
||||
eosio::check(user_info.street == "123 Main St.", "street not 123 Main St.");
|
||||
return user_info;
|
||||
}
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "sync_call_callee.hpp"
|
||||
#include <eosio/print.hpp>
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t sync_call_callee::return_ten() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t sync_call_callee::echo_input(uint32_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
void sync_call_callee::void_func() {
|
||||
eosio::print("I am a void function");
|
||||
}
|
||||
|
||||
[[eosio::action, eosio::call]]
|
||||
uint32_t sync_call_callee::sum(uint32_t a, uint32_t b, uint32_t c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
struct1_t sync_call_callee::pass_single_struct(struct1_t s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
[[eosio::call]]
|
||||
struct1_t sync_call_callee::pass_multi_structs(struct1_t s1, int32_t m, struct2_t s2) {
|
||||
return { .a = s1.a * m + s2.c, .b = s1.b * m + s2.d };
|
||||
}
|
||||
|
||||
bool sync_call_callee::issynccall() {
|
||||
return is_sync_call();
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
#include <eosio/call.hpp>
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
struct struct1_t {
|
||||
int64_t a;
|
||||
uint64_t b;
|
||||
};
|
||||
|
||||
struct struct2_t {
|
||||
char a;
|
||||
bool b;
|
||||
int64_t c;
|
||||
uint64_t d;
|
||||
};
|
||||
|
||||
class [[eosio::contract]] sync_call_callee : public eosio::contract{
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t return_ten();
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t echo_input(uint32_t in);
|
||||
|
||||
[[eosio::call]]
|
||||
void void_func();
|
||||
|
||||
[[eosio::action, eosio::call]]
|
||||
uint32_t sum(uint32_t a, uint32_t b, uint32_t c);
|
||||
|
||||
// pass in a struct and return it
|
||||
[[eosio::call]]
|
||||
struct1_t pass_single_struct(struct1_t s);
|
||||
|
||||
// pass in two structs and an integer, multiply each field in the struct by
|
||||
// the integer, add last two fields of the second struct, and return the result
|
||||
[[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>;
|
||||
using sum_func = eosio::call_wrapper<"sum"_i, &sync_call_callee::sum>;
|
||||
using pass_single_struct_func = eosio::call_wrapper<"pass_single_struct"_i, &sync_call_callee::pass_single_struct>;
|
||||
using pass_multi_structs_func = eosio::call_wrapper<"pass_multi_structs"_i, &sync_call_callee::pass_multi_structs>;
|
||||
|
||||
using void_no_op_success_func = eosio::call_wrapper<"void_func"_i, &sync_call_callee::void_func, eosio::access_mode::read_write, eosio::support_mode::no_op>;
|
||||
using sum_no_op_success_func = eosio::call_wrapper<"sum"_i, &sync_call_callee::sum, eosio::access_mode::read_write, eosio::support_mode::no_op>;
|
||||
};
|
||||
@@ -1,182 +0,0 @@
|
||||
#include "sync_call_callee.hpp"
|
||||
#include "sync_call_not_supported.hpp"
|
||||
|
||||
#include <eosio/eosio.hpp>
|
||||
#include <eosio/call.hpp>
|
||||
|
||||
using namespace eosio;
|
||||
|
||||
class [[eosio::contract]] sync_call_caller : public eosio::contract{
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
// Using host function directly
|
||||
[[eosio::action]]
|
||||
void hstretvaltst() {
|
||||
call_data_header header{ .version = 0, .func_name = "return_ten"_i.id };
|
||||
const std::vector<char> data{ eosio::pack(header) };
|
||||
auto expected_size = eosio::call("callee"_n, 0, data.data(), data.size());
|
||||
eosio::check(expected_size >= 0, "call did not return a positive value");
|
||||
|
||||
std::vector<char> return_value;
|
||||
return_value.resize(expected_size);
|
||||
auto actual_size = eosio::get_call_return_value(return_value.data(), return_value.size());
|
||||
eosio::check(actual_size == expected_size, "actual_size not equal to expected_size");
|
||||
eosio::check(eosio::unpack<uint32_t>(return_value) == 10u, "return value not 10"); // return_ten always returns 10
|
||||
}
|
||||
|
||||
// Using call_wrapper
|
||||
[[eosio::action]]
|
||||
void wrpretvaltst() {
|
||||
sync_call_callee::return_ten_func return_ten{ "callee"_n };
|
||||
eosio::check(return_ten() == 10u, "return value not 10");
|
||||
}
|
||||
|
||||
// Using host function directly, testing one parameter passing
|
||||
[[eosio::action]]
|
||||
void hstoneprmtst() {
|
||||
// `echo_input(uint32_t p)` returns p
|
||||
call_data_header header{ .version = 0, .func_name = "echo_input"_i.id };
|
||||
const std::vector<char> data{ eosio::pack(std::make_tuple(header, 5)) };
|
||||
auto expected_size = eosio::call("callee"_n, 0, data.data(), data.size());
|
||||
eosio::check(expected_size >= 0, "call did not return a positive value");
|
||||
|
||||
std::vector<char> return_value;
|
||||
return_value.resize(expected_size);
|
||||
auto actual_size = eosio::get_call_return_value(return_value.data(), return_value.size());
|
||||
eosio::check(actual_size == expected_size, "actual_size not equal to expected_size");
|
||||
eosio::check(eosio::unpack<uint32_t>(return_value) == 5u, "return value not 5"); // echo_input returns back the same value of parameter
|
||||
}
|
||||
|
||||
// Using call_wrapper, testing one parameter passing
|
||||
[[eosio::action]]
|
||||
void wrponeprmtst() {
|
||||
sync_call_callee::echo_input_func echo_input{ "callee"_n };
|
||||
eosio::check(echo_input(5) == 5u, "return value not 5");
|
||||
}
|
||||
|
||||
// Using host function directly, testing multiple parameters passing
|
||||
[[eosio::action]]
|
||||
void hstmulprmtst() {
|
||||
call_data_header header{ .version = 0, .func_name = "sum"_i.id };
|
||||
const std::vector<char> data{ eosio::pack(std::make_tuple(header, 10, 20, 30)) };
|
||||
auto expected_size = eosio::call("callee"_n, 0, data.data(), data.size());
|
||||
eosio::check(expected_size >= 0, "call did not return a positive value");
|
||||
|
||||
std::vector<char> return_value;
|
||||
return_value.resize(expected_size);
|
||||
auto actual_size = eosio::get_call_return_value(return_value.data(), return_value.size());
|
||||
eosio::check(actual_size == expected_size, "actual_size not equal to expected_size");
|
||||
eosio::check(eosio::unpack<uint32_t>(return_value) == 60u, "sum of 10, 20, and 30 not 60"); // sum returns the sum of the 3 arguments
|
||||
}
|
||||
|
||||
// Using call_wrapper, testing multiple parameters passing
|
||||
[[eosio::action]]
|
||||
void wrpmulprmtst() {
|
||||
sync_call_callee::sum_func sum{ "callee"_n };
|
||||
eosio::check(sum(10, 20, 30) == 60u, "sum of 10, 20, and 30 not 60");
|
||||
}
|
||||
|
||||
// Verify single struct parameter passing
|
||||
[[eosio::action]]
|
||||
void structtest() {
|
||||
sync_call_callee::pass_single_struct_func func{ "callee"_n };
|
||||
struct1_t input = { 10, 20 };
|
||||
auto output = func(input); // pass_single_struct_func returns the input as is
|
||||
eosio::check(output.a == input.a, "field a in output is not equal to a in input");
|
||||
eosio::check(output.b == input.b, "field b in output is not equal to b in input");
|
||||
}
|
||||
|
||||
// Verify mix of struct and integer parameters passing
|
||||
[[eosio::action]]
|
||||
void structinttst() {
|
||||
sync_call_callee::pass_multi_structs_func func{ "callee"_n };
|
||||
struct1_t input1 = { 10, 20 };
|
||||
struct2_t input2 = { 'a', true, 50, 100 };
|
||||
int32_t m = 2;
|
||||
|
||||
// pass_multi_structs_func multiply each field of input1 by m,
|
||||
// add last two fields of input2, and return a struct1_t
|
||||
auto output = func(input1, m, input2);
|
||||
eosio::check(output.a == m * input1.a + input2.c, "field a of output is not correct");
|
||||
eosio::check(output.b == m * input1.b + input2.d, "field b of output is not correct");
|
||||
}
|
||||
|
||||
[[eosio::action]]
|
||||
void hstvodfuntst() {
|
||||
call_data_header header{ .version = 0, .func_name = "void_func"_i.id };
|
||||
const std::vector<char> data{ eosio::pack(header) };
|
||||
auto expected_size = eosio::call("callee"_n, 0, data.data(), data.size());
|
||||
eosio::check(expected_size == 0, "call did not return 0"); // void function. return value size should be 0
|
||||
}
|
||||
|
||||
[[eosio::action]]
|
||||
void wrpvodfuntst() {
|
||||
sync_call_callee::void_func_func void_func{ "callee"_n };
|
||||
void_func();
|
||||
}
|
||||
|
||||
// Verify void call. void_func uses default support_mode::abort
|
||||
[[eosio::action]]
|
||||
void voidfncabort() {
|
||||
sync_call_not_supported::void_func void_func_abort{ "callee"_n };
|
||||
void_func_abort(); // Will throw. Tester will verify that.
|
||||
}
|
||||
|
||||
// void_func uses support_mode::no_op
|
||||
[[eosio::action]]
|
||||
void voidfncnoop() {
|
||||
sync_call_not_supported::void_no_op_func void_func_no_op{ "callee"_n };
|
||||
check(void_func_no_op() == std::nullopt, "void_func_io_op did not return std::nullopt");
|
||||
}
|
||||
|
||||
// verify non-void call. int_func uses default support_mode::abort
|
||||
[[eosio::action]]
|
||||
void intfuncabort() {
|
||||
sync_call_not_supported::int_func int_func_abort{ "callee"_n };
|
||||
int_func_abort(); // Will throw. Tester will verify that.
|
||||
}
|
||||
|
||||
// int_func uses support_mode::no_opabort
|
||||
[[eosio::action]]
|
||||
void intfuncnoop() {
|
||||
sync_call_not_supported::int_no_op_func int_func_no_op{ "callee"_n };
|
||||
check(int_func_no_op() == std::nullopt, "void_func_io_op did not return std::nullopt");
|
||||
}
|
||||
|
||||
// void_io_op_success_func uses support_mode::no_op
|
||||
[[eosio::action]]
|
||||
void voidnoopsucc() {
|
||||
sync_call_callee::void_no_op_success_func f{ "callee"_n };
|
||||
check(f().has_value(), "void_io_op_success_func did not return a value");
|
||||
}
|
||||
|
||||
// void_io_op_success_func uses support_mode::no_op
|
||||
[[eosio::action]]
|
||||
void sumnoopsucc() {
|
||||
sync_call_callee::sum_no_op_success_func f{ "callee"_n };
|
||||
check(*f(7, 8, 9) == 24, "sum_io_op_success_func did not return a value");
|
||||
}
|
||||
|
||||
[[eosio::action]]
|
||||
void hdrvaltest() {
|
||||
// Verify function name validation works
|
||||
call_data_header unkwn_func_header{ .version = 0, .func_name = "unknwnfunc"_i.id };
|
||||
const std::vector<char> unkwn_func_data{ eosio::pack(unkwn_func_header) }; // unknwnfunc is not in "callee"_i contract
|
||||
auto status = eosio::call("callee"_n, 0, unkwn_func_data.data(), unkwn_func_data.size());
|
||||
eosio::check(status == -10001, "call did not return -10001 for unknown function");
|
||||
|
||||
// Verify version validation works
|
||||
call_data_header bad_version_header{ .version = 1, .func_name = "sum"_i.id }; // version 1 is not supported
|
||||
const std::vector<char> bad_version_data{ eosio::pack(bad_version_header) };
|
||||
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();
|
||||
}
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "sync_call_not_supported.hpp"
|
||||
|
||||
[[eosio::action]]
|
||||
void sync_call_not_supported::voidfunc() {
|
||||
}
|
||||
|
||||
[[eosio::action]]
|
||||
int sync_call_not_supported::intfunc() {
|
||||
return 1;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#include <eosio/eosio.hpp>
|
||||
#include <eosio/call.hpp>
|
||||
|
||||
// Because this contract does not tag any functions by `call` attribute,
|
||||
// `sync_call` entry point is not generated.
|
||||
// Any sync calls to this contract will return a status indicating
|
||||
// sync calls are not supported by the receiver.
|
||||
class [[eosio::contract]] sync_call_not_supported : public eosio::contract{
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
[[eosio::action]]
|
||||
void voidfunc();
|
||||
|
||||
[[eosio::action]]
|
||||
int intfunc();
|
||||
|
||||
using void_func = eosio::call_wrapper<"voidfunc"_i, &sync_call_not_supported::voidfunc>; // default behavior: abort when called
|
||||
using void_no_op_func = eosio::call_wrapper<"voidfunc"_i, &sync_call_not_supported::voidfunc, eosio::access_mode::read_write, eosio::support_mode::no_op>; // no op when called
|
||||
|
||||
using int_func = eosio::call_wrapper<"intfunc"_i, &sync_call_not_supported::intfunc>; // default behavior: abort when called
|
||||
using int_no_op_func = eosio::call_wrapper<"intfunc"_i, &sync_call_not_supported::intfunc, eosio::access_mode::read_write, eosio::support_mode::no_op>; // no op when called
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
#include <eosio/call.hpp>
|
||||
#include <eosio/print.hpp>
|
||||
#include <eosio/eosio.hpp>
|
||||
|
||||
class [[eosio::contract]] sync_call_single_func : public eosio::contract{
|
||||
public:
|
||||
using contract::contract;
|
||||
|
||||
[[eosio::call]]
|
||||
uint32_t return_ten() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
// Do NOT add any more functions tagged as `eosio::call` in this contract.
|
||||
// It is used to test a contract having only one function tagged as `eosio::call`.
|
||||
};
|
||||
@@ -64,15 +64,7 @@ void generate(const std::vector<std::string>& base_options, std::string input, s
|
||||
|
||||
abigen::get().set_contract_name(contract_name);
|
||||
abigen::get().set_resource_dirs(resource_paths);
|
||||
|
||||
// We used to accept a command line option `--abi-version` which allowed a user to force an abi version
|
||||
// to be generated.
|
||||
// Since https://github.com/AntelopeIO/cdt/pull/360, this command line flag is ignored, and the abi
|
||||
// version is generated according to the features used in the contract being compiled, starting from a
|
||||
// base `1.2` version and up.
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
// abigen::get().set_abi_version(std::get<0>(abi_version), std::get<1>(abi_version));
|
||||
|
||||
abigen::get().set_abi_version(std::get<0>(abi_version), std::get<1>(abi_version));
|
||||
abigen::get().set_suppress_ricardian_warning(suppress_ricardian_warning);
|
||||
codegen::get().set_contract_name(contract_name);
|
||||
codegen::get().set_warn_action_read_only(warn_action_read_only);
|
||||
|
||||
@@ -509,7 +509,6 @@ static void GetLdDefaults(std::vector<std::string>& ldopts) {
|
||||
ldopts.insert(ldopts.end(), { "-e", "apply" });
|
||||
ldopts.insert(ldopts.end(), { "--only-export", "apply:function" });
|
||||
ldopts.insert(ldopts.end(), { "--only-export", "*:memory" });
|
||||
ldopts.insert(ldopts.end(), { "--only-export", "sync_call:function" });
|
||||
}
|
||||
ldopts.emplace_back("-lc++");
|
||||
ldopts.emplace_back("-lc");
|
||||
@@ -948,13 +947,9 @@ static Options CreateOptions(bool add_defaults=true) {
|
||||
|
||||
#endif
|
||||
|
||||
// 1.2 -- action results
|
||||
// 1.3 -- sync calls, bitset type, action and sync call names in C++
|
||||
// identifier format
|
||||
|
||||
/* TODO add some way of defaulting these to the current highest version */
|
||||
int abi_version_major = 1;
|
||||
int abi_version_minor = 3;
|
||||
int abi_version_minor = 2;
|
||||
|
||||
if (!abi_version_opt.empty()) {
|
||||
abi_version_major = std::stoi(abi_version_opt);
|
||||
|
||||
@@ -30,14 +30,6 @@ struct abi_action {
|
||||
bool operator<(const abi_action& s) const { return name < s.name; }
|
||||
};
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
struct abi_table {
|
||||
std::string name;
|
||||
std::string type;
|
||||
@@ -69,43 +61,19 @@ struct abi_action_result {
|
||||
bool operator<(const abi_action_result& ar) const { return name < ar.name; }
|
||||
};
|
||||
|
||||
struct version_t {
|
||||
uint8_t major = 0;
|
||||
uint8_t minor = 0;
|
||||
|
||||
version_t(uint8_t major, uint8_t minor)
|
||||
: major(major)
|
||||
, minor(minor) {}
|
||||
|
||||
friend bool operator<(const version_t& a, const version_t& b) {
|
||||
return std::tie(a.major, a.minor) < std::tie(b.major, b.minor);
|
||||
}
|
||||
friend bool operator==(const version_t& a, const version_t& b) {
|
||||
return std::tie(a.major, a.minor) == std::tie(b.major, b.minor);
|
||||
}
|
||||
|
||||
void set_min(version_t o) {
|
||||
if (*this < o)
|
||||
*this = o;
|
||||
}
|
||||
|
||||
std::string str() const { return std::to_string(major) + "." + std::to_string(minor); }
|
||||
};
|
||||
|
||||
/// From eosio libraries/chain/include/eosio/chain/abi_def.hpp
|
||||
struct abi {
|
||||
version_t version{1,2}; // base version is 1.2, add features (bitset, sync calls) push to 1.3
|
||||
int version_major = 1;
|
||||
int version_minor = 1;
|
||||
std::string version_string()const { return std::string("eosio::abi/")+std::to_string(version_major)+"."+std::to_string(version_minor); }
|
||||
std::set<abi_struct> structs;
|
||||
std::set<abi_typedef> typedefs;
|
||||
std::set<abi_action> actions;
|
||||
std::set<abi_call> calls;
|
||||
std::set<abi_table> tables;
|
||||
std::set<abi_variant> variants;
|
||||
std::vector<abi_ricardian_clause_pair> ricardian_clauses;
|
||||
std::vector<abi_error_message> error_messages;
|
||||
std::set<abi_action_result> action_results;
|
||||
|
||||
std::string version_string() const { return std::string("eosio::abi/")+version.str(); }
|
||||
};
|
||||
|
||||
inline void dump( const abi& abi ) {
|
||||
|
||||
+13
-104
@@ -26,7 +26,6 @@
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <jsoncons/json.hpp>
|
||||
@@ -37,11 +36,7 @@ using namespace eosio::cdt;
|
||||
using jsoncons::json;
|
||||
using jsoncons::ojson;
|
||||
|
||||
namespace eosio::cdt {
|
||||
|
||||
const version_t bitset_min_version{1,3};
|
||||
const version_t sync_calls_min_version{1,3};
|
||||
|
||||
namespace eosio { namespace cdt {
|
||||
class abigen : public generation_utils {
|
||||
std::set<std::string> checked_actions;
|
||||
public:
|
||||
@@ -53,7 +48,8 @@ namespace eosio::cdt {
|
||||
}
|
||||
|
||||
void set_abi_version(int major, int minor) {
|
||||
_abi.version = version_t{static_cast<uint8_t>(major), static_cast<uint8_t>(minor)};
|
||||
_abi.version_major = major;
|
||||
_abi.version_minor = minor;
|
||||
}
|
||||
|
||||
void add_typedef( const clang::QualType& t ) {
|
||||
@@ -135,50 +131,6 @@ namespace eosio::cdt {
|
||||
}
|
||||
}
|
||||
|
||||
void add_call( const clang::CXXRecordDecl* decl ) {
|
||||
abi_call ret;
|
||||
auto call_name = decl->getEosioCallAttr()->getName();
|
||||
|
||||
if (call_name.empty()) {
|
||||
validate_name(decl->getName().str(), [&](auto s) { CDT_ERROR("abigen_error", decl->getLocation(), s); });
|
||||
ret.name = decl->getName().str();
|
||||
}
|
||||
else {
|
||||
validate_name( call_name.str(), [&](auto s) { CDT_ERROR("abigen_error", decl->getLocation(), s); });
|
||||
ret.name = call_name.str();
|
||||
}
|
||||
ret.type = decl->getName().str();
|
||||
ret.id = to_hash_id(ret.name);
|
||||
_abi.calls.insert(ret);
|
||||
|
||||
_abi.version.set_min(sync_calls_min_version);
|
||||
}
|
||||
|
||||
void add_call( const clang::CXXMethodDecl* decl ) {
|
||||
abi_call ret;
|
||||
|
||||
auto call_name = decl->getEosioCallAttr()->getName();
|
||||
|
||||
if (call_name.empty()) {
|
||||
validate_hash_id( decl->getNameAsString(), [&](auto s) { CDT_ERROR("abigen_error", decl->getLocation(), s); } );
|
||||
ret.name = decl->getNameAsString();
|
||||
}
|
||||
else {
|
||||
validate_hash_id( call_name.str(), [&](auto s) { CDT_ERROR("abigen_error", decl->getLocation(), s); } );
|
||||
ret.name = call_name.str();
|
||||
}
|
||||
ret.type = decl->getNameAsString();
|
||||
ret.id = to_hash_id(ret.name);
|
||||
auto result_type = translate_type(decl->getReturnType());
|
||||
if (result_type != "void") {
|
||||
add_type(decl->getReturnType());
|
||||
ret.result_type = result_type;
|
||||
}
|
||||
_abi.calls.insert(ret);
|
||||
|
||||
_abi.version.set_min(sync_calls_min_version);
|
||||
}
|
||||
|
||||
void add_tuple(const clang::QualType& type) {
|
||||
auto pt = llvm::dyn_cast<clang::ElaboratedType>(type.getTypePtr());
|
||||
auto tst = llvm::dyn_cast<clang::TemplateSpecializationType>((pt) ? pt->desugar().getTypePtr() : type.getTypePtr());
|
||||
@@ -199,6 +151,7 @@ namespace eosio::cdt {
|
||||
void add_pair(const clang::QualType& type) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
clang::QualType ftype = std::get<clang::QualType>(get_template_argument(type, i));
|
||||
std::string ty = translate_type(ftype);
|
||||
add_type(ftype);
|
||||
}
|
||||
abi_struct pair;
|
||||
@@ -263,7 +216,6 @@ namespace eosio::cdt {
|
||||
void add_struct( const clang::CXXMethodDecl* decl ) {
|
||||
abi_struct new_struct;
|
||||
new_struct.name = decl->getNameAsString();
|
||||
|
||||
for (auto param : decl->parameters() ) {
|
||||
auto param_type = param->getType().getNonReferenceType().getUnqualifiedType();
|
||||
new_struct.fields.push_back({param->getNameAsString(), get_type(param_type)});
|
||||
@@ -528,8 +480,7 @@ namespace eosio::cdt {
|
||||
add_explicit_nested_type(t.getNonReferenceType());
|
||||
return;
|
||||
}
|
||||
auto type_str = translate_type(type);
|
||||
if (!is_builtin_type(type_str)) {
|
||||
if (!is_builtin_type(translate_type(type))) {
|
||||
if (is_aliasing(type)) {
|
||||
add_typedef(type);
|
||||
}
|
||||
@@ -551,13 +502,6 @@ namespace eosio::cdt {
|
||||
}
|
||||
else if (type.getTypePtr()->isRecordType())
|
||||
add_struct(type.getTypePtr()->getAsCXXRecordDecl());
|
||||
} else {
|
||||
static std::unordered_map<std::string, version_t> versioned_types {
|
||||
{ "bitset", bitset_min_version }
|
||||
};
|
||||
|
||||
if (auto it = versioned_types.find(type_str); it != versioned_types.end())
|
||||
_abi.version.set_min(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,15 +551,6 @@ namespace eosio::cdt {
|
||||
return o;
|
||||
}
|
||||
|
||||
ojson call_to_json( const abi_call& c ) {
|
||||
ojson o;
|
||||
o["name"] = c.name;
|
||||
o["type"] = c.type;
|
||||
o["id"] = c.id;
|
||||
o["result_type"] = c.result_type;
|
||||
return o;
|
||||
}
|
||||
|
||||
ojson clause_to_json( const abi_ricardian_clause_pair& clause ) {
|
||||
ojson o;
|
||||
o["id"] = clause.id;
|
||||
@@ -658,15 +593,13 @@ namespace eosio::cdt {
|
||||
set_of_tables.insert(t);
|
||||
}
|
||||
|
||||
return _abi.structs.empty() && _abi.typedefs.empty() && _abi.actions.empty() && _abi.calls.empty() && set_of_tables.empty() && _abi.ricardian_clauses.empty() && _abi.variants.empty();
|
||||
return _abi.structs.empty() && _abi.typedefs.empty() && _abi.actions.empty() && set_of_tables.empty() && _abi.ricardian_clauses.empty() && _abi.variants.empty();
|
||||
}
|
||||
|
||||
ojson to_json() {
|
||||
ojson o;
|
||||
o["____comment"] = generate_json_comment();
|
||||
|
||||
o["version"] = _abi.version_string();
|
||||
|
||||
o["structs"] = ojson::array();
|
||||
auto remove_suffix = [&]( std::string name ) {
|
||||
int i = name.length()-1;
|
||||
@@ -725,10 +658,6 @@ namespace eosio::cdt {
|
||||
if (as.name == _translate_type(a.type))
|
||||
return true;
|
||||
}
|
||||
for ( auto a : _abi.calls ) {
|
||||
if (as.name == _translate_type(a.type))
|
||||
return true;
|
||||
}
|
||||
for( auto t : set_of_tables ) {
|
||||
if (as.name == _translate_type(t.type))
|
||||
return true;
|
||||
@@ -766,9 +695,6 @@ namespace eosio::cdt {
|
||||
for ( auto a : _abi.actions )
|
||||
if ( a.type == td.new_type_name )
|
||||
return true;
|
||||
for ( auto a : _abi.calls )
|
||||
if ( a.type == td.new_type_name )
|
||||
return true;
|
||||
for ( auto _td : _abi.typedefs )
|
||||
if ( remove_suffix(_td.type) == td.new_type_name )
|
||||
return true;
|
||||
@@ -793,12 +719,6 @@ namespace eosio::cdt {
|
||||
for ( auto a : _abi.actions ) {
|
||||
o["actions"].push_back(action_to_json( a ));
|
||||
}
|
||||
if (!_abi.calls.empty()) { // add calls section only when sync calls are used
|
||||
o["calls"] = ojson::array();
|
||||
for ( auto a : _abi.calls ) {
|
||||
o["calls"].push_back(call_to_json( a ));
|
||||
}
|
||||
}
|
||||
o["tables"] = ojson::array();
|
||||
for ( auto t : set_of_tables ) {
|
||||
o["tables"].push_back(table_to_json( t ));
|
||||
@@ -807,17 +727,16 @@ namespace eosio::cdt {
|
||||
for ( auto rc : _abi.ricardian_clauses ) {
|
||||
o["ricardian_clauses"].push_back(clause_to_json( rc ));
|
||||
}
|
||||
|
||||
o["variants"] = ojson::array();
|
||||
for ( auto v : _abi.variants ) {
|
||||
o["variants"].push_back(variant_to_json( v ));
|
||||
}
|
||||
|
||||
o["abi_extensions"] = ojson::array();
|
||||
|
||||
o["action_results"] = ojson::array();
|
||||
for ( auto ar : _abi.action_results ) {
|
||||
o["action_results"].push_back(action_result_to_json( ar ));
|
||||
if (_abi.version_major == 1 && _abi.version_minor >= 2) {
|
||||
o["action_results"] = ojson::array();
|
||||
for ( auto ar : _abi.action_results ) {
|
||||
o["action_results"].push_back(action_result_to_json( ar ));
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
@@ -859,14 +778,6 @@ namespace eosio::cdt {
|
||||
ag.add_type( param->getType() );
|
||||
}
|
||||
}
|
||||
|
||||
if (decl->isEosioCall() && ag.is_eosio_contract(decl, ag.get_contract_name())) {
|
||||
ag.add_struct(decl);
|
||||
ag.add_call(decl);
|
||||
for (auto param : decl->parameters()) {
|
||||
ag.add_type( param->getType() );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl* decl) {
|
||||
@@ -875,12 +786,10 @@ namespace eosio::cdt {
|
||||
ag.add_contracts(ag.parse_contracts());
|
||||
has_added_clauses = true;
|
||||
}
|
||||
if ((decl->isEosioAction() || decl->isEosioCall() || decl->isEosioTable()) && ag.is_eosio_contract(decl, ag.get_contract_name())) {
|
||||
if ((decl->isEosioAction() || decl->isEosioTable()) && ag.is_eosio_contract(decl, ag.get_contract_name())) {
|
||||
ag.add_struct(decl);
|
||||
if (decl->isEosioAction())
|
||||
ag.add_action(decl);
|
||||
if (decl->isEosioCall())
|
||||
ag.add_call(decl);
|
||||
if (decl->isEosioTable())
|
||||
ag.add_table(decl);
|
||||
for (auto field : decl->fields()) {
|
||||
@@ -1013,4 +922,4 @@ namespace eosio::cdt {
|
||||
return std::make_unique<eosio_abigen_consumer>(&CI, file);
|
||||
}
|
||||
};
|
||||
} // ns eosio::cdt
|
||||
}} // ns eosio::cdt
|
||||
|
||||
@@ -37,10 +37,6 @@ class ABIMerger {
|
||||
if (std::stod(vers.substr(vers.size()-3))*10 >= 12) {
|
||||
ret["action_results"] = merge_action_results(other);
|
||||
}
|
||||
auto it = abi.find("calls");
|
||||
if (it != abi.object_range().end()) { // merge `calls` section only when it exists
|
||||
ret["calls"] = merge_calls(other);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
private:
|
||||
@@ -86,10 +82,6 @@ class ABIMerger {
|
||||
a["type"] == b["type"];
|
||||
}
|
||||
|
||||
static bool call_is_same(ojson a, ojson b) {
|
||||
return a["name"] == b["name"] &&
|
||||
a["type"] == b["type"];
|
||||
}
|
||||
|
||||
static bool variant_is_same(ojson a, ojson b) {
|
||||
for (auto tya : a["types"].array_range()) {
|
||||
@@ -189,12 +181,6 @@ class ABIMerger {
|
||||
return acts;
|
||||
}
|
||||
|
||||
ojson merge_calls(ojson b) {
|
||||
ojson calls = ojson::array();
|
||||
add_object_to_array(calls, abi, b, "calls", "name", call_is_same);
|
||||
return calls;
|
||||
}
|
||||
|
||||
ojson merge_tables(ojson b) {
|
||||
ojson tabs = ojson::array();
|
||||
add_object_to_array(tabs, abi, b, "tables", "name", table_is_same);
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace eosio { namespace cdt {
|
||||
std::set<std::string> datastream_uses;
|
||||
std::set<std::string> actions;
|
||||
std::set<std::string> notify_handlers;
|
||||
std::set<std::string> calls;
|
||||
ASTContext *ast_context;
|
||||
std::map<std::string, CXXMethodDecl*> cxx_methods;
|
||||
std::map<std::string, CXXRecordDecl*> cxx_records;
|
||||
@@ -146,28 +145,11 @@ 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";
|
||||
@@ -207,22 +189,8 @@ 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 << "obj." << decl->getNameAsString() << "(";
|
||||
ss << decl->getParent()->getQualifiedNameAsString() << "{eosio::name{r},eosio::name{c},ds}." << decl->getNameAsString() << "(";
|
||||
for (int i=0; i < decl->parameters().size(); i++) {
|
||||
ss << "arg" << i;
|
||||
if (i < decl->parameters().size()-1)
|
||||
@@ -253,116 +221,10 @@ namespace eosio { namespace cdt {
|
||||
create_dispatch("eosio_wasm_notify", "__eosio_notify_", func, decl);
|
||||
}
|
||||
|
||||
// Generate sync call dispatcher
|
||||
void create_call_dispatch(CXXMethodDecl* decl) {
|
||||
const std::string attr = "eosio_wasm_call";
|
||||
const std::string func_name = "__eosio_call_";
|
||||
const std::string call_name = generation_utils::get_call_name(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/call.hpp>\n";
|
||||
ss << "extern \"C\" {\n";
|
||||
const auto& return_ty = decl->getReturnType().getAsString();
|
||||
if (return_ty != "void") {
|
||||
ss << "__attribute__((eosio_wasm_import))\n";
|
||||
ss << "void set_call_return_value(void*, size_t);\n";
|
||||
}
|
||||
ss << "__attribute__((weak, " << attr << "(\"";
|
||||
ss << call_name;
|
||||
ss << ":";
|
||||
ss << func_name << nm;
|
||||
ss << "\"))) void " << func_name << nm << "(unsigned long long sender, unsigned long long receiver, size_t data_size, void* data) {\n";
|
||||
ss << "eosio::datastream<const char*> ds{(char*)data, data_size};\n";
|
||||
ss << "eosio::call_data_header header; ds >> header;\n"; // skip header
|
||||
int i=0;
|
||||
for (auto param : decl->parameters()) {
|
||||
clang::LangOptions lang_opts;
|
||||
lang_opts.CPlusPlus = true;
|
||||
lang_opts.Bool = true;
|
||||
clang::PrintingPolicy policy(lang_opts);
|
||||
auto qt = param->getOriginalType().getNonReferenceType();
|
||||
qt.removeLocalConst();
|
||||
qt.removeLocalVolatile();
|
||||
qt.removeLocalRestrict();
|
||||
std::string tn = clang::TypeName::getFullyQualifiedName(qt, *(cg.ast_context), policy);
|
||||
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 << "obj." << decl->getNameAsString() << "(";
|
||||
for (int i=0; i < decl->parameters().size(); i++) {
|
||||
ss << "arg" << i;
|
||||
if (i < decl->parameters().size()-1)
|
||||
ss << ", ";
|
||||
}
|
||||
ss << ");\n";
|
||||
};
|
||||
if (return_ty != "void") {
|
||||
ss << "const auto& result = ";
|
||||
}
|
||||
call_function();
|
||||
if (return_ty != "void") {
|
||||
ss << "const auto& packed_result = eosio::pack(result);\n";
|
||||
ss << "::set_call_return_value((void*)packed_result.data(), packed_result.size());\n";
|
||||
}
|
||||
ss << "}}\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Generate get_sync_call_data_version which returns the version of call data.
|
||||
// In version 0, call data is packed as header + arguments, where
|
||||
// header is `struct header { uint32_t version; uint64_t func_name }`
|
||||
static void create_get_sync_call_data_header(std::stringstream& ss) {
|
||||
ss << "\n\n#include <eosio/datastream.hpp>\n";
|
||||
ss << "#include <eosio/call.hpp>\n";
|
||||
ss << "extern \"C\" {\n";
|
||||
ss << "__attribute__((weak)) void* __eos_get_sync_call_data_header_(void* data) {\n";
|
||||
ss << "size_t size = sizeof(eosio::call_data_header);\n";
|
||||
ss << "eosio::datastream<const char*> ds{(char*)data, size};\n";
|
||||
ss << "eosio::call_data_header header; ds >> header;\n";
|
||||
ss << "void* ptr = malloc(size);\n";
|
||||
ss << "memcpy(ptr, &header, size);\n";
|
||||
ss << "return ptr;\n";
|
||||
ss << "}}\n";
|
||||
}
|
||||
|
||||
// Generate get_sync_call_data which returns call data which consists of
|
||||
// header and arguments
|
||||
static void create_get_sync_call_data(std::stringstream& ss) {
|
||||
ss << "\n\n#include <eosio/datastream.hpp>\n";
|
||||
ss << "#include <eosio/name.hpp>\n";
|
||||
ss << "extern \"C\" {\n";
|
||||
ss << "__attribute__((eosio_wasm_import)) uint32_t get_call_data(void*, uint32_t);\n";
|
||||
ss << "__attribute__((weak)) void* __eos_get_sync_call_data_(unsigned long size) {\n";
|
||||
ss << "void* data = malloc(size);\n"; // store data in linear memory
|
||||
ss << "::get_call_data(data, size);\n";
|
||||
ss << "return data;\n";
|
||||
ss << "}}\n";
|
||||
}
|
||||
|
||||
virtual bool VisitCXXMethodDecl(CXXMethodDecl* decl) {
|
||||
std::string name = decl->getNameAsString();
|
||||
static std::set<std::string> _action_set; //used for validations
|
||||
static std::set<std::string> _notify_set; //used for validations
|
||||
static std::set<std::string> _call_set; //used for validations
|
||||
|
||||
if (decl->isEosioAction()) {
|
||||
name = generation_utils::get_action_name(decl);
|
||||
validate_name(name, [&](auto s) {
|
||||
@@ -378,8 +240,8 @@ namespace eosio { namespace cdt {
|
||||
std::string full_action_name = decl->getNameAsString() + ((decl->getParent()) ? decl->getParent()->getNameAsString() : "");
|
||||
if (cg.actions.count(full_action_name) == 0) {
|
||||
create_action_dispatch(decl);
|
||||
cg.actions.insert(full_action_name); // insert the method action, so we don't create the dispatcher twice
|
||||
}
|
||||
cg.actions.insert(full_action_name); // insert the method action, so we don't create the dispatcher twice
|
||||
|
||||
if (decl->isEosioReadOnly()) {
|
||||
read_only_actions.insert(decl);
|
||||
@@ -407,49 +269,8 @@ namespace eosio { namespace cdt {
|
||||
std::string full_notify_name = decl->getNameAsString() + ((decl->getParent()) ? decl->getParent()->getNameAsString() : "");
|
||||
if (cg.notify_handlers.count(full_notify_name) == 0) {
|
||||
create_notify_dispatch(decl);
|
||||
cg.notify_handlers.insert(full_notify_name); // insert the method action, so we don't create the dispatcher twice
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
validate_hash_id(name, [&](auto s) {
|
||||
CDT_ERROR("codegen_error", decl->getLocation(), std::string("call name (")+s+") is not a valid C++ identifier");
|
||||
});
|
||||
|
||||
// Make sure there are no conflicts of IDs
|
||||
auto id = to_hash_id(name);
|
||||
auto it = _call_id_map.find(id);
|
||||
if (it != _call_id_map.end()) {
|
||||
if (name != it->second) {
|
||||
CDT_ERROR("codegen_error",
|
||||
decl->getLocation(),
|
||||
std::string("call name (") + name + ")'s ID " + std::to_string(id) + " conflicts with a previous call name: " + it->second + ". Please choose another name");
|
||||
}
|
||||
} else {
|
||||
_call_id_map.insert({id, name});
|
||||
}
|
||||
|
||||
// Genereate create_get_sync_call_data and create_get_sync_call_data_header only once
|
||||
if (_call_set.empty()) {
|
||||
create_get_sync_call_data(ss);
|
||||
create_get_sync_call_data_header(ss);
|
||||
}
|
||||
|
||||
if (!_call_set.count(name))
|
||||
_call_set.insert(name);
|
||||
else {
|
||||
auto itr = _call_set.find(name);
|
||||
CDT_CHECK_ERROR(*itr == name, "codegen_error", decl->getLocation(), "call declaration doesn't match previous declaration");
|
||||
}
|
||||
std::string full_call_name = decl->getNameAsString() + ((decl->getParent()) ? decl->getParent()->getNameAsString() : "");
|
||||
if (cg.calls.count(full_call_name) == 0) {
|
||||
create_call_dispatch(decl);
|
||||
cg.calls.insert(full_call_name); // insert the sync call method name, so we don't create the dispatcher twice
|
||||
}
|
||||
cg.notify_handlers.insert(full_notify_name); // insert the method action, so we don't create the dispatcher twice
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -644,7 +465,7 @@ namespace eosio { namespace cdt {
|
||||
for (auto nd : visitor->notify_decls)
|
||||
visitor->create_notify_dispatch(nd);
|
||||
|
||||
if (cg.actions.size() < 1 && cg.notify_handlers.size() < 1 && cg.calls.size() < 1) {
|
||||
if (cg.actions.size() < 1 && cg.notify_handlers.size() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <regex>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
@@ -192,18 +191,6 @@ struct generation_utils {
|
||||
return tmp;
|
||||
return decl->getNameAsString();
|
||||
}
|
||||
static inline std::string get_call_name( const clang::CXXMethodDecl* decl ) {
|
||||
auto tmp = decl->getEosioCallAttr()->getName();
|
||||
if (!tmp.empty())
|
||||
return tmp;
|
||||
return decl->getNameAsString();
|
||||
}
|
||||
static inline std::string get_call_name( const clang::CXXRecordDecl* decl ) {
|
||||
auto tmp = decl->getEosioCallAttr()->getName();
|
||||
if (!tmp.empty())
|
||||
return tmp;
|
||||
return decl->getName();
|
||||
}
|
||||
static inline std::string get_notify_pair( const clang::CXXMethodDecl* decl ) {
|
||||
std::string notify_pair = "";
|
||||
auto tmp = decl->getEosioNotifyAttr()->getName();
|
||||
@@ -433,7 +420,7 @@ struct generation_utils {
|
||||
}
|
||||
|
||||
std::string _translate_type( const std::string& t ) {
|
||||
static std::unordered_map<std::string, std::string> translation_table =
|
||||
static std::map<std::string, std::string> translation_table =
|
||||
{
|
||||
{"unsigned __int128", "uint128"},
|
||||
{"__int128", "int128"},
|
||||
@@ -485,10 +472,11 @@ struct generation_utils {
|
||||
{"fixed_bytes_64", "checksum512"}
|
||||
};
|
||||
|
||||
if (auto it = translation_table.find(t); it != translation_table.end())
|
||||
return it->second;
|
||||
auto ret = translation_table[t];
|
||||
|
||||
return t;
|
||||
if (ret == "")
|
||||
return t;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string replace_in_name( std::string name ) {
|
||||
@@ -776,8 +764,7 @@ struct generation_utils {
|
||||
"symbol",
|
||||
"symbol_code",
|
||||
"asset",
|
||||
"extended_asset",
|
||||
"bitset"
|
||||
"extended_asset"
|
||||
};
|
||||
return builtins.count(_translate_type(t)) >= 1;
|
||||
}
|
||||
|
||||
@@ -97,39 +97,6 @@ std::string name_to_string( uint64_t nm ) {
|
||||
return str;
|
||||
}
|
||||
|
||||
static constexpr uint32_t max_string_length_for_hash_id = 128;
|
||||
|
||||
// Validate the input `str` is a valid C/C++ identifier
|
||||
template <typename Lambda>
|
||||
void validate_hash_id( const std::string& str, Lambda&& error_handler ) {
|
||||
if (str.empty()) {
|
||||
return error_handler("string is empty");
|
||||
}
|
||||
|
||||
const auto len = str.length();
|
||||
if ( len > max_string_length_for_hash_id ) {
|
||||
return error_handler(std::string("string {") + str + "} is more than " + std::to_string(max_string_length_for_hash_id) + " characters long");
|
||||
}
|
||||
|
||||
if (!(std::isalpha(str[0]) || str[0] == '_')) {
|
||||
return error_handler(std::string("string {") + str + "} does not start with letter or underscore");
|
||||
}
|
||||
|
||||
for (char ch : str.substr(1)) {
|
||||
if (!(std::isalnum(static_cast<unsigned char>(ch)) || ch == '_')) {
|
||||
return error_handler(std::string("string {") + str + "} has a character {" + ch + "} which is not a number, letter, or _");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t to_hash_id(std::string str) {
|
||||
uint64_t hash = 5381;
|
||||
for (char c : str) {
|
||||
hash = ((hash << 5) + hash) + static_cast<uint8_t>(c); // hash * 33 + c
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
struct environment {
|
||||
static llvm::ArrayRef<llvm::StringRef> get() {
|
||||
static std::vector<llvm::StringRef> env_table;
|
||||
|
||||
Reference in New Issue
Block a user