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

Add get_transaction_status to chain_plugin #145

Merged
35 changes: 35 additions & 0 deletions docs/02_cleos/03_command-reference/get/transaction-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Description

Gets current blockchain state and, if available, transaction information given the transaction id

## Position Parameters

- `id` _TEXT_ - The string ID of the transaction to retrieve status about (required)

## Options
- `-h` - --help Print this help message and exit
## Example


```sh
cleos get transaction-status 6438df82216dfaf46978f703fb818b49110dbfc5d9b521b5d08c342277438b29
```

This command simply returns the current chain status and transaction status information (if available).

```json
{
"state": "IN_BLOCK",
"block_number": 90,
"block_id": "0000005accfd59ba80a05380f60d51687406337b2aedd28b7daa33fdb8c16b5a",
"block_timestamp": "2022-04-27T16:11:26.500",
"expiration": "2022-04-27T16:11:56.000",
"head_number": 186,
"head_id": "000000bab27da51f76f483bb629b532510c22e2eb1acc632f5b37b421adecf63",
"head_timestamp": "2022-04-27T16:12:14.500",
"irreversible_number": 25,
"irreversible_id": "0000001922118216bc16bf2c60d950598d80af3beca820eab751f7beecdb29e4",
"irreversible_timestamp": "2022-04-27T16:10:54.000",
"last_tracked_block_id": "000000129cee97f3e27312f0184d52d006a470f0e620553dfb4c5b4f3c856ab2"
}
```
1 change: 1 addition & 0 deletions plugins/chain_api_plugin/chain_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ void chain_api_plugin::plugin_startup() {
CHAIN_RO_CALL(abi_bin_to_json, 200),
CHAIN_RO_CALL(get_required_keys, 200),
CHAIN_RO_CALL(get_transaction_id, 200),
CHAIN_RO_CALL_WITH_400(get_transaction_status, 200),
CHAIN_RO_CALL_ASYNC_WITH_400(compute_transaction, chain_apis::read_only::compute_transaction_results, 200),
CHAIN_RW_CALL_ASYNC(push_block, chain_apis::read_write::push_block_results, 202),
CHAIN_RW_CALL_ASYNC(push_transaction, chain_apis::read_write::push_transaction_results, 202),
Expand Down
47 changes: 43 additions & 4 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,9 +794,15 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
my->_trx_signals_processor.emplace();
if (my->_trx_finality_status_processing) {
my->_trx_signals_processor->register_callbacks(
[]( const chain::signals_processor::trx_deque& trxs, const chain::block_state_ptr& blk ) {},
[]( const chain::block_state_ptr& blk ) {},
[]( uint32_t block_num ) {}
[this]( const chain::signals_processor::trx_deque& trxs, const chain::block_state_ptr& blk ) {
my->_trx_finality_status_processing->signal_applied_transactions(trxs, blk);
},
[this]( const chain::block_state_ptr& blk ) {
my->_trx_finality_status_processing->signal_irreversible_block(blk);
},
[this]( uint32_t block_num ) {
my->_trx_finality_status_processing->signal_block_start(block_num);
}
);
}
}
Expand Down Expand Up @@ -1362,7 +1368,7 @@ chain_apis::read_write chain_plugin::get_read_write_api() {
}

chain_apis::read_only chain_plugin::get_read_only_api() const {
return chain_apis::read_only(chain(), my->_account_query_db, get_abi_serializer_max_time(), my->producer_plug);
return chain_apis::read_only(chain(), my->_account_query_db, get_abi_serializer_max_time(), my->producer_plug, my->_trx_finality_status_processing.get());
}


Expand Down Expand Up @@ -1683,6 +1689,39 @@ read_only::get_info_results read_only::get_info(const read_only::get_info_params
};
}

read_only::get_transaction_status_results read_only::get_transaction_status(const read_only::get_transaction_status_params& param) const {
transaction_id_type input_id;
auto input_id_length = param.id.size();
try
{
FC_ASSERT(input_id_length <= 64, "hex string is too long to represent an actual transaction id");
FC_ASSERT(input_id_length >= 8, "hex string representing transaction id should be at least 8 characters long to avoid excessive collisions");
input_id = transaction_id_type(param.id);
}
EOS_RETHROW_EXCEPTIONS(transaction_id_type_exception, "Invalid transaction ID: ${transaction_id}", ("transaction_id", param.id))

EOS_ASSERT(trx_finality_status_proc, unsupported_feature, "Transaction Status Interface not enabled. To enable, configure nodeos with '--transaction-finality-status-max-storage-size-gb <size>'.");

trx_finality_status_processing::chain_state ch_state = trx_finality_status_proc->get_chain_state();

auto trx_st = trx_finality_status_proc->get_trx_state(input_id);

return {
trx_st ? trx_st->status : "UNKNOWN",
trx_st ? std::optional<uint32_t>(chain::block_header::num_from_id(trx_st->block_id)) : std::optional<uint32_t>{},
trx_st ? std::optional<chain::block_id_type>(trx_st->block_id) : std::optional<chain::block_id_type>{},
trx_st ? std::optional<fc::time_point>(trx_st->block_timestamp) : std::optional<fc::time_point>{},
trx_st ? std::optional<fc::time_point_sec>(trx_st->expiration) : std::optional<fc::time_point_sec>{},
chain::block_header::num_from_id(ch_state.head_id),
ch_state.head_id,
ch_state.head_block_timestamp,
chain::block_header::num_from_id(ch_state.irr_id),
ch_state.irr_id,
ch_state.irr_block_timestamp,
ch_state.last_tracked_block_id
};
}

read_only::get_activated_protocol_features_results
read_only::get_activated_protocol_features( const read_only::get_activated_protocol_features_params& params )const {
read_only::get_activated_protocol_features_results result;
Expand Down
30 changes: 28 additions & 2 deletions plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <eosio/chain_plugin/account_query_db.hpp>
#include <eosio/chain_plugin/trx_retry_db.hpp>
#include <eosio/chain_plugin/trx_finality_status_processing.hpp>

#include <fc/static_variant.hpp>

Expand Down Expand Up @@ -86,12 +87,13 @@ class read_only {
const fc::microseconds abi_serializer_max_time;
bool shorten_abi_errors = true;
const producer_plugin* producer_plug;
const trx_finality_status_processing* trx_finality_status_proc;

public:
static const string KEYi64;

read_only(const controller& db, const std::optional<account_query_db>& aqdb, const fc::microseconds& abi_serializer_max_time, const producer_plugin* producer_plug)
: db(db), aqdb(aqdb), abi_serializer_max_time(abi_serializer_max_time), producer_plug(producer_plug) {
read_only(const controller& db, const std::optional<account_query_db>& aqdb, const fc::microseconds& abi_serializer_max_time, const producer_plugin* producer_plug, const trx_finality_status_processing* trx_finality_status_proc)
: db(db), aqdb(aqdb), abi_serializer_max_time(abi_serializer_max_time), producer_plug(producer_plug), trx_finality_status_proc(trx_finality_status_proc) {
}

void validate() const {}
Expand Down Expand Up @@ -126,6 +128,27 @@ class read_only {
};
get_info_results get_info(const get_info_params&) const;

struct get_transaction_status_params {
string id;
};

struct get_transaction_status_results {
string state;
std::optional<uint32_t> block_number;
std::optional<chain::block_id_type> block_id;
std::optional<fc::time_point> block_timestamp;
std::optional<fc::time_point> expiration;
uint32_t head_number = 0;
chain::block_id_type head_id;
fc::time_point head_timestamp;
uint32_t irreversible_number = 0;
chain::block_id_type irreversible_id;
fc::time_point irreversible_timestamp;
chain::block_id_type last_tracked_block_id;
};
get_transaction_status_results get_transaction_status(const get_transaction_status_params& params) const;


struct get_activated_protocol_features_params {
std::optional<uint32_t> lower_bound;
std::optional<uint32_t> upper_bound;
Expand Down Expand Up @@ -790,6 +813,9 @@ FC_REFLECT(eosio::chain_apis::read_only::get_info_results,
(head_block_id)(head_block_time)(head_block_producer)
(virtual_block_cpu_limit)(virtual_block_net_limit)(block_cpu_limit)(block_net_limit)
(server_version_string)(fork_db_head_block_num)(fork_db_head_block_id)(server_full_version_string)(total_cpu_weight)(total_net_weight) )
FC_REFLECT(eosio::chain_apis::read_only::get_transaction_status_params, (id) )
FC_REFLECT(eosio::chain_apis::read_only::get_transaction_status_results, (state)(block_number)(block_id)(block_timestamp)(expiration)(head_number)(head_id)
(head_timestamp)(irreversible_number)(irreversible_id)(irreversible_timestamp)(last_tracked_block_id) )
FC_REFLECT(eosio::chain_apis::read_only::get_activated_protocol_features_params, (lower_bound)(upper_bound)(limit)(search_by_block_num)(reverse) )
FC_REFLECT(eosio::chain_apis::read_only::get_activated_protocol_features_results, (activated_protocol_features)(more) )
FC_REFLECT(eosio::chain_apis::read_only::get_block_params, (block_num_or_id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ namespace eosio::chain_apis {
public:

struct chain_state {
chain::block_id_type head_id;
chain::block_id_type irr_id;
chain::block_id_type last_tracked_block_id;
chain::block_id_type head_id;
chain::block_timestamp_type head_block_timestamp;
chain::block_id_type irr_id;
chain::block_timestamp_type irr_block_timestamp;
chain::block_id_type last_tracked_block_id;
};

struct trx_state {
chain::block_id_type block_id;
fc::time_point block_timestamp;
fc::time_point received;
fc::time_point expiration;
std::string status;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ BOOST_AUTO_TEST_CASE(trx_finality_status_logic) { try {
BOOST_REQUIRE(ts);
BOOST_CHECK(ts->block_id == bs_20->id);
BOOST_CHECK(ts->block_timestamp == bs_20->block->timestamp);
BOOST_CHECK(fc::time_point_sec(ts->expiration) == (std::get<1>(trx_pairs_20[1])->expiration()));
BOOST_CHECK_EQUAL(std::string(ts->received), pre_block_20_time);
BOOST_CHECK_EQUAL(ts->status, "IN_BLOCK");

Expand Down Expand Up @@ -706,6 +707,7 @@ BOOST_AUTO_TEST_CASE(trx_finality_status_logic) { try {
cs = status.get_chain_state();
BOOST_CHECK(cs.head_id == bs_19_alt->id);
BOOST_CHECK(cs.irr_id == bs_19_alt->id);
BOOST_CHECK(cs.irr_block_timestamp == bs_19_alt->block->timestamp);
BOOST_CHECK(cs.last_tracked_block_id == bs_19_alt->id);


Expand Down Expand Up @@ -1004,7 +1006,9 @@ BOOST_AUTO_TEST_CASE(trx_finality_status_storage_reduction) { try {

cs = status.get_chain_state();
BOOST_CHECK(cs.head_id == b_12.bs->id);
BOOST_CHECK(cs.head_block_timestamp == b_12.bs->block->timestamp);
BOOST_CHECK(cs.irr_id == eosio::chain::block_id_type{});
BOOST_CHECK(cs.irr_block_timestamp == eosio::chain::block_timestamp_type{});
brianjohnson5972 marked this conversation as resolved.
Show resolved Hide resolved
BOOST_CHECK(cs.last_tracked_block_id == b_03.bs->id);


Expand Down
8 changes: 6 additions & 2 deletions plugins/chain_plugin/trx_finality_status_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ namespace eosio::chain_apis {
fc::tracked_storage<finality_status_multi_index> _storage;
uint32_t _last_proc_block_num = finality_status::no_block_num;
chain::block_id_type _head_block_id;
chain::block_timestamp_type _head_block_timestamp;
chain::block_id_type _irr_block_id;
chain::block_timestamp_type _irr_block_timestamp;
chain::block_id_type _last_tracked_block_id;
const fc::microseconds _success_duration;
const fc::microseconds _failure_duration;
Expand All @@ -44,6 +46,7 @@ namespace eosio::chain_apis {

void trx_finality_status_processing::signal_irreversible_block( const chain::block_state_ptr& bsp ) {
_my->_irr_block_id = bsp->id;
_my->_irr_block_timestamp = bsp->block->timestamp;
}

void trx_finality_status_processing::signal_block_start( uint32_t block_num ) {
Expand All @@ -57,6 +60,7 @@ namespace eosio::chain_apis {
chain::block_timestamp_type block_timestamp;
if (bsp) {
_head_block_id = block_id;
_head_block_timestamp = bsp->block->timestamp;
block_timestamp = bsp->block->timestamp;
if (chain::block_header::num_from_id(_head_block_id) <= _last_proc_block_num) {
handle_rollback();
Expand Down Expand Up @@ -235,7 +239,7 @@ namespace eosio::chain_apis {
}

trx_finality_status_processing::chain_state trx_finality_status_processing::get_chain_state() const {
return { .head_id = _my->_head_block_id, .irr_id = _my->_irr_block_id, .last_tracked_block_id = _my->_last_tracked_block_id };
return { .head_id = _my->_head_block_id, .head_block_timestamp = _my->_head_block_timestamp, .irr_id = _my->_irr_block_id, .irr_block_timestamp = _my->_irr_block_timestamp, .last_tracked_block_id = _my->_last_tracked_block_id };
}

std::optional<trx_finality_status_processing::trx_state> trx_finality_status_processing::get_trx_state( const chain::transaction_id_type& id ) const {
Expand All @@ -258,7 +262,7 @@ namespace eosio::chain_apis {
const auto lib = chain::block_header::num_from_id(_my->_irr_block_id);
status = (block_num > lib) ? "IN_BLOCK" : "IRREVERSIBLE";
}
return trx_finality_status_processing::trx_state{ .block_id = iter->block_id, .block_timestamp = iter->block_timestamp, .received = iter->received, .status = status };
return trx_finality_status_processing::trx_state{ .block_id = iter->block_id, .block_timestamp = iter->block_timestamp, .received = iter->received, .expiration = iter->trx_expiry, .status = status };
}

size_t trx_finality_status_processing::get_storage_memory_size() const {
Expand Down
1 change: 1 addition & 0 deletions programs/cleos/httpc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ namespace eosio { namespace client { namespace http {

const string chain_func_base = "/v1/chain";
const string get_info_func = chain_func_base + "/get_info";
const string get_transaction_status_func = chain_func_base + "/get_transaction_status";
const string send_txn_func = chain_func_base + "/send_transaction";
const string push_txn_func = chain_func_base + "/push_transaction";
const string send2_txn_func = chain_func_base + "/send_transaction2";
Expand Down
9 changes: 9 additions & 0 deletions programs/cleos/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2625,6 +2625,15 @@ int main( int argc, char** argv ) {
std::cout << fc::json::to_pretty_string(get_info()) << std::endl;
});

// get transaction status
string status_transaction_id_str;
auto getTransactionStatus = get->add_subcommand("transaction-status", localized("Get transaction status information"));
getTransactionStatus->add_option("id", status_transaction_id_str, localized("ID of the transaction to retrieve"))->required();
getTransactionStatus->callback([&status_transaction_id_str] {
auto arg= fc::mutable_variant_object( "id", status_transaction_id_str);
std::cout << fc::json::to_pretty_string(call(get_transaction_status_func, arg)) << std::endl;
});

// get block
string blockArg;
bool get_bhs = false;
Expand Down
2 changes: 1 addition & 1 deletion tests/chain_plugin_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, TESTER ) try {
char headnumstr[20];
sprintf(headnumstr, "%d", headnum);
chain_apis::read_only::get_block_params param{headnumstr};
chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {});
chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {}, {});

// block should be decoded successfully
std::string block_str = json::to_pretty_string(plugin.get_block(param));
Expand Down
8 changes: 4 additions & 4 deletions tests/get_table_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ BOOST_FIXTURE_TEST_CASE( get_scope_test, TESTER ) try {
produce_blocks(1);

// iterate over scope
eosio::chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {});
eosio::chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {}, {});
eosio::chain_apis::read_only::get_table_by_scope_params param{"eosio.token"_n, "accounts"_n, "inita", "", 10};
eosio::chain_apis::read_only::get_table_by_scope_result result = plugin.read_only::get_table_by_scope(param);

Expand Down Expand Up @@ -194,7 +194,7 @@ BOOST_FIXTURE_TEST_CASE( get_table_test, TESTER ) try {
produce_blocks(1);

// get table: normal case
eosio::chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {});
eosio::chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {}, {});
eosio::chain_apis::read_only::get_table_rows_params p;
p.code = "eosio.token"_n;
p.scope = "inita";
Expand Down Expand Up @@ -363,7 +363,7 @@ BOOST_FIXTURE_TEST_CASE( get_table_by_seckey_test, TESTER ) try {
produce_blocks(1);

// get table: normal case
eosio::chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {});
eosio::chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {}, {});
eosio::chain_apis::read_only::get_table_rows_params p;
p.code = "eosio"_n;
p.scope = "eosio";
Expand Down Expand Up @@ -515,7 +515,7 @@ BOOST_FIXTURE_TEST_CASE( get_table_next_key_test, TESTER ) try {
// }


chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {});
chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), {}, {});
chain_apis::read_only::get_table_rows_params params{
.json=true,
.code="test"_n,
Expand Down