未验证 提交 fe20d5b7 编写于 作者: M Matt Witherspoon 提交者: GitHub

Merge branch 'slim' into cleos-pretty-get-account-3

......@@ -187,8 +187,8 @@ add_subdirectory( contracts )
add_subdirectory( plugins )
add_subdirectory( programs )
add_subdirectory( scripts )
add_subdirectory( tests )
add_subdirectory( unittests )
add_subdirectory( tests )
add_subdirectory( tools )
add_subdirectory( debian )
......
......@@ -53,18 +53,15 @@ namespace eosio {
EOSLIB_SERIALIZE_DERIVED( transaction, transaction_header, (context_free_actions)(actions) )
};
class deferred_transaction : public transaction {
public:
uint128_t sender_id;
account_name sender;
account_name payer;
time execute_after;
static deferred_transaction from_current_action() {
return unpack_action_data<deferred_transaction>();
}
EOSLIB_SERIALIZE_DERIVED( deferred_transaction, transaction, (sender_id)(sender)(payer)(execute_after) )
struct onerror {
uint128_t sender_id;
transaction sent_trx;
static onerror from_current_action() {
return unpack_action_data<onerror>();
}
EOSLIB_SERIALIZE( onerror, (sender_id)(sent_trx) )
};
/**
......
......@@ -68,7 +68,7 @@ namespace proxy {
}
template<size_t ...Args>
void apply_onerror(uint64_t receiver, const deferred_transaction& failed_dtrx ) {
void apply_onerror(uint64_t receiver, const onerror& error ) {
eosio::print("starting onerror\n");
const auto self = receiver;
config code_config;
......@@ -77,10 +77,10 @@ namespace proxy {
auto id = code_config.next_id++;
configs::store(code_config, self);
eosio::print("Resending Transaction: ", failed_dtrx.sender_id, " as ", id, "\n");
deferred_transaction failed_dtrx_copy = failed_dtrx;
failed_dtrx_copy.delay_sec = code_config.delay;
failed_dtrx_copy.send(id, self);
eosio::print("Resending Transaction: ", error.sender_id, " as ", id, "\n");
transaction dtrx = error.sent_trx;
dtrx.delay_sec = code_config.delay;
dtrx.send(id, self);
}
}
......@@ -91,17 +91,16 @@ extern "C" {
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
if ( code == N(eosio.token) ) {
if( action == N(transfer) ) {
apply_transfer(receiver, code, unpack_action_data<eosio::token::transfer_args>());
}
} else if (code == receiver ) {
if (action == N(onerror)) {
apply_onerror(receiver, deferred_transaction::from_current_action());
}
if ( action == N(setowner)) {
apply_setowner(receiver, unpack_action_data<set_owner>());
}
}
}
if( code == N(eosio) && action == N(onerror) ) {
apply_onerror( receiver, onerror::from_current_action() );
} else if( code == N(eosio.token) ) {
if( action == N(transfer) ) {
apply_transfer(receiver, code, unpack_action_data<eosio::token::transfer_args>());
}
} else if( code == receiver ) {
if( action == N(setowner) ) {
apply_setowner(receiver, unpack_action_data<set_owner>());
}
}
}
}
......@@ -23,9 +23,9 @@ account_name global_receiver;
extern "C" {
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
if( code == N(eosio) && action == N(onerror) ) {
auto error_dtrx = eosio::deferred_transaction::from_current_action();
auto error = eosio::onerror::from_current_action();
eosio::print("onerror called\n");
auto error_action = error_dtrx.actions.at(0).name;
auto error_action = error.sent_trx.actions.at(0).name;
// Error handlers for deferred transactions in these tests currently only support the first action
......@@ -133,7 +133,6 @@ extern "C" {
WASM_TEST_HANDLER_EX(test_transaction, send_transaction_trigger_error_handler);
WASM_TEST_HANDLER_EX(test_transaction, send_transaction_large);
WASM_TEST_HANDLER_EX(test_transaction, send_action_sender);
WASM_TEST_HANDLER_EX(test_transaction, send_transaction_expiring_late);
WASM_TEST_HANDLER(test_transaction, deferred_print);
WASM_TEST_HANDLER_EX(test_transaction, send_deferred_transaction);
WASM_TEST_HANDLER(test_transaction, send_deferred_tx_given_payer);
......
......@@ -8,7 +8,7 @@
namespace eosio {
class deferred_transaction;
class transaction;
}
......@@ -31,7 +31,7 @@ namespace eosio {
#define WASM_TEST_ERROR_HANDLER(CALLED_CLASS_STR, CALLED_METHOD_STR, HANDLER_CLASS, HANDLER_METHOD) \
if( error_action == WASM_TEST_ACTION(CALLED_CLASS_STR, CALLED_METHOD_STR) ) { \
HANDLER_CLASS::HANDLER_METHOD(error_dtrx); \
HANDLER_CLASS::HANDLER_METHOD(error.sent_trx); \
return; \
}
......@@ -159,10 +159,9 @@ struct test_transaction {
static void send_transaction(uint64_t receiver, uint64_t code, uint64_t action);
static void send_transaction_empty(uint64_t receiver, uint64_t code, uint64_t action);
static void send_transaction_trigger_error_handler(uint64_t receiver, uint64_t code, uint64_t action);
static void assert_false_error_handler(const eosio::deferred_transaction&);
static void assert_false_error_handler(const eosio::transaction&);
static void send_transaction_max();
static void send_transaction_large(uint64_t receiver, uint64_t code, uint64_t action);
static void send_transaction_expiring_late(uint64_t receiver, uint64_t code, uint64_t action);
static void send_action_sender(uint64_t receiver, uint64_t code, uint64_t action);
static void deferred_print();
static void send_deferred_transaction(uint64_t receiver, uint64_t code, uint64_t action);
......
......@@ -202,7 +202,7 @@ void test_transaction::send_transaction_trigger_error_handler(uint64_t receiver,
trx.send(0, receiver);
}
void test_transaction::assert_false_error_handler(const eosio::deferred_transaction& dtrx) {
void test_transaction::assert_false_error_handler(const eosio::transaction& dtrx) {
auto onerror_action = eosio::get_action(1, 0);
eosio_assert( onerror_action.authorization.at(0).actor == dtrx.actions.at(0).account,
"authorizer of onerror action does not match receiver of original action in the deferred transaction" );
......@@ -226,20 +226,6 @@ void test_transaction::send_transaction_large(uint64_t receiver, uint64_t, uint6
eosio_assert(false, "send_transaction_large() should've thrown an error");
}
void test_transaction::send_transaction_expiring_late(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
account_name cur_send;
read_action_data( &cur_send, sizeof(account_name) );
test_action_action<N(testapi), WASM_TEST_ACTION("test_action", "test_current_sender")> test_action;
copy_data((char*)&cur_send, sizeof(account_name), test_action.data);
auto trx = transaction(now() + 60*60*24*365);
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
trx.send(0, receiver);
eosio_assert(false, "send_transaction_expiring_late() should've thrown an error");
}
/**
* deferred transaction
*/
......
......@@ -40,10 +40,6 @@ action_trace apply_context::exec_one()
const auto& cfg = control.get_global_properties().configuration;
checktime( cfg.base_per_action_cpu_usage );
try {
if( act.account == config::system_account_name && act.name == N(setcode) && receiver == config::system_account_name ) {
checktime( cfg.base_setcode_cpu_usage );
}
const auto &a = control.get_account(receiver);
privileged = a.privileged;
auto native = control.find_apply_handler(receiver, act.account, act.name);
......@@ -51,7 +47,8 @@ action_trace apply_context::exec_one()
(*native)(*this);
}
if( a.code.size() > 0 && !(act.account == config::system_account_name && act.name == N(setcode)) ) {
if( a.code.size() > 0
&& !(act.account == config::system_account_name && act.name == N(setcode) && receiver == config::system_account_name) ) {
try {
control.get_wasm_interface().apply(a.code_version, a.code, *this);
} catch ( const wasm_exit& ){}
......@@ -104,12 +101,14 @@ void apply_context::exec()
}
for( const auto& inline_action : _cfa_inline_actions ) {
trace.inline_traces.emplace_back( trx_context.dispatch_action( inline_action, inline_action.account, true, recurse_depth + 1 ) );
trace.inline_traces.emplace_back();
trx_context.dispatch_action( trace.inline_traces.back(), inline_action, inline_action.account, true, recurse_depth + 1 );
trace.total_cpu_usage += trace.inline_traces.back().total_cpu_usage;
}
for( const auto& inline_action : _inline_actions ) {
trace.inline_traces.emplace_back( trx_context.dispatch_action( inline_action, inline_action.account, false, recurse_depth + 1 ) );
trace.inline_traces.emplace_back();
trx_context.dispatch_action( trace.inline_traces.back(), inline_action, inline_action.account, false, recurse_depth + 1 );
trace.total_cpu_usage += trace.inline_traces.back().total_cpu_usage;
}
......@@ -221,11 +220,10 @@ void apply_context::execute_context_free_inline( action&& a ) {
void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx ) {
trx.set_reference_block(control.head_block_id()); // No TaPoS check necessary
//trx.validate(); // Not needed anymore since overflow is prevented by using uint64_t instead of uint32_t
FC_ASSERT( trx.context_free_actions.size() == 0, "context free actions are not currently allowed in generated transactions" );
trx.expiration = control.pending_block_time() + fc::microseconds(999'999); // Rounds up to nearest second (makes expiration check unnecessary)
trx.set_reference_block(control.head_block_id()); // No TaPoS check necessary
control.validate_referenced_accounts( trx );
control.validate_expiration( trx );
// Charge ahead of time for the additional net usage needed to retire the deferred transaction
// whether that be by successfully executing, soft failure, hard failure, or expiration.
......
......@@ -18,6 +18,7 @@
#include <chainbase/chainbase.hpp>
#include <fc/io/json.hpp>
#include <fc/scoped_exit.hpp>
#include <eosio/chain/eosio_contract.hpp>
......@@ -76,8 +77,8 @@ struct controller_impl {
}
void set_apply_handler( account_name contract, scope_name scope, action_name action, apply_handler v ) {
apply_handlers[contract][make_pair(scope,action)] = v;
void set_apply_handler( account_name receiver, account_name contract, action_name action, apply_handler v ) {
apply_handlers[receiver][make_pair(contract,action)] = v;
}
controller_impl( const controller::config& cfg, controller& s )
......@@ -93,20 +94,20 @@ struct controller_impl {
conf( cfg )
{
#define SET_APP_HANDLER( contract, scope, action, nspace ) \
set_apply_handler( #contract, #scope, #action, &BOOST_PP_CAT(apply_, BOOST_PP_CAT(contract, BOOST_PP_CAT(_,action) ) ) )
SET_APP_HANDLER( eosio, eosio, newaccount, eosio );
SET_APP_HANDLER( eosio, eosio, setcode, eosio );
SET_APP_HANDLER( eosio, eosio, setabi, eosio );
SET_APP_HANDLER( eosio, eosio, updateauth, eosio );
SET_APP_HANDLER( eosio, eosio, deleteauth, eosio );
SET_APP_HANDLER( eosio, eosio, linkauth, eosio );
SET_APP_HANDLER( eosio, eosio, unlinkauth, eosio );
SET_APP_HANDLER( eosio, eosio, onerror, eosio );
SET_APP_HANDLER( eosio, eosio, postrecovery, eosio );
SET_APP_HANDLER( eosio, eosio, passrecovery, eosio );
SET_APP_HANDLER( eosio, eosio, vetorecovery, eosio );
SET_APP_HANDLER( eosio, eosio, canceldelay, eosio );
#define SET_APP_HANDLER( receiver, contract, action) \
set_apply_handler( #receiver, #contract, #action, &BOOST_PP_CAT(apply_, BOOST_PP_CAT(contract, BOOST_PP_CAT(_,action) ) ) )
SET_APP_HANDLER( eosio, eosio, newaccount );
SET_APP_HANDLER( eosio, eosio, setcode );
SET_APP_HANDLER( eosio, eosio, setabi );
SET_APP_HANDLER( eosio, eosio, updateauth );
SET_APP_HANDLER( eosio, eosio, deleteauth );
SET_APP_HANDLER( eosio, eosio, linkauth );
SET_APP_HANDLER( eosio, eosio, unlinkauth );
SET_APP_HANDLER( eosio, eosio, postrecovery );
SET_APP_HANDLER( eosio, eosio, passrecovery );
SET_APP_HANDLER( eosio, eosio, vetorecovery );
SET_APP_HANDLER( eosio, eosio, canceldelay );
fork_db.irreversible.connect( [&]( auto b ) {
on_irreversible(b);
......@@ -364,15 +365,33 @@ struct controller_impl {
self.log_irreversible_blocks();
}
// The returned scoped_exit should not exceed the lifetime of the pending which existed when make_block_restore_point was called.
fc::scoped_exit<std::function<void()>> make_block_restore_point() {
auto orig_block_transactions_size = pending->_pending_block_state->block->transactions.size();
auto orig_state_transactions_size = pending->_pending_block_state->trxs.size();
auto orig_state_actions_size = pending->_actions.size();
std::function<void()> callback = [this,
orig_block_transactions_size,
orig_state_transactions_size,
orig_state_actions_size]()
{
pending->_pending_block_state->block->transactions.resize(orig_block_transactions_size);
pending->_pending_block_state->trxs.resize(orig_state_transactions_size);
pending->_actions.resize(orig_state_actions_size);
};
return fc::make_scoped_exit( std::move(callback) );
}
transaction_trace_ptr apply_onerror( const generated_transaction_object& gto,
fc::time_point deadline,
uint64_t cpu_usage,
fc::time_point start ) {
signed_transaction etrx;
// Deliver onerror action containing the failed deferred transaction directly back to the sender.
etrx.actions.emplace_back( vector<permission_level>{{gto.sender,config::active_name}},
gto.sender, onerror::get_name(),
fc::raw::pack( onerror( gto.sender_id, gto.packed_trx.data(), gto.packed_trx.size() ) ) );
etrx.actions.emplace_back( vector<permission_level>{},
onerror( gto.sender_id, gto.packed_trx.data(), gto.packed_trx.size() ) );
etrx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to avoid appearing expired
etrx.set_reference_block( self.head_block_id() );
......@@ -380,30 +399,26 @@ struct controller_impl {
transaction_trace_ptr trace = trx_context.trace;
try {
trx_context.init_for_implicit_trx( deadline, 0, cpu_usage );
trx_context.exec(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trx_context.published = gto.published;
trx_context.trace->action_traces.emplace_back();
trx_context.dispatch_action( trx_context.trace->action_traces.back(), etrx.actions.back(), gto.sender );
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
auto orig_block_transactions_size = pending->_pending_block_state->block->transactions.size();
auto orig_state_actions_size = pending->_actions.size();
try {
trace->receipt = push_receipt( gto.trx_id, transaction_receipt::soft_fail, trace->cpu_usage, trace->net_usage );
fc::move_append( pending->_actions, move(trx_context.executed) );
auto restore = make_block_restore_point();
trace->receipt = push_receipt( gto.trx_id, transaction_receipt::soft_fail, trace->cpu_usage, trace->net_usage );
fc::move_append( pending->_actions, move(trx_context.executed) );
remove_scheduled_transaction( gto );
remove_scheduled_transaction( gto );
emit( self.applied_transaction, trace );
emit( self.applied_transaction, trace );
trx_context.squash();
return trace;
} catch( ... ) {
pending->_pending_block_state->block->transactions.resize(orig_block_transactions_size);
pending->_actions.resize(orig_state_actions_size);
throw;
}
trx_context.squash();
restore.cancel();
return trace;
} catch( const fc::exception& e ) {
trace->hard_except = e;
trace->hard_except_ptr = std::current_exception();
trace->except = e;
trace->except_ptr = std::current_exception();
}
return trace;
}
......@@ -446,19 +461,21 @@ struct controller_impl {
bool abort_on_error = false;
try {
trx_context.init_for_deferred_trx( deadline, gto.published );
bill_to_accounts = trx_context.bill_to_accounts;
max_cpu = trx_context.initial_max_billable_cpu;
trx_context.exec(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trx_context.exec();
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
fc::move_append( pending->_actions, move(trx_context.executed) );
auto restore = make_block_restore_point();
trace->receipt = push_receipt( gto.trx_id,
transaction_receipt::executed,
trace->cpu_usage,
trace->net_usage );
fc::move_append( pending->_actions, move(trx_context.executed) );
remove_scheduled_transaction( gto );
try {
......@@ -469,12 +486,13 @@ struct controller_impl {
}
trx_context.squash();
undo_session.squash();
restore.cancel();
return;
} catch( const fc::exception& e ) {
if( abort_on_error ) // abort_on_error should normally should not be set at this point, but if it is then that means
throw; // something went wrong while emitting the applied_transaction signal and we should abort
trace->soft_except = e;
trace->soft_except_ptr = std::current_exception();
trace->except = e;
trace->except_ptr = std::current_exception();
trace->elapsed = fc::time_point::now() - start;
}
trx_context.undo_session.undo();
......@@ -483,21 +501,16 @@ struct controller_impl {
if( gto.sender != account_name() ) {
// Attempt error handling for the generated transaction.
edump((trace->soft_except->to_detail_string()));
edump((trace->except->to_detail_string()));
auto error_trace = apply_onerror( gto, deadline, trace->cpu_usage, start );
error_trace->failed_dtrx_trace = trace;
trace = error_trace;
if( !trace->hard_except_ptr ) {
if( !trace->except_ptr ) {
undo_session.squash();
return;
}
} else {
// Soft failures are not allowed for delayed transactions; upgrade to hard failure.
trace->hard_except = std::move(trace->soft_except);
trace->soft_except.reset();
trace->hard_except_ptr = trace->soft_except_ptr;
trace->soft_except_ptr = nullptr;
}
// Only hard failure logic below:
trace->cpu_usage = ((trace->cpu_usage + 1023)/1024)*1024; // Round up cpu_usage to nearest multiple of 1024
......@@ -595,43 +608,38 @@ struct controller_impl {
"authorization imposes a delay (${required_delay} sec) greater than the delay specified in transaction header (${specified_delay} sec)",
("required_delay", required_delay.to_seconds())("specified_delay", trx_context.delay.to_seconds()) );
trx_context.exec(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trx_context.exec();
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
trace->elapsed = fc::time_point::now() - start;
auto orig_block_transactions_size = pending->_pending_block_state->block->transactions.size();
auto orig_state_transactions_size = pending->_pending_block_state->trxs.size();
auto orig_state_actions_size = pending->_actions.size();
auto restore = make_block_restore_point();
try {
if( !implicit ) {
transaction_receipt::status_enum s = ( trx_context.delay == fc::seconds(0) )
? transaction_receipt::executed
: transaction_receipt::delayed;
trace->receipt = push_receipt( trx->packed_trx, s, trace->cpu_usage, trace->net_usage );
pending->_pending_block_state->trxs.emplace_back(trx);
} else {
trace->receipt.status = transaction_receipt::executed;
trace->receipt.kcpu_usage = trace->cpu_usage / 1024;
trace->receipt.net_usage_words = trace->net_usage / 8;
}
fc::move_append( pending->_actions, move(trx_context.executed) );
if( !implicit ) {
transaction_receipt::status_enum s = ( trx_context.delay == fc::seconds(0) )
? transaction_receipt::executed
: transaction_receipt::delayed;
trace->receipt = push_receipt( trx->packed_trx, s, trace->cpu_usage, trace->net_usage );
pending->_pending_block_state->trxs.emplace_back(trx);
} else {
transaction_receipt_header r;
r.status = transaction_receipt::executed;
r.kcpu_usage = trace->cpu_usage / 1024;
r.net_usage_words = trace->net_usage / 8;
trace->receipt = r;
}
transaction_trace_notify( trx, trace );
emit( self.applied_transaction, trace );
fc::move_append( pending->_actions, move(trx_context.executed) );
trx_context.squash();
return;
} catch( ... ) {
pending->_pending_block_state->block->transactions.resize(orig_block_transactions_size);
pending->_pending_block_state->trxs.resize(orig_state_transactions_size);
pending->_actions.resize(orig_state_actions_size);
throw;
}
transaction_trace_notify( trx, trace );
emit( self.applied_transaction, trace );
trx_context.squash();
restore.cancel();
return;
} catch( const fc::exception& e ) {
if (trace) {
trace->hard_except = e;
trace->hard_except_ptr = std::current_exception();
}
trace->except = e;
trace->except_ptr = std::current_exception();
}
transaction_trace_notify( trx, trace );
} FC_CAPTURE_AND_RETHROW() } /// push_transaction
......@@ -966,7 +974,7 @@ struct controller_impl {
signed_transaction trx;
trx.actions.emplace_back(std::move(on_block_act));
trx.set_reference_block(self.head_block_id());
trx.expiration = self.pending_block_time() + fc::seconds(1);
trx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to nearest second to avoid appearing expired
return trx;
}
......
......@@ -104,6 +104,9 @@ void apply_eosio_newaccount(apply_context& context) {
} FC_CAPTURE_AND_RETHROW( (create) ) }
void apply_eosio_setcode(apply_context& context) {
const auto& cfg = context.control.get_global_properties().configuration;
context.checktime( cfg.base_setcode_cpu_usage );
auto& db = context.db;
auto act = context.act.data_as<setcode>();
context.require_authorization(act.account);
......@@ -112,6 +115,8 @@ void apply_eosio_setcode(apply_context& context) {
FC_ASSERT( act.vmtype == 0 );
FC_ASSERT( act.vmversion == 0 );
context.checktime( act.code.size() * 20 );
auto code_id = fc::sha256::hash( act.code.data(), (uint32_t)act.code.size() );
wasm_interface::validate(act.code);
......@@ -122,8 +127,6 @@ void apply_eosio_setcode(apply_context& context) {
int64_t old_size = (int64_t)account.code.size() * config::setcode_ram_bytes_multiplier;
int64_t new_size = code_size * config::setcode_ram_bytes_multiplier;
context.checktime( act.code.size() * 20 );
FC_ASSERT( account.code_version != code_id, "contract is already running this version of code" );
// wlog( "set code: ${size}", ("size",act.code.size()));
db.modify( account, [&]( auto& a ) {
......@@ -348,10 +351,6 @@ void apply_eosio_unlinkauth(apply_context& context) {
context.checktime( 3000 );
}
void apply_eosio_onerror(apply_context& context) {
context.require_authorization(config::system_account_name);
}
static const abi_serializer& get_abi_serializer() {
static optional<abi_serializer> _abi_serializer;
if (!_abi_serializer) {
......
......@@ -220,8 +220,8 @@ struct unlinkauth {
};
struct onerror {
uint128_t sender_id;
bytes sent_trx;
uint128_t sender_id;
bytes sent_trx;
onerror( uint128_t sid, const char* data, size_t len )
:sender_id(sid),sent_trx(data,data+len){}
......
......@@ -169,10 +169,14 @@ namespace eosio { namespace chain {
optional<abi_serializer> get_abi_serializer( account_name n )const {
const auto& a = get_account(n);
abi_def abi;
if( abi_serializer::to_abi( a.abi, abi ) )
return abi_serializer(abi);
if( n.good() ) {
try {
const auto& a = get_account( n );
abi_def abi;
if( abi_serializer::to_abi( a.abi, abi ))
return abi_serializer( abi );
} FC_CAPTURE_AND_LOG((n))
}
return optional<abi_serializer>();
}
......
......@@ -34,20 +34,17 @@ namespace eosio { namespace chain {
using transaction_trace_ptr = std::shared_ptr<transaction_trace>;
struct transaction_trace {
transaction_id_type id;
transaction_receipt_header receipt;
fc::microseconds elapsed;
uint64_t net_usage = 0;
uint64_t cpu_usage = 0;
bool scheduled = false;
vector<action_trace> action_traces; ///< disposable
transaction_trace_ptr failed_dtrx_trace;
fc::optional<fc::exception> soft_except;
fc::optional<fc::exception> hard_except;
std::exception_ptr soft_except_ptr;
std::exception_ptr hard_except_ptr;
transaction_id_type id;
fc::optional<transaction_receipt_header> receipt;
fc::microseconds elapsed;
uint64_t net_usage = 0;
uint64_t cpu_usage = 0;
bool scheduled = false;
vector<action_trace> action_traces; ///< disposable
transaction_trace_ptr failed_dtrx_trace;
fc::optional<fc::exception> except;
std::exception_ptr except_ptr;
};
struct block_trace {
......@@ -65,6 +62,6 @@ FC_REFLECT( eosio::chain::base_action_trace,
FC_REFLECT_DERIVED( eosio::chain::action_trace,
(eosio::chain::base_action_trace), (inline_traces) )
FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(net_usage)(cpu_usage)(scheduled)(action_traces)
(failed_dtrx_trace)(soft_except)(hard_except) )
FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(net_usage)(cpu_usage)(scheduled)
(action_traces)(failed_dtrx_trace)(except) )
FC_REFLECT( eosio::chain::block_trace, (elapsed)(cpu_usage)(trx_traces) )
......@@ -27,6 +27,7 @@ namespace eosio { namespace chain {
fc::time_point published );
void exec();
void finalize();
void squash();
inline void add_net_usage( uint64_t u ) { net_usage += u; check_net_usage(); }
......@@ -44,11 +45,12 @@ namespace eosio { namespace chain {
private:
friend struct controller_impl;
friend class apply_context;
action_trace dispatch_action( const action& a, account_name receiver, bool context_free = false, uint32_t recurse_depth = 0 );
inline action_trace dispatch_action( const action& a, bool context_free = false ) {
return dispatch_action(a, a.account, context_free);
void dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free = false, uint32_t recurse_depth = 0 );
inline void dispatch_action( action_trace& trace, const action& a, bool context_free = false ) {
dispatch_action(trace, a, a.account, context_free);
};
void schedule_transaction();
void record_transaction( const transaction_id_type& id, fc::time_point_sec expire );
......@@ -76,7 +78,7 @@ namespace eosio { namespace chain {
uint64_t max_cpu = 0;
uint64_t eager_cpu_limit = 0; ///< cpu usage limit (in virtual CPU instructions) to check against eagerly
/// the maximum number of virtual CPU instructions of the transaction that can be safely billed to the billable accounts
uint64_t initial_max_billable_cpu = 0;
uint64_t initial_max_billable_cpu = 0;
fc::microseconds delay;
bool is_input = false;
......
......@@ -165,6 +165,10 @@ namespace eosio { namespace chain {
deadline = d;
is_input = true;
init( initial_net_usage, initial_cpu_usage );
control.validate_tapos( trx );
control.validate_referenced_accounts( trx );
control.validate_expiration( trx );
record_transaction( id, trx.expiration ); /// checks for dupes
}
void transaction_context::init_for_deferred_trx( fc::time_point d,
......@@ -180,30 +184,25 @@ namespace eosio { namespace chain {
void transaction_context::exec() {
FC_ASSERT( is_initialized, "must first initialize" );
//trx.validate(); // Not needed anymore since overflow is prevented by using uint64_t instead of uint32_t
control.validate_tapos( trx );
control.validate_referenced_accounts( trx );
if( is_input ) { /// signed transaction from user rather than a deferred transaction
control.validate_expiration( trx );
record_transaction( id, trx.expiration ); /// checks for dupes
}
if( apply_context_free ) {
for( const auto& act : trx.context_free_actions ) {
trace->action_traces.emplace_back();
trace->action_traces.back() = dispatch_action( act, true );
dispatch_action( trace->action_traces.back(), act, true );
}
}
if( delay == fc::microseconds() ) {
for( const auto& act : trx.actions ) {
trace->action_traces.emplace_back();
trace->action_traces.back() = dispatch_action( act );
dispatch_action( trace->action_traces.back(), act );
}
} else {
schedule_transaction();
}
}
void transaction_context::finalize() {
FC_ASSERT( is_initialized, "must first initialize" );
add_cpu_usage( validate_ram_usage.size() * config::ram_usage_validation_overhead_per_account );
......@@ -314,7 +313,7 @@ namespace eosio { namespace chain {
}
}
action_trace transaction_context::dispatch_action( const action& a, account_name receiver, bool context_free, uint32_t recurse_depth ) {
void transaction_context::dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free, uint32_t recurse_depth ) {
apply_context acontext( control, *this, a, recurse_depth );
acontext.context_free = context_free;
acontext.receiver = receiver;
......@@ -322,11 +321,15 @@ namespace eosio { namespace chain {
try {
acontext.exec();
} catch( const action_cpu_usage_exceeded& e ) {
trace = move(acontext.trace);
add_action_cpu_usage( acontext.cpu_usage, context_free ); // Will update cpu_usage to latest value and throw appropriate exception
FC_ASSERT(false, "should not have reached here" );
} catch( ... ) {
trace = move(acontext.trace);
throw;
}
return move(acontext.trace);
trace = move(acontext.trace);
}
void transaction_context::schedule_transaction() {
......
......@@ -7,13 +7,16 @@ namespace fc {
public:
template<typename C>
scoped_exit( C&& c ):callback( std::forward<C>(c) ){}
scoped_exit( scoped_exit&& mv ):callback( std::move( mv.callback ) ){}
scoped_exit( scoped_exit&& mv )
:callback( std::move( mv.callback ) ),canceled(mv.canceled)
{
mv.canceled = true;
}
scoped_exit( const scoped_exit& ) = delete;
scoped_exit& operator=( const scoped_exit& ) = delete;
void cancel() { canceled = true; }
~scoped_exit() {
if (!canceled)
try { callback(); } catch( ... ) {}
......@@ -29,6 +32,9 @@ namespace fc {
return *this;
}
void cancel() { canceled = true; }
private:
Callback callback;
bool canceled = false;
......
......@@ -213,10 +213,8 @@ namespace eosio { namespace testing {
if( !control->pending_block_state() )
_start_block(control->head_block_time() + fc::microseconds(config::block_interval_us));
auto r = control->sync_push( std::make_shared<transaction_metadata>(trx), deadline );
if( r->hard_except_ptr ) std::rethrow_exception( r->hard_except_ptr );
if( r->soft_except_ptr ) std::rethrow_exception( r->soft_except_ptr );
if( r->hard_except) throw *r->hard_except;
if( r->soft_except ) throw *r->soft_except;
if( r->except_ptr ) std::rethrow_exception( r->except_ptr );
if( r->except ) throw *r->except;
return r;
} FC_CAPTURE_AND_RETHROW( (transaction_header(trx.get_transaction())) ) }
......@@ -224,10 +222,8 @@ namespace eosio { namespace testing {
if( !control->pending_block_state() )
_start_block(control->head_block_time() + fc::microseconds(config::block_interval_us));
auto r = control->sync_push( std::make_shared<transaction_metadata>(trx), deadline );
if( r->hard_except_ptr ) std::rethrow_exception( r->hard_except_ptr );
if( r->soft_except_ptr ) std::rethrow_exception( r->soft_except_ptr );
if( r->hard_except) throw *r->hard_except;
if( r->soft_except ) throw *r->soft_except;
if( r->except_ptr ) std::rethrow_exception( r->except_ptr );
if( r->except) throw *r->except;
return r;
} FC_CAPTURE_AND_RETHROW( (transaction_header(trx)) ) }
......
The following steps must be taken for the example script to work.
0. Create wallet
0. Create account for eosio.token
0. Create account for scott
0. Create account for exchange
0. Set token contract on eosio.token
0. Create EOS token
0. Issue initial tokens to scott
**Note**:
Deleting the `transactions.txt` file will prevent replay from working.
### Create wallet
`cleos wallet create`
### Create account steps
`cleos create key`
`cleos create key`
`cleos wallet import <private key from step 1>`
`cleos wallet import <private key from step 2>`
`cleos create account eosio <account_name> <public key from step 1> <public key from step 2>`
### Set contract steps
`cleos set contract eosio.token /contracts/eosio.token -p eosio.token@active`
### Create EOS token steps
`cleos push action eosio.token create '{"issuer": "eosio.token", "maximum_supply": "100000.0000 EOS", "can_freeze": 1, "can_recall": 1, "can_whitelist": 1}' -p eosio.token@active`
### Issue token steps
`cleos push action eosio.token issue '{"to": "scott", "quantity": "900.0000 EOS", "memo": "testing"}' -p eosio.token@active`
import json
import pprint
import os
import sys
import subprocess
import time
from subprocess import PIPE
# This key would be different for each user.
KEY_TO_INTERNAL_ACCOUNT='12345'
DEMO_USER='scott'
def main():
try:
command = sys.argv[1]
if command == 'monitor':
setup()
while True:
monitor_exchange()
time.sleep(.1)
elif command == 'transfer':
if len(sys.argv) == 4:
transfer(sys.argv[2], sys.argv[3])
else:
print('Transfer must be called by `python exchange_tutorial.py transfer {} 1.0000`'.format(DEMO_USER))
except subprocess.CalledProcessError as e:
print(e)
print(str(e.stderr, 'utf-8'))
def monitor_exchange():
action_num = get_last_action() + 1
results = cleos('get actions exchange {} 0 -j'.format(action_num))
results = json.loads(results.stdout)
action_list = results['actions']
if len(action_list) == 0:
return
action = action_list[0]
last_irreversible_block = results['last_irreversible_block']
to = action['action_trace']['act']['data']['to']
block_num = action['block_num']
if is_irreversible(block_num, last_irreversible_block):
update_balance(action, to)
set_last_action(action_num)
def update_balance(action, to):
current_balance = get_balance()
new_balance = current_balance
transfer_quantity = action['action_trace']['act']['data']['quantity'].split()[0]
transfer_quantity = float(transfer_quantity)
if to == 'exchange':
if is_valid_deposit(action):
new_balance = current_balance + transfer_quantity
set_balance(new_balance)
elif is_valid_withdrawal(action):
new_balance = current_balance - transfer_quantity
set_balance(new_balance)
def transfer(to, quantity):
if quantity[:-4] != ' EOS':
quantity += ' EOS'
results = cleos('transfer exchange {} "{}" {} -j'.format(to, quantity, KEY_TO_INTERNAL_ACCOUNT))
transaction_info = json.loads(str(results.stdout, 'utf-8'))
transaction_id = transaction_info['transaction_id']
transaction_status = transaction_info['processed']['receipt']['status']
if transaction_status == 'hard_fail':
print('Transaction failed.')
return
add_transactions(transaction_id)
print('Initiated transfer of {} to {}. Transaction id is {}.'.format(quantity, to, transaction_id))
def is_irreversible(block_num, last_irreversible_block):
return block_num <= last_irreversible_block
def is_valid_deposit(action):
account = action['action_trace']['act']['account']
action_name = action['action_trace']['act']['name']
memo = action['action_trace']['act']['data']['memo']
receiver = action['action_trace']['receipt']['receiver']
token = action['action_trace']['act']['data']['quantity'].split()[1]
valid_user = action['action_trace']['act']['data']['to'] == 'exchange'
from_user = action['action_trace']['act']['data']['from']
# Filter only to actions that notify the exchange account.
if receiver != 'exchange':
return False
if (account == 'eosio.token' and
action_name == 'transfer' and
memo == KEY_TO_INTERNAL_ACCOUNT and
valid_user and
from_user == DEMO_USER and
token == 'EOS'):
return True
print('Invalid deposit')
return False
def is_valid_withdrawal(action):
account = action['action_trace']['act']['account']
action_name = action['action_trace']['act']['name']
memo = action['action_trace']['act']['data']['memo']
receiver = action['action_trace']['receipt']['receiver']
token = action['action_trace']['act']['data']['quantity'].split()[1]
transaction_id = action['action_trace']['trx_id']
valid_user = action['action_trace']['act']['data']['from'] == 'exchange'
to_user = action['action_trace']['act']['data']['to']
# Filter only to actions that notify the exchange account.
if receiver != 'exchange':
return False
if (account == 'eosio.token' and
action_name == 'transfer' and
memo == KEY_TO_INTERNAL_ACCOUNT and
valid_user and
to_user == DEMO_USER and
transaction_id in get_transactions() and
token == 'EOS'):
return True
print('Invalid withdrawal')
return False
def cleos(args):
if isinstance(args, list):
command = ['./cleos']
command.extend(args)
else:
command = './cleos ' + args
results = subprocess.run(command, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True, check=True)
return results
def setup():
if not os.path.exists('last_action.txt'):
set_last_action(-1)
if not os.path.exists('balance.txt'):
set_balance(0)
if not os.path.exists('transactions.txt'):
with open('transactions.txt', 'w') as f:
f.write(json.dumps({"transactions": []}))
def get_transactions():
with open('transactions.txt', 'r') as f:
transactions = json.load(f)
return set(transactions['transactions'])
def add_transactions(transaction_id):
transactions = get_transactions()
transactions.add(transaction_id)
with open('transactions.txt', 'w') as f:
transactions = json.dumps({'transactions': list(transactions)})
f.write(transactions)
def get_last_action():
with open('last_action.txt', 'r') as f:
last_action = int(f.read())
return last_action
def set_last_action(action):
with open('last_action.txt', 'w') as f:
f.write(str(action))
def get_balance():
with open('balance.txt', 'r') as f:
balance = float(f.read())
return balance
def set_balance(balance):
with open('balance.txt', 'w') as f:
f.write(str(balance))
print("{}'s balance is: {}".format(DEMO_USER, balance))
if __name__ == '__main__':
main()
......@@ -138,7 +138,7 @@ transaction_trace_ptr CallAction(TESTER& test, T ac, const vector<account_name>&
auto sigs = trx.sign(test.get_private_key(scope[0], "active"), chain_id_type());
trx.get_signature_keys(chain_id_type());
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt.status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
test.produce_block();
return res;
}
......@@ -162,7 +162,7 @@ transaction_trace_ptr CallFunction(TESTER& test, T ac, const vector<char>& data,
auto sigs = trx.sign(test.get_private_key(scope[0], "active"), chain_id_type());
trx.get_signature_keys(chain_id_type() );
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt.status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
test.produce_block();
return res;
}
......@@ -291,7 +291,7 @@ BOOST_FIXTURE_TEST_CASE(action_tests, TESTER) { try {
test.set_transaction_headers(trx);
trx.sign(test.get_private_key(N(inita), "active"), chain_id_type());
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt.status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
};
BOOST_CHECK_EXCEPTION(test_require_notice(*this, raw_bytes, scope), tx_missing_sigs,
[](const tx_missing_sigs& e) {
......@@ -345,7 +345,7 @@ BOOST_FIXTURE_TEST_CASE(action_tests, TESTER) { try {
trx.sign(get_private_key(N(acc3), "active"), chain_id_type());
trx.sign(get_private_key(N(acc4), "active"), chain_id_type());
auto res = push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt.status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
}
uint64_t now = static_cast<uint64_t>( control->head_block_time().time_since_epoch().count() );
......@@ -431,7 +431,7 @@ BOOST_FIXTURE_TEST_CASE(cf_action_tests, TESTER) { try {
auto sigs = trx.sign(get_private_key(N(testapi), "active"), chain_id_type());
auto res = push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt.status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
// attempt to access context free api in non context free action
......@@ -612,7 +612,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_cfa_success, TESTER) try {
auto trace = push_transaction( trx );
BOOST_REQUIRE(trace != nullptr);
if (trace) {
BOOST_REQUIRE_EQUAL(transaction_receipt_header::status_enum::delayed, trace->receipt.status);
BOOST_REQUIRE_EQUAL(transaction_receipt_header::status_enum::delayed, trace->receipt->status);
BOOST_REQUIRE_EQUAL(1, trace->action_traces.size());
}
produce_blocks(10);
......@@ -664,7 +664,7 @@ BOOST_AUTO_TEST_CASE(checktime_fail_tests) { try {
auto sigs = trx.sign(test.get_private_key(N(testapi), "active"), chain_id_type());
trx.get_signature_keys(chain_id_type() );
auto res = test.push_transaction(trx);
BOOST_CHECK_EQUAL(res->receipt.status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(res->receipt->status, transaction_receipt::executed);
test.produce_block();
};
......@@ -800,7 +800,7 @@ BOOST_FIXTURE_TEST_CASE(transaction_tests, TESTER) { try {
control->push_next_scheduled_transaction();
BOOST_CHECK(trace);
BOOST_CHECK_EQUAL(trace->receipt.status, transaction_receipt::soft_fail);
BOOST_CHECK_EQUAL(trace->receipt->status, transaction_receipt::soft_fail);
#endif
// test test_transaction_size
......@@ -824,14 +824,7 @@ BOOST_FIXTURE_TEST_CASE(transaction_tests, TESTER) { try {
return expect_assert_message(e, "inline action recursion depth reached");
}
);
// test send_transaction_expiring_late
BOOST_CHECK_EXCEPTION(CALL_TEST_FUNCTION( *this, "test_transaction", "send_transaction_expiring_late", fc::raw::pack(N(testapi))),
eosio::chain::transaction_exception, [](const eosio::chain::transaction_exception& e) {
return expect_assert_message(e, "Transaction expiration is too far");
}
);
BOOST_REQUIRE_EQUAL( validate(), true );
} FC_LOG_AND_RETHROW() }
......@@ -1656,20 +1649,20 @@ BOOST_FIXTURE_TEST_CASE(datastream_tests, TESTER) { try {
* new api feature test
*************************************************************************************/
BOOST_FIXTURE_TEST_CASE(new_api_feature_tests, TESTER) { try {
produce_blocks(1);
create_account(N(testapi) );
produce_blocks(1);
set_code(N(testapi), test_api_wast);
produce_blocks(1);
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "new_feature", {} ),
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "new_feature", {} ),
assert_exception,
[](const fc::exception& e) {
return expect_assert_message(e, "context.privileged: testapi does not have permission to call this API");
});
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "active_new_feature", {} ),
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "active_new_feature", {} ),
assert_exception,
[](const fc::exception& e) {
return expect_assert_message(e, "context.privileged: testapi does not have permission to call this API");
......@@ -1696,7 +1689,7 @@ BOOST_FIXTURE_TEST_CASE(new_api_feature_tests, TESTER) { try {
CALL_TEST_FUNCTION( *this, "test_transaction", "new_feature", {} );
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "active_new_feature", {} ),
BOOST_CHECK_EXCEPTION( CALL_TEST_FUNCTION( *this, "test_transaction", "active_new_feature", {} ),
assert_exception,
[](const fc::exception& e) {
return expect_assert_message(e, "Unsupported Hardfork Detected");
......
此差异已折叠。
......@@ -126,7 +126,7 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_execute, eosio_msig_tester ) try {
BOOST_REQUIRE( bool(trace) );
BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() );
BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt.status );
BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status );
} FC_LOG_AND_RETHROW()
......@@ -203,7 +203,7 @@ BOOST_FIXTURE_TEST_CASE( propose_approve_by_two, eosio_msig_tester ) try {
BOOST_REQUIRE( bool(trace) );
BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() );
BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt.status );
BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status );
} FC_LOG_AND_RETHROW()
......@@ -279,7 +279,7 @@ BOOST_FIXTURE_TEST_CASE( big_transaction, eosio_msig_tester ) try {
BOOST_REQUIRE( bool(trace) );
BOOST_REQUIRE_EQUAL( 1, trace->action_traces.size() );
BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt.status );
BOOST_REQUIRE_EQUAL( transaction_receipt::executed, trace->receipt->status );
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END()
......@@ -93,7 +93,7 @@ BOOST_FIXTURE_TEST_CASE( basic_test, TESTER ) try {
set_transaction_headers(trx);
trx.sign( get_private_key( N(asserter), "active" ), chain_id_type() );
auto result = push_transaction( trx );
BOOST_CHECK_EQUAL(result->receipt.status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(result->receipt->status, transaction_receipt::executed);
BOOST_CHECK_EQUAL(result->action_traces.size(), 1);
BOOST_CHECK_EQUAL(result->action_traces.at(0).receipt.receiver.to_string(), name(N(asserter)).to_string() );
BOOST_CHECK_EQUAL(result->action_traces.at(0).act.account.to_string(), name(N(asserter)).to_string() );
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册