未验证 提交 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 ...@@ -111,13 +111,6 @@ void multisig::exec( account_name proposer, name proposal_name, account_name exe
ds >> trx_header; ds >> trx_header;
eosio_assert( trx_header.expiration >= now(), "transaction expired" ); 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); bytes packed_provided_approvals = pack(prop_it->provided_approvals);
auto res = ::check_transaction_authorization( prop_it->packed_transaction.data(), prop_it->packed_transaction.size(), auto res = ::check_transaction_authorization( prop_it->packed_transaction.data(), prop_it->packed_transaction.size(),
(const char*)0, 0, (const char*)0, 0,
......
...@@ -366,7 +366,7 @@ namespace eosiosystem { ...@@ -366,7 +366,7 @@ namespace eosiosystem {
eosio::transaction out; eosio::transaction out;
out.actions.emplace_back( permission_level{ from, N(active) }, _self, N(refund), from ); out.actions.emplace_back( permission_level{ from, N(active) }, _self, N(refund), from );
out.delay_sec = refund_delay; out.delay_sec = refund_delay;
out.send( from, receiver ); out.send( from, receiver, true );
const auto& fromv = _voters.get( from ); const auto& fromv = _voters.get( from );
......
...@@ -58,8 +58,7 @@ extern "C" { ...@@ -58,8 +58,7 @@ extern "C" {
* *
* @{ * @{
*/ */
void send_deferred(const uint128_t& sender_id, account_name payer, const char *serialized_transaction, size_t size, uint32_t replace_existing = 0);
void send_deferred(const uint128_t& sender_id, account_name payer, const char *serialized_transaction, size_t size);
void cancel_deferred(const uint128_t& sender_id); void cancel_deferred(const uint128_t& sender_id);
......
...@@ -42,9 +42,9 @@ namespace eosio { ...@@ -42,9 +42,9 @@ namespace eosio {
public: public:
transaction(time exp = now() + 60) : transaction_header( exp ) {} 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); 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; vector<action> context_free_actions;
......
...@@ -136,6 +136,7 @@ extern "C" { ...@@ -136,6 +136,7 @@ extern "C" {
WASM_TEST_HANDLER_EX(test_transaction, send_action_sender); WASM_TEST_HANDLER_EX(test_transaction, send_action_sender);
WASM_TEST_HANDLER(test_transaction, deferred_print); 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);
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, send_deferred_tx_with_dtt_action);
WASM_TEST_HANDLER(test_transaction, cancel_deferred_transaction); WASM_TEST_HANDLER(test_transaction, cancel_deferred_transaction);
WASM_TEST_HANDLER(test_transaction, send_cf_action); WASM_TEST_HANDLER(test_transaction, send_cf_action);
......
...@@ -167,6 +167,7 @@ struct test_transaction { ...@@ -167,6 +167,7 @@ struct test_transaction {
static void send_action_sender(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 deferred_print();
static void send_deferred_transaction(uint64_t receiver, uint64_t code, uint64_t action); 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 send_deferred_tx_with_dtt_action();
static void cancel_deferred_transaction(); static void cancel_deferred_transaction();
static void send_cf_action(); static void send_cf_action();
......
...@@ -245,6 +245,15 @@ void test_transaction::send_deferred_transaction(uint64_t receiver, uint64_t, ui ...@@ -245,6 +245,15 @@ void test_transaction::send_deferred_transaction(uint64_t receiver, uint64_t, ui
trx.send( 0xffffffffffffffff, receiver ); 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() { void test_transaction::send_deferred_tx_with_dtt_action() {
using namespace eosio; using namespace eosio;
dtt_action dtt_act; dtt_action dtt_act;
...@@ -258,7 +267,7 @@ void test_transaction::send_deferred_tx_with_dtt_action() { ...@@ -258,7 +267,7 @@ void test_transaction::send_deferred_tx_with_dtt_action() {
auto trx = transaction(); auto trx = transaction();
trx.actions.emplace_back(deferred_act); trx.actions.emplace_back(deferred_act);
trx.delay_sec = dtt_act.delay_sec; 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 ) { ...@@ -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" ); 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.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 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 ...@@ -257,6 +257,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
uint32_t trx_size = 0; uint32_t trx_size = 0;
auto& d = control.db(); auto& d = control.db();
if ( auto ptr = d.find<generated_transaction_object,by_sender_id>(boost::make_tuple(receiver, sender_id)) ) { 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 ) { d.modify<generated_transaction_object>( *ptr, [&]( auto& gtx ) {
gtx.sender = receiver; gtx.sender = receiver;
gtx.sender_id = sender_id; gtx.sender_id = sender_id;
......
...@@ -476,7 +476,7 @@ class apply_context { ...@@ -476,7 +476,7 @@ class apply_context {
void exec(); void exec();
void execute_inline( action&& a ); void execute_inline( action&& a );
void execute_context_free_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, account_name sender );
void cancel_deferred_transaction( const uint128_t& sender_id ) { cancel_deferred_transaction(sender_id, receiver); } void cancel_deferred_transaction( const uint128_t& sender_id ) { cancel_deferred_transaction(sender_id, receiver); }
......
...@@ -156,6 +156,8 @@ namespace eosio { namespace chain { ...@@ -156,6 +156,8 @@ namespace eosio { namespace chain {
3040007, "Invalid Reference Block" ) 3040007, "Invalid Reference Block" )
FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate, transaction_exception, FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate, transaction_exception,
3040008, "duplicate transaction" ) 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, FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception,
......
...@@ -1250,11 +1250,11 @@ class transaction_api : public context_aware_api { ...@@ -1250,11 +1250,11 @@ class transaction_api : public context_aware_api {
context.execute_context_free_inline(std::move(act)); 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 { try {
transaction trx; transaction trx;
fc::raw::unpack<transaction>(data, data_len, 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))); } FC_CAPTURE_AND_RETHROW((fc::to_hex(data, data_len)));
} }
...@@ -1754,7 +1754,7 @@ REGISTER_INTRINSICS(context_free_transaction_api, ...@@ -1754,7 +1754,7 @@ REGISTER_INTRINSICS(context_free_transaction_api,
REGISTER_INTRINSICS(transaction_api, REGISTER_INTRINSICS(transaction_api,
(send_inline, void(int, int) ) (send_inline, void(int, int) )
(send_context_free_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) ) (cancel_deferred, void(int) )
); );
......
...@@ -879,13 +879,36 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { ...@@ -879,13 +879,36 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
produce_blocks(10); 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; transaction_trace_ptr trace;
uint32_t count = 0; uint32_t count = 0;
auto c = control->applied_transaction.connect([&]( const transaction_trace_ptr& t) { if (t && t->scheduled) { trace = t; ++count; } } ); 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", {});
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 ); produce_blocks( 3 );
//check that only one deferred transaction executed //check that only one deferred transaction executed
...@@ -966,7 +989,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try { ...@@ -966,7 +989,7 @@ BOOST_FIXTURE_TEST_CASE(deferred_transaction_tests, TESTER) { try {
("code", name(dtt_act3.deferred_account)) ("code", name(dtt_act3.deferred_account))
("type", name(dtt_act3.deferred_action)) ("type", name(dtt_act3.deferred_action))
("requirement", name(dtt_act3.permission_name))); ("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: // If we make testapi account to be priviledged account:
// - the deferred transaction will work no matter who is the payer // - 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.
先完成此消息的编辑!
想要评论请 注册