提交 45ad2937 编写于 作者: B Bart Wyatt

initial implementation of wasm interface for transaction contract api ref EOSIO/eos#175

上级 f7158527
......@@ -58,6 +58,8 @@ extern "C" {
typedef uint32_t TransactionHandle;
#define InvalidTransactionHandle (0xFFFFFFFFUL)
#define SendInline (1)
#define SendDeferred (0)
/**
* @brief create a pending transaction
......@@ -103,9 +105,10 @@ extern "C" {
* This function adds a @ref PermissionName to the pending message
*
* @param trx - the `TransactionHandle` of the pending transaction to modify
* @param account - the `AccountName` to add
* @param permission - the `PermissionName` to add
*/
void transactionAddMessagePermission(TransactionHandle trx, PermissionName permission);
void transactionAddMessagePermission(TransactionHandle trx, AccountName account, PermissionName permission);
/**
......@@ -147,7 +150,7 @@ extern "C" {
* @param trx - the `TransactionHandle` of the pending transaction to send
* @param inlineMode - whether to send as an inline transaction (!=0) or deferred(=0)
*/
void transactionSend(TransactionHandle trx, int inlineMode = 0);
void transactionSend(TransactionHandle trx, int mode = SendDeferred);
/**
* @brief drop a pending transaction
......
......@@ -57,6 +57,8 @@ namespace eos { namespace chain {
FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate, eos::chain::transaction_exception, 3030011, "duplicate transaction" )
FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction_exception, eos::chain::transaction_exception, 3030012, "unknown transaction" )
FC_DECLARE_DERIVED_EXCEPTION( tx_scheduling_exception, eos::chain::transaction_exception, 3030013, "transaction failed during sheduling" )
FC_DECLARE_DERIVED_EXCEPTION( tx_unknown_argument, eos::chain::transaction_exception, 3030014, "transaction provided an unknown value to a system call" )
FC_DECLARE_DERIVED_EXCEPTION( tx_resource_exhausted, eos::chain::transaction_exception, 3030015, "transaction exhausted allowed resources" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, eos::chain::utility_exception, 3060001, "invalid pts address" )
FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, eos::chain::chain_exception, 37006, "insufficient feeds" )
......
......@@ -19,7 +19,8 @@ public:
const chain::Message& m,
const types::AccountName& code)
: controller(con), db(db), trx(t), msg(m), code(code), mutable_controller(con),
mutable_db(db), used_authorizations(msg.authorization.size(), false){}
mutable_db(db), used_authorizations(msg.authorization.size(), false),
next_pending_transaction_serial(0){}
template <typename ObjectType>
int32_t store_record( Name scope, Name code, Name table, typename ObjectType::key_type* keys, char* value, uint32_t valuelen ) {
......@@ -286,11 +287,49 @@ public:
chainbase::database& mutable_db;
std::deque<AccountName> notified;
std::deque<ProcessedTransaction> sync_transactions; ///< sync calls made
std::deque<GeneratedTransaction> async_transactions; ///< async calls requested
std::deque<Transaction> inline_transactions; ///< queued inline txs
std::deque<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 {
typedef uint32_t handle_type;
static const handle_type Invalid_handle = 0xFFFFFFFFUL;
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;
vector<pending_transaction> pending_transactions;
pending_transaction& get_pending_transaction(pending_transaction::handle_type handle);
pending_transaction& create_pending_transaction();
void release_pending_transaction(pending_transaction::handle_type handle);
};
using apply_handler = std::function<void(apply_context&)>;
......
......@@ -51,4 +51,49 @@ 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) {
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");
return *itr;
}
const int Max_pending_transactions = 4;
const uint32_t Max_pending_transaction_size = 16 * 1024;
const auto Pending_transaction_expiration = fc::seconds(21 * 3);
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));)
pending_transaction::handle handle = next_pending_transaction_serial++;
pending_transactions.push_back({handle});
return pending_transaction.back();
}
void apply_context::release_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");
auto last = pending_transactions.end() - 1;
if (itr != last) {
std::swap(itr, last);
}
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();
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));
}
} } // namespace eos::chain
......@@ -241,39 +241,98 @@ DEFINE_INTRINSIC_FUNCTION3(env,memcpy,memcpy,i32,i32,dstp,i32,srcp,i32,len) {
}
DEFINE_INTRINSIC_FUNCTION2(env,send,send,i32,i32,trx_buffer, i32,trx_buffer_size ) {
/**
* Transaction C API implementation
* @{
*/
static const uint32_t InvalidTransactionHandle = 0xFFFFFFFFUL;
DEFINE_INTRINSIC_FUNCTION0(env,transactionCreate,transactionCreate,i32) {
auto& ptrx = wasm_interface::get().current_apply_context->create_pending_transaction();
return ptrx.handle;
}
DEFINE_INTRINSIC_FUNCTION3(env,transactionAddScope,transactionAddScope,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);
} else {
ptrx.read_scopes.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_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_FUNCTION3(env,transactionPushMessage,transactionPushMessage,none,i32,handle,i32,msg_buffer,i32,msg_size) {
auto& wasm = wasm_interface::get();
auto mem = wasm.current_memory;
const char* buffer = &memoryRef<const char>( mem, trx_buffer );
FC_ASSERT( trx_buffer_size > 0 );
FC_ASSERT( wasm.current_apply_context, "not in apply context" );
EOS_ASSERT( msg_size > 0, tx_unknown_argument
"Attempting to push an empty message" );
fc::datastream<const char*> ds(buffer, trx_buffer_size );
eos::chain::GeneratedTransaction gtrx;
eos::chain::Transaction& trx = gtrx;
fc::raw::unpack( ds, trx );
const char* buffer = nullptr;
try {
// 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 );
} catch( const Runtime::Exception& e ) {
FC_THROW_EXCEPTION(tx_unknown_argument, "Message data is not valid");
}
/**
* The code below this section provides sanity checks that the generated message is well formed
* before being accepted. These checks do not need to be applied during reindex.
*/
#warning TODO: reserve per-thread static memory for MAX TRX SIZE
/** make sure that packing what we just unpacked produces expected output */
auto test = fc::raw::pack( trx );
FC_ASSERT( 0 == memcmp( buffer, test.data(), test.size() ) );
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();
}
DEFINE_INTRINSIC_FUNCTION1(env,transactionResetMessage,transactionResetMessage,none,i32,handle) {
auto& ptrx = wasm_interface::get().current_apply_context->get_pending_transaction(handle);
ptrx.reset_message();
}
/** TODO: make sure that we can call validate() on the message and it passes, this is thread safe and
* ensures the type is properly registered and can be deserialized... one issue is that this could
* construct a RECURSIVE virtual machine state which means the wasm_interface state needs to be a STACK vs
* a per-thread global.
**/
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));
// wasm.current_apply_context->generated.emplace_back( std::move(gtrx) );
auto apply_context = wasm_interface::get().current_apply_context;
auto& ptrx = apply_context->get_pending_transaction(handle);
return 0;
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());
}
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);
}
/**
* @} Transaction C API implementation
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册