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 - 1.8 #8742

Merged
merged 10 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 @@ -2502,22 +2502,18 @@ void controller::push_block( std::future<block_state_ptr>& block_state_future )
my->push_block( block_state_future );
}

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 @@ -47,8 +47,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 @@ -275,7 +273,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 @@ -37,7 +37,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 @@ -154,6 +154,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 @@ -241,11 +244,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 @@ -883,6 +887,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 @@ -961,6 +980,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 @@ -1000,14 +1021,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 @@ -1239,6 +1262,19 @@ fc::microseconds chain_plugin::get_abi_serializer_max_time() const {
return my->abi_serializer_max_time_ms;
}

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 @@ -580,8 +580,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 @@ -684,7 +685,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& trx, chain::plugin_interface::next_function<chain::transaction_trace_ptr> next);
Expand Down Expand Up @@ -714,6 +715,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);
static void handle_db_exhaustion();
Expand Down
38 changes: 28 additions & 10 deletions plugins/net_plugin/net_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ namespace eosio {
uint32_t max_client_count = 0;
uint32_t max_nodes_per_host = 1;
uint32_t num_clients = 0;
bool p2p_accept_transactions = true;

vector<string> supplied_peers;
vector<chain::public_key_type> allowed_peers; ///< peer keys allowed to connect
Expand Down Expand Up @@ -2219,6 +2220,11 @@ namespace eosio {
if( msg.contains<signed_block>() ) {
m( std::move( msg.get<signed_block>() ) );
} else if( msg.contains<packed_transaction>() ) {
if( !my_impl->p2p_accept_transactions ) {
fc_dlog( logger, "p2p-accept-transaction=false - dropping txn" );
conn->pending_message_buffer.advance_read_ptr( message_length );
return true;
}
m( std::move( msg.get<packed_transaction>() ) );
} else {
msg.visit( m );
Expand Down Expand Up @@ -2565,10 +2571,6 @@ namespace eosio {
void net_plugin_impl::handle_message(const connection_ptr& c, const packed_transaction_ptr& trx) {
peer_dlog(c, "got a packed transaction, cancel wait");
controller& cc = my_impl->chain_plug->chain();
if( db_mode_is_immutable(cc.get_read_mode()) ) {
fc_dlog(logger, "got a txn in read-only mode - dropping");
return;
}
if( sync_master->is_active(c) ) {
fc_dlog(logger, "got a txn during sync - dropping");
return;
Expand Down Expand Up @@ -2970,6 +2972,7 @@ namespace eosio {
( "p2p-server-address", bpo::value<string>(), "An externally accessible host:port for identifying this node. Defaults to p2p-listen-endpoint.")
( "p2p-peer-address", bpo::value< vector<string> >()->composing(), "The public endpoint of a peer node to connect to. Use multiple p2p-peer-address options as needed to compose a network.")
( "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 @@ -2983,7 +2986,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 @@ -3022,6 +3025,7 @@ namespace eosio {
my->max_nodes_per_host = options.at( "p2p-max-nodes-per-host" ).as<int>();
my->num_clients = 0;
my->started_sessions = 0;
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 @@ -3090,6 +3094,18 @@ namespace eosio {
fc::rand_pseudo_bytes( my->node_id.data(), my->node_id.data_size());
fc_ilog( logger, "my node_id is ${id}", ("id", my->node_id ));

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 @@ -3102,6 +3118,13 @@ namespace eosio {
my->thread_pool.emplace( "net", my->thread_pool_size );

shared_ptr<tcp::resolver> resolver = std::make_shared<tcp::resolver>( my_impl->thread_pool->get_executor() );
if( !my->p2p_accept_transactions && my->p2p_address.size() ) {
fc_ilog( logger, "\n"
"***********************************\n"
"* p2p-accept-transactions = false *\n"
"* Transactions not forwarded *\n"
"***********************************\n" );
}
if( my->p2p_address.size() > 0 ) {
auto host = my->p2p_address.substr( 0, my->p2p_address.find( ':' ));
auto port = my->p2p_address.substr( host.size() + 1, my->p2p_address.size());
Expand Down Expand Up @@ -3154,11 +3177,6 @@ namespace eosio {

my->incoming_transaction_ack_subscription = app().get_channel<channels::transaction_ack>().subscribe(boost::bind(&net_plugin_impl::transaction_ack, my.get(), _1));

if( cc.in_immutable_mode() ) {
my->max_nodes_per_host = 0;
fc_ilog( logger, "node in read-only mode setting max_nodes_per_host to 0 to prevent connections" );
}

my->start_monitors();

for( auto seed_node : my->supplied_peers ) {
Expand Down
4 changes: 3 additions & 1 deletion plugins/producer_plugin/producer_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,8 @@ 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 ); } ));
Expand Down Expand Up @@ -1371,7 +1373,7 @@ enum class tx_category {
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