From 59b5c209a982b28f1cb71e2cb2b2d37d0fe7ebb6 Mon Sep 17 00:00:00 2001 From: Bart Wyatt Date: Tue, 5 Sep 2017 18:11:08 -0400 Subject: [PATCH] changed API to be inline messages and deferred transactions first swipe at processing thereof, need tests badly ref EOSIO/eos#175 --- contracts/eoslib/transaction.h | 141 +++++++++--------- libraries/chain/chain_controller.cpp | 30 ++-- .../eos/chain/message_handling_contexts.hpp | 54 +++---- .../chain/include/eos/chain/transaction.hpp | 43 ++++-- libraries/chain/message_handling_contexts.cpp | 71 +++++++-- libraries/chain/wasm_interface.cpp | 82 +++++----- 6 files changed, 247 insertions(+), 174 deletions(-) diff --git a/contracts/eoslib/transaction.h b/contracts/eoslib/transaction.h index fb531e75f..bf223b2d9 100644 --- a/contracts/eoslib/transaction.h +++ b/contracts/eoslib/transaction.h @@ -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 } diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 2eb11f545..cd0fda6ce 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -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(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 @@ -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& 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 diff --git a/libraries/chain/include/eos/chain/message_handling_contexts.hpp b/libraries/chain/include/eos/chain/message_handling_contexts.hpp index 10b1c4059..9e142a6b8 100644 --- a/libraries/chain/include/eos/chain/message_handling_contexts.hpp +++ b/libraries/chain/include/eos/chain/message_handling_contexts.hpp @@ -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 notified; - std::deque inline_transactions; ///< queued inline txs - std::deque deferred_transactions; ///< deferred txs + std::deque notified; + std::vector inline_messages; ///< queued inline messages + std::vector deferred_transactions; ///< deferred txs ///< Parallel to msg.authorization; tracks which permissions have been used while processing the message vector 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(), vector(), vector()) + , handle(_handle) {} + + handle_type handle; - struct message_dest { - AccountName code; - FuncName type; - }; - // state set that applies to pushed message data - optional current_destination; - vector current_permissions; - - // state to apply when the transaction is pushed - vector scopes; - vector read_scopes; - vector 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(), data) + , handle(_handle) {} + + handle_type handle; + }; + + pending_transaction::handle_type next_pending_message_serial; + vector 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; diff --git a/libraries/chain/include/eos/chain/transaction.hpp b/libraries/chain/include/eos/chain/transaction.hpp index fe4023946..93f0ecd1c 100644 --- a/libraries/chain/include/eos/chain/transaction.hpp +++ b/libraries/chain/include/eos/chain/transaction.hpp @@ -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 output; + }; + struct NotifyOutput; + /** * Output generated by applying a particular message. */ struct MessageOutput { vector notify; ///< accounts to notify, may only be notified once - vector sync_transactions; ///< transactions generated and applied after notify - vector async_transactions; ///< transactions generated but not applied + InlineTransaction inline_transaction; ///< transactions generated and applied after notify + vector deferred_transactions; ///< transactions generated but not applied }; struct NotifyOutput { @@ -180,13 +203,6 @@ namespace eos { namespace chain { vector output; }; - struct ProcessedSyncTransaction : public Transaction { - explicit ProcessedSyncTransaction( const Transaction& t ):Transaction(t){} - ProcessedSyncTransaction(){} - - vector 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) ) diff --git a/libraries/chain/message_handling_contexts.cpp b/libraries/chain/message_handling_contexts.cpp index 211f26614..cd0c45eb2 100644 --- a/libraries/chain/message_handling_contexts.cpp +++ b/libraries/chain/message_handling_contexts.cpp @@ -6,6 +6,8 @@ #include +#include + #include #include @@ -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 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 diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 41a4030c0..1b9db4f68 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -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( mem, msg_buffer, msg_size ); + buffer = memoryArrayPtr( 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); } /** -- GitLab