提交 59b5c209 编写于 作者: B Bart Wyatt

changed API to be inline messages and deferred transactions

first swipe at processing thereof, need tests badly

ref EOSIO/eos#175
上级 45ad2937
......@@ -5,8 +5,7 @@ extern "C" {
/**
* @defgroup transactionapi Transaction API
* @ingroup contractdev
* @brief Define API for sending transactions to other contracts inline with
* the current transaction or deferred to a future block.
* @brief Define API for sending transactions and inline messages
*
* A EOS.IO transaction has the following abstract structure:
*
......@@ -20,32 +19,32 @@ extern "C" {
*
* This API enables your contract to construct and send transactions
*
* A Transaction can be processed immediately after the execution of the
* parent transaction (inline). An inline transaction is more restrictive
* than a deferred transaction however, it allows the success or failure
* of the parent transaction to be affected by the new transaction. In
* other words, if an inline transaction fails then the whole tree of
* transactions rooted in the block will me marked as failing and none of
* their affects on the database will persist.
*
* Because of this and the parallel nature of transaction application,
* inline transactions may not adopt any `scope` which is not listed in
* their parent transaction's scope. They may adopt any `readScope` listed
* in either their parent's `scope` or `readScope`.
*
* Deferred transactions carry no `scope` or `readScope` restrictions. They
* may adopt any valid accounts for either field.
*
* Deferred transactions will not be processed until a future block. They
* can therefore have no effect on the success of failure of their parent
* transaction so long as they appear well formed. If any other condition
* causes the parent transaction to be marked as failing, then the deferred
* transaction will never be processed.
*
* Both Deferred and Inline transactions must adhere to the permissions
* available to the parent transaction or, in the future, delegated to the
* contract account for future use.
* Deferred transactions must adhere to the permissions available to the
* parent transaction or, in the future, delegated to the contract account
* for future use.
*
* 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 messages rooted in the
* block will me marked as failing and none of effects on the database will
* persist.
*
* Because of this and the parallel nature of transaction application,
* inline messages may not affect any `scope` which is not listed in
* their parent transaction's `scope`. They also may not read any `scope`
* not listed in either their parent transaction's `scope` or `readScope`.
*
* Inline messages 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.
*/
/**
......@@ -58,8 +57,9 @@ extern "C" {
typedef uint32_t TransactionHandle;
#define InvalidTransactionHandle (0xFFFFFFFFUL)
#define SendInline (1)
#define SendDeferred (0)
typedef uint32_t MessageHandle;
#define InvalidMessageHandle (0xFFFFFFFFUL)
/**
* @brief create a pending transaction
......@@ -75,7 +75,7 @@ extern "C" {
TransactionHandle transactionCreate();
/**
* @brief add a scope to a pending transaction
* @brief require a scope to process a pending transaction
*
* This function adds either a `scope` or `readScope` to the given pending
* transaction.
......@@ -84,86 +84,91 @@ extern "C" {
* @param scope - the `AccountName` to add
* @param readOnly - whether `scope` should be added to `scope[]` or `readScope[]`
*/
void transactionAddScope(TransactionHandle trx, AccountName scope, int readOnly = 0);
void transactionRequireScope(TransactionHandle trx, AccountName scope, int readOnly = 0);
/**
* @brief set the destination for the pending message
* @brief finalize the pending message and add it to the transaction
*
* This function sets the `AccountName` of the owner of the contract
* that is acting as the reciever of the pending message and the type of
* message;
* The message handle should be considered invalid after the call
*
* @param trx - the `TransactionHandle` of the pending transaction to modify
* @param code - the `AccountName` which owns the contract code to execute
* @param type - the type of this message
* @param msg - the `MessageHandle` of the pending message to add
*/
void transactionSetMessageDestination(TransactionHandle trx, AccountName code, FuncName type);
void transactionAddMessage(TransactionHandle trx, MessageHandle msg);
/**
* @brief add a permission to the pending message
* @brief send a pending transaction
*
* This function adds a @ref PermissionName to the pending message
* This function will finalize a pending transaction and enqueue it for
* processing either immediately after this transaction and its previously
* sent inline transactions complete OR in a future block.
*
* This handle should be considered invalid after the call
*
* @param trx - the `TransactionHandle` of the pending transaction to modify
* @param account - the `AccountName` to add
* @param permission - the `PermissionName` to add
* @param trx - the `TransactionHandle` of the pending transaction to send
*/
void transactionAddMessagePermission(TransactionHandle trx, AccountName account, PermissionName permission);
void transactionSend(TransactionHandle trx);
/**
* @brief drop a pending transaction
*
* This function will discard a pending transaction and its associated
* resources. Once a transaction is sent it can no longer be dropped.
*
* This handle should be considered invalid after the call
*
* @param trx - the `TransactionHandle` of the pending transaction to send
*/
void transactionDrop(TransactionHandle trx);
/**
* @brief finalize the pending message and add it to the transaction
* @brief create a pending message
*
* This function adds payload data to the pending message and pushes it into
* the given transaction.
* This function creates a pending message to be included in a deferred
* transaction or to be send as an inline message
*
* This will reset the destination and permissions for the pending message on
* the given transaction.
* This message has no default permissions, see @ref messageRequirePermission
*
* @param trx - the `TransactionHandle` of the pending transaction to modify
* @param code - the `AccountName` which owns the contract code to execute
* @param type - the type of this message
* @param data - the payload data for this message
* @param size - the size of `data`
*/
void transactionPushMessage(TransactionHandle trx, void* data, int size);
MessageHandle messageCreate(AccountName code, FuncName type, void* data, int size);
/**
* @brief reset the destination and persmisions of the pending transaction
* @brief require a permission for the pending message
*
* This will reset the destination and permissions for the pending message on
* the given transaction without committing the existing settings effectively
* dropping any changes made so far.
* Indicates that a given pending message requires a certain permission
*
* @param trx - the `TransactionHandle` of the pending transaction to modify
* @param msg - the `MessageHandle` pending message referred to
* @param account - the `AccountName` to of the permission
* @param permission - the `PermissionName` to of the permision
*/
void transactionResetMessage(TransactionHandle trx);
void messageRequirePermission(MessageHandle msg, AccountName account, PermissionName permission);
/**
* @brief send a pending transaction
* @brief send a pending message as an inline message
*
* This function will finalize a pending transaction and enqueue it for
* processing either immediately after this transaction and its previously
* sent inline transactions complete OR in a future block.
*
* This handle should be considered invalid after the call
*
* @param trx - the `TransactionHandle` of the pending transaction to send
* @param inlineMode - whether to send as an inline transaction (!=0) or deferred(=0)
* @param msg - the `MessageHandle` of the pending message to send inline
*/
void transactionSend(TransactionHandle trx, int mode = SendDeferred);
void messageSend(MessageHandle msg);
/**
* @brief drop a pending transaction
* @brief discard a pending message
*
* This function will discard a pending transaction and its associated
* resources. Once a transaction is sent it can no longer be dropped.
*
* This handle should be considered invalid after the call
*
* @param trx - the `TransactionHandle` of the pending transaction to send
* @param trx - the `MessageHandle` of the pending message to discard
*/
void transactionDrop(TransactionHandle trx);
void messageDrop(MessageHandle msg);
///@ } transactioncapi
}
......@@ -626,7 +626,7 @@ void check_output(const Transaction& expected, const Transaction& actual, const
template<>
void check_output(const ProcessedSyncTransaction& expected, const ProcessedSyncTransaction& actual, const path_cons_list& path) {
void check_output(const InlineTransaction& expected, const InlineTransaction& actual, const path_cons_list& path) {
check_output<Transaction>(expected, actual, path);
check_output(expected.output, actual.output, path(".output"));
}
......@@ -640,8 +640,8 @@ void check_output(const GeneratedTransaction& expected, const GeneratedTransacti
template<>
void check_output(const MessageOutput& expected, const MessageOutput& actual, const path_cons_list& path) {
check_output(expected.notify, actual.notify, path(".notify"));
check_output(expected.sync_transactions, actual.sync_transactions, path(".sync_transactions"));
check_output(expected.async_transactions, actual.async_transactions, path(".async_transactions"));
check_output(expected.inline_transaction, actual.inline_transaction, path(".inline_transaction"));
check_output(expected.deferred_transactions, actual.deferred_transactions, path(".deferred_transactions"));
}
template<typename T>
......@@ -898,19 +898,27 @@ void chain_controller::process_message(const Transaction& trx, AccountName code,
} FC_CAPTURE_AND_RETHROW((apply_ctx.notified[i]))
}
for( const auto& generated : apply_ctx.sync_transactions ) {
try {
output.sync_transactions.emplace_back( process_transaction( generated ) );
} FC_CAPTURE_AND_RETHROW((generated))
}
// combine inline messages and process
auto inline_transaction = PendingInlineTransaction(trx);
inline_transaction.messages = std::move(apply_ctx.inline_messages);
try {
output.inline_transaction = process_transaction(inline_transaction);
} FC_CAPTURE_AND_RETHROW((inline_transaction))
for( auto& asynctrx : apply_ctx.deferred_transactions ) {
digest_type::encoder enc;
fc::raw::pack( enc, trx );
fc::raw::pack( enc, asynctrx );
auto id = enc.result();
auto gtrx = GeneratedTransaction(id, asynctrx);
for( auto& asynctrx : apply_ctx.async_transactions ) {
_db.create<generated_transaction_object>([&](generated_transaction_object& transaction) {
transaction.trx = asynctrx;
transaction.trx = gtrx;
transaction.status = generated_transaction_object::PENDING;
});
output.async_transactions.emplace_back( std::move( asynctrx ) );
output.deferred_transactions.emplace_back( gtrx );
}
// propagate used_authorizations up the context chain
......
......@@ -271,6 +271,7 @@ public:
* @throws tx_missing_auth If no sufficient permission was found
*/
void require_authorization(const types::AccountName& account);
void require_authorization(const types::AccountName& account, const types::PermissionName& permission);
void require_scope(const types::AccountName& account)const;
void require_recipient(const types::AccountName& account);
......@@ -286,41 +287,26 @@ public:
chain_controller& mutable_controller;
chainbase::database& mutable_db;
std::deque<AccountName> notified;
std::deque<Transaction> inline_transactions; ///< queued inline txs
std::deque<Transaction> deferred_transactions; ///< deferred txs
std::deque<AccountName> notified;
std::vector<types::Message> inline_messages; ///< queued inline messages
std::vector<types::Transaction> deferred_transactions; ///< deferred txs
///< Parallel to msg.authorization; tracks which permissions have been used while processing the message
vector<bool> used_authorizations;
///< pending transaction construction
typedef uint32_t pending_transaction_handle;
struct pending_transaction {
struct pending_transaction : public types::Transaction {
typedef uint32_t handle_type;
static const handle_type Invalid_handle = 0xFFFFFFFFUL;
pending_transaction(const handle_type& _handle, const UInt16& block_num, const UInt32& block_ref, const Time& expiration )
: types::Transaction(block_num, block_ref, expiration, vector<types::AccountName>(), vector<types::AccountName>(), vector<types::Message>())
, handle(_handle) {}
handle_type handle;
struct message_dest {
AccountName code;
FuncName type;
};
// state set that applies to pushed message data
optional<message_destination> current_destination;
vector<types::AccountPermission> current_permissions;
// state to apply when the transaction is pushed
vector<AccountName> scopes;
vector<AccountName> read_scopes;
vector<types::Message> messages;
types::Transaction as_transaction() const;
void check_size() const;
void reset_message() {
current_destination = decltype(current_destination)();
current_permissions.clear();
}
};
pending_transaction::handle_type next_pending_transaction_serial;
......@@ -330,6 +316,24 @@ public:
pending_transaction& create_pending_transaction();
void release_pending_transaction(pending_transaction::handle_type handle);
///< pending message construction
typedef uint32_t pending_message_handle;
struct pending_message : public types::Message {
typedef uint32_t handle_type;
pending_message(const handle_type& _handle, const AccountName& code, const FuncName& type, const Bytes& data)
: types::Message(code, type, vector<types::AccountPermission>(), data)
, handle(_handle) {}
handle_type handle;
};
pending_transaction::handle_type next_pending_message_serial;
vector<pending_message> pending_messages;
pending_message& get_pending_message(pending_message::handle_type handle);
pending_message& create_pending_message(const AccountName& code, const FuncName& type, const Bytes& data);
void release_pending_message(pending_message::handle_type handle);
};
using apply_handler = std::function<void(apply_context&)>;
......
......@@ -53,7 +53,7 @@ namespace eos { namespace chain {
*/
struct ProcessedTransaction;
struct ProcessedSyncTransaction;
struct InlineTransaction;
struct ProcessedGeneratedTransaction;
......@@ -115,6 +115,15 @@ namespace eos { namespace chain {
* subsequent blocks by referencing this ID.
*/
struct GeneratedTransaction : public types::Transaction {
GeneratedTransaction() = default;
GeneratedTransaction(generated_transaction_id_type _id, const Transaction& trx)
: types::Transaction(trx)
, id(_id) {}
GeneratedTransaction(generated_transaction_id_type _id, const Transaction&& trx)
: types::Transaction(trx)
, id(_id) {}
generated_transaction_id_type id;
digest_type merkle_digest() const;
......@@ -156,16 +165,30 @@ namespace eos { namespace chain {
typedef ProcessedTransaction Processed;
};
struct NotifyOutput;
struct PendingInlineTransaction : public types::Transaction {
typedef types::Transaction super;
using super::super;
typedef InlineTransaction Processed;
};
struct MessageOutput;
struct InlineTransaction : public types::Transaction {
explicit InlineTransaction( const PendingInlineTransaction& t ):Transaction(t){}
InlineTransaction(){}
vector<MessageOutput> output;
};
struct NotifyOutput;
/**
* Output generated by applying a particular message.
*/
struct MessageOutput {
vector<NotifyOutput> notify; ///< accounts to notify, may only be notified once
vector<ProcessedSyncTransaction> sync_transactions; ///< transactions generated and applied after notify
vector<GeneratedTransaction> async_transactions; ///< transactions generated but not applied
InlineTransaction inline_transaction; ///< transactions generated and applied after notify
vector<GeneratedTransaction> deferred_transactions; ///< transactions generated but not applied
};
struct NotifyOutput {
......@@ -180,13 +203,6 @@ namespace eos { namespace chain {
vector<MessageOutput> output;
};
struct ProcessedSyncTransaction : public Transaction {
explicit ProcessedSyncTransaction( const Transaction& t ):Transaction(t){}
ProcessedSyncTransaction(){}
vector<MessageOutput> output;
};
struct ProcessedGeneratedTransaction {
explicit ProcessedGeneratedTransaction( const generated_transaction_id_type& _id ):id(_id){}
explicit ProcessedGeneratedTransaction( const GeneratedTransaction& t ):id(t.id){}
......@@ -201,8 +217,9 @@ namespace eos { namespace chain {
FC_REFLECT(eos::chain::GeneratedTransaction, (id))
FC_REFLECT_DERIVED(eos::chain::SignedTransaction, (eos::types::SignedTransaction), )
FC_REFLECT(eos::chain::MessageOutput, (notify)(sync_transactions)(async_transactions) )
FC_REFLECT(eos::chain::MessageOutput, (notify)(inline_transaction)(deferred_transactions) )
FC_REFLECT_DERIVED(eos::chain::ProcessedTransaction, (eos::types::SignedTransaction), (output) )
FC_REFLECT_DERIVED(eos::chain::ProcessedSyncTransaction, (eos::types::Transaction), (output) )
FC_REFLECT_DERIVED(eos::chain::PendingInlineTransaction, (eos::types::Transaction), )
FC_REFLECT_DERIVED(eos::chain::InlineTransaction, (eos::types::Transaction), (output) )
FC_REFLECT(eos::chain::ProcessedGeneratedTransaction, (id)(output) )
FC_REFLECT(eos::chain::NotifyOutput, (name)(output) )
......@@ -6,6 +6,8 @@
#include <eos/utilities/parallel_markers.hpp>
#include <fc/bitutil.hpp>
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/range/algorithm/find_if.hpp>
......@@ -18,6 +20,13 @@ void apply_context::require_authorization(const types::AccountName& account) {
used_authorizations[itr - msg.authorization.begin()] = true;
}
void apply_context::require_authorization(const types::AccountName& account,const types::PermissionName& permission) {
auto itr = boost::find_if(msg.authorization, [&account, &permission](const auto& auth) { return auth.account == account && auth.permission == permission; });
EOS_ASSERT(itr != msg.authorization.end(), tx_missing_auth,
"Transaction is missing required authorization from ${acct} with permission ${permission}", ("acct", account)("permission", permission));
used_authorizations[itr - msg.authorization.begin()] = true;
}
void apply_context::require_scope(const types::AccountName& account)const {
auto itr = boost::find_if(trx.scope, [&account](const auto& scope) {
return scope == account;
......@@ -51,7 +60,7 @@ vector<types::AccountPermission> apply_context::unused_authorizations() const {
return {range.begin(), range.end()};
}
pending_transaction& apply_context::get_pending_transaction(pending_transaction::handle_type handle) {
apply_context::pending_transaction& apply_context::get_pending_transaction(pending_transaction::handle_type handle) {
auto itr = boost::find_if(pending_transactions, [&](const auto& trx) { return trx.handle == handle; });
EOS_ASSERT(itr != pending_transactions.end(), tx_unknown_argument,
"Transaction refers to non-existant/destroyed pending transaction");
......@@ -59,16 +68,21 @@ pending_transaction& apply_context::get_pending_transaction(pending_transaction:
}
const int Max_pending_transactions = 4;
const uint32_t Max_pending_transaction_size = 16 * 1024;
const uint32_t Max_pending_transaction_size = 64 * 1024;
const auto Pending_transaction_expiration = fc::seconds(21 * 3);
pending_transaction& apply_context::create_pending_transaction() {
apply_context::pending_transaction& apply_context::create_pending_transaction() {
EOS_ASSERT(pending_transactions.size() < Max_pending_transactions, tx_resource_exhausted,
"Transaction is attempting to create too many pending transactions. The max is ${max}", ("max", Max_pending_transactions));)
"Transaction is attempting to create too many pending transactions. The max is ${max}", ("max", Max_pending_transactions));
pending_transaction::handle handle = next_pending_transaction_serial++;
pending_transactions.push_back({handle});
return pending_transaction.back();
pending_transaction::handle_type handle = next_pending_transaction_serial++;
auto head_block_id = controller.head_block_id();
decltype(pending_transaction::refBlockNum) head_block_num = fc::endian_reverse_u32(head_block_id._hash[0]);
decltype(pending_transaction::refBlockPrefix) head_block_ref = head_block_id._hash[1];
decltype(pending_transaction::expiration) expiration = controller.head_block_time() + Pending_transaction_expiration;
pending_transactions.emplace_back(handle, head_block_num, head_block_ref, expiration);
return pending_transactions.back();
}
void apply_context::release_pending_transaction(pending_transaction::handle_type handle) {
......@@ -83,17 +97,44 @@ void apply_context::release_pending_transaction(pending_transaction::handle_type
pending_transactions.pop_back();
}
types::Transaction apply_context::pending_transaction::as_transaction() const {
decltype(types::Transaction::refBlockNum) head_block_num = chain_controller.head_block_num();
decltype(types::Transaction::refBlockRef) head_block_ref = fc::endian_reverse_u32(chain_controller.head_block_ref()._hash[0]);
decltype(types::Transaction::expiration) expiration = chain_controller.head_block_time() + Pending_transaction_expiration;
return types::Transaction(head_block_num, head_block_ref, expiration, scopes, read_scopes, messages);
}
void apply_context::pending_transaction::check_size() const {
auto trx = as_transaction();
const types::Transaction& trx = *this;
EOS_ASSERT(fc::raw::pack_size(trx) <= Max_pending_transaction_size, tx_resource_exhausted,
"Transaction is attempting to create a transaction which is too large. The max size is ${max} bytes", ("max", Max_pending_transaction_size));
}
apply_context::pending_message& apply_context::get_pending_message(pending_message::handle_type handle) {
auto itr = boost::find_if(pending_messages, [&](const auto& msg) { return msg.handle == handle; });
EOS_ASSERT(itr != pending_messages.end(), tx_unknown_argument,
"Transaction refers to non-existant/destroyed pending message");
return *itr;
}
const int Max_pending_messages = 4;
const uint32_t Max_pending_message_size = 4 * 1024;
apply_context::pending_message& apply_context::create_pending_message(const AccountName& code, const FuncName& type, const Bytes& data) {
EOS_ASSERT(pending_messages.size() < Max_pending_messages, tx_resource_exhausted,
"Transaction is attempting to create too many pending messages. The max is ${max}", ("max", Max_pending_messages));
EOS_ASSERT(data.size() < Max_pending_message_size, tx_resource_exhausted,
"Transaction is attempting to create a pending message that is too large. The max is ${max}", ("max", Max_pending_message_size));
pending_message::handle_type handle = next_pending_message_serial++;
pending_messages.emplace_back(handle, code, type, data);
return pending_messages.back();
}
void apply_context::release_pending_message(pending_message::handle_type handle) {
auto itr = boost::find_if(pending_messages, [&](const auto& trx) { return trx.handle == handle; });
EOS_ASSERT(itr != pending_messages.end(), tx_unknown_argument,
"Transaction refers to non-existant/destroyed pending message");
auto last = pending_messages.end() - 1;
if (itr != last) {
std::swap(itr, last);
}
pending_messages.pop_back();
}
} } // namespace eos::chain
......@@ -253,33 +253,46 @@ DEFINE_INTRINSIC_FUNCTION0(env,transactionCreate,transactionCreate,i32) {
return ptrx.handle;
}
DEFINE_INTRINSIC_FUNCTION3(env,transactionAddScope,transactionAddScope,none,i32,handle,i64,scope,i32,readOnly) {
DEFINE_INTRINSIC_FUNCTION3(env,transactionRequireScope,transactionRequireScope,none,i32,handle,i64,scope,i32,readOnly) {
auto& ptrx = wasm_interface::get().current_apply_context->get_pending_transaction(handle);
if(readOnly == 0) {
ptrx.scopes.emplace_back(scope);
ptrx.scope.emplace_back(scope);
} else {
ptrx.read_scopes.emplace_back(scope);
ptrx.readscope.emplace_back(scope);
}
ptrx.check_size();
}
DEFINE_INTRINSIC_FUNCTION3(env,transactionSetMessageDestination,transactionSetMessageDestination,none,i32,handle,i64,code,i64,type) {
auto& ptrx = wasm_interface::get().current_apply_context->get_pending_transaction(handle);
ptrx.current_destination = decltype(ptrx.current_destination)({Name(code), Name(type)});
DEFINE_INTRINSIC_FUNCTION2(env,transactionAddMessage,transactionAddMessage,none,i32,handle,i32,msg_handle) {
auto apply_context = wasm_interface::get().current_apply_context;
auto& ptrx = apply_context->get_pending_transaction(handle);
auto& pmsg = apply_context->get_pending_message(msg_handle);
ptrx.messages.emplace_back(pmsg);
ptrx.check_size();
apply_context->release_pending_message(msg_handle);
}
DEFINE_INTRINSIC_FUNCTION3(env,transactionAddMessagePermission,transactionAddMessagePermission,none,i32,handle,i64,account,i64,permission) {
wasm_interface::get().current_apply_context->require_authorization(Name(account), Name(permission));
auto& ptrx = wasm_interface::get().current_apply_context->get_pending_transaction(handle);
ptrx.current_permissions.emplace_back(Name(account), Name(permission));
DEFINE_INTRINSIC_FUNCTION1(env,transactionSend,transactionSend,none,i32,handle) {
auto apply_context = wasm_interface::get().current_apply_context;
auto& ptrx = apply_context->get_pending_transaction(handle);
EOS_ASSERT(ptrx.messages.size() > 0, tx_unknown_argument,
"Attempting to send a transaction with no messages");
apply_context->deferred_transactions.emplace_back(ptrx);
apply_context->release_pending_transaction(handle);
}
DEFINE_INTRINSIC_FUNCTION1(env,transactionDrop,transactionDrop,none,i32,handle) {
wasm_interface::get().current_apply_context->release_pending_transaction(handle);
}
DEFINE_INTRINSIC_FUNCTION3(env,transactionPushMessage,transactionPushMessage,none,i32,handle,i32,msg_buffer,i32,msg_size) {
DEFINE_INTRINSIC_FUNCTION4(env,messageCreate,messageCreate,i32,i64,code,i64,type,i32,data,i32,length) {
auto& wasm = wasm_interface::get();
auto mem = wasm.current_memory;
EOS_ASSERT( msg_size > 0, tx_unknown_argument
EOS_ASSERT( length > 0, tx_unknown_argument,
"Attempting to push an empty message" );
const char* buffer = nullptr;
......@@ -287,47 +300,32 @@ DEFINE_INTRINSIC_FUNCTION3(env,transactionPushMessage,transactionPushMessage,non
// memoryArrayPtr checks that the entire array of bytes is valid and
// within the bounds of the memory segment so that transactions cannot pass
// bad values in attempts to read improper memory
buffer = memoryArrayPtr<const char>( mem, msg_buffer, msg_size );
buffer = memoryArrayPtr<const char>( mem, data, uint32_t(length) );
} catch( const Runtime::Exception& e ) {
FC_THROW_EXCEPTION(tx_unknown_argument, "Message data is not valid");
FC_THROW_EXCEPTION(tx_unknown_argument, "Message data is not a valid memory range");
}
auto& ptrx = wasm.current_apply_context->get_pending_transaction(handle);
EOS_ASSERT(ptrx.destination.valid(), tx_unknown_argument
"Attempting to push a message without setting a destination");
auto& dest = *ptrx.destination;
ptrx.messages.emplace_back(dest.code, dest.type, ptrx.current_permissions, Bytes(buffer, buffer + msg_size));
ptrx.reset_message();
ptrx.check_size();
auto& pmsg = wasm.current_apply_context->create_pending_message(Name(code), Name(type), Bytes(buffer, buffer + length));
return pmsg.handle;
}
DEFINE_INTRINSIC_FUNCTION1(env,transactionResetMessage,transactionResetMessage,none,i32,handle) {
auto& ptrx = wasm_interface::get().current_apply_context->get_pending_transaction(handle);
ptrx.reset_message();
DEFINE_INTRINSIC_FUNCTION3(env,messageRequirePermission,messageRequirePermission,none,i32,handle,i64,account,i64,permission) {
auto apply_context = wasm_interface::get().current_apply_context;
apply_context->require_authorization(Name(account), Name(permission));
auto& pmsg = apply_context->get_pending_message(handle);
pmsg.authorization.emplace_back(Name(account), Name(permission));
}
DEFINE_INTRINSIC_FUNCTION2(env,transactionSend,transactionSend,none,i32,handle,i32,mode) {
EOS_ASSERT(mode == 0 || mode == 1, tx_unknown_argument
"Unknown delivery mode when sending transaction: ${mode}", ("mode":mode));
DEFINE_INTRINSIC_FUNCTION1(env,messageSend,messageSend,none,i32,handle) {
auto apply_context = wasm_interface::get().current_apply_context;
auto& ptrx = apply_context->get_pending_transaction(handle);
EOS_ASSERT(ptrx.messages.size() > 0, , tx_unknown_argument
"Attempting to send a transaction with no messages");
if (mode == 0) {
apply_context->deferred_transactions.emplace_back(ptrx.as_transaction());
} else {
apply_context->inline_transactions.emplace_back(ptrx.as_transaction());
}
auto& pmsg = apply_context->get_pending_message(handle);
apply_context->release_pending_transaction(handle);
apply_context->inline_messages.emplace_back(pmsg);
apply_context->release_pending_message(handle);
}
DEFINE_INTRINSIC_FUNCTION1(env,transactionDrop,transactionDrop,none,i32,handle) {
wasm_interface::get().current_apply_context->release_pending_transaction(handle);
DEFINE_INTRINSIC_FUNCTION1(env,messageDrop,messageDrop,none,i32,handle) {
wasm_interface::get().current_apply_context->release_pending_message(handle);
}
/**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册