Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0.3] Trace_API: Fix get_transaction_trace endpoint fails to return transaction trace if the initial block including the transaction forks out #966

Open
wants to merge 9 commits into
base: release/1.0
Choose a base branch
from
49 changes: 47 additions & 2 deletions plugins/trace_api_plugin/store_provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,43 @@ namespace eosio::trace_api {
return std::make_tuple( entry.value(), irreversible );
}

// Returns true if `block` includes the transaction identified by `trx_id`
static bool block_has_trx(const get_block_t& block, const chain::transaction_id_type& trx_id) {
bool has_trx = false;
data_log_entry block_trace = std::get<0>(*block);

if (std::holds_alternative<block_trace_v0> (block_trace)) {
auto& trace_v0 = std::get<block_trace_v0>(block_trace);
auto itr = std::find_if(trace_v0.transactions.begin(), trace_v0.transactions.end(),
[&](const auto& trx) { return trx.id == trx_id; });
has_trx = (itr != trace_v0.transactions.end());
} else if (std::holds_alternative<block_trace_v1>(block_trace)) {
auto& trace_v1 = std::get<block_trace_v1>(block_trace);
auto itr = std::find_if(trace_v1.transactions_v1.begin(), trace_v1.transactions_v1.end(),
[&](const auto& trx) { return trx.id == trx_id; });
has_trx = (itr != trace_v1.transactions_v1.end());
} else if (std::holds_alternative<block_trace_v2>(block_trace)) {
auto& trace_v2 = std::get<block_trace_v2>(block_trace);
if (std::holds_alternative<std::vector<transaction_trace_v2>>(trace_v2.transactions)) {
auto& trxs = std::get<std::vector<transaction_trace_v2>>(trace_v2.transactions);
auto itr = std::find_if(trxs.begin(), trxs.end(),
[&](const auto& trx) { return trx.id == trx_id; });
has_trx = (itr != trxs.end());
} else if (std::holds_alternative<std::vector<transaction_trace_v3>>(trace_v2.transactions)) {
auto& trxs = std::get<std::vector<transaction_trace_v3>>(trace_v2.transactions);
auto itr = std::find_if(trxs.begin(), trxs.end(),
[&](const auto& trx) { return trx.id == trx_id; });
has_trx = (itr != trxs.end());
} else {
FC_ASSERT( false, "block_trace_v2 should contain transaction_trace_v2 or transaction_trace_v3 transactions" );;
}
} else {
FC_ASSERT( false, "block_trace should be a block_trace_v0, block_trace_v1, or a block_trace_v3" );
}

return has_trx;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't really tell for sure, but would a visit() + overloaded lambda be more concise or cleaner?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function is no longer needed.


get_block_n store_provider::get_trx_block_number(const chain::transaction_id_type& trx_id, std::optional<uint32_t> minimum_irreversible_history_blocks, const yield_function& yield) {
fc::cfile trx_id_file;
int32_t slice_number;
Expand Down Expand Up @@ -125,8 +162,16 @@ namespace eosio::trace_api {
const auto& trxs_entry = std::get<block_trxs_entry>(entry);
for (auto i = 0U; i < trxs_entry.ids.size(); ++i) {
if (trxs_entry.ids[i] == trx_id) {
trx_entries++;
trx_block_num = trxs_entry.block_num;
// Update trx_block_num only if the block contains the transaction.
// This is to prevent the problem where the initial block containing
// the transaction forks out and the transaction is not in the latest
// block which has the same block number as the initial block.

get_block_t block = get_block(trxs_entry.block_num);
if (block && block_has_trx(block, trx_id)) {
trx_entries++;
trx_block_num = trxs_entry.block_num;
}
}
}
} else if (std::holds_alternative<lib_entry_v0>(entry)) {
Expand Down
101 changes: 101 additions & 0 deletions plugins/trace_api_plugin/test/test_trace_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1066,5 +1066,106 @@ BOOST_AUTO_TEST_SUITE(slice_tests)
BOOST_REQUIRE(!block2);
}

// This test verifies the bug reported by https://github.com/AntelopeIO/spring/issues/942
// is fixed. The bug was if the block containing a transaction forked out,
// get_trx_block_number() always returned the latest block whose block number was
// the same as the initial block's, but this latest block might not include the
// transaction anymore.
BOOST_FIXTURE_TEST_CASE(test_get_trx_block_number_forked, test_fixture)
{
chain::transaction_id_type target_trx_id = "0000000000000000000000000000000000000000000000000000000000000001"_h;
uint32_t initial_block_num = 1;
uint32_t final_block_num = 3;

transaction_trace_v2 trx_trace1 {
target_trx_id,
actions,
fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
10,
5,
{ chain::signature_type() },
{ chain::time_point_sec(), 1, 0, 100, 50, 0 }
};

transaction_trace_v2 trx_trace2 {
"0000000000000000000000000000000000000000000000000000000000000002"_h,
actions,
fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
10,
5,
{ chain::signature_type() },
{ chain::time_point_sec(), 1, 0, 100, 50, 0 }
};

// Initial block including trx_trace1
block_trace_v2 initial_block_trace {
"b000000000000000000000000000000000000000000000000000000000000001"_h,
initial_block_num,
"0000000000000000000000000000000000000000000000000000000000000000"_h,
chain::block_timestamp_type(0),
"test"_n,
"0000000000000000000000000000000000000000000000000000000000000000"_h,
"0000000000000000000000000000000000000000000000000000000000000000"_h,
0,
std::vector<transaction_trace_v2> {
trx_trace1
}
};

// Initial block is forked. The original trx_trace1 is forked out and
// replaced by trx_trace2.
block_trace_v2 forked_block_trace = initial_block_trace;
forked_block_trace.transactions = std::vector<transaction_trace_v2> { trx_trace2 };

// Final block including original trx_trace1
block_trace_v2 final_block_trace {
"b000000000000000000000000000000000000000000000000000000000000003"_h,
final_block_num,
"0000000000000000000000000000000000000000000000000000000000000000"_h,
chain::block_timestamp_type(0),
"test"_n,
"0000000000000000000000000000000000000000000000000000000000000000"_h,
"0000000000000000000000000000000000000000000000000000000000000000"_h,
0,
std::vector<transaction_trace_v2> {
trx_trace1
}
};

block_trxs_entry initial_block_trxs_entry {
.ids = {target_trx_id},
.block_num = initial_block_num
};

block_trxs_entry final_block_trxs_entry {
.ids = {target_trx_id},
.block_num = final_block_num
};

fc::temp_directory tempdir;
store_provider sp(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);

// on_accepted_block of the initial block
sp.append(initial_block_trace);
sp.append_trx_ids(initial_block_trxs_entry);

// initial block forks out
sp.append(forked_block_trace);

// forked block becomes final
sp.append_lib(initial_block_num);

// on_accepted_block of the final block
sp.append(final_block_trace);
sp.append_trx_ids(final_block_trxs_entry);

// final block becomes final
sp.append_lib(final_block_num);

get_block_n block_num = sp.get_trx_block_number(target_trx_id, {});

BOOST_REQUIRE(block_num);
BOOST_REQUIRE_EQUAL(*block_num, final_block_num); // target trx is in final block
}

BOOST_AUTO_TEST_SUITE_END()