Skip to content

Commit 33654f3

Browse files
authored
Merge pull request #966 from AntelopeIO/fix_trace_api_get_trx_block_number
[1.0.3] Trace_API: Fix get_transaction_trace endpoint fails to return transaction trace if the initial block including the transaction forks out
2 parents 84932fb + c0937e4 commit 33654f3

File tree

2 files changed

+241
-8
lines changed

2 files changed

+241
-8
lines changed

plugins/trace_api_plugin/store_provider.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ namespace eosio::trace_api {
107107
slice_number = 0;
108108
}
109109

110-
uint32_t trx_block_num = 0; // number of the block that contains the target trx
111-
uint32_t trx_entries = 0; // number of entries that contain the target trx
110+
std::set<uint32_t> trx_block_nums;
112111
while (true){
113112
const bool found = _slice_directory.find_trx_id_slice(slice_number, open_state::read, trx_id_file);
114113
if( !found )
@@ -123,16 +122,21 @@ namespace eosio::trace_api {
123122
fc::raw::unpack(ds, entry);
124123
if (std::holds_alternative<block_trxs_entry>(entry)) {
125124
const auto& trxs_entry = std::get<block_trxs_entry>(entry);
125+
bool found_in_block = false;
126126
for (auto i = 0U; i < trxs_entry.ids.size(); ++i) {
127127
if (trxs_entry.ids[i] == trx_id) {
128-
trx_entries++;
129-
trx_block_num = trxs_entry.block_num;
128+
trx_block_nums.insert(trxs_entry.block_num);
129+
found_in_block = true;
130+
break;
130131
}
131132
}
133+
// block can be seen again when a fork happens, if not in the new block remove it from blocks that have the trx
134+
if (!found_in_block)
135+
trx_block_nums.erase(trxs_entry.block_num);
132136
} else if (std::holds_alternative<lib_entry_v0>(entry)) {
133137
auto lib = std::get<lib_entry_v0>(entry).lib;
134-
if (trx_entries > 0 && lib >= trx_block_num) {
135-
return trx_block_num;
138+
if (!trx_block_nums.empty() && lib >= *(--trx_block_nums.end())) {
139+
return *(--trx_block_nums.end()); // *(--trx_block_nums.end()) is the block with highest block number which is final
136140
}
137141
} else {
138142
FC_ASSERT( false, "unpacked data should be a block_trxs_entry or a lib_entry_v0" );;
@@ -143,8 +147,8 @@ namespace eosio::trace_api {
143147
}
144148

145149
// transaction's block is not irreversible
146-
if (trx_entries > 0)
147-
return trx_block_num;
150+
if (!trx_block_nums.empty())
151+
return *(--trx_block_nums.end());
148152

149153
return get_block_n{};
150154
}

plugins/trace_api_plugin/test/test_trace_file.cpp

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,5 +1066,234 @@ BOOST_AUTO_TEST_SUITE(slice_tests)
10661066
BOOST_REQUIRE(!block2);
10671067
}
10681068

1069+
// Verify basics of get_trx_block_number()
1070+
BOOST_FIXTURE_TEST_CASE(test_get_trx_block_number_basic, test_fixture)
1071+
{
1072+
chain::transaction_id_type trx_id1 = "0000000000000000000000000000000000000000000000000000000000000001"_h;
1073+
chain::transaction_id_type trx_id2 = "0000000000000000000000000000000000000000000000000000000000000002"_h;
1074+
uint32_t block_num1 = 1;
1075+
uint32_t block_num2 = 2;
1076+
1077+
transaction_trace_v2 trx_trace1 {
1078+
trx_id1,
1079+
actions,
1080+
fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
1081+
10,
1082+
5,
1083+
{ chain::signature_type() },
1084+
{ chain::time_point_sec(), 1, 0, 100, 50, 0 }
1085+
};
1086+
1087+
transaction_trace_v2 trx_trace2 {
1088+
trx_id2,
1089+
actions,
1090+
fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
1091+
10,
1092+
5,
1093+
{ chain::signature_type() },
1094+
{ chain::time_point_sec(), 1, 0, 100, 50, 0 }
1095+
};
1096+
1097+
// block 1 includes trx_trace1
1098+
block_trace_v2 block_trace1 {
1099+
"b000000000000000000000000000000000000000000000000000000000000001"_h,
1100+
block_num1,
1101+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1102+
chain::block_timestamp_type(0),
1103+
"test"_n,
1104+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1105+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1106+
0,
1107+
std::vector<transaction_trace_v2> {
1108+
trx_trace1
1109+
}
1110+
};
1111+
1112+
// block 2 includes trx_trace2
1113+
block_trace_v2 block_trace2 {
1114+
"b000000000000000000000000000000000000000000000000000000000000003"_h,
1115+
block_num2,
1116+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1117+
chain::block_timestamp_type(0),
1118+
"test"_n,
1119+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1120+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1121+
0,
1122+
std::vector<transaction_trace_v2> {
1123+
trx_trace2
1124+
}
1125+
};
1126+
1127+
block_trxs_entry block_trxs_entry1 {
1128+
.ids = {trx_id1},
1129+
.block_num = block_num1
1130+
};
1131+
1132+
block_trxs_entry block_trxs_entry2 {
1133+
.ids = {trx_id2},
1134+
.block_num = block_num2
1135+
};
1136+
1137+
fc::temp_directory tempdir;
1138+
store_provider sp(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
1139+
1140+
// on_accepted_block of block 1
1141+
sp.append(block_trace1);
1142+
sp.append_trx_ids(block_trxs_entry1);
1143+
1144+
// block 1 is reversible and get_trx_block_number should find trx_id1 in block 1
1145+
get_block_n block_num = sp.get_trx_block_number(trx_id1, {});
1146+
BOOST_REQUIRE(block_num);
1147+
BOOST_REQUIRE_EQUAL(*block_num, block_num1);
1148+
1149+
// block 1 becomes final
1150+
sp.append_lib(block_num1);
1151+
1152+
// get_trx_block_number should find trx_id1 in block 1
1153+
block_num = sp.get_trx_block_number(trx_id1, {});
1154+
BOOST_REQUIRE(block_num);
1155+
BOOST_REQUIRE_EQUAL(*block_num, block_num1);
1156+
1157+
// on_accepted_block of block 2
1158+
sp.append(block_trace2);
1159+
sp.append_trx_ids(block_trxs_entry2);
1160+
1161+
// get_trx_block_number should find both trx_id1 and trx_id2
1162+
block_num = sp.get_trx_block_number(trx_id1, {});
1163+
BOOST_REQUIRE(block_num);
1164+
BOOST_REQUIRE_EQUAL(*block_num, block_num1);
1165+
block_num = sp.get_trx_block_number(trx_id2, {});
1166+
BOOST_REQUIRE(block_num);
1167+
BOOST_REQUIRE_EQUAL(*block_num, block_num2);
1168+
1169+
// block 2 becomes final
1170+
sp.append_lib(block_num2);
1171+
1172+
// get_trx_block_number should still find both trx_id1 and trx_id2
1173+
block_num = sp.get_trx_block_number(trx_id1, {});
1174+
BOOST_REQUIRE(block_num);
1175+
BOOST_REQUIRE_EQUAL(*block_num, block_num1);
1176+
block_num = sp.get_trx_block_number(trx_id2, {});
1177+
BOOST_REQUIRE(block_num);
1178+
BOOST_REQUIRE_EQUAL(*block_num, block_num2);
1179+
}
1180+
1181+
// This test verifies the bug reported by https://github.com/AntelopeIO/spring/issues/942
1182+
// is fixed. The bug was if the block containing a transaction forked out,
1183+
// get_trx_block_number() always returned the latest block whose block number was
1184+
// the same as the initial block's, but this latest block might not include the
1185+
// transaction anymore.
1186+
BOOST_FIXTURE_TEST_CASE(test_get_trx_block_number_forked, test_fixture)
1187+
{
1188+
chain::transaction_id_type target_trx_id = "0000000000000000000000000000000000000000000000000000000000000001"_h;
1189+
uint32_t initial_block_num = 1;
1190+
uint32_t final_block_num = 3;
1191+
1192+
transaction_trace_v2 trx_trace1 {
1193+
target_trx_id,
1194+
actions,
1195+
fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
1196+
10,
1197+
5,
1198+
{ chain::signature_type() },
1199+
{ chain::time_point_sec(), 1, 0, 100, 50, 0 }
1200+
};
1201+
1202+
transaction_trace_v2 trx_trace2 {
1203+
"0000000000000000000000000000000000000000000000000000000000000002"_h,
1204+
actions,
1205+
fc::enum_type<uint8_t, chain::transaction_receipt_header::status_enum>{chain::transaction_receipt_header::status_enum::executed},
1206+
10,
1207+
5,
1208+
{ chain::signature_type() },
1209+
{ chain::time_point_sec(), 1, 0, 100, 50, 0 }
1210+
};
1211+
1212+
// Initial block including trx_trace1
1213+
block_trace_v2 initial_block_trace {
1214+
"b000000000000000000000000000000000000000000000000000000000000001"_h,
1215+
initial_block_num,
1216+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1217+
chain::block_timestamp_type(0),
1218+
"test"_n,
1219+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1220+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1221+
0,
1222+
std::vector<transaction_trace_v2> {
1223+
trx_trace1
1224+
}
1225+
};
1226+
1227+
// Initial block is forked. The original trx_trace1 is forked out and
1228+
// replaced by trx_trace2.
1229+
block_trace_v2 forked_block_trace = initial_block_trace;
1230+
forked_block_trace.transactions = std::vector<transaction_trace_v2> { trx_trace2 };
1231+
1232+
// Final block including original trx_trace1
1233+
block_trace_v2 final_block_trace {
1234+
"b000000000000000000000000000000000000000000000000000000000000003"_h,
1235+
final_block_num,
1236+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1237+
chain::block_timestamp_type(0),
1238+
"test"_n,
1239+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1240+
"0000000000000000000000000000000000000000000000000000000000000000"_h,
1241+
0,
1242+
std::vector<transaction_trace_v2> {
1243+
trx_trace1
1244+
}
1245+
};
1246+
1247+
block_trxs_entry initial_block_trxs_entry {
1248+
.ids = {target_trx_id},
1249+
.block_num = initial_block_num
1250+
};
1251+
1252+
block_trxs_entry forked_block_trxs_entry {
1253+
.ids = {trx_trace2.id},
1254+
.block_num = initial_block_num
1255+
};
1256+
1257+
block_trxs_entry final_block_trxs_entry {
1258+
.ids = {target_trx_id},
1259+
.block_num = final_block_num
1260+
};
1261+
1262+
fc::temp_directory tempdir;
1263+
store_provider sp(tempdir.path(), 100, std::optional<uint32_t>(), std::optional<uint32_t>(), 0);
1264+
1265+
// on_accepted_block of the initial block
1266+
sp.append(initial_block_trace); // block 1
1267+
sp.append_trx_ids(initial_block_trxs_entry); // block 1
1268+
1269+
// target trx is in the first block (is still reversible)
1270+
get_block_n block_num = sp.get_trx_block_number(target_trx_id, {});
1271+
BOOST_REQUIRE(block_num);
1272+
BOOST_REQUIRE_EQUAL(*block_num, initial_block_num);
1273+
1274+
// initial block forks out
1275+
sp.append(forked_block_trace); // block 1
1276+
sp.append_trx_ids(forked_block_trxs_entry); // block 1
1277+
1278+
// forked block becomes final
1279+
sp.append_lib(initial_block_num); // block 1
1280+
1281+
// target trx is forked out. block 1 does not include
1282+
// target_trx (trx_trace1) but trx_trace2;
1283+
// therefore no block is found for target_trx_id.
1284+
block_num = sp.get_trx_block_number(target_trx_id, {});
1285+
BOOST_REQUIRE(!block_num);
1286+
1287+
// on_accepted_block of the final block
1288+
sp.append(final_block_trace); // block 3
1289+
sp.append_trx_ids(final_block_trxs_entry); // block 3
1290+
1291+
// final block becomes final
1292+
sp.append_lib(final_block_num); // block 3
1293+
1294+
block_num = sp.get_trx_block_number(target_trx_id, {});
1295+
BOOST_REQUIRE(block_num);
1296+
BOOST_REQUIRE_EQUAL(*block_num, final_block_num); // target trx is in final block
1297+
}
10691298

10701299
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)