未验证 提交 0cf2fbac 编写于 作者: D Daniel Larimer 提交者: GitHub

Merge pull request #2971 from EOSIO/send-deferred-replace-parameter

send_deferred_transaction : new parameter "replace_existing"
......@@ -111,13 +111,6 @@ void multisig::exec( account_name proposer, name proposal_name, account_name exe
ds >> trx_header;
eosio_assert( trx_header.expiration >= now(), "transaction expired" );
// Updating expiration is not necessary because the expiration field of a deferred transaction is modified to be valid anyway
/*
trx_header.expiration = now() + 60;
ds.seekp(0);
ds << trx_header;
*/
bytes packed_provided_approvals = pack(prop_it->provided_approvals);
auto res = ::check_transaction_authorization( prop_it->packed_transaction.data(), prop_it->packed_transaction.size(),
(const char*)0, 0,
......
......@@ -366,7 +366,7 @@ namespace eosiosystem {
eosio::transaction out;
out.actions.emplace_back( permission_level{ from, N(active) }, _self, N(refund), from );
out.delay_sec = refund_delay;
out.send( from, receiver );
out.send( from, receiver, true );
const auto& fromv = _voters.get( from );
......
......@@ -58,8 +58,7 @@ extern "C" {
*
* @{
*/
void send_deferred(const uint128_t& sender_id, account_name payer, const char *serialized_transaction, size_t size);
void send_deferred(const uint128_t& sender_id, account_name payer, const char *serialized_transaction, size_t size, uint32_t replace_existing = 0);
void cancel_deferred(const uint128_t& sender_id);
......
......@@ -42,9 +42,9 @@ namespace eosio {
public:
transaction(time exp = now() + 60) : transaction_header( exp ) {}
void send(uint64_t sender_id, account_name payer) const {
void send(uint64_t sender_id, account_name payer, bool replace_existing = false) const {
auto serialize = pack(*this);
send_deferred(sender_id, payer, serialize.data(), serialize.size());
send_deferred(sender_id, payer, serialize.data(), serialize.size(), replace_existing);
}
vector<action> context_free_actions;
......
......@@ -136,6 +136,7 @@ extern "C" {
WASM_TEST_HANDLER_EX(test_transaction, send_action_sender);
WASM_TEST_HANDLER(test_transaction, deferred_print);
WASM_TEST_HANDLER_EX(test_transaction, send_deferred_transaction);
WASM_TEST_HANDLER_EX(test_transaction, send_deferred_transaction_replace);
WASM_TEST_HANDLER(test_transaction, send_deferred_tx_with_dtt_action);
WASM_TEST_HANDLER(test_transaction, cancel_deferred_transaction);
WASM_TEST_HANDLER(test_transaction, send_cf_action);
......
......@@ -167,6 +167,7 @@ struct test_transaction {
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);
static void send_deferred_transaction_replace(uint64_t receiver, uint64_t code, uint64_t action);
static void send_deferred_tx_with_dtt_action();
static void cancel_deferred_transaction();
static void send_cf_action();
......
......@@ -245,6 +245,15 @@ void test_transaction::send_deferred_transaction(uint64_t receiver, uint64_t, ui
trx.send( 0xffffffffffffffff, receiver );
}
void test_transaction::send_deferred_transaction_replace(uint64_t receiver, uint64_t, uint64_t) {
using namespace eosio;
auto trx = transaction();
test_action_action<N(testapi), WASM_TEST_ACTION("test_transaction", "deferred_print")> test_action;
trx.actions.emplace_back(vector<permission_level>{{N(testapi), N(active)}}, test_action);
trx.delay_sec = 2;
trx.send( 0xffffffffffffffff, receiver, true );
}
void test_transaction::send_deferred_tx_with_dtt_action() {
using namespace eosio;
dtt_action dtt_act;
......@@ -258,7 +267,7 @@ void test_transaction::send_deferred_tx_with_dtt_action() {
auto trx = transaction();
trx.actions.emplace_back(deferred_act);
trx.delay_sec = dtt_act.delay_sec;
trx.send( 0xffffffffffffffff, dtt_act.payer );
trx.send( 0xffffffffffffffff, dtt_act.payer, true );
}
......
......@@ -214,7 +214,7 @@ 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 ) {
void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx, bool replace_existing ) {
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
......@@ -257,6 +257,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
uint32_t trx_size = 0;
auto& d = control.db();
if ( auto ptr = d.find<generated_transaction_object,by_sender_id>(boost::make_tuple(receiver, sender_id)) ) {
EOS_ASSERT( replace_existing, deferred_tx_duplicate, "deferred transaction with the same sender_id and payer already exists" );
d.modify<generated_transaction_object>( *ptr, [&]( auto& gtx ) {
gtx.sender = receiver;
gtx.sender_id = sender_id;
......
......@@ -476,7 +476,7 @@ class apply_context {
void exec();
void execute_inline( action&& a );
void execute_context_free_inline( action&& a );
void schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx );
void schedule_deferred_transaction( const uint128_t& sender_id, account_name payer, transaction&& trx, bool replace_existing );
void cancel_deferred_transaction( const uint128_t& sender_id, account_name sender );
void cancel_deferred_transaction( const uint128_t& sender_id ) { cancel_deferred_transaction(sender_id, receiver); }
......
......@@ -156,6 +156,8 @@ namespace eosio { namespace chain {
3040007, "Invalid Reference Block" )
FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate, transaction_exception,
3040008, "duplicate transaction" )
FC_DECLARE_DERIVED_EXCEPTION( deferred_tx_duplicate, transaction_exception,
3040009, "duplicate deferred transaction" )
FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception,
......
......@@ -1250,11 +1250,11 @@ class transaction_api : public context_aware_api {
context.execute_context_free_inline(std::move(act));
}
void send_deferred( const uint128_t& sender_id, account_name payer, array_ptr<char> data, size_t data_len ) {
void send_deferred( const uint128_t& sender_id, account_name payer, array_ptr<char> data, size_t data_len, uint32_t replace_existing) {
try {
transaction trx;
fc::raw::unpack<transaction>(data, data_len, trx);
context.schedule_deferred_transaction(sender_id, payer, std::move(trx));
context.schedule_deferred_transaction(sender_id, payer, std::move(trx), replace_existing);
} FC_CAPTURE_AND_RETHROW((fc::to_hex(data, data_len)));
}
......@@ -1754,7 +1754,7 @@ REGISTER_INTRINSICS(context_free_transaction_api,
REGISTER_INTRINSICS(transaction_api,
(send_inline, void(int, int) )
(send_context_free_inline, void(int, int) )
(send_deferred, void(int, int64_t, int, int) )
(send_deferred, void(int, int64_t, int, int, int32_t) )
(cancel_deferred, void(int) )
);
......
......@@ -879,13 +879,36 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
produce_blocks(10);
//schedule twice (second deferred transaction should replace first one)
//schedule twice without replace_existing flag (second deferred transaction should replace first one)
{
transaction_trace_ptr trace;
uint32_t count = 0;
auto c = control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t && t->scheduled) { trace = t; ++count; } } );
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {});
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {});
BOOST_CHECK_THROW(CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction", {}), deferred_tx_duplicate);
produce_blocks( 3 );
//check that only one deferred transaction executed
auto dtrxs = control->get_scheduled_transactions();
BOOST_CHECK_EQUAL(dtrxs.size(), 1);
for (const auto& trx: dtrxs) {
control->push_scheduled_transaction(trx, fc::time_point::maximum());
}
BOOST_CHECK_EQUAL(1, count);
BOOST_CHECK(trace);
BOOST_CHECK_EQUAL( 1, trace->action_traces.size() );
c.disconnect();
}
produce_blocks(10);
//schedule twice with replace_existing flag (second deferred transaction should replace first one)
{
transaction_trace_ptr trace;
uint32_t count = 0;
auto c = control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t && t->scheduled) { trace = t; ++count; } } );
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction_replace", {});
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_transaction_replace", {});
produce_blocks( 3 );
//check that only one deferred transaction executed
......@@ -966,7 +989,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
("code", name(dtt_act3.deferred_account))
("type", name(dtt_act3.deferred_action))
("requirement", name(dtt_act3.permission_name)));
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act3));
CALL_TEST_FUNCTION(*this, "test_transaction", "send_deferred_tx_with_dtt_action", fc::raw::pack(dtt_act3)); //will replace existing transaction
// If we make testapi account to be priviledged account:
// - the deferred transaction will work no matter who is the payer
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册