Compare commits

...

26 Commits

Author SHA1 Message Date
766C6164 aada079735 Cleanup 2022-10-14 10:57:17 -04:00
766C6164 882ae7fba4 Added print-log, unified subcommand handlers 2022-10-13 16:43:13 -04:00
766C6164 68c1f80a74 Refactoring of block-log subcommand 2022-10-13 12:31:18 -04:00
Kevin Heifner 815189eba8 Merge pull request #309 from AntelopeIO/GH-274-test-main
[3.2 -> main] Test fix: trx_finality_status_forked_test.py
2022-10-10 09:53:43 -05:00
Kevin Heifner d8b3167498 Merge remote-tracking branch 'origin/release/3.2' into GH-274-test-main 2022-10-10 09:23:00 -05:00
Kevin Heifner fcfe827615 Merge pull request #308 from AntelopeIO/GH-274-test-3.2
[3.1 -> 3.2] Test fix: trx_finality_status_forked_test.py
2022-10-10 09:21:55 -05:00
Kevin Heifner 4f03913543 Merge remote-tracking branch 'origin/release/3.1' into GH-274-test-3.2 2022-10-10 08:52:52 -05:00
Kevin Heifner 4cd534a148 Merge pull request #307 from AntelopeIO/GH-274-test
[3.1] Test fix: trx_finality_status_forked_test.py
2022-10-10 08:52:05 -05:00
Kevin Heifner fce62ceff5 GH-274 Cleanup and provide more time before fork resolution 2022-10-10 08:14:34 -05:00
Matt Witherspoon c877b1e78b Merge pull request #278 from AntelopeIO/remove_login_plugin
[4.0] remove login_plugin
2022-10-07 19:24:58 -04:00
Matt Witherspoon 81c088f567 Merge pull request #306 from AntelopeIO/remove_yubihsm_take2
[4.0] remove YubiHSM support from keosd
2022-10-07 17:28:15 -04:00
Matt Witherspoon f4f5d1814d remove global OPENSSL_API_COMPAT & OPENSSL_NO_DEPRECATED defs 2022-10-07 16:57:10 -04:00
Matt Witherspoon 3c56203544 remove YubiHSM support from keosd 2022-10-07 16:56:00 -04:00
Lin Huang 2b9a2a87f8 Merge pull request #303 from AntelopeIO/merge_3_2_version
[3.2 -> main] Merge version bump of 3.2.0-rc1
2022-10-06 17:51:38 -04:00
Lin Huang 714a0a929a merge version bump of 3.2.0-rc1 2022-10-06 17:14:50 -04:00
Lin Huang 05fda58d33 Merge pull request #301 from AntelopeIO/bump_3_2_0_rc1
[3.2] bump Leap to 3.2.0-rc1
2022-10-06 17:05:36 -04:00
Lin Huang 9077f9bbe2 bump Leap to 3.2.0-rc1 2022-10-06 16:35:18 -04:00
Lin Huang c1f8889088 Merge pull request #300 from AntelopeIO/bump_4_0_0
[4.0] bump Leap version to 4.0.0-dev
2022-10-06 16:13:27 -04:00
Lin Huang b0c409a2f8 bump Leap to 4.0.0-dev 2022-10-06 15:18:00 -04:00
ClaytonCalabrese dc13e7271d Merge pull request #289 from AntelopeIO/waitForTransactionsInBlock
[4.0] add waitForTransactionsInBlock
2022-10-06 10:46:23 -05:00
Lin Huang 60cedbc26b Merge pull request #292 from AntelopeIO/merge_bump_version_3_1_2
[3.1 -> main] merge changes of bumping version release/3.1.2
2022-10-06 11:29:46 -04:00
Lin Huang e11e7dddca keep version on main as 3.2.0-dev 2022-10-05 20:47:35 -04:00
Lin Huang 0b64f879e3 Merge pull request #288 from AntelopeIO/bump_3_1_2
[3.1] bump Leap to release/3.1.2
2022-10-05 20:26:39 -04:00
Clayton Calabrese 7f017d5590 change name of waitForTransInBlock and add waitForTransactionsInBlock which takes multiple transactions 2022-10-05 16:19:26 -05:00
Lin Huang 2b5140489c bump Leap to release/3.1.2 2022-10-05 16:28:02 -04:00
Matt Witherspoon 050dbd82f7 remove login_plugin 2022-10-03 23:04:41 -04:00
34 changed files with 221 additions and 991 deletions
-3
View File
@@ -7,13 +7,10 @@ RUN apt-get update && apt-get upgrade -y && \
g++-8 \
git \
jq \
libcurl4-openssl-dev \
libgmp-dev \
libssl-dev \
libusb-1.0-0-dev \
llvm-7-dev \
ninja-build \
pkg-config \
python3 \
software-properties-common \
zlib1g-dev \
-3
View File
@@ -7,11 +7,8 @@ RUN apt-get update && apt-get upgrade -y && \
git \
jq \
libboost-all-dev \
libcurl4-openssl-dev \
libgmp-dev \
libssl-dev \
libusb-1.0-0-dev \
llvm-11-dev \
ninja-build \
pkg-config \
zstd
-3
View File
@@ -7,11 +7,8 @@ RUN apt-get update && apt-get upgrade -y && \
git \
jq \
libboost-all-dev \
libcurl4-openssl-dev \
libgmp-dev \
libssl-dev \
libusb-1.0-0-dev \
llvm-11-dev \
ninja-build \
pkg-config \
zstd
-3
View File
@@ -13,9 +13,6 @@
[submodule "libraries/rapidjson"]
path = libraries/rapidjson
url = https://github.com/Tencent/rapidjson/
[submodule "libraries/yubihsm"]
path = libraries/yubihsm
url = https://github.com/Yubico/yubihsm-shell
[submodule "tests/abieos"]
path = tests/abieos
url = https://github.com/AntelopeIO/abieos
+2 -8
View File
@@ -13,8 +13,8 @@ set( CMAKE_CXX_STANDARD 17 )
set( CMAKE_CXX_EXTENSIONS ON )
set( CXX_STANDARD_REQUIRED ON)
set(VERSION_MAJOR 3)
set(VERSION_MINOR 2)
set(VERSION_MAJOR 4)
set(VERSION_MINOR 0)
set(VERSION_PATCH 0)
set(VERSION_SUFFIX dev)
@@ -60,8 +60,6 @@ if(APPLE AND UNIX AND "${OPENSSL_ROOT_DIR}" STREQUAL "")
set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl@3;/usr/local/opt/openssl@1.1")
endif()
endif()
# fc also adds these definitions to its public interface. once fc becomes the sole importer of openssl, this should be removed
add_definitions(-DOPENSSL_API_COMPAT=0x10100000L -DOPENSSL_NO_DEPRECATED)
option(ENABLE_OC "Enable eosvm-oc on supported platforms" ON)
@@ -224,7 +222,6 @@ configure_file(libraries/libfc/src/network/LICENSE.go licen
configure_file(libraries/eos-vm/LICENSE licenses/leap/LICENSE.eos-vm COPYONLY)
configure_file(libraries/libfc/libraries/ff/LICENSE licenses/leap/LICENSE.libff COPYONLY)
configure_file(programs/cleos/LICENSE.CLI11 licenses/leap/LICENSE.CLI11 COPYONLY)
configure_file(libraries/yubihsm/LICENSE licenses/leap/LICENSE.yubihsm COPYONLY)
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/licenses/leap" DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/licenses/" COMPONENT base)
@@ -243,9 +240,6 @@ add_custom_target(dev-install
USES_TERMINAL
)
get_property(_CTEST_CUSTOM_TESTS_IGNORE GLOBAL PROPERTY CTEST_CUSTOM_TESTS_IGNORE)
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake" "SET(CTEST_CUSTOM_TESTS_IGNORE ${_CTEST_CUSTOM_TESTS_IGNORE})")
include(doxygen)
include(package.cmake)
+4 -8
View File
@@ -18,7 +18,7 @@ Recent Ubuntu LTS releases are the only Linux distributions that we fully suppor
* CMake 3.8+
* (for Linux only) LLVM 7 - 11 (newer versions do not work)
A few other common libraries are tools also required such as openssl 1.1+, libcurl, curl, libusb, GMP, Python 3, and zlib.
A few other common libraries are tools also required such as openssl 1.1+, curl, GMP, Python 3, and zlib.
**A Warning On Parallel Compilation Jobs (`-j` flag)**: When building C/C++ software often the build is performed in parallel via a command such as `make -j $(nproc)` which uses the number of CPU cores as the number of compilation jobs to perform simultaneously. However, be aware that some compilation units (.cpp files) in Leap are extremely complex and will consume nearly 4GB of memory to compile. You may need to reduce the level of parallelization depending on the amount of memory on your build host. e.g. instead of `make -j $(nproc)` run `make -j2`. Failures due to memory exhaustion will typically but not always manifest as compiler crashes.
@@ -33,6 +33,8 @@ The binary package will be produced in the Leap build directory that was supplie
#### Manual (non "pinned") Build Instructions
These instructions are valid for this branch. Other release branches may have different requirements so ensure you follow the directions in the branch or release you intend to build.
<details>
<summary>Ubuntu 20.04 & 22.04 Build Instructions</summary>
@@ -44,12 +46,9 @@ apt-get update && apt-get install \
curl \
git \
libboost-all-dev \
libcurl4-openssl-dev \
libgmp-dev \
libssl-dev \
libusb-1.0-0-dev \
llvm-11-dev \
pkg-config
llvm-11-dev
```
and perform the build:
```
@@ -72,12 +71,9 @@ apt-get update && apt-get install \
curl \
g++-8 \
git \
libcurl4-openssl-dev \
libgmp-dev \
libssl-dev \
libusb-1.0-0-dev \
llvm-7-dev \
pkg-config \
python3 \
zlib1g-dev
-1
View File
@@ -13,7 +13,6 @@ For information on specific plugins, just select from the list below:
* [`db_size_api_plugin`](db_size_api_plugin/index.md)
* [`http_client_plugin`](http_client_plugin/index.md)
* [`http_plugin`](http_plugin/index.md)
* [`login_plugin`](login_plugin/index.md)
* [`net_api_plugin`](net_api_plugin/index.md)
* [`net_plugin`](net_plugin/index.md)
* [`producer_plugin`](producer_plugin/index.md)
@@ -1,47 +0,0 @@
## Description
The `login_plugin` supports the concept of applications authenticating with the Antelope blockchain. The `login_plugin` API allows an application to verify whether an account is allowed to sign in order to satisfy a specified authority.
## Usage
```console
# config.ini
plugin = eosio::login_plugin
[options]
```
```sh
# command-line
nodeos ... --plugin eosio::login_plugin [options]
```
## Options
These can be specified from both the `nodeos` command-line or the `config.ini` file:
```console
Config Options for eosio::login_plugin:
--max-login-requests arg (=1000000) The maximum number of pending login
requests
--max-login-timeout arg (=60) The maximum timeout for pending login
requests (in seconds)
```
## Dependencies
* [`chain_plugin`](../chain_plugin/index.md)
* [`http_plugin`](../http_plugin/index.md)
### Load Dependency Examples
```console
# config.ini
plugin = eosio::chain_plugin
[options]
plugin = eosio::http_plugin
[options]
```
```sh
# command-line
nodeos ... --plugin eosio::chain_plugin [options] \
--plugin eosio::http_plugin [options]
```
-5
View File
@@ -91,11 +91,6 @@ Config Options for eosio::wallet_plugin:
number of seconds of inactivity.
Activity is defined as any wallet
command e.g. list-wallets.
--yubihsm-url URL Override default URL of
http://localhost:12345 for connecting
to yubihsm-connector
--yubihsm-authkey key_num Enables YubiHSM support using given
Authkey
Application Config Options:
--plugin arg Plugin(s) to enable, may be specified
@@ -1,73 +0,0 @@
---
content_title: How To Attach a YubiHSM Hard Wallet
link_text: How To Attach a YubiHSM Hard Wallet
---
## Goal
Attach a YubiHSM as a hard wallet
## Before you begin
* Install the currently supported version of `keosd`
* Install YubiHSM2 Software Toolkit (YubiHSM2 SDK)
* Create an AuthKey with at least the following Capabilities:
* sign-ecdsa
* generate-asymmetric-key
* export-wrapped
* **Delete the default AuthKey**
[[warning | Security]]
| It is extremely important to create a new AuthKey and remove the default AuthKey before proceed to the following steps.
## Steps
### Configure `keosd`
There are two options to connect `keosd` to YubiHSM:
#### Using a YubiHSM connector
By default, `keosd` will connect to the YubiHSM connector on the default host and port. If a non-default URL is used, set the `--yubihsm-url` option or `yubihsm-url` in `config.ini` with the correct connector URL
#### Directly connect via USB
`keosd` also can directly connect to YubiHSM via USB protocol
If this option is used, set `keosd` startup option as the below:
```sh
--yubihsm-url=ysb://
```
### Start `keosd` with AuthKey:
```sh
--yubihsm-authkey Your_AuthKey_Object_Number
```
if a YubiHSM connector is used, check the YubiHSM connector is up and running by visiting YubiHSM URL:
http://YubiHSM_HOST:YubiHSM_PORT/connector/status ((Default HOST and Port: http://127.0.0.1:12345)
You should see something like this:
```console
status=OK
serial=*
version=2.0.0
pid=666
address=localhost
port=12345
```
### Unlock YubiHSM wallet with the password of AuthKey using the following option:
```sh
cleos wallet unlock -n YubiHSM --password YOUR_AUTHKEY_PASSWORD
```
After unlocking the wallet, you can use `cleos wallet` commands as usual. Beware as a part of security mechanism, some wallet subcommands, such as retrieve private keys, or remove a key, are not supported when a YubiHSM is used
-5
View File
@@ -1,5 +0,0 @@
---
content_title: Keosd How-to Guides
---
* [How to attach a YubiHSM hard wallet](how-to-attach-a-yubihsm-hard-wallet.md)
-20
View File
@@ -26,23 +26,3 @@ set(ENABLE_PROFILE OFF CACHE BOOL "Enable for profile builds")
if(eos-vm IN_LIST EOSIO_WASM_RUNTIMES OR eos-vm-jit IN_LIST EOSIO_WASM_RUNTIMES)
add_subdirectory( eos-vm )
endif()
#yubihsm's openssl discovery is via pkg-config instead of find_package. Help it out on macOS otherwise openssl's pkgconfig
# files may not be found down in the /opt/homebrew/opt directory
if(APPLE)
get_filename_component(OPENSSL_LIB_PATH "${OPENSSL_CRYPTO_LIBRARY}" DIRECTORY)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${OPENSSL_LIB_PATH}/pkgconfig")
endif()
set(ENABLE_STATIC ON)
set(CMAKE_MACOSX_RPATH OFF)
set(BUILD_ONLY_LIB ON CACHE BOOL "Library only build")
message(STATUS "Starting yubihsm configuration...")
add_subdirectory( yubihsm EXCLUDE_FROM_ALL )
target_compile_options(yubihsm_static PRIVATE -fno-lto -fcommon)
message(STATUS "yubihsm configuration complete")
get_property(_CTEST_CUSTOM_TESTS_IGNORE GLOBAL PROPERTY CTEST_CUSTOM_TESTS_IGNORE)
set_property(GLOBAL PROPERTY CTEST_CUSTOM_TESTS_IGNORE
"change_authkey import_ed decrypt_ec decrypt_rsa ssh logs generate_rsa import_ec echo\
yubico_otp wrap_data wrap info import_rsa import_authkey generate_hmac generate_ec\
attest pbkdf2 parsing ${_CTEST_CUSTOM_TESTS_IGNORE}")
-1
View File
@@ -14,6 +14,5 @@ add_subdirectory(wallet_plugin)
add_subdirectory(wallet_api_plugin)
add_subdirectory(txn_test_gen_plugin)
add_subdirectory(db_size_api_plugin)
add_subdirectory(login_plugin)
add_subdirectory(test_control_plugin)
add_subdirectory(test_control_api_plugin)
-5
View File
@@ -1,5 +0,0 @@
BasedOnStyle: LLVM
IndentWidth: 3
ColumnLimit: 120
PointerAlignment: Left
AlwaysBreakTemplateDeclarations: true
-7
View File
@@ -1,7 +0,0 @@
file(GLOB HEADERS "include/eosio/login_plugin/*.hpp")
add_library( login_plugin
login_plugin.cpp
${HEADERS} )
target_link_libraries( login_plugin chain_plugin http_plugin appbase )
target_include_directories( login_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
@@ -1,94 +0,0 @@
#pragma once
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <appbase/application.hpp>
#include <eosio/chain/controller.hpp>
namespace eosio {
class login_plugin : public plugin<login_plugin> {
public:
APPBASE_PLUGIN_REQUIRES((chain_plugin)(http_plugin))
login_plugin();
virtual ~login_plugin();
virtual void set_program_options(options_description&, options_description&) override;
void plugin_initialize(const variables_map&);
void plugin_startup();
void plugin_shutdown();
struct start_login_request_params {
chain::time_point_sec expiration_time;
};
struct start_login_request_results {
chain::public_key_type server_ephemeral_pub_key;
};
struct finalize_login_request_params {
chain::public_key_type server_ephemeral_pub_key;
chain::public_key_type client_ephemeral_pub_key;
chain::permission_level permission;
std::string data;
std::vector<chain::signature_type> signatures;
};
struct finalize_login_request_results {
chain::sha256 digest{};
flat_set<chain::public_key_type> recovered_keys{};
bool permission_satisfied = false;
std::string error{};
};
struct do_not_use_gen_r1_key_params {};
struct do_not_use_gen_r1_key_results {
chain::public_key_type pub_key;
chain::private_key_type priv_key;
};
struct do_not_use_sign_params {
chain::private_key_type priv_key;
chain::bytes data;
};
struct do_not_use_sign_results {
chain::signature_type sig;
};
struct do_not_use_get_secret_params {
chain::public_key_type pub_key;
chain::private_key_type priv_key;
};
struct do_not_use_get_secret_results {
chain::sha512 secret;
};
start_login_request_results start_login_request(const start_login_request_params&);
finalize_login_request_results finalize_login_request(const finalize_login_request_params&);
do_not_use_gen_r1_key_results do_not_use_gen_r1_key(const do_not_use_gen_r1_key_params&);
do_not_use_sign_results do_not_use_sign(const do_not_use_sign_params&);
do_not_use_get_secret_results do_not_use_get_secret(const do_not_use_get_secret_params&);
private:
unique_ptr<class login_plugin_impl> my;
};
} // namespace eosio
FC_REFLECT(eosio::login_plugin::start_login_request_params, (expiration_time))
FC_REFLECT(eosio::login_plugin::start_login_request_results, (server_ephemeral_pub_key))
FC_REFLECT(eosio::login_plugin::finalize_login_request_params,
(server_ephemeral_pub_key)(client_ephemeral_pub_key)(permission)(data)(signatures))
FC_REFLECT(eosio::login_plugin::finalize_login_request_results, (digest)(recovered_keys)(permission_satisfied)(error))
FC_REFLECT_EMPTY(eosio::login_plugin::do_not_use_gen_r1_key_params)
FC_REFLECT(eosio::login_plugin::do_not_use_gen_r1_key_results, (pub_key)(priv_key))
FC_REFLECT(eosio::login_plugin::do_not_use_sign_params, (priv_key)(data))
FC_REFLECT(eosio::login_plugin::do_not_use_sign_results, (sig))
FC_REFLECT(eosio::login_plugin::do_not_use_get_secret_params, (pub_key)(priv_key))
FC_REFLECT(eosio::login_plugin::do_not_use_get_secret_results, (secret))
-160
View File
@@ -1,160 +0,0 @@
#include <eosio/chain/authorization_manager.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/login_plugin/login_plugin.hpp>
#include <fc/io/json.hpp>
namespace eosio {
static appbase::abstract_plugin& _login_plugin = app().register_plugin<login_plugin>();
using namespace eosio;
struct login_request {
chain::private_key_type server_ephemeral_priv_key{};
chain::public_key_type server_ephemeral_pub_key{};
chain::time_point_sec expiration_time;
};
struct login_request_pub_key_index {};
struct login_request_time_index {};
using login_request_container = boost::multi_index_container<
login_request,
indexed_by< //
ordered_unique<tag<login_request_pub_key_index>,
member<login_request, chain::public_key_type, &login_request::server_ephemeral_pub_key>>,
ordered_non_unique<tag<login_request_time_index>,
member<login_request, chain::time_point_sec, &login_request::expiration_time>> //
>>;
class login_plugin_impl {
public:
login_request_container requests{};
uint32_t max_login_requests = 1000000;
uint32_t max_login_timeout = 60;
void expire_requests() {
auto& index = requests.get<login_request_time_index>();
auto now = fc::time_point::now();
for (auto it = index.begin(); it != index.end() && it->expiration_time < now; it = index.erase(it))
;
}
};
login_plugin::login_plugin() : my{std::make_unique<login_plugin_impl>()} {}
login_plugin::~login_plugin() {}
void login_plugin::set_program_options(options_description&, options_description& cfg) {
cfg.add_options() //
("max-login-requests", bpo::value<uint32_t>()->default_value(1000000),
"The maximum number of pending login requests") //
("max-login-timeout", bpo::value<uint32_t>()->default_value(60),
"The maximum timeout for pending login requests (in seconds)");
}
void login_plugin::plugin_initialize(const variables_map& options) {
my->max_login_requests = options.at("max-login-requests").as<uint32_t>();
my->max_login_timeout = options.at("max-login-timeout").as<uint32_t>();
}
#define CALL(call_name, http_response_code) \
{ \
std::string("/v1/login/" #call_name), [this](string, string body, url_response_callback cb) mutable { \
try { \
if (body.empty()) \
body = "{}"; \
fc::variant result( call_name(fc::json::from_string(body).as<login_plugin::call_name##_params>()) ); \
cb(http_response_code, fc::time_point::maximum(), std::move(result)); \
} catch (...) { \
http_plugin::handle_exception("login", #call_name, body, cb); \
} \
} \
}
void login_plugin::plugin_startup() {
ilog("starting login_plugin");
app().get_plugin<http_plugin>().add_api({
CALL(start_login_request, 200), //
CALL(finalize_login_request, 200),
// CALL(do_not_use_gen_r1_key, 200), //
// CALL(do_not_use_sign, 200), //
// CALL(do_not_use_get_secret, 200), //
});
}
void login_plugin::plugin_shutdown() {}
login_plugin::start_login_request_results
login_plugin::start_login_request(const login_plugin::start_login_request_params& params) {
my->expire_requests();
EOS_ASSERT(params.expiration_time > fc::time_point::now(), fc::timeout_exception,
"Requested expiration time ${expiration_time} is in the past",
("expiration_time", params.expiration_time));
EOS_ASSERT(my->requests.size() < my->max_login_requests, fc::timeout_exception, "Too many pending login requests");
login_request request;
request.server_ephemeral_priv_key = chain::private_key_type::generate_r1();
request.server_ephemeral_pub_key = request.server_ephemeral_priv_key.get_public_key();
request.expiration_time =
std::min(params.expiration_time, fc::time_point_sec{fc::time_point::now()} + my->max_login_timeout);
my->requests.insert(request);
return {request.server_ephemeral_pub_key};
}
login_plugin::finalize_login_request_results
login_plugin::finalize_login_request(const login_plugin::finalize_login_request_params& params) {
finalize_login_request_results result;
my->expire_requests();
auto& index = my->requests.get<login_request_pub_key_index>();
auto it = index.find(params.server_ephemeral_pub_key);
if (it == index.end()) {
result.error = "server_ephemeral_pub_key expired or not found";
return result;
}
auto request = *it;
index.erase(it);
auto shared_secret = request.server_ephemeral_priv_key.generate_shared_secret(params.client_ephemeral_pub_key);
chain::bytes combined_data(1024 * 1024);
chain::datastream<char*> sig_data_ds{combined_data.data(), combined_data.size()};
fc::raw::pack(sig_data_ds, params.permission);
fc::raw::pack(sig_data_ds, shared_secret);
fc::raw::pack(sig_data_ds, params.data);
combined_data.resize(sig_data_ds.tellp());
result.digest = chain::sha256::hash(combined_data);
for (auto& sig : params.signatures)
result.recovered_keys.insert(chain::public_key_type{sig, result.digest});
try {
auto noop_checktime = [] {};
auto& chain = app().get_plugin<chain_plugin>().chain();
chain.get_authorization_manager().check_authorization( //
params.permission.actor, params.permission.permission, result.recovered_keys, {}, fc::microseconds(0),
noop_checktime, true);
result.permission_satisfied = true;
} catch (...) {
result.error = "keys do not satisfy permission";
}
return result;
}
login_plugin::do_not_use_gen_r1_key_results
login_plugin::do_not_use_gen_r1_key(const login_plugin::do_not_use_gen_r1_key_params& params) {
auto priv = chain::private_key_type::generate_r1();
return {priv.get_public_key(), priv};
}
login_plugin::do_not_use_sign_results
login_plugin::do_not_use_sign(const login_plugin::do_not_use_sign_params& params) {
return {params.priv_key.sign(chain::sha256::hash(params.data))};
}
login_plugin::do_not_use_get_secret_results
login_plugin::do_not_use_get_secret(const login_plugin::do_not_use_get_secret_params& params) {
return {params.priv_key.generate_shared_secret(params.pub_key)};
}
} // namespace eosio
+1 -5
View File
@@ -19,11 +19,7 @@ add_library( wallet_plugin
wallet_plugin.cpp
wallet_manager.cpp
${SE_WALLET_SOURCES}
yubihsm_wallet.cpp
${HEADERS} )
target_link_libraries( wallet_plugin yubihsm_static eosio_chain appbase ${security_framework} ${corefoundation_framework} ${localauthentication_framework} ${cocoa_framework})
target_link_libraries( wallet_plugin eosio_chain appbase ${security_framework} ${corefoundation_framework} ${localauthentication_framework} ${cocoa_framework})
target_include_directories( wallet_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
#sadly old cmake 2.8 support in yubihsm cmake prevents usage of target_include_directories there
target_include_directories( wallet_plugin PRIVATE "${CMAKE_SOURCE_DIR}/libraries/yubihsm/lib" )
@@ -1,41 +0,0 @@
#pragma once
#include <eosio/chain/types.hpp>
#include <eosio/wallet_plugin/wallet_api.hpp>
using namespace std;
using namespace eosio::chain;
namespace eosio { namespace wallet {
namespace detail {
struct yubihsm_wallet_impl;
}
class yubihsm_wallet final : public wallet_api {
public:
yubihsm_wallet(const string& connector, const uint16_t authkey);
~yubihsm_wallet();
private_key_type get_private_key(public_key_type pubkey) const override;
bool is_locked() const override;
void lock() override;
void unlock(string password) override;
void check_password(string password) override;
void set_password(string password) override;
map<public_key_type, private_key_type> list_keys() override;
flat_set<public_key_type> list_public_keys() override;
bool import_key(string wif_key) override;
string create_key(string key_type) override;
bool remove_key(string key) override;
std::optional<signature_type> try_sign_digest(const digest_type digest, const public_key_type public_key) override;
private:
std::unique_ptr<detail::yubihsm_wallet_impl> my;
};
}}
-14
View File
@@ -1,6 +1,5 @@
#include <eosio/wallet_plugin/wallet_plugin.hpp>
#include <eosio/wallet_plugin/wallet_manager.hpp>
#include <eosio/wallet_plugin/yubihsm_wallet.hpp>
#include <eosio/chain/exceptions.hpp>
#include <boost/filesystem/path.hpp>
#include <chrono>
@@ -27,10 +26,6 @@ void wallet_plugin::set_program_options(options_description& cli, options_descri
"Timeout for unlocked wallet in seconds (default 900 (15 minutes)). "
"Wallets will automatically lock after specified number of seconds of inactivity. "
"Activity is defined as any wallet command e.g. list-wallets.")
("yubihsm-url", bpo::value<string>()->value_name("URL"),
"Override default URL of http://localhost:12345 for connecting to yubihsm-connector")
("yubihsm-authkey", bpo::value<uint16_t>()->value_name("key_num"),
"Enables YubiHSM support using given Authkey")
;
}
@@ -53,15 +48,6 @@ void wallet_plugin::plugin_initialize(const variables_map& options) {
std::chrono::seconds t(timeout);
wallet_manager_ptr->set_timeout(t);
}
if (options.count("yubihsm-authkey")) {
uint16_t key = options.at("yubihsm-authkey").as<uint16_t>();
string connector_endpoint = "http://localhost:12345";
if(options.count("yubihsm-url"))
connector_endpoint = options.at("yubihsm-url").as<string>();
try {
wallet_manager_ptr->own_and_use_wallet("YubiHSM", make_unique<yubihsm_wallet>(connector_endpoint, key));
}FC_LOG_AND_RETHROW()
}
} FC_LOG_AND_RETHROW()
}
-268
View File
@@ -1,268 +0,0 @@
#include <appbase/application.hpp>
#include <eosio/wallet_plugin/yubihsm_wallet.hpp>
#include <eosio/chain/exceptions.hpp>
#include <yubihsm.h>
#include <fc/crypto/openssl.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/dll/runtime_symbol_info.hpp>
namespace eosio { namespace wallet {
using namespace fc::crypto::r1;
namespace detail {
struct yubihsm_wallet_impl {
using key_map_type = map<public_key_type,uint16_t>;
yubihsm_wallet_impl(const string& ep, const uint16_t ak) : endpoint(ep), authkey(ak) {
yh_rc rc;
if((rc = yh_init()))
FC_THROW("yubihsm init failure: ${c}", ("c", yh_strerror(rc)));
}
~yubihsm_wallet_impl() {
lock();
yh_exit();
//bizarre, is there no way to destroy a yh_connector??
///XXX Probably a race condition on timer shutdown and appbase destruction
}
bool is_locked() const {
return !connector;
}
key_map_type::iterator populate_key_map_with_keyid(const uint16_t key_id) {
yh_rc rc;
size_t blob_sz = 128;
uint8_t blob[blob_sz];
if((rc = yh_util_get_public_key(session, key_id, blob, &blob_sz, nullptr)))
FC_THROW_EXCEPTION(chain::wallet_exception, "yh_util_get_public_key failed: ${m}", ("m", yh_strerror(rc)));
if(blob_sz != 64)
FC_THROW_EXCEPTION(chain::wallet_exception, "unexpected pubkey size from yh_util_get_public_key");
///XXX This is junky and common with SE wallet; commonize it
char serialized_pub_key[sizeof(public_key_data) + 1];
serialized_pub_key[0] = 0x01; //means R1 key
serialized_pub_key[1] = 0x02 + (blob[63]&1); //R1 header; even or odd Y
memcpy(serialized_pub_key+2, blob, 32); //copy in the 32 bytes of X
public_key_type pub_key;
fc::datastream<const char *> ds(serialized_pub_key, sizeof(serialized_pub_key));
fc::raw::unpack(ds, pub_key);
return _keys.emplace(pub_key, key_id).first;
}
void unlock(const string& password) {
yh_rc rc;
try {
if((rc = yh_init_connector(endpoint.c_str(), &connector)))
FC_THROW_EXCEPTION(chain::wallet_exception, "Failled to initialize yubihsm connector URL: ${c}", ("c", yh_strerror(rc)));
if((rc = yh_connect(connector, 0)))
FC_THROW_EXCEPTION(chain::wallet_exception, "Failed to connect to YubiHSM connector: ${m}", ("m", yh_strerror(rc)));
if((rc = yh_create_session_derived(connector, authkey, (const uint8_t *)password.data(), password.size(), false, &session)))
FC_THROW_EXCEPTION(chain::wallet_exception, "Failed to create YubiHSM session: ${m}", ("m", yh_strerror(rc)));
if((rc = yh_authenticate_session(session)))
FC_THROW_EXCEPTION(chain::wallet_exception, "Failed to authenticate YubiHSM session: ${m}", ("m", yh_strerror(rc)));
yh_object_descriptor authkey_desc;
if((rc = yh_util_get_object_info(session, authkey, YH_AUTHENTICATION_KEY, &authkey_desc)))
FC_THROW_EXCEPTION(chain::wallet_exception, "Failed to get authkey info: ${m}", ("m", yh_strerror(rc)));
authkey_caps = authkey_desc.capabilities;
authkey_domains = authkey_desc.domains;
if(!yh_check_capability(&authkey_caps, "sign-ecdsa"))
FC_THROW_EXCEPTION(chain::wallet_exception, "Given authkey cannot perform signing");
size_t found_objects_n = 64*1024;
yh_object_descriptor found_objs[found_objects_n];
yh_capabilities find_caps;
yh_string_to_capabilities("sign-ecdsa", &find_caps);
if((rc = yh_util_list_objects(session, 0, YH_ASYMMETRIC_KEY, 0, &find_caps, YH_ALGO_EC_P256, nullptr, found_objs, &found_objects_n)))
FC_THROW_EXCEPTION(chain::wallet_exception, "yh_util_list_objects failed: ${m}", ("m", yh_strerror(rc)));
for(size_t i = 0; i < found_objects_n; ++i)
populate_key_map_with_keyid(found_objs[i].id);
}
catch(chain::wallet_exception& e) {
lock();
throw;
}
prime_keepalive_timer();
}
void lock() {
if(session) {
yh_util_close_session(session);
yh_destroy_session(&session);
}
session = nullptr;
if(connector)
yh_disconnect(connector);
//it would seem like this would leak-- there is no destroy() call for it. But I clearly can't reuse connectors
// as that fails with a "Unable to find a suitable connector"
connector = nullptr;
_keys.clear();
keepalive_timer.cancel();
}
void prime_keepalive_timer() {
keepalive_timer.expires_at(std::chrono::steady_clock::now() + std::chrono::seconds(20));
keepalive_timer.async_wait([this](const boost::system::error_code& ec){
if(ec || !session)
return;
uint8_t data, resp;
yh_cmd resp_cmd;
size_t resp_sz = 1;
if(yh_send_secure_msg(session, YHC_ECHO, &data, 1, &resp_cmd, &resp, &resp_sz))
lock();
else
prime_keepalive_timer();
});
}
std::optional<signature_type> try_sign_digest(const digest_type d, const public_key_type public_key) {
auto it = _keys.find(public_key);
if(it == _keys.end())
return std::optional<signature_type>();
size_t der_sig_sz = 128;
uint8_t der_sig[der_sig_sz];
yh_rc rc;
if((rc = yh_util_sign_ecdsa(session, it->second, (uint8_t*)d.data(), d.data_size(), der_sig, &der_sig_sz))) {
lock();
FC_THROW_EXCEPTION(chain::wallet_exception, "yh_util_sign_ecdsa failed: ${m}", ("m", yh_strerror(rc)));
}
///XXX a lot of this below is similar to SE wallet; commonize it in non-junky way
fc::ecdsa_sig sig = ECDSA_SIG_new();
BIGNUM *r = BN_new(), *s = BN_new();
BN_bin2bn(der_sig+4, der_sig[3], r);
BN_bin2bn(der_sig+6+der_sig[3], der_sig[4+der_sig[3]+1], s);
ECDSA_SIG_set0(sig, r, s);
char pub_key_shim_data[64];
fc::datastream<char *> eds(pub_key_shim_data, sizeof(pub_key_shim_data));
fc::raw::pack(eds, it->first);
public_key_data* kd = (public_key_data*)(pub_key_shim_data+1);
compact_signature compact_sig;
compact_sig = signature_from_ecdsa(key, *kd, sig, d);
char serialized_signature[sizeof(compact_sig) + 1];
serialized_signature[0] = 0x01;
memcpy(serialized_signature+1, compact_sig.data, sizeof(compact_sig));
signature_type final_signature;
fc::datastream<const char *> ds(serialized_signature, sizeof(serialized_signature));
fc::raw::unpack(ds, final_signature);
return final_signature;
}
public_key_type create() {
if(!yh_check_capability(&authkey_caps, "generate-asymmetric-key"))
FC_THROW_EXCEPTION(chain::wallet_exception, "Given authkey cannot create keys");
yh_rc rc;
uint16_t new_key_id = 0;
yh_capabilities creation_caps = {};
if(yh_string_to_capabilities("sign-ecdsa:export-wrapped", &creation_caps))
FC_THROW_EXCEPTION(chain::wallet_exception, "Cannot create caps mask");
try {
if((rc = yh_util_generate_ec_key(session, &new_key_id, "keosd created key", authkey_domains, &creation_caps, YH_ALGO_EC_P256)))
FC_THROW_EXCEPTION(chain::wallet_exception, "yh_util_generate_ec_key failed: ${m}", ("m", yh_strerror(rc)));
return populate_key_map_with_keyid(new_key_id)->first;
}
catch(chain::wallet_exception& e) {
lock();
throw;
}
}
yh_connector* connector = nullptr;
yh_session* session = nullptr;
string endpoint;
uint16_t authkey;
map<public_key_type,uint16_t> _keys;
yh_capabilities authkey_caps;
uint16_t authkey_domains;
boost::asio::steady_timer keepalive_timer{appbase::app().get_io_service()};
fc::ec_key key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
};
}
yubihsm_wallet::yubihsm_wallet(const string& connector, const uint16_t authkey) : my(new detail::yubihsm_wallet_impl(connector, authkey)) {
}
yubihsm_wallet::~yubihsm_wallet() {
}
private_key_type yubihsm_wallet::get_private_key(public_key_type pubkey) const {
FC_THROW_EXCEPTION(chain::wallet_exception, "Obtaining private key for a key stored in YubiHSM is impossible");
}
bool yubihsm_wallet::is_locked() const {
return my->is_locked();
}
void yubihsm_wallet::lock() {
FC_ASSERT(!is_locked());
my->lock();
}
void yubihsm_wallet::unlock(string password) {
my->unlock(password);
}
void yubihsm_wallet::check_password(string password) {
//just leave this as a noop for now; remove_key from wallet_mgr calls through here
}
void yubihsm_wallet::set_password(string password) {
FC_THROW_EXCEPTION(chain::wallet_exception, "YubiHSM wallet cannot have a password set");
}
map<public_key_type, private_key_type> yubihsm_wallet::list_keys() {
FC_THROW_EXCEPTION(chain::wallet_exception, "Getting the private keys from the YubiHSM wallet is impossible");
}
flat_set<public_key_type> yubihsm_wallet::list_public_keys() {
flat_set<public_key_type> keys;
boost::copy(my->_keys | boost::adaptors::map_keys, std::inserter(keys, keys.end()));
return keys;
}
bool yubihsm_wallet::import_key(string wif_key) {
FC_THROW_EXCEPTION(chain::wallet_exception, "It is not possible to import a key in to the YubiHSM wallet");
}
string yubihsm_wallet::create_key(string key_type) {
EOS_ASSERT(key_type.empty() || key_type == "R1", chain::unsupported_key_type_exception, "YubiHSM wallet only supports R1 keys");
return my->create().to_string();
}
bool yubihsm_wallet::remove_key(string key) {
FC_ASSERT(!is_locked());
FC_THROW_EXCEPTION(chain::wallet_exception, "YubiHSM wallet does not currently support removal of keys");
return true;
}
std::optional<signature_type> yubihsm_wallet::try_sign_digest(const digest_type digest, const public_key_type public_key) {
return my->try_sign_digest(digest, public_key);
}
}}
+142 -121
View File
@@ -47,142 +47,76 @@ struct report_time {
void blocklog_actions::setup(CLI::App& app) {
// callback helper with error code handling
auto err_guard = [this](int (blocklog_actions::*fun)()) {
try {
int rc = (this->*fun)();
if(rc) throw(CLI::RuntimeError(rc));
} catch(...) {
print_exception();
throw(CLI::RuntimeError(-1));
}
};
// main command
auto* sub = app.add_subcommand("block-log", "Blocklog utility");
sub->require_subcommand(1);
// callback with error code handling
auto cb = [this]() {
int rc = run_subcommand();
// properly return err code in main
if(rc) throw(CLI::RuntimeError(rc));
};
// subcommand - print log
auto* print_log = sub->add_subcommand("print-log", "Print blocks.log as JSON")->callback([err_guard]() { err_guard(&blocklog_actions::read_log); });
print_log->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
print_log->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
print_log->add_option("--first,-f", opt->first_block, "The first block number to log or the first to keep if trim-blocklog.");
print_log->add_option("--last,-l", opt->last_block, "The last block number to log or the last to keep if trim-blocklog.");
print_log->add_flag("--no-pretty-print", opt->no_pretty_print, "Do not pretty print the output. Useful if piping to jq to improve performance.");
print_log->add_flag("--as-json-array", opt->as_json_array, "Print out json blocks wrapped in json array (otherwise the output is free-standing json objects).");
// options
sub->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
sub->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
sub->add_option("--first,-f", opt->first_block, "The first block number to log or the first to keep if trim-blocklog.");
sub->add_option("--last,-l", opt->last_block, "The last block number to log or the last to keep if trim-blocklog.");
sub->add_option("--output-dir", opt->output_dir, "The output directory for the block log extracted from blocks-dir.");
// subcommand - make index
auto* make_index = sub->add_subcommand("make-index", "Create blocks.index from blocks.log. Must give 'blocks-dir'. Give 'output-file' relative to current directory or absolute path (default is <blocks-dir>/blocks.index).")->callback([err_guard]() { err_guard(&blocklog_actions::make_index); });
make_index->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
make_index->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
// flags
sub->add_flag("--no-pretty-print", opt->no_pretty_print, "Do not pretty print the output. Useful if piping to jq to improve performance.");
sub->add_flag("--as-json-array", opt->as_json_array, "Print out json blocks wrapped in json array (otherwise the output is free-standing json objects).");
// subcommand - trim blocklog
auto* trim_blocklog = sub->add_subcommand("trim-blocklog", "Trim blocks.log and blocks.index. Must give 'blocks-dir' and 'first' and/or 'last'.")->callback([err_guard]() { err_guard(&blocklog_actions::trim_blocklog); });
trim_blocklog->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
trim_blocklog->add_option("--first,-f", opt->first_block, "The first block number to keep.")->required();
trim_blocklog->add_option("--last,-l", opt->last_block, "The last block number to keep.")->required();
// subcommands
sub->add_subcommand("make-index", "Create blocks.index from blocks.log. Must give 'blocks-dir'. Give 'output-file' relative to current directory or absolute path (default is <blocks-dir>/blocks.index).")->callback([this, cb]() {opt->make_index=true; cb(); });
sub->add_subcommand("trim-blocklog", "Trim blocks.log and blocks.index. Must give 'blocks-dir' and 'first' and/or 'last'.")->callback([this, cb]() {opt->trim_blocklog=true; cb(); });
sub->add_subcommand("extract-blocks", "Extract range of blocks from blocks.log and write to output-dir. Must give 'first' and/or 'last'.")->callback([this, cb]() {opt->extract_blocks=true; cb(); });
sub->add_subcommand("smoke-test", "Quick test that blocks.log and blocks.index are well formed and agree with each other.")->callback([this, cb]() {opt->smoke_test=true; cb(); });
sub->add_subcommand("vacuum", "Vacuum a pruned blocks.log in to an un-pruned blocks.log")->callback([this, cb]() {opt->vacuum=true; cb(); });
sub->add_subcommand("genesis", "Extract genesis_state from blocks.log as JSON")->callback([this, cb]() {opt->genesis=true; cb(); });
// subcommand - extract blocks
auto* extract_blocks = sub->add_subcommand("extract-blocks", "Extract range of blocks from blocks.log and write to output-dir. Must give 'first' and/or 'last'.")->callback([err_guard]() { err_guard(&blocklog_actions::extract_blocks); });
extract_blocks->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
extract_blocks->add_option("--first,-f", opt->first_block, "The first block number to keep.")->required();
extract_blocks->add_option("--last,-l", opt->last_block, "The last block number to keep.")->required();
extract_blocks->add_option("--output-dir", opt->output_dir, "The output directory for the block log extracted from blocks-dir.");
sub->callback([cb]() { cb(); });
// subcommand - smoke test
auto* smoke_test = sub->add_subcommand("smoke-test", "Quick test that blocks.log and blocks.index are well formed and agree with each other.")->callback([err_guard]() { err_guard(&blocklog_actions::smoke_test); });
smoke_test->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
// subcommand - vacuum
auto* vacuum = sub->add_subcommand("vacuum", "Vacuum a pruned blocks.log in to an un-pruned blocks.log")->callback([err_guard]() { err_guard(&blocklog_actions::do_vacuum); });
vacuum->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
// subcommand - genesis
auto* genesis = sub->add_subcommand("genesis", "Extract genesis_state from blocks.log as JSON")->callback([err_guard]() { err_guard(&blocklog_actions::do_genesis); });
genesis->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
genesis->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
}
int blocklog_actions::run_subcommand() {
std::ios::sync_with_stdio(false);// for potential performance boost for large block log files
void blocklog_actions::print_exception() noexcept {
try {
if(opt->trim_blocklog) {
if(opt->first_block == 0 && opt->last_block == std::numeric_limits<uint32_t>::max()) {
std::cerr << "trim-blocklog does nothing unless first and/or last block are specified.";
return -1;
}
if(opt->last_block != std::numeric_limits<uint32_t>::max()) {
if(trim_blocklog_end(opt->blocks_dir, opt->last_block) != 0)
return -1;
}
if(opt->first_block != 0) {
if(!trim_blocklog_front(opt->blocks_dir, opt->first_block))
return -1;
}
return 0;
}
if(opt->extract_blocks) {
if(opt->first_block == 0 && opt->last_block == std::numeric_limits<uint32_t>::max()) {
std::cerr << "extract-blocklog does nothing unless first and/or last block are specified.";
return -1;
}
if(!extract_block_range(opt->blocks_dir, opt->output_dir, opt->first_block, opt->last_block))
return -1;
return 0;
}
if(opt->vacuum) {
initialize();
return do_vacuum();
}
if(opt->genesis) {
initialize();
return do_genesis();
}
if(opt->make_index) {
const bfs::path blocks_dir = opt->blocks_dir;
bfs::path out_file = blocks_dir / "blocks.index";
const bfs::path block_file = blocks_dir / "blocks.log";
if(!opt->output_file.empty())
out_file = opt->output_file;
report_time rt("making index");
const auto log_level = fc::logger::get(DEFAULT_LOGGER).get_log_level();
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
block_log::construct_index(block_file.generic_string(), out_file.generic_string());
fc::logger::get(DEFAULT_LOGGER).set_log_level(log_level);
rt.report();
return 0;
}
//else print blocks.log as JSON
initialize();
read_log();
throw;
} catch(const fc::exception& e) {
elog("${e}", ("e", e.to_detail_string()));
return -1;
} catch(const boost::exception& e) {
elog("${e}", ("e", boost::diagnostic_information(e)));
return -1;
} catch(const CLI::RuntimeError& e) {
// avoid reporting it twice, RuntimeError is only for cli11
} catch(const std::exception& e) {
elog("${e}", ("e", e.what()));
return -1;
} catch(...) {
elog("unknown exception");
return -1;
}
return 0;
}
int blocklog_actions::do_genesis() {
std::optional<genesis_state> gs;
bfs::path bld = opt->blocks_dir;
auto full_path = (bld / "blocks.log").generic_string();
if(fc::exists(bld / "blocks.log")) {
gs = block_log::extract_genesis_state(opt->blocks_dir);
if (!gs) {
std::cerr << "Block log at '" << full_path
<< "' does not contain a genesis state, it only has the chain-id." << std::endl;
return -1;
}
} else {
std::cerr << "No blocks.log found at '" << full_path << "'." << std::endl;
return -1;
}
// just print if output not set
if(opt->output_file.empty()) {
std::cout << json::to_pretty_string(*gs) << std::endl;
} else {
bfs::path p = opt->output_file;
if(p.is_relative()) {
p = bfs::current_path() / p;
}
if (!fc::json::save_to_file(*gs, p, true)) {
std::cerr << "Error occurred while writing genesis JSON to '" << p.generic_string() << "'" << std::endl;
return -1;
}
std::cout << "Saved genesis JSON to '" << p.generic_string() << "'" << std::endl;
}
return 0;
}
void blocklog_actions::initialize() {
@@ -211,6 +145,78 @@ void blocklog_actions::initialize() {
FC_LOG_AND_RETHROW()
}
int blocklog_actions::make_index() {
initialize();
const bfs::path blocks_dir = opt->blocks_dir;
bfs::path out_file = blocks_dir / "blocks.index";
const bfs::path block_file = blocks_dir / "blocks.log";
if(!opt->output_file.empty()) out_file = opt->output_file;
report_time rt("making index");
const auto log_level = fc::logger::get(DEFAULT_LOGGER).get_log_level();
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
block_log::construct_index(block_file.generic_string(), out_file.generic_string());
fc::logger::get(DEFAULT_LOGGER).set_log_level(log_level);
rt.report();
return 0;
}
int blocklog_actions::trim_blocklog() {
initialize();
if(opt->last_block != std::numeric_limits<uint32_t>::max()) {
if(trim_blocklog_end(opt->blocks_dir, opt->last_block) != 0) return -1;
}
if(opt->first_block != 0) {
if(!trim_blocklog_front(opt->blocks_dir, opt->first_block)) return -1;
}
return 0;
}
int blocklog_actions::extract_blocks() {
initialize();
if(!extract_block_range(opt->blocks_dir, opt->output_dir, opt->first_block, opt->last_block))
return -1;
return 0;
}
int blocklog_actions::do_genesis() {
initialize();
std::optional<genesis_state> gs;
bfs::path bld = opt->blocks_dir;
auto full_path = (bld / "blocks.log").generic_string();
if(fc::exists(bld / "blocks.log")) {
gs = block_log::extract_genesis_state(opt->blocks_dir);
if(!gs) {
std::cerr << "Block log at '" << full_path
<< "' does not contain a genesis state, it only has the chain-id." << std::endl;
return -1;
}
} else {
std::cerr << "No blocks.log found at '" << full_path << "'." << std::endl;
return -1;
}
// just print if output not set
if(opt->output_file.empty()) {
std::cout << json::to_pretty_string(*gs) << std::endl;
} else {
bfs::path p = opt->output_file;
if(p.is_relative()) {
p = bfs::current_path() / p;
}
if(!fc::json::save_to_file(*gs, p, true)) {
std::cerr << "Error occurred while writing genesis JSON to '" << p.generic_string() << "'" << std::endl;
return -1;
}
std::cout << "Saved genesis JSON to '" << p.generic_string() << "'" << std::endl;
}
return 0;
}
int blocklog_actions::trim_blocklog_end(bfs::path block_dir, uint32_t n) {//n is last block to keep (remove later blocks)
report_time rt("trimming blocklog end");
using namespace std;
@@ -250,9 +256,10 @@ bool blocklog_actions::extract_block_range(bfs::path block_dir, bfs::path output
return status;
}
void blocklog_actions::smoke_test(bfs::path block_dir) {
int blocklog_actions::smoke_test() {
initialize();
using namespace std;
bfs::path block_dir = opt->blocks_dir;
cout << "\nSmoke test of blocks.log and blocks.index in directory " << block_dir << '\n';
trim_data td(block_dir);
auto status = fseek(td.blk_in, -sizeof(uint64_t), SEEK_END);//get last_block from blocks.log, compare to from blocks.index
@@ -279,10 +286,21 @@ void blocklog_actions::smoke_test(bfs::path block_dir) {
break;
}
cout << "\nno problems found\n";//if get here there were no exceptions
return 0;
}
int blocklog_actions::do_vacuum() {
if (!opt->blog_keep_prune_conf) {
initialize();
bfs::path bld = opt->blocks_dir;
auto full_path = (bld / "blocks.log").generic_string();
if(!fc::exists(bld / "blocks.log")) {
std::cerr << "No blocks.log found at '" << full_path << "'." << std::endl;
return -1;
}
if(!opt->blog_keep_prune_conf) {
std::cerr << "blocks.log is not a pruned log; nothing to vacuum" << std::endl;
return -1;
}
@@ -291,7 +309,8 @@ int blocklog_actions::do_vacuum() {
return 0;
}
void blocklog_actions::read_log() {
int blocklog_actions::read_log() {
initialize();
report_time rt("reading log");
block_log block_logger(opt->blocks_dir, opt->blog_keep_prune_conf);
const auto end = block_logger.read_head();
@@ -385,4 +404,6 @@ void blocklog_actions::read_log() {
if(opt->as_json_array)
*out << "]";
rt.report();
return 0;
}
+11 -16
View File
@@ -12,20 +12,12 @@ struct blocklog_options {
std::string output_file = "";
uint32_t first_block = 0;
uint32_t last_block = std::numeric_limits<uint32_t>::max();
std::string output_dir;
std::string output_dir = "";
// flags
bool no_pretty_print = false;
bool as_json_array = false;
// subcommands
bool make_index = false;
bool trim_blocklog = false;
bool extract_blocks = false;
bool smoke_test = false;
bool vacuum = false;
bool genesis = false;
std::optional<block_log_prune_config> blog_keep_prune_conf;
};
@@ -34,16 +26,19 @@ public:
blocklog_actions() : sub_command() {}
void setup(CLI::App& app);
// callbacks
int run_subcommand();
protected:
void print_exception() noexcept;
void initialize();
int trim_blocklog_end(bfs::path block_dir, uint32_t n);
bool trim_blocklog_front(bfs::path block_dir, uint32_t n);
bool extract_block_range(bfs::path block_dir, bfs::path output_dir, uint32_t start, uint32_t end);
void smoke_test(bfs::path block_dir);
int do_vacuum();
int do_genesis();
void read_log();
int make_index();
int trim_blocklog();
int extract_blocks();
int smoke_test();
int do_vacuum();
int do_genesis();
int read_log();
};
-1
View File
@@ -26,7 +26,6 @@ endif()
target_link_libraries( ${NODE_EXECUTABLE_NAME}
PRIVATE appbase version
PRIVATE -Wl,${whole_archive_flag} login_plugin -Wl,${no_whole_archive_flag}
PRIVATE -Wl,${whole_archive_flag} state_history_plugin -Wl,${no_whole_archive_flag}
PRIVATE -Wl,${whole_archive_flag} trace_api_plugin -Wl,${no_whole_archive_flag}
PRIVATE -Wl,${whole_archive_flag} chain_api_plugin -Wl,${no_whole_archive_flag}
+1 -1
View File
@@ -3,4 +3,4 @@
apt-get update
apt-get update --fix-missing
DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install tzdata
apt-get -y install zip unzip libncurses5 wget git build-essential cmake curl libcurl4-openssl-dev libgmp-dev libssl-dev libusb-1.0.0-dev libzstd-dev time pkg-config zlib1g-dev libtinfo-dev bzip2 libbz2-dev python3 file
apt-get -y install zip unzip libncurses5 wget git build-essential cmake curl libgmp-dev libssl-dev libzstd-dev time zlib1g-dev libtinfo-dev bzip2 libbz2-dev python3 file
+11 -11
View File
@@ -783,7 +783,7 @@ class Cluster(object):
#Utils.Print("nextEosIdx: %d, count: %d" % (nextEosIdx, count))
node=self.nodes[nextEosIdx]
if Utils.Debug: Utils.Print("Wait for transaction id %s on node port %d" % (transId, node.port))
if node.waitForTransInBlock(transId) is False:
if node.waitForTransactionInBlock(transId) is False:
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, node.port))
return False
@@ -804,7 +804,7 @@ class Cluster(object):
# As an extra step wait for last transaction on the root node
node=self.nodes[0]
if Utils.Debug: Utils.Print("Wait for transaction id %s on node port %d" % (transId, node.port))
if node.waitForTransInBlock(transId) is False:
if node.waitForTransactionInBlock(transId) is False:
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, node.port))
return False
@@ -1062,7 +1062,7 @@ class Cluster(object):
Utils.Print("Wait for last transfer transaction to become finalized.")
transId=Node.getTransId(trans[1])
if not biosNode.waitForTransInBlock(transId):
if not biosNode.waitForTransactionInBlock(transId):
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, biosNode.port))
return None
@@ -1146,7 +1146,7 @@ class Cluster(object):
accounts.append(initx)
transId=Node.getTransId(trans)
if not biosNode.waitForTransInBlock(transId):
if not biosNode.waitForTransactionInBlock(transId):
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, biosNode.port))
return None
@@ -1195,7 +1195,7 @@ class Cluster(object):
trans=trans[1]
transId=Node.getTransId(trans)
if not biosNode.waitForTransInBlock(transId):
if not biosNode.waitForTransactionInBlock(transId):
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, biosNode.port))
return None
@@ -1238,7 +1238,7 @@ class Cluster(object):
Node.validateTransaction(trans)
transId=Node.getTransId(trans)
if not biosNode.waitForTransInBlock(transId):
if not biosNode.waitForTransactionInBlock(transId):
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, biosNode.port))
return None
@@ -1265,7 +1265,7 @@ class Cluster(object):
Node.validateTransaction(trans[1])
transId=Node.getTransId(trans[1])
if not biosNode.waitForTransInBlock(transId):
if not biosNode.waitForTransactionInBlock(transId):
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, biosNode.port))
return None
@@ -1282,7 +1282,7 @@ class Cluster(object):
Node.validateTransaction(trans[1])
Utils.Print("Wait for issue action transaction to become finalized.")
transId=Node.getTransId(trans[1])
# biosNode.waitForTransInBlock(transId)
# biosNode.waitForTransactionInBlock(transId)
# guesstimating block finalization timeout. Two production rounds of 12 blocks per node, plus 60 seconds buffer
timeout = .5 * 12 * 2 * len(producerKeys) + 60
if not biosNode.waitForTransFinalization(transId, timeout=timeout):
@@ -1327,7 +1327,7 @@ class Cluster(object):
Utils.Print("Wait for last transfer transaction to become finalized.")
transId=Node.getTransId(trans[1])
if not biosNode.waitForTransInBlock(transId):
if not biosNode.waitForTransactionInBlock(transId):
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, biosNode.port))
return None
@@ -1339,7 +1339,7 @@ class Cluster(object):
trans=biosNode.pushMessage(eosioAccount.name, action, data, opts)
transId=Node.getTransId(trans[1])
Utils.Print("Wait for system init transaction to be in a block.")
if not biosNode.waitForTransInBlock(transId):
if not biosNode.waitForTransactionInBlock(transId):
Utils.Print("ERROR: Failed to validate transaction %s in block on server port %d." % (transId, biosNode.port))
return None
@@ -1566,7 +1566,7 @@ class Cluster(object):
if waitForTransBlock and transId is not None:
node=self.nodes[0]
if Utils.Debug: Utils.Print("Wait for transaction id %s on server port %d." % ( transId, node.port))
if node.waitForTransInBlock(transId) is False:
if node.waitForTransactionInBlock(transId) is False:
Utils.Print("ERROR: Failed to validate transaction %s got rolled into a block on server port %d." % (transId, node.port))
return False
+8 -4
View File
@@ -384,7 +384,7 @@ class Node(object):
transId=Node.getTransId(trans)
if stakedDeposit > 0:
self.waitForTransInBlock(transId) # seems like account creation needs to be finalized before transfer can happen
self.waitForTransactionInBlock(transId) # seems like account creation needs to be finalized before transfer can happen
trans = self.transferFunds(creatorAccount, account, Node.currencyIntToStr(stakedDeposit, CORE_SYMBOL), "init")
transId=Node.getTransId(trans)
@@ -403,7 +403,7 @@ class Node(object):
transId=Node.getTransId(trans)
if stakedDeposit > 0:
self.waitForTransInBlock(transId) # seems like account creation needs to be finlized before transfer can happen
self.waitForTransactionInBlock(transId) # seems like account creation needs to be finlized before transfer can happen
trans = self.transferFunds(creatorAccount, account, "%0.04f %s" % (stakedDeposit/10000, CORE_SYMBOL), "init")
self.trackCmdTransaction(trans)
transId=Node.getTransId(trans)
@@ -482,13 +482,17 @@ class Node(object):
return None
def waitForTransInBlock(self, transId, timeout=None):
def waitForTransactionInBlock(self, transId, timeout=None):
"""Wait for trans id to be finalized."""
assert(isinstance(transId, str))
lam = lambda: self.isTransInAnyBlock(transId)
ret=Utils.waitForBool(lam, timeout)
return ret
def waitForTransactionsInBlock(self, transIds, timeout=None):
for transId in transIds:
self.waitForTransactionInBlock(transId, timeout)
def waitForTransFinalization(self, transId, timeout=None):
"""Wait for trans id to be finalized."""
assert(isinstance(transId, str))
@@ -1079,7 +1083,7 @@ class Node(object):
return trans
transId=Node.getTransId(trans)
if not self.waitForTransInBlock(transId):
if not self.waitForTransactionInBlock(transId):
if exitOnError:
Utils.cmdError("transaction with id %s never made it to a block" % (transId))
Utils.errorExit("Failed to find transaction with id %s in a block before timeout" % (transId))
+1 -1
View File
@@ -180,7 +180,7 @@ try:
cmdError("FAILURE - transfer failed")
errorExit("Transfer verification failed. Excepted %s, actual: %s" % (expectedAmount, actualAmount))
node.waitForTransInBlock(transId)
node.waitForTransactionInBlock(transId)
transaction=node.getTransaction(transId, exitOnError=True, delayedRetry=False)
+1 -1
View File
@@ -171,7 +171,7 @@ try:
Print("Transaction not found for trans id: %s. Will wait %d seconds to see if it arrives in a block." %
(transId, args.transaction_time_delta))
transTimeDelayed = True
node.waitForTransInBlock(transId, timeout = args.transaction_time_delta)
node.waitForTransactionInBlock(transId, timeout = args.transaction_time_delta)
continue
lastIrreversibleBlockNum = node.getIrreversibleBlockNum()
+1 -1
View File
@@ -192,7 +192,7 @@ try:
Print("Transaction not found for trans id: %s. Will wait %d seconds to see if it arrives in a block." %
(transId, args.transaction_time_delta))
transTimeDelayed = True
node.waitForTransInBlock(transId, timeout = args.transaction_time_delta)
node.waitForTransactionInBlock(transId, timeout = args.transaction_time_delta)
continue
lastIrreversibleBlockNum = node.getIrreversibleBlockNum()
+2 -2
View File
@@ -277,7 +277,7 @@ try:
cmdError("FAILURE - transfer failed")
errorExit("Transfer verification failed. Excepted %s, actual: %s" % (expectedAmount, actualAmount))
node.waitForTransInBlock(transId)
node.waitForTransactionInBlock(transId)
transaction=node.getTransaction(transId, exitOnError=True, delayedRetry=False)
@@ -417,7 +417,7 @@ try:
errorExit("Failed to reject duplicate message for currency1111 contract")
Print("verify transaction exists")
if not node.waitForTransInBlock(transId):
if not node.waitForTransactionInBlock(transId):
cmdError("%s get transaction trans_id" % (ClientName))
errorExit("Failed to verify push message transaction id.")
+2 -2
View File
@@ -59,7 +59,7 @@ class StressNetwork:
if trid is None:
return ([], "", 0.0, "failed to issue currency0000")
print("transaction id %s" % (trid))
node.waitForTransInBlock(trid)
node.waitForTransactionInBlock(trid)
self.trList = []
expBal = 0
@@ -97,7 +97,7 @@ class StressNetwork:
for tr in self.trList:
trid = node.getTransId(tr)
transIdlist.append(trid)
node.waitForTransInBlock(trid)
node.waitForTransactionInBlock(trid)
return (transIdlist, acc2.name, expBal, "")
def on_exit(self):
+34 -55
View File
@@ -86,10 +86,10 @@ try:
nonProdNode=None
prodNodes=[]
producers=[]
for node in cluster.getNodes():
for i, node in enumerate(cluster.getNodes()):
node.producers=Cluster.parseProducers(node.nodeId)
numProducers=len(node.producers)
Print(f"node has producers={node.producers}")
Print(f"node {i} has producers={node.producers}")
if numProducers==0:
if nonProdNode is None:
nonProdNode=node
@@ -99,9 +99,8 @@ try:
prodNodes.append(node)
producers.extend(node.producers)
node=prodNodes[0]
node1=prodNodes[1]
prodAB=prodNodes[0] # defproducera, defproducerb
prodC=prodNodes[1] # defproducerc
# *** Identify a block where production is stable ***
@@ -115,24 +114,15 @@ try:
account1.activePublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY
cluster.createAccountAndVerify(account1, cluster.eosioAccount, stakedDeposit=1000)
Print("Creating account2")
account2 = Account('account2')
account2.ownerPublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY
account2.activePublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY
cluster.createAccountAndVerify(account2, cluster.eosioAccount, stakedDeposit=1000)
Print("Validating accounts after bootstrap")
cluster.validateAccounts([account1, account2])
cluster.validateAccounts([account1])
# *** Killing the "bridge" node ***
Print("Sending command to kill \"bridge\" node to separate the 2 producer groups.")
# block number to start expecting node killed after
preKillBlockNum=nonProdNode.getBlockNum()
preKillBlockProducer=nonProdNode.getBlockProducerByNum(preKillBlockNum)
# kill at the end of the production window for defproducera, so there is still time for the fork for
# kill at the beginning of the production window for defproducera, so there is time for the fork for
# defproducerc to grow before it would overtake the fork for defproducera and defproducerb
killAtProducer="defproducerc"
nonProdNode.killNodeOnProducer(producer=killAtProducer, whereInSequence=11)
killAtProducer="defproducera"
nonProdNode.killNodeOnProducer(producer=killAtProducer, whereInSequence=1)
#verify that the non producing node is not alive (and populate the producer nodes with current getInfo data to report if
#an error occurs)
@@ -143,7 +133,7 @@ try:
while nonProdNode.verifyAlive() and count > 0:
# wait on prodNode 0 since it will continue to advance, since defproducera and defproducerb are its producers
Print("Wait for next block")
assert prodNodes[0].waitForNextBlock(timeout=6), "Production node 0 should continue to advance, even after bridge node is killed"
assert prodAB.waitForNextBlock(timeout=6), "Production node AB should continue to advance, even after bridge node is killed"
count -= 1
assert not nonProdNode.verifyAlive(), "Bridge node should have been killed if test was functioning correctly."
@@ -155,9 +145,9 @@ try:
return status["state"]
transferAmount = 10
prodNodes[1].transferFunds(cluster.eosioAccount, account1, f"{transferAmount}.0000 {CORE_SYMBOL}", "fund account")
transId = prodNodes[1].getLastTrackedTransactionId()
retStatus = prodNodes[1].getTransactionStatus(transId)
prodC.transferFunds(cluster.eosioAccount, account1, f"{transferAmount}.0000 {CORE_SYMBOL}", "fund account")
transId = prodC.getLastTrackedTransactionId()
retStatus = prodC.getTransactionStatus(transId)
state = getState(retStatus)
localState = "LOCALLY_APPLIED"
@@ -169,21 +159,21 @@ try:
assert state == localState, \
f"ERROR: getTransactionStatus didn't return \"{localState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}"
assert prodNodes[1].waitForNextBlock(), "Production node 1 should continue to advance, even after bridge node is killed"
assert prodC.waitForNextBlock(), "Production node C should continue to advance, even after bridge node is killed"
# since the Bridge node is killed when this producer is producing its last block in its window, there is plenty of time for the transfer to be
# sent before the first block is created, but adding this to ensure it is in one of these blocks
numTries = 2
preInfo = prodNodes[1].getInfo()
preInfo = prodC.getInfo()
while numTries > 0:
retStatus = prodNodes[1].getTransactionStatus(transId)
retStatus = prodC.getTransactionStatus(transId)
state = getState(retStatus)
if state == inBlockState:
break
numTries -= 1
assert prodNodes[1].waitForNextBlock(), "Production node 1 should continue to advance, even after bridge node is killed"
assert prodC.waitForNextBlock(), "Production node C should continue to advance, even after bridge node is killed"
postInfo = prodNodes[1].getInfo()
postInfo = prodC.getInfo()
Print(f"getTransactionStatus returned status: {json.dumps(retStatus, indent=1)}")
assert state == inBlockState, \
@@ -195,42 +185,31 @@ try:
if not nonProdNode.relaunch():
errorExit(f"Failure - (non-production) node {nonProdNode.nodeNum} should have restarted")
Print("Wait for LIB to move, which indicates prodNode[1] has forked out the branch")
assert prodNodes[1].waitForLibToAdvance(), \
"ERROR: Network did not reach concensus after bridge node was restarted."
Print("Wait for LIB to move, which indicates prodC has forked out the branch")
assert prodC.waitForLibToAdvance(), \
"ERROR: Network did not reach consensus after bridge node was restarted."
Print("Wait till prodNodes[1] is reporting at least the same head block number as the forked out block")
assert prodNodes[1].waitForBlock(originalInBlockState["block_number"]), \
f"Production node 1 should continue to advance after LIB starts advancing. \n\originalInBlockState: {json.dumps(originalInBlockState, indent=1)}"
retStatus = prodNodes[1].getTransactionStatus(transId)
retStatus = prodC.getTransactionStatus(transId)
state = getState(retStatus)
assert state == forkedOutState, \
f"ERROR: getTransactionStatus didn't return \"{forkedOutState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}" + \
f"\n\nprod 0 info: {json.dumps(prodNodes[0].getInfo(), indent=1)}\n\nprod 1 info: {json.dumps(prodNodes[1].getInfo(), indent=1)}"
f"\n\nprod AB info: {json.dumps(prodAB.getInfo(), indent=1)}\n\nprod C info: {json.dumps(prodC.getInfo(), indent=1)}"
for prodNode in prodNodes:
info=prodNode.getInfo()
Print(f"node info: {json.dumps(info, indent=1)}")
retStatus = prodNodes[1].getTransactionStatus(transId)
state = getState(retStatus)
assert prodC.waitForProducer("defproducerc"), \
f"Waiting for prodC to produce, but it never happened" + \
f"\n\nprod AB info: {json.dumps(prodAB.getInfo(), indent=1)}\n\nprod C info: {json.dumps(prodC.getInfo(), indent=1)}"
assert state == forkedOutState, \
f"ERROR: getTransactionStatus didn't return \"{forkedOutState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}" + \
f"\n\nprod 0 info: {json.dumps(prodNodes[0].getInfo(), indent=1)}\n\nprod 1 info: {json.dumps(prodNodes[1].getInfo(), indent=1)}"
assert prodNodes[1].waitForProducer("defproducerc"), \
f"Waiting for prodNode 1 to produce, but it never happened" + \
f"\n\nprod 0 info: {json.dumps(prodNodes[0].getInfo(), indent=1)}\n\nprod 1 info: {json.dumps(prodNodes[1].getInfo(), indent=1)}"
retStatus = prodNodes[1].getTransactionStatus(transId)
retStatus = prodC.getTransactionStatus(transId)
state = getState(retStatus)
assert state == inBlockState, \
f"ERROR: getTransactionStatus didn't return \"{inBlockState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}" + \
f"\n\nprod 0 info: {json.dumps(prodNodes[0].getInfo(), indent=1)}\n\nprod 1 info: {json.dumps(prodNodes[1].getInfo(), indent=1)}"
f"\n\nprod AB info: {json.dumps(prodAB.getInfo(), indent=1)}\n\nprod C info: {json.dumps(prodC.getInfo(), indent=1)}"
afterForkInBlockState = retStatus
afterForkBlockId = retStatus["block_id"]
@@ -238,22 +217,22 @@ try:
"ERROR: The way the test is designed, the transaction should be added to a block that has a higher number than it was in originally before it was forked out." + \
f"\n\noriginal in block state: {json.dumps(originalInBlockState, indent=1)}\n\nafter fork in block state: {json.dumps(afterForkInBlockState, indent=1)}"
assert prodNodes[1].waitForBlock(afterForkInBlockState["block_number"], timeout=120, blockType=BlockType.lib), \
f"ERROR: Block never finalized.\n\nprod 0 info: {json.dumps(prodNodes[0].getInfo(), indent=1)}\n\nprod 1 info: {json.dumps(prodNodes[1].getInfo(), indent=1)}" + \
assert prodC.waitForBlock(afterForkInBlockState["block_number"], timeout=120, blockType=BlockType.lib), \
f"ERROR: Block never finalized.\n\nprod AB info: {json.dumps(prodAB.getInfo(), indent=1)}\n\nprod C info: {json.dumps(prodC.getInfo(), indent=1)}" + \
f"\n\nafter fork in block state: {json.dumps(afterForkInBlockState, indent=1)}"
retStatus = prodNodes[1].getTransactionStatus(transId)
retStatus = prodC.getTransactionStatus(transId)
if afterForkBlockId != retStatus["block_id"]: # might have been forked out, if so wait for new block to become LIB
assert prodNodes[1].waitForBlock(retStatus["block_number"], timeout=120, blockType=BlockType.lib), \
f"ERROR: Block never finalized.\n\nprod 0 info: {json.dumps(prodNodes[0].getInfo(), indent=1)}\n\nprod 1 info: {json.dumps(prodNodes[1].getInfo(), indent=1)}" + \
assert prodC.waitForBlock(retStatus["block_number"], timeout=120, blockType=BlockType.lib), \
f"ERROR: Block never finalized.\n\nprod AB info: {json.dumps(prodAB.getInfo(), indent=1)}\n\nprod C info: {json.dumps(prodC.getInfo(), indent=1)}" + \
f"\n\nafter fork in block state: {json.dumps(afterForkInBlockState, indent=1)}"
retStatus = prodNodes[1].getTransactionStatus(transId)
retStatus = prodC.getTransactionStatus(transId)
state = getState(retStatus)
assert state == irreversibleState, \
f"ERROR: getTransactionStatus didn't return \"{irreversibleState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}" + \
f"\n\nprod 0 info: {json.dumps(prodNodes[0].getInfo(), indent=1)}\n\nprod 1 info: {json.dumps(prodNodes[1].getInfo(), indent=1)}"
f"\n\nprod AB info: {json.dumps(prodAB.getInfo(), indent=1)}\n\nprod C info: {json.dumps(prodC.getInfo(), indent=1)}"
testSuccessful=True
finally: