Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

New options for api nodes - 2.0 #8702

Merged
merged 12 commits into from
Mar 3, 2020
8 changes: 2 additions & 6 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2678,22 +2678,18 @@ void controller::push_block( std::future<block_state_ptr>& block_state_future,
my->push_block( block_state_future, forked_branch_cb, trx_lookup );
}

bool controller::in_immutable_mode()const{
return (db_mode_is_immutable(get_read_mode()));
}

transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point deadline,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time ) {
validate_db_available_size();
EOS_ASSERT( !in_immutable_mode(), transaction_type_exception, "push transaction not allowed in read-only mode" );
EOS_ASSERT( get_read_mode() != db_read_mode::IRREVERSIBLE, transaction_type_exception, "push transaction not allowed in irreversible mode" );
EOS_ASSERT( trx && !trx->implicit && !trx->scheduled, transaction_type_exception, "Implicit/Scheduled transaction not allowed" );
return my->push_transaction(trx, deadline, billed_cpu_time_us, explicit_billed_cpu_time );
}

transaction_trace_ptr controller::push_scheduled_transaction( const transaction_id_type& trxid, fc::time_point deadline,
uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time )
{
EOS_ASSERT( !in_immutable_mode(), transaction_type_exception, "push scheduled transaction not allowed in read-only mode" );
EOS_ASSERT( get_read_mode() != db_read_mode::IRREVERSIBLE, transaction_type_exception, "push scheduled transaction not allowed in irreversible mode" );
validate_db_available_size();
return my->push_scheduled_transaction( trxid, deadline, billed_cpu_time_us, explicit_billed_cpu_time );
}
Expand Down
3 changes: 0 additions & 3 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ namespace eosio { namespace chain {
IRREVERSIBLE
};

inline bool db_mode_is_immutable(db_read_mode m) {return db_read_mode::READ_ONLY == m || db_read_mode::IRREVERSIBLE ==m;}

enum class validation_mode {
FULL,
LIGHT
Expand Down Expand Up @@ -284,7 +282,6 @@ namespace eosio { namespace chain {

db_read_mode get_read_mode()const;
validation_mode get_validation_mode()const;
bool in_immutable_mode()const;

void set_subjective_cpu_leeway(fc::microseconds leeway);
fc::optional<fc::microseconds> get_subjective_cpu_leeway() const;
Expand Down
50 changes: 43 additions & 7 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ std::ostream& operator<<(std::ostream& osm, eosio::chain::db_read_mode m) {
osm << "speculative";
} else if ( m == eosio::chain::db_read_mode::HEAD ) {
osm << "head";
} else if ( m == eosio::chain::db_read_mode::READ_ONLY ) {
} else if ( m == eosio::chain::db_read_mode::READ_ONLY ) { // deprecated
osm << "read-only";
} else if ( m == eosio::chain::db_read_mode::IRREVERSIBLE ) {
osm << "irreversible";
Expand Down Expand Up @@ -140,6 +140,9 @@ class chain_plugin_impl {
bfs::path blocks_dir;
bool readonly = false;
flat_map<uint32_t,block_id_type> loaded_checkpoints;
bool accept_transactions = false;
bool api_accept_transactions = true;


fc::optional<fork_database> fork_db;
fc::optional<block_log> block_logger;
Expand Down Expand Up @@ -235,11 +238,12 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
"Deferred transactions sent by accounts in this list do not have any of the subjective whitelist/blacklist checks applied to them (may specify multiple times)")
("read-mode", boost::program_options::value<eosio::chain::db_read_mode>()->default_value(eosio::chain::db_read_mode::SPECULATIVE),
"Database read mode (\"speculative\", \"head\", \"read-only\", \"irreversible\").\n"
"In \"speculative\" mode database contains changes done up to the head block plus changes made by transactions not yet included to the blockchain.\n"
"In \"head\" mode database contains changes done up to the current head block.\n"
"In \"read-only\" mode database contains changes done up to the current head block and transactions cannot be pushed to the chain API.\n"
"In \"irreversible\" mode database contains changes done up to the last irreversible block and transactions cannot be pushed to the chain API.\n"
"In \"speculative\" mode: database contains state changes by transactions in the blockchain up to the head block as well as some transactions not yet included in the blockchain.\n"
"In \"head\" mode: database contains state changes by only transactions in the blockchain up to the head block; transactions received by the node are relayed if valid.\n"
"In \"read-only\" mode: (DEPRECATED: see p2p-accept-transactions & api-accept-transactions) database contains state changes by only transactions in the blockchain up to the head block; transactions received via the P2P network are not relayed and transactions cannot be pushed via the chain API.\n"
"In \"irreversible\" mode: database contains state changes by only transactions in the blockchain up to the last irreversible block; transactions received via the P2P network are not relayed and transactions cannot be pushed via the chain API.\n"
)
( "api-accept-transactions", bpo::value<bool>()->default_value(true), "Allow API transactions to be evaluated and relayed if valid.")
("validation-mode", boost::program_options::value<eosio::chain::validation_mode>()->default_value(eosio::chain::validation_mode::FULL),
"Chain validation mode (\"full\" or \"light\").\n"
"In \"full\" mode all incoming blocks will be fully validated.\n"
Expand Down Expand Up @@ -991,6 +995,21 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
if ( options.count("read-mode") ) {
my->chain_config->read_mode = options.at("read-mode").as<db_read_mode>();
}
my->api_accept_transactions = options.at( "api-accept-transactions" ).as<bool>();

if( my->chain_config->read_mode == db_read_mode::IRREVERSIBLE || my->chain_config->read_mode == db_read_mode::READ_ONLY ) {
if( my->chain_config->read_mode == db_read_mode::READ_ONLY ) {
wlog( "read-mode = read-only is deprecated use p2p-accept-transactions = false, api-accept-transactions = false instead." );
}
if( my->api_accept_transactions ) {
my->api_accept_transactions = false;
std::stringstream ss; ss << my->chain_config->read_mode;
wlog( "api-accept-transactions set to false due to read-mode: ${m}", ("m", ss.str()) );
}
}
if( my->api_accept_transactions ) {
enable_accept_transactions();
}

if ( options.count("validation-mode") ) {
my->chain_config->block_validation_mode = options.at("validation-mode").as<validation_mode>();
Expand Down Expand Up @@ -1077,6 +1096,8 @@ void chain_plugin::plugin_initialize(const variables_map& options) {

void chain_plugin::plugin_startup()
{ try {
EOS_ASSERT( my->chain_config->read_mode != db_read_mode::IRREVERSIBLE || !accept_transactions(), plugin_config_exception,
"read-mode = irreversible. transactions should not be enabled by enable_accept_transactions" );
try {
auto shutdown = [](){ return app().is_quiting(); };
if (my->snapshot_path) {
Expand Down Expand Up @@ -1123,14 +1144,16 @@ void chain_plugin::plugin_shutdown() {
my->chain.reset();
}

chain_apis::read_write::read_write(controller& db, const fc::microseconds& abi_serializer_max_time)
chain_apis::read_write::read_write(controller& db, const fc::microseconds& abi_serializer_max_time, bool api_accept_transactions)
: db(db)
, abi_serializer_max_time(abi_serializer_max_time)
, api_accept_transactions(api_accept_transactions)
{
}

void chain_apis::read_write::validate() const {
EOS_ASSERT( !db.in_immutable_mode(), missing_chain_api_plugin_exception, "Not allowed, node in read-only mode" );
EOS_ASSERT( api_accept_transactions, missing_chain_api_plugin_exception,
"Not allowed, node has api-accept-transactions = false" );
}

bool chain_plugin::accept_block(const signed_block_ptr& block, const block_id_type& id ) {
Expand Down Expand Up @@ -1366,6 +1389,19 @@ fc::microseconds chain_plugin::get_abi_serializer_max_time() const {
return my->abi_serializer_max_time_us;
}

bool chain_plugin::api_accept_transactions() const{
return my->api_accept_transactions;
}

bool chain_plugin::accept_transactions() const {
return my->accept_transactions;
}

void chain_plugin::enable_accept_transactions() {
my->accept_transactions = true;
}


void chain_plugin::log_guard_exception(const chain::guard_exception&e ) {
if (e.code() == chain::database_guard_exception::code_value) {
elog("Database has reached an unsafe level of usage, shutting down to avoid corrupting the database. "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,9 @@ class read_only {
class read_write {
controller& db;
const fc::microseconds abi_serializer_max_time;
const bool api_accept_transactions;
public:
read_write(controller& db, const fc::microseconds& abi_serializer_max_time);
read_write(controller& db, const fc::microseconds& abi_serializer_max_time, bool api_accept_transactions);
void validate() const;

using push_block_params = chain::signed_block;
Expand Down Expand Up @@ -704,7 +705,7 @@ class chain_plugin : public plugin<chain_plugin> {
void plugin_shutdown();

chain_apis::read_only get_read_only_api() const { return chain_apis::read_only(chain(), get_abi_serializer_max_time()); }
chain_apis::read_write get_read_write_api() { return chain_apis::read_write(chain(), get_abi_serializer_max_time()); }
chain_apis::read_write get_read_write_api() { return chain_apis::read_write(chain(), get_abi_serializer_max_time(), api_accept_transactions()); }

bool accept_block( const chain::signed_block_ptr& block, const chain::block_id_type& id );
void accept_transaction(const chain::packed_transaction_ptr& trx, chain::plugin_interface::next_function<chain::transaction_trace_ptr> next);
Expand Down Expand Up @@ -733,6 +734,10 @@ class chain_plugin : public plugin<chain_plugin> {

chain::chain_id_type get_chain_id() const;
fc::microseconds get_abi_serializer_max_time() const;
bool api_accept_transactions() const;
// set true by other plugins if any plugin allows transactions
bool accept_transactions() const;
void enable_accept_transactions();

static void handle_guard_exception(const chain::guard_exception& e);
void do_hard_replay(const variables_map& options);
Expand Down
44 changes: 29 additions & 15 deletions plugins/net_plugin/net_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ namespace eosio {
int max_cleanup_time_ms = 0;
uint32_t max_client_count = 0;
uint32_t max_nodes_per_host = 1;
bool p2p_accept_transactions = true;

/// Peer clock may be no more than 1 second skewed from our clock, including network latency.
const std::chrono::system_clock::duration peer_authentication_interval{std::chrono::seconds{1}};
Expand All @@ -245,7 +246,6 @@ namespace eosio {
fc::sha256 node_id;
string user_agent_name;

eosio::db_read_mode db_read_mode = eosio::db_read_mode::SPECULATIVE;
chain_plugin* chain_plug = nullptr;
producer_plugin* producer_plug = nullptr;
bool use_socket_read_watermark = false;
Expand Down Expand Up @@ -2433,6 +2433,12 @@ namespace eosio {
handle_message( blk_id, std::move( ptr ) );

} else if( which == packed_transaction_which ) {
if( !my_impl->p2p_accept_transactions ) {
fc_dlog( logger, "p2p-accept-transaction=false - dropping txn" );
pending_message_buffer.advance_read_ptr( message_length );
return true;
}

auto ds = pending_message_buffer.create_datastream();
fc::raw::unpack( ds, which ); // throw away
shared_ptr<packed_transaction> ptr = std::make_shared<packed_transaction>();
Expand Down Expand Up @@ -2832,11 +2838,6 @@ namespace eosio {
}

void connection::handle_message( packed_transaction_ptr trx ) {
if( db_mode_is_immutable(my_impl->db_read_mode) ) {
fc_dlog( logger, "got a txn in read-only mode - dropping" );
return;
}

const auto& tid = trx->id();
peer_dlog( this, "received packed_transaction ${id}", ("id", tid) );

Expand Down Expand Up @@ -3254,6 +3255,7 @@ namespace eosio {
" p2p.trx.eos.io:9876:trx\n"
" p2p.blk.eos.io:9876:blk\n")
( "p2p-max-nodes-per-host", bpo::value<int>()->default_value(def_max_nodes_per_host), "Maximum number of client nodes from any single IP address")
( "p2p-accept-transactions", bpo::value<bool>()->default_value(true), "Allow transactions received over p2p network to be evaluated and relayed if valid.")
( "agent-name", bpo::value<string>()->default_value("\"EOS Test Agent\""), "The name supplied to identify this node amongst the peers.")
( "allowed-connection", bpo::value<vector<string>>()->multitoken()->default_value({"any"}, "any"), "Can be 'any' or 'producers' or 'specified' or 'none'. If 'specified', peer-key must be specified at least once. If only 'producers', peer-key is not required. 'producers' and 'specified' may be combined.")
( "peer-key", bpo::value<vector<string>>()->composing()->multitoken(), "Optional public key of peer allowed to connect. May be used multiple times.")
Expand All @@ -3265,7 +3267,7 @@ namespace eosio {
( "net-threads", bpo::value<uint16_t>()->default_value(my->thread_pool_size),
"Number of worker threads in net_plugin thread pool" )
( "sync-fetch-span", bpo::value<uint32_t>()->default_value(def_sync_fetch_span), "number of blocks to retrieve in a chunk from any individual peer during synchronization")
( "use-socket-read-watermark", bpo::value<bool>()->default_value(false), "Enable expirimental socket read watermark optimization")
( "use-socket-read-watermark", bpo::value<bool>()->default_value(false), "Enable experimental socket read watermark optimization")
( "peer-log-format", bpo::value<string>()->default_value( "[\"${_name}\" ${_ip}:${_port}]" ),
"The string used to format peers when logging messages about them. Variables are escaped with ${<variable name>}.\n"
"Available Variables:\n"
Expand Down Expand Up @@ -3297,6 +3299,7 @@ namespace eosio {
my->resp_expected_period = def_resp_expected_wait;
my->max_client_count = options.at( "max-clients" ).as<int>();
my->max_nodes_per_host = options.at( "p2p-max-nodes-per-host" ).as<int>();
my->p2p_accept_transactions = options.at( "p2p-accept-transactions" ).as<bool>();

my->use_socket_read_watermark = options.at( "use-socket-read-watermark" ).as<bool>();

Expand Down Expand Up @@ -3363,6 +3366,18 @@ namespace eosio {
EOS_ASSERT( my->chain_plug, chain::missing_chain_plugin_exception, "" );
my->chain_id = my->chain_plug->get_chain_id();
fc::rand_pseudo_bytes( my->node_id.data(), my->node_id.data_size());
const controller& cc = my->chain_plug->chain();

if( cc.get_read_mode() == db_read_mode::IRREVERSIBLE || cc.get_read_mode() == db_read_mode::READ_ONLY ) {
if( my->p2p_accept_transactions ) {
my->p2p_accept_transactions = false;
string m = cc.get_read_mode() == db_read_mode::IRREVERSIBLE ? "irreversible" : "read-only";
wlog( "p2p-accept-transactions set to false due to read-mode: ${m}", ("m", m) );
}
}
if( my->p2p_accept_transactions ) {
my->chain_plug->enable_accept_transactions();
}

} FC_LOG_AND_RETHROW()
}
Expand All @@ -3379,14 +3394,12 @@ namespace eosio {

my->dispatcher.reset( new dispatch_manager( my_impl->thread_pool->get_executor() ) );

chain::controller&cc = my->chain_plug->chain();
my->db_read_mode = cc.get_read_mode();
if( cc.in_immutable_mode() && my->p2p_address.size() ) {
fc_wlog( logger, "\n"
"**********************************\n"
"* Read Only Mode *\n"
"* - Transactions not forwarded - *\n"
"**********************************\n" );
if( !my->p2p_accept_transactions && my->p2p_address.size() ) {
fc_ilog( logger, "\n"
"***********************************\n"
"* p2p-accept-transactions = false *\n"
"* Transactions not forwarded *\n"
"***********************************\n" );
}

tcp::endpoint listen_endpoint;
Expand Down Expand Up @@ -3433,6 +3446,7 @@ namespace eosio {
my->start_listen_loop();
}
{
chain::controller& cc = my->chain_plug->chain();
cc.accepted_block.connect( [my = my]( const block_state_ptr& s ) {
my->on_accepted_block( s );
} );
Expand Down
5 changes: 4 additions & 1 deletion plugins/producer_plugin/producer_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,9 @@ void producer_plugin::plugin_startup()
EOS_ASSERT( my->_producers.empty() || chain.get_validation_mode() == chain::validation_mode::FULL, plugin_config_exception,
"node cannot have any producer-name configured because block production is not safe when validation_mode is not \"full\"" );

EOS_ASSERT( my->_producers.empty() || my->chain_plug->accept_transactions(), plugin_config_exception,
"node cannot have any producer-name configured because no block production is possible with no [api|p2p]-accepted-transactions" );

my->_accepted_block_connection.emplace(chain.accepted_block.connect( [this]( const auto& bsp ){ my->on_block( bsp ); } ));
my->_accepted_block_header_connection.emplace(chain.accepted_block_header.connect( [this]( const auto& bsp ){ my->on_block_header( bsp ); } ));
my->_irreversible_block_connection.emplace(chain.irreversible_block.connect( [this]( const auto& bsp ){ my->on_irreversible_block( bsp->block ); } ));
Expand Down Expand Up @@ -1415,7 +1418,7 @@ fc::time_point producer_plugin_impl::calculate_block_deadline( const fc::time_po
producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
chain::controller& chain = chain_plug->chain();

if( chain.in_immutable_mode() )
if( !chain_plug->accept_transactions() )
return start_block_result::waiting_for_block;

const auto& hbs = chain.head_block_state();
Expand Down