未验证 提交 5a520fb2 编写于 作者: A Anton Perkov 提交者: GitHub

Merge pull request #4373 from EOSIO/revert-speculative-state-2

Revert speculative state #4196
......@@ -57,6 +57,7 @@ struct controller_impl {
controller::config conf;
chain_id_type chain_id;
bool replaying = false;
db_read_mode read_mode = db_read_mode::SPECULATIVE;
bool in_trx_requiring_checks = false; ///< if true, checks that are normally skipped on replay (e.g. auth checks) cannot be skipped
typedef pair<scope_name,action_name> handler_key;
......@@ -78,8 +79,10 @@ struct controller_impl {
reversible_blocks.remove( *b );
}
for( const auto& t : head->trxs )
unapplied_transactions[t->signed_id] = t;
if ( read_mode == db_read_mode::SPECULATIVE ) {
for( const auto& t : head->trxs )
unapplied_transactions[t->signed_id] = t;
}
head = prev;
db.undo();
......@@ -104,7 +107,8 @@ struct controller_impl {
resource_limits( db ),
authorization( s, db ),
conf( cfg ),
chain_id( cfg.genesis.compute_chain_id() )
chain_id( cfg.genesis.compute_chain_id() ),
read_mode( cfg.read_mode )
{
#define SET_APP_HANDLER( receiver, contract, action) \
......@@ -699,8 +703,14 @@ struct controller_impl {
emit(self.applied_transaction, trace);
trx_context.squash();
restore.cancel();
if ( read_mode != db_read_mode::SPECULATIVE && pending->_block_status == controller::block_status::incomplete ) {
//this may happen automatically in destructor, but I prefere make it more explicit
trx_context.undo();
} else {
restore.cancel();
trx_context.squash();
}
if (!implicit) {
unapplied_transactions.erase( trx->signed_id );
......@@ -741,48 +751,50 @@ struct controller_impl {
auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending();
//modify state in speculative block only if we are speculative reads mode (other wise we need clean state for head or irreversible reads)
if ( read_mode == db_read_mode::SPECULATIVE || pending->_block_status != controller::block_status::incomplete ) {
const auto& gpo = db.get<global_property_object>();
if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...
( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ...
pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ...
!was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then:
)
{
// Promote proposed schedule to pending schedule.
if( !replaying ) {
ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ",
("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num)
("lib", pending->_pending_block_state->dpos_irreversible_blocknum)
("schedule", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) );
}
pending->_pending_block_state->set_new_producers( gpo.proposed_schedule );
db.modify( gpo, [&]( auto& gp ) {
gp.proposed_schedule_block_num = optional<block_num_type>();
gp.proposed_schedule.clear();
});
}
const auto& gpo = db.get<global_property_object>();
if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...
( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ...
pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ...
!was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then:
)
{
// Promote proposed schedule to pending schedule.
if( !replaying ) {
ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ",
("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num)
("lib", pending->_pending_block_state->dpos_irreversible_blocknum)
("schedule", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) );
try {
auto onbtrx = std::make_shared<transaction_metadata>( get_on_block_transaction() );
auto reset_in_trx_requiring_checks = fc::make_scoped_exit([old_value=in_trx_requiring_checks,this](){
in_trx_requiring_checks = old_value;
});
in_trx_requiring_checks = true;
push_transaction( onbtrx, fc::time_point::maximum(), true, self.get_global_properties().configuration.min_transaction_cpu_usage );
} catch( const boost::interprocess::bad_alloc& e ) {
elog( "on block transaction failed due to a bad allocation" );
throw;
} catch( const fc::exception& e ) {
wlog( "on block transaction failed, but shouldn't impact block generation, system contract needs update" );
edump((e.to_detail_string()));
} catch( ... ) {
}
pending->_pending_block_state->set_new_producers( gpo.proposed_schedule );
db.modify( gpo, [&]( auto& gp ) {
gp.proposed_schedule_block_num = optional<block_num_type>();
gp.proposed_schedule.clear();
});
}
try {
auto onbtrx = std::make_shared<transaction_metadata>( get_on_block_transaction() );
auto reset_in_trx_requiring_checks = fc::make_scoped_exit([old_value=in_trx_requiring_checks,this](){
in_trx_requiring_checks = old_value;
});
in_trx_requiring_checks = true;
push_transaction( onbtrx, fc::time_point::maximum(), true, self.get_global_properties().configuration.min_transaction_cpu_usage );
} catch( const boost::interprocess::bad_alloc& e ) {
elog( "on block transaction failed due to a bad allocation" );
throw;
} catch( const fc::exception& e ) {
wlog( "on block transaction failed, but shouldn't impact block generation, system contract needs update" );
edump((e.to_detail_string()));
} catch( ... ) {
wlog( "on block transaction failed, but shouldn't impact block generation, system contract needs update" );
clear_expired_input_transactions();
update_producers_authority();
}
clear_expired_input_transactions();
update_producers_authority();
guard_pending.cancel();
} // start_block
......@@ -939,8 +951,10 @@ struct controller_impl {
void abort_block() {
if( pending ) {
for( const auto& t : pending->_pending_block_state->trxs )
unapplied_transactions[t->signed_id] = t;
if ( read_mode == db_read_mode::SPECULATIVE ) {
for( const auto& t : pending->_pending_block_state->trxs )
unapplied_transactions[t->signed_id] = t;
}
pending.reset();
}
}
......@@ -1416,6 +1430,10 @@ chain_id_type controller::get_chain_id()const {
return my->chain_id;
}
db_read_mode controller::get_read_mode()const {
return my->read_mode;
}
const apply_handler* controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const
{
auto native_handler_scope = my->apply_handlers.find( receiver );
......@@ -1437,9 +1455,13 @@ const account_object& controller::get_account( account_name name )const
vector<transaction_metadata_ptr> controller::get_unapplied_transactions() const {
vector<transaction_metadata_ptr> result;
result.reserve(my->unapplied_transactions.size());
for ( const auto& entry: my->unapplied_transactions ) {
result.emplace_back(entry.second);
if ( my->read_mode == db_read_mode::SPECULATIVE ) {
result.reserve(my->unapplied_transactions.size());
for ( const auto& entry: my->unapplied_transactions ) {
result.emplace_back(entry.second);
}
} else {
EOS_ASSERT( my->unapplied_transactions.empty(), transaction_exception, "not empty unapplied_transactions in non-speculative mode" ); //should never happen
}
return result;
}
......
......@@ -33,8 +33,14 @@ namespace eosio { namespace chain {
class fork_database;
enum class db_read_mode {
SPECULATIVE,
HEAD
};
class controller {
public:
struct config {
flat_set<account_name> actor_whitelist;
flat_set<account_name> actor_blacklist;
......@@ -52,6 +58,8 @@ namespace eosio { namespace chain {
genesis_state genesis;
wasm_interface::vm_type wasm_runtime = chain::config::default_wasm_runtime;
db_read_mode read_mode = db_read_mode::SPECULATIVE;
};
enum class block_status {
......@@ -180,6 +188,8 @@ namespace eosio { namespace chain {
chain_id_type get_chain_id()const;
db_read_mode get_read_mode()const;
signal<void(const block_state_ptr&)> accepted_block_header;
signal<void(const block_state_ptr&)> accepted_block;
signal<void(const block_state_ptr&)> irreversible_block;
......
......@@ -26,6 +26,7 @@ namespace eosio { namespace chain {
void exec();
void finalize();
void squash();
void undo();
inline void add_net_usage( uint64_t u ) { net_usage += u; check_net_usage(); }
......
......@@ -265,6 +265,9 @@ namespace eosio { namespace chain {
undo_session.squash();
}
void transaction_context::undo() {
undo_session.undo();
}
void transaction_context::check_net_usage()const {
if( BOOST_UNLIKELY(net_usage > eager_net_limit) ) {
......
......@@ -13,6 +13,7 @@
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/reversible_block_object.hpp>
#include <eosio/chain/controller.hpp>
#include <eosio/chain/eosio_contract.hpp>
......@@ -30,6 +31,43 @@
namespace eosio {
//declare operator<< and validate funciton for read_mode in the same namespace as read_mode itself
namespace chain {
std::ostream& operator<<(std::ostream& osm, eosio::chain::db_read_mode m) {
if ( m == eosio::chain::db_read_mode::SPECULATIVE ) {
osm << "speculative";
} else if ( m == eosio::chain::db_read_mode::HEAD ) {
osm << "head";
}
return osm;
}
void validate(boost::any& v,
std::vector<std::string> const& values,
eosio::chain::db_read_mode* /* target_type */,
int)
{
using namespace boost::program_options;
// Make sure no previous assignment to 'v' was made.
validators::check_first_occurrence(v);
// Extract the first string from 'values'. If there is more than
// one string, it's an error, and exception will be thrown.
std::string const& s = validators::get_single_string(values);
if ( s == "speculative" ) {
v = boost::any(eosio::chain::db_read_mode::SPECULATIVE);
} else if ( s == "head" ) {
v = boost::any(eosio::chain::db_read_mode::HEAD);
} else {
throw validation_error(validation_error::invalid_option_value);
}
}
}
using namespace eosio;
using namespace eosio::chain;
using namespace eosio::chain::config;
......@@ -145,6 +183,8 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
"Action (in the form code::action) added to action blacklist (may specify multiple times)")
("key-blacklist", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"Public key added to blacklist of keys that should not be included in authorities (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\" or \"head\")")
;
// TODO: rate limiting
......@@ -177,6 +217,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
("truncate-at-block", bpo::value<uint32_t>()->default_value(0),
"stop hard replay / block log recovery at this block number (if set to non-zero number)")
;
}
#define LOAD_VALUE_SET(options, name, container) \
......@@ -395,6 +436,10 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
wlog( "Starting up fresh blockchain with default genesis state." );
}
if ( options.count("read-mode") ) {
my->chain_config->read_mode = options.at("read-mode").as<db_read_mode>();
}
my->chain.emplace(*my->chain_config);
my->chain_id.emplace(my->chain->get_chain_id());
......
......@@ -577,6 +577,9 @@ void producer_plugin::plugin_startup()
ilog("producer plugin: plugin_startup() begin");
chain::controller& chain = app().get_plugin<chain_plugin>().chain();
FC_ASSERT( my->_producers.empty() || chain.get_read_mode() == chain::db_read_mode::SPECULATIVE,
"node cannot have any producer-name configured because block production is impossible when read_mode is not \"speculative\"" );
my->_accepted_block_connection.emplace(chain.accepted_block.connect( [this]( const auto& bsp ){ my->on_block( bsp ); } ));
my->_irreversible_block_connection.emplace(chain.irreversible_block.connect( [this]( const auto& bsp ){ my->on_irreversible_block( bsp->block ); } ));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册