提交 7ddef42d 编写于 作者: J Jonathan Giszczak

Authenticate peers during network handshake.

Authenticates producers using producer keys, optionally authenticates
non-producing peers using configured keys, and further optionally
allows unauthenticated connections.  Includes launcher support.

Also fix bug when a 100% producer node started up after a long (>64
second) delay and thought it's a 0% node.

Add additional information for debugging Travis builds.
上级 f5871d1c
......@@ -558,7 +558,7 @@ void check_output(const T& expected, const T& actual, const path_cons_list& path
template<typename T>
void check_output(const vector<T>& expected, const vector<T>& actual, const path_cons_list& path) {
check_output(expected.size(), actual.size(), path(".size()"));
for(int idx=0; idx < expected.size(); idx++) {
for(size_t idx=0; idx < expected.size(); idx++) {
const auto &expected_element = expected.at(idx);
const auto &actual_element = actual.at(idx);
check_output(expected_element, actual_element, path(idx));
......@@ -666,16 +666,16 @@ void chain_controller::_apply_block(const signed_block& next_block)
* when building a block.
*/
auto root_path = path_cons_list("next_block.cycles");
for (int c_idx = 0; c_idx < next_block.cycles.size(); c_idx++) {
for (size_t c_idx = 0; c_idx < next_block.cycles.size(); c_idx++) {
const auto& cycle = next_block.cycles.at(c_idx);
auto c_path = path_cons_list(c_idx, root_path);
for (int t_idx = 0; t_idx < cycle.size(); t_idx++) {
for (size_t t_idx = 0; t_idx < cycle.size(); t_idx++) {
const auto& thread = cycle.at(t_idx);
auto t_path = path_cons_list(t_idx, c_path);
auto gen_path = path_cons_list(".generated_input", t_path);
for(int p_idx = 0; p_idx < thread.generated_input.size(); p_idx++ ) {
for(size_t p_idx = 0; p_idx < thread.generated_input.size(); p_idx++ ) {
const auto& ptrx = thread.generated_input.at(p_idx);
const auto& trx = get_generated_transaction(ptrx.id);
auto processed = apply_transaction(trx);
......@@ -683,7 +683,7 @@ void chain_controller::_apply_block(const signed_block& next_block)
}
auto user_path = path_cons_list(".user_input", t_path);
for(int p_idx = 0; p_idx < thread.user_input.size(); p_idx++ ) {
for(size_t p_idx = 0; p_idx < thread.user_input.size(); p_idx++ ) {
const auto& ptrx = thread.user_input.at(p_idx);
const signed_transaction& trx = ptrx;
auto processed = apply_transaction(trx);
......@@ -994,7 +994,7 @@ void chain_controller::process_message(const transaction& trx, account_name code
// propagate used_authorizations up the context chain
if (parent_context != nullptr)
for (int i = 0; i < apply_ctx.used_authorizations.size(); ++i)
for (size_t i = 0; i < apply_ctx.used_authorizations.size(); ++i)
if (apply_ctx.used_authorizations[i])
parent_context->used_authorizations[i] = true;
......@@ -1400,6 +1400,9 @@ void chain_controller::update_global_dynamic_data(const signed_block& b) {
dgp.recent_slots_filled += 1;
dgp.recent_slots_filled <<= missed_blocks;
} else
if(config::percent100 * get_global_properties().active_producers.size() / config::blocks_per_round > config::required_producer_participation)
dgp.recent_slots_filled = uint64_t(-1);
else
dgp.recent_slots_filled = 0;
});
......
......@@ -39,6 +39,8 @@ const static int default_per_auth_account = 1800;
const static int default_per_code_account_time_frame_seconds = 18;
const static int default_per_code_account = 18000;
const static uint32 required_producer_participation = 33 * config::percent1;
const static uint32 default_max_block_size = 5 * 1024 * 1024;
const static uint32 default_target_block_size = 128 * 1024;
const static uint64 default_max_storage_size = 10 * 1024;
......
......@@ -3,7 +3,7 @@ add_library( net_plugin
net_plugin.cpp
${HEADERS} )
target_link_libraries( net_plugin chain_plugin appbase fc )
target_link_libraries( net_plugin chain_plugin producer_plugin appbase fc )
target_include_directories( net_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
install( TARGETS
......
......@@ -11,10 +11,17 @@ namespace eosio {
using namespace chain;
using namespace fc;
static_assert(sizeof(std::chrono::system_clock::duration::rep) >= 8, "system_clock is expected to be at least 64 bits");
typedef std::chrono::system_clock::duration::rep tstamp;
struct handshake_message {
int16_t network_version = 0; ///< derived from git commit hash, not sequential
chain_id_type chain_id; ///< used to identify chain
fc::sha256 node_id; ///< used to identify peers and prevent self-connect
chain::public_key_type key; ///< authentication key; may be a producer or peer key, or empty
tstamp time;
fc::sha256 token; ///< digest of time to prove we own the private key of the key above
fc::ecc::compact_signature sig; ///< signature for the digest
string p2p_address;
uint32_t last_irreversible_block_num = 0;
block_id_type last_irreversible_block_id;
......@@ -139,8 +146,8 @@ namespace eosio {
FC_REFLECT( eosio::select_ids<fc::sha256>, (mode)(pending)(ids) )
FC_REFLECT( eosio::handshake_message,
(network_version)(chain_id)(node_id)
(p2p_address)
(network_version)(chain_id)(node_id)(key)
(time)(token)(sig)(p2p_address)
(last_irreversible_block_num)(last_irreversible_block_id)
(head_num)(head_id)
(os)(agent)(generation) )
......
......@@ -9,8 +9,12 @@
#include <eos/chain/chain_controller.hpp>
#include <eos/chain/exceptions.hpp>
#include <eos/chain/block.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/producer_plugin/producer_plugin.hpp>
#include <eos/utilities/key_conversion.hpp>
#include <fc/network/ip.hpp>
#include <fc/io/json.hpp>
#include <fc/io/raw.hpp>
#include <fc/log/appender.hpp>
#include <fc/container/flat.hpp>
......@@ -23,6 +27,10 @@
#include <boost/asio/steady_timer.hpp>
#include <boost/intrusive/set.hpp>
namespace fc {
extern std::unordered_map<std::string,logger>& get_logger_map();
}
namespace eosio {
using std::vector;
......@@ -114,6 +122,17 @@ namespace eosio {
uint32_t num_clients;
vector<string> supplied_peers;
vector<chain::public_key_type> allowed_peers; ///< peer keys allowed to connect
std::map<chain::public_key_type,
fc::ecc::private_key> private_keys; ///< overlapping with producer keys, also authenticating non-producing nodes
enum possible_connections : char {
None = 0,
Producers = 1 << 0,
Specified = 1 << 1,
Any = 1 << 2
};
possible_connections allowed_connections{None};
std::set< connection_ptr > connections;
bool done = false;
......@@ -127,6 +146,8 @@ namespace eosio {
boost::asio::steady_timer::duration resp_expected_period;
boost::asio::steady_timer::duration keepalive_interval{std::chrono::seconds{32}};
const std::chrono::system_clock::duration peer_authentication_interval{std::chrono::seconds{1}}; ///< Peer clock may be no more than 1 second skewed from our clock, including network latency.
int16_t network_version;
chain_id_type chain_id;
fc::sha256 node_id;
......@@ -198,10 +219,40 @@ namespace eosio {
*/
void ticker();
/** @} */
/** \brief Determine if a peer is allowed to connect.
*
* Checks current connection mode and key authentication.
*
* \return False if the peer should not connect, true otherwise.
*/
bool authenticate_peer(const handshake_message& msg) const;
/** \brief Retrieve public key used to authenticate with peers.
*
* Finds a key to use for authentication. If this node is a producer, use
* the front of the producer key map. If the node is not a producer but has
* a configured private key, use it. If the node is neither a producer nor has
* a private key, returns an empty key.
*
* \note On a node with multiple private keys configured, the key with the first
* numerically smaller byte will always be used.
*/
chain::public_key_type get_authentication_key() const;
/** \brief Returns a signature of the digest using the corresponding private key of the signer.
*
* If there are no configured private keys, returns an empty signature.
*/
fc::ecc::compact_signature sign_compact(const chain::public_key_type& signer, const fc::sha256& digest) const;
static const fc::string logger_name;
static fc::logger logger;
};
template<class enum_type, class=typename std::enable_if<std::is_enum<enum_type>::value>::type>
inline enum_type& operator|=(enum_type& lhs, const enum_type& rhs)
{
using T = std::underlying_type_t <enum_type>;
return lhs = static_cast<enum_type>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
static net_plugin_impl *my_impl;
const fc::string net_plugin_impl::logger_name("net_plugin_impl");
fc::logger net_plugin_impl::logger(net_plugin_impl::logger_name);
......@@ -750,7 +801,7 @@ namespace eosio {
char* connection::convert_tstamp(const tstamp& t)
{
const long NsecPerSec{1000000000};
const long long NsecPerSec{1000000000};
time_t seconds = t / NsecPerSec;
strftime(ts, ts_buffer_size, "%F %T", localtime(&seconds));
snprintf(ts+19, ts_buffer_size-19, ".%lld", t % NsecPerSec);
......@@ -1337,6 +1388,12 @@ namespace eosio {
if( c->node_id != msg.node_id) {
c->node_id = msg.node_id;
}
if(!authenticate_peer(msg)) {
dlog("Peer not authenticated. Closing connection.");
close(c);
return;
}
}
chain_controller& cc = chain_plug->chain();
......@@ -2017,11 +2074,97 @@ namespace eosio {
});
}
bool net_plugin_impl::authenticate_peer(const handshake_message& msg) const {
if(allowed_connections == None)
return false;
if(allowed_connections == Any)
return true;
if(allowed_connections & (Producers | Specified)) {
auto allowed_it = std::find(allowed_peers.begin(), allowed_peers.end(), msg.key);
auto private_it = private_keys.find(msg.key);
bool found_producer_key = false;
producer_plugin* pp = app().find_plugin<producer_plugin>();
if(pp != nullptr)
found_producer_key = pp->is_producer_key(msg.key);
if( allowed_it == allowed_peers.end() && private_it == private_keys.end() && !found_producer_key) {
elog( "Peer ${peer} sent a handshake with an unauthorized key: ${key}.",
("peer", msg.p2p_address)("key", msg.key));
return false;
}
}
namespace sc = std::chrono;
sc::system_clock::duration msg_time(msg.time);
auto time = sc::system_clock::now().time_since_epoch();
if(time - msg_time > peer_authentication_interval) {
elog( "Peer ${peer} sent a handshake with a timestamp skewed by more than ${time}.",
("peer", msg.p2p_address)("time", "1 second")); // TODO Add to_variant for std::chrono::system_clock::duration
return false;
}
if(msg.sig != ecc::compact_signature() && msg.token != sha256()) {
sha256 hash = fc::sha256::hash(msg.time);
if(hash != msg.token) {
elog( "Peer ${peer} sent a handshake with an invalid token.",
("peer", msg.p2p_address));
return false;
}
types::public_key peer_key;
try {
peer_key = ecc::public_key(msg.sig, msg.token, true);
}
catch (fc::exception& /*e*/) {
elog( "Peer ${peer} sent a handshake with an unrecoverable key.",
("peer", msg.p2p_address));
return false;
}
if((allowed_connections & (Producers | Specified)) && peer_key != msg.key) {
elog( "Peer ${peer} sent a handshake with an unauthenticated key.",
("peer", msg.p2p_address));
return false;
}
}
else if(allowed_connections & (Producers | Specified)) {
dlog( "Peer sent a handshake with blank signature and token, but this node accepts only authenticated connections.");
return false;
}
return true;
}
chain::public_key_type net_plugin_impl::get_authentication_key() const {
producer_plugin* pp = app().find_plugin<producer_plugin>();
if(pp != nullptr)
return pp->first_producer_public_key();
if(!private_keys.empty())
return private_keys.begin()->first;
return chain::public_key_type();
}
fc::ecc::compact_signature net_plugin_impl::sign_compact(const chain::public_key_type& signer, const fc::sha256& digest) const
{
producer_plugin* pp = app().find_plugin<producer_plugin>();
if(pp != nullptr)
return pp->sign_compact(signer, digest);
auto private_key_itr = private_keys.find(signer);
if(private_key_itr == private_keys.end())
return ecc::compact_signature();
return private_key_itr->second.sign_compact(digest);
}
void
handshake_initializer::populate( handshake_message &hello) {
hello.network_version = my_impl->network_version;
hello.chain_id = my_impl->chain_id;
hello.node_id = my_impl->node_id;
hello.key = my_impl->get_authentication_key();
hello.time = std::chrono::system_clock::now().time_since_epoch().count();
hello.token = fc::sha256::hash(hello.time);
hello.sig = my_impl->sign_compact(hello.key, hello.token);
// If we couldn't sign, don't send a token.
if(hello.sig == ecc::compact_signature())
hello.token = sha256();
hello.p2p_address = my_impl->p2p_address;
#if defined( __APPLE__ )
hello.os = "osx";
......@@ -2067,7 +2210,7 @@ namespace eosio {
net_plugin::~net_plugin() {
}
void net_plugin::set_program_options( options_description& cli, options_description& cfg )
void net_plugin::set_program_options( options_description& /*cli*/, options_description& cfg )
{
cfg.add_options()
( "listen-endpoint", bpo::value<string>()->default_value( "0.0.0.0:9876" ), "The local IP address and port to listen for incoming connections.")
......@@ -2075,13 +2218,27 @@ namespace eosio {
( "public-endpoint", bpo::value<string>(), "Overrides the advertised listen endpointlisten ip address.")
( "agent-name", bpo::value<string>()->default_value("EOS Test Agent"), "The name supplied to identify this node amongst the peers.")
( "send-whole-blocks", bpo::value<bool>()->default_value(def_send_whole_blocks), "True to always send full blocks, false to send block summaries" )
( "allowed-connection", bpo::value<vector<string>>()->multitoken()->default_value({"none"}, "none"), "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.")
( "peer-private-key", boost::program_options::value<vector<string>>()->composing()->multitoken(),
"Tuple of [PublicKey, WIF private key] (may specify multiple times)")
( "log-level-net-plugin", bpo::value<string>()->default_value("info"), "Log level: one of 'all', 'debug', 'info', 'warn', 'error', or 'off'")
;
}
template<typename T>
T dejsonify(const string& s) {
return fc::json::from_string(s).as<T>();
}
void net_plugin::plugin_initialize( const variables_map& options ) {
ilog("Initialize net plugin");
// Housekeeping so fc::logger::get() will work as expected
fc::get_logger_map()[connection::logger_name] = connection::logger;
fc::get_logger_map()[net_plugin_impl::logger_name] = net_plugin_impl::logger;
fc::get_logger_map()[sync_manager::logger_name] = sync_manager::logger;
// Setting a parent would in theory get us the default appenders for free but
// a) the parent's log level overrides our own in that case; and
// b) fc library's logger was never finished - the _additivity flag tested is never true.
......@@ -2114,7 +2271,7 @@ namespace eosio {
my->num_clients = 0;
my->resolver = std::make_shared<tcp::resolver>( std::ref( app().get_io_service() ) );
if( options.count( "listen-endpoint" ) ) {
if(options.count("listen-endpoint")) {
my->p2p_address = options.at("listen-endpoint").as< string >();
auto host = my->p2p_address.substr( 0, my->p2p_address.find(':') );
auto port = my->p2p_address.substr( host.size()+1, my->p2p_address.size() );
......@@ -2126,11 +2283,11 @@ namespace eosio {
my->acceptor.reset( new tcp::acceptor( app().get_io_service() ) );
}
if( options.count( "public-endpoint") ) {
if(options.count("public-endpoint")) {
my->p2p_address = options.at("public-endpoint").as< string >();
}
else {
if( my->listen_endpoint.address().to_v4() == address_v4::any()) {
if(my->listen_endpoint.address().to_v4() == address_v4::any()) {
boost::system::error_code ec;
auto host = host_name(ec);
if( ec.value() != boost::system::errc::success) {
......@@ -2144,12 +2301,52 @@ namespace eosio {
}
}
if( options.count( "remote-endpoint" ) ) {
my->supplied_peers = options.at( "remote-endpoint" ).as< vector<string> >();
if(options.count("remote-endpoint")) {
my->supplied_peers = options.at("remote-endpoint").as<vector<string> >();
}
if(options.count("agent-name")) {
my->user_agent_name = options.at("agent-name").as<string>();
}
if(options.count("allowed-connection")) {
const std::vector<std::string> allowed_remotes = options["allowed-connection"].as<std::vector<std::string>>();
for(const std::string& allowed_remote : allowed_remotes)
{
if(allowed_remote == "any")
my->allowed_connections |= net_plugin_impl::Any;
else if(allowed_remote == "producers")
my->allowed_connections |= net_plugin_impl::Producers;
else if(allowed_remote == "specified")
my->allowed_connections |= net_plugin_impl::Specified;
else if(allowed_remote == "none")
my->allowed_connections = net_plugin_impl::None;
}
}
if(my->allowed_connections & net_plugin_impl::Specified)
FC_ASSERT(options.count("peer-key"), "At least one peer-key must accompany 'allowed-connection=specified'");
if(options.count("peer-key")) {
const std::vector<std::string> key_strings = options["peer-key"].as<std::vector<std::string>>();
for(const std::string& key_string : key_strings)
{
my->allowed_peers.push_back(dejsonify<chain::public_key_type>(key_string));
}
}
if(options.count("peer-private-key"))
{
const std::vector<std::string> key_id_to_wif_pair_strings = options["peer-private-key"].as<std::vector<std::string>>();
for(const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings)
{
auto key_id_to_wif_pair = dejsonify<std::pair<chain::public_key_type, std::string>>(key_id_to_wif_pair_string);
fc::optional<fc::ecc::private_key> private_key = eosio::utilities::wif_to_key(key_id_to_wif_pair.second);
FC_ASSERT(private_key, "Invalid WIF-format private key ${key_string}",
("key_string", key_id_to_wif_pair.second));
my->private_keys[key_id_to_wif_pair.first] = *private_key;
}
if( options.count("agent-name")) {
my->user_agent_name = options.at( "agent-name").as< string >( );
}
if( options.count( "send-whole-blocks")) {
my->send_whole_blocks = options.at( "send-whole-blocks" ).as<bool>();
}
......
......@@ -38,6 +38,10 @@ public:
boost::program_options::options_description &config_file_options
) override;
chain::public_key_type first_producer_public_key() const;
bool is_producer_key(const chain::public_key_type& key) const;
fc::ecc::compact_signature sign_compact(const chain::public_key_type& key, const fc::sha256& digest) const;
virtual void plugin_initialize(const boost::program_options::variables_map& options);
virtual void plugin_startup();
virtual void plugin_shutdown();
......
......@@ -33,7 +33,7 @@ public:
boost::program_options::variables_map _options;
bool _production_enabled = false;
uint32_t _required_producer_participation = 33 * config::percent1;
uint32_t _required_producer_participation = uint32_t(config::required_producer_participation);
uint32_t _production_skip_flags = eosio::chain::chain_controller::skip_nothing;
eosio::chain::block_schedule::factory _production_scheduler = eosio::chain::block_schedule::in_single_thread;
......@@ -86,8 +86,9 @@ void producer_plugin::set_program_options(
("ID of producer controlled by this node (e.g. inita; may specify multiple times)"))
("private-key", boost::program_options::value<vector<string>>()->composing()->multitoken()->default_value({fc::json::to_string(private_key_default)},
fc::json::to_string(private_key_default)),
"Tuple of [public_key, WIF private key] (may specify multiple times)")
"Tuple of [public key, WIF private key] (may specify multiple times)")
;
command_line_options.add(producer_options);
config_file_options.add(producer_options);
......@@ -115,6 +116,37 @@ void producer_plugin::set_program_options(
;
}
chain::public_key_type producer_plugin::first_producer_public_key() const
{
chain::chain_controller& chain = app().get_plugin<chain_plugin>().chain();
try {
return chain.get_producer(*my->_producers.begin()).signing_key;
} catch(std::out_of_range) {
return chain::public_key_type();
}
}
bool producer_plugin::is_producer_key(const chain::public_key_type& key) const
{
auto private_key_itr = my->_private_keys.find(key);
if(private_key_itr != my->_private_keys.end())
return true;
return false;
}
fc::ecc::compact_signature producer_plugin::sign_compact(const chain::public_key_type& key, const fc::sha256& digest) const
{
if(key != chain::public_key_type()) {
auto private_key_itr = my->_private_keys.find(key);
FC_ASSERT(private_key_itr != my->_private_keys.end(), "Local producer has no private key in config.ini corresponding to public key ${key}", ("key", key));
return private_key_itr->second.sign_compact(digest);
}
else {
return fc::ecc::compact_signature();
}
}
template<typename T>
T dejsonify(const string& s) {
return fc::json::from_string(s).as<T>();
......@@ -335,4 +367,4 @@ block_production_condition::block_production_condition_enum producer_plugin_impl
return block_production_condition::produced;
}
} // namespace eos
} // namespace eosio
......@@ -220,11 +220,19 @@ enum launch_modes {
LM_ALL
};
enum allowed_connection : char {
PC_NONE = 0,
PC_PRODUCERS = 1 << 0,
PC_SPECIFIED = 1 << 1,
PC_ANY = 1 << 2
};
struct launcher_def {
int producers;
int total_nodes;
int prod_nodes;
size_t producers;
size_t total_nodes;
size_t prod_nodes;
string shape;
allowed_connection allowed_connections = PC_NONE;
bf::path genesis;
bf::path output;
bool skip_transaction_signatures = false;
......@@ -257,36 +265,43 @@ struct launcher_def {
void
launcher_def::set_options (bpo::options_description &cli) {
cli.add_options()
("nodes,n",bpo::value<int>()->default_value(1),"total number of nodes to configure and launch")
("pnodes,p",bpo::value<int>()->default_value(1),"number of nodes that are producers")
("shape,s",bpo::value<string>()->default_value("star"),"network topology, use \"ring\" \"star\" \"mesh\" or give a filename for custom")
("genesis,g",bpo::value<bf::path>()->default_value("./genesis.json"),"set the path to genesis.json")
("output,o",bpo::value<bf::path>(),"save a copy of the generated topology in this file")
("skip-signature", bpo::bool_switch()->default_value(false), "EOSD does not require transaction signatures.")
("eosd", bpo::value<string>(), "forward eosd command line argument(s) to each instance of eosd, enclose arg in quotes")
("delay,d",bpo::value<int>()->default_value(0),"seconds delay before starting each node after the first")
("nodes,n",bpo::value<size_t>(&total_nodes)->default_value(1),"total number of nodes to configure and launch")
("pnodes,p",bpo::value<size_t>(&prod_nodes)->default_value(1),"number of nodes that are producers")
("mode,m",bpo::value<vector<string>>()->multitoken()->default_value({"any"}, "any"),"connection mode, combination of \"any\", \"producers\", \"specified\", \"none\"")
("shape,s",bpo::value<string>(&shape)->default_value("star"),"network topology, use \"ring\" \"star\" \"mesh\" or give a filename for custom")
("genesis,g",bpo::value<bf::path>(&genesis)->default_value("./genesis.json"),"set the path to genesis.json")
("output,o",bpo::value<bf::path>(&output),"save a copy of the generated topology in this file")
("skip-signature", bpo::bool_switch(&skip_transaction_signatures)->default_value(false), "EOSD does not require transaction signatures.")
("eosd", bpo::value<string>(&eosd_extra_args), "forward eosd command line argument(s) to each instance of eosd, enclose arg in quotes")
("delay,d",bpo::value<int>(&start_delay)->default_value(0),"seconds delay before starting each node after the first")
;
}
template<class enum_type, class=typename std::enable_if<std::is_enum<enum_type>::value>::type>
inline enum_type& operator|=(enum_type&lhs, const enum_type& rhs)
{
using T = std::underlying_type_t <enum_type>;
return lhs = static_cast<enum_type>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
void
launcher_def::initialize (const variables_map &vmap) {
if (vmap.count("nodes"))
total_nodes = vmap["nodes"].as<int>();
if (vmap.count("pnodes"))
prod_nodes = vmap["pnodes"].as<int>();
if (vmap.count("shape"))
shape = vmap["shape"].as<string>();
if (vmap.count("genesis"))
genesis = vmap["genesis"].as<bf::path>();
if (vmap.count("output"))
output = vmap["output"].as<bf::path>();
if (vmap.count("skip-signature"))
skip_transaction_signatures = vmap["skip-signature"].as<bool>();
if (vmap.count("eosd"))
eosd_extra_args = vmap["eosd"].as<string>();
if (vmap.count("delay"))
start_delay = vmap["delay"].as<int>();
if (vmap.count("mode")) {
const vector<string> modes = vmap["mode"].as<vector<string>>();
for(const string&m : modes)
{
if (boost::iequals(m, "any"))
allowed_connections |= PC_ANY;
else if (boost::iequals(m, "producers"))
allowed_connections |= PC_PRODUCERS;
else if (boost::iequals(m, "specified"))
allowed_connections |= PC_SPECIFIED;
else {
cerr << "unrecognized connection mode: " << m << endl;
exit (-1);
}
}
}
producers = 21;
data_dir_base = "tn_data_";
alias_base = "testnet_";
......@@ -359,7 +374,7 @@ void
launcher_def::define_nodes () {
int per_node = producers / prod_nodes;
int extra = producers % prod_nodes;
for (int i = 0; i < total_nodes; i++) {
for (size_t i = 0; i < total_nodes; i++) {
eosd_def node;
string dex = boost::lexical_cast<string,int>(i);
string name = alias_base + dex;
......@@ -432,6 +447,21 @@ launcher_def::write_config_file (eosd_def &node) {
<< "http-server-endpoint = " << node.hostname << ":" << node.http_port << "\n"
<< "listen-endpoint = 0.0.0.0:" << node.p2p_port << "\n"
<< "public-endpoint = " << node.public_name << ":" << node.p2p_port << "\n";
if (allowed_connections & PC_ANY) {
cfg << "allowed-connection = any\n";
}
else
{
if (allowed_connections & PC_PRODUCERS) {
cfg << "allowed-connection = producers\n";
}
if (allowed_connections & PC_SPECIFIED) {
cfg << "allowed-connection = specified\n";
cfg << "peer-key = \"" << node.keys.begin()->public_key << "\"\n";
cfg << "peer-private-key = [\"" << node.keys.begin()->public_key
<< "\",\"" << node.keys.begin()->wif_private_key << "\"]\n";
}
}
for (const auto &p : node.peers) {
cfg << "remote-endpoint = " << network.nodes.find(p)->second.p2p_endpoint() << "\n";
}
......@@ -750,23 +780,19 @@ int main (int argc, char *argv[]) {
top.set_options(opts);
opts.add_options()
("timestamp,i",bpo::value<string>(),"set the timestamp for the first block. Use \"now\" to indicate the current time")
("timestamp,i",bpo::value<string>(&gts),"set the timestamp for the first block. Use \"now\" to indicate the current time")
("launch,l",bpo::value<string>(), "select a subset of nodes to launch. Currently may be \"all\", \"none\", or \"local\". If not set, the default is to launch all unless an output file is named, in which case it starts none.")
("kill,k", bpo::value<string>(),"The launcher retrieves the previously started process ids and issue a kill signal to each.")
("kill,k", bpo::value<string>(&kill_arg),"The launcher retrieves the previously started process ids and issue a kill signal to each.")
("version,v", "print version information")
("help,h","print this list");
try {
bpo::store(bpo::parse_command_line(argc, argv, opts), vmap);
bpo::notify(vmap);
top.initialize(vmap);
if (vmap.count("timestamp"))
gts = vmap["timestamp"].as<string>();
if (vmap.count("kill")) {
kill_arg = vmap["kill"].as<string>();
}
if (vmap.count("help") > 0) {
opts.print(cerr);
return 0;
......
......@@ -36,6 +36,7 @@ else
fi
fi
fi
total_nodes=`expr $pnodes + $npnodes`
rm -rf tn_data_*
......
......@@ -414,9 +414,9 @@ BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, testing_fixture )
chain.produce_blocks(1, 64);
BOOST_CHECK_EQUAL( rsf(),
"0000000000000000000000000000000000000000000000000000000000000000"
);
BOOST_CHECK_EQUAL( chain.producer_participation_rate(), pct(0) );
"1111111111111111111111111111111111111111111111111111111111111111"
); // For very small producer nets. All zeros otherwise.
BOOST_CHECK_EQUAL( chain.producer_participation_rate(), config::percent100 );
chain.produce_blocks(1, 63);
BOOST_CHECK_EQUAL( rsf(),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册