提交 f5d9d6c4 编写于 作者: D Daniel Larimer 提交者: GitHub

Merge pull request #309 from EOSIO/cleanup

removing unused files from source tree
#include <fc/thread/thread.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#if defined(__linux__) && !defined(NDEBUG)
# include <pthread.h>
static void set_thread_name(const char* threadName)
{ pthread_setname_np(pthread_self(), threadName); }
#elif defined(__APPLE__) && !defined(NDEBUG)
# include <pthread.h>
static void set_thread_name(const char* threadName)
{ pthread_setname_np(threadName); }
#else// do nothing in release mode
static void set_thread_name(const char* threadName)
{}
#endif
namespace fc {
using namespace boost::multi_index;
struct by_time;
typedef multi_index_container <
std::shared_ptr<scheduled_task>,
indexed_by<
ordered_non_unique< tag<by_time>, const_mem_fun< scheduled_task, time_point, &scheduled_task::get_scheduled_time > >
>
> scheduled_task_index;
thread*& current_thread() {
static __thread thread* t = nullptr;
return t;
}
class thread_detail {
public:
thread_detail( fc::thread& t ):fc_thread(t) {
this_id = std::this_thread::get_id();
current_thread() = &t;
task_in_queue = nullptr;
}
~thread_detail(){ }
fc::thread& fc_thread;
std::thread::id this_id;
std::thread* std_thread = nullptr;
boost::fibers::promise<void> exit_promise;
std::string name;
boost::fibers::condition_variable task_ready;
boost::fibers::mutex task_ready_mutex;
std::atomic<detail::task*> task_in_queue;
detail::task* _queue = nullptr;
detail::task* _queue_end = nullptr;
bool _running = false;
bool _done = false;
scheduled_task_index _scheduled;
void async_task( detail::task* t ) {
idump((name));
if( _done ) {
delete t;
throw std::runtime_error( "attempt to async task on thread that has quit" );
}
auto stale_head = task_in_queue.load(std::memory_order_relaxed);
do { t->next = stale_head;
}while( !task_in_queue.compare_exchange_weak( stale_head, t, std::memory_order_release ) );
if( !stale_head )
{
dlog( "----grabbing ready mutex" );
std::unique_lock<boost::fibers::mutex> lock(task_ready_mutex);
dlog("--- got ready mutex, notify one" );
task_ready.notify_one();
}
}
bool exec_next_task() {
auto itr = _scheduled.begin();
if( _scheduled.end() != itr && (*itr)->get_scheduled_time() < fc::time_point::now() ) {
idump(((*itr)->get_scheduled_time()));
auto tsk = *itr;
_scheduled.erase(itr);
tsk->exec();
return true;
}
if( !_queue )
{
move_newly_scheduled_tasks_to_task_queue();
if( !_queue ) return false;
}
auto tmp = _queue;
_queue = _queue->next;
if( !_queue )
_queue_end = nullptr;
tmp->exec();
delete tmp;
return true;
}
/**
* Start a new fiber which will process tasks until it
* blocks on some event, at that time it should resume
* the exec() loop which will look for new tasks. If there
* are no new tasks then it will block on a wait condition
* until a new task comes in.
*
*/
void exec() {
_running = true;
while( !_done ) {
move_newly_scheduled_tasks_to_task_queue();
if( _queue || _scheduled.size() ) {
#if 1
boost::fibers::async( boost::fibers::launch::dispatch, [this](){ while( exec_next_task() ){} } );
#else
// elog( "creating new fiber... " );
static int tmp = 0;
++tmp;
/**
* First we execute the task, then delete it, and
* finally look for other tasks to execute, and
* exit when there are no more tasks in the queue
*/
boost::fibers::fiber fib( boost::fibers::launch::dispatch, [this,t=tmp](){
// wlog( "starting new fiber... ${d}", ("d",int64_t(t)) );
while( exec_next_task() ){}
// dlog( "exit fiber... ${d}", ("d",int64_t(t)) );
});
fib.detach();
#endif
} else {
//ilog( "grabbing task_read_mutex..." );
std::unique_lock<boost::fibers::mutex> lock(task_ready_mutex);
move_newly_scheduled_tasks_to_task_queue();
if( !(_queue || _scheduled.size()) ) {
if( !_scheduled.size() ) {
// wlog( "waiting until next event" );
task_ready.wait( lock );
// ilog( "wake up..." );
} else {
// wlog( "waiting for ${t} or until next event", ("t", (*_scheduled.begin())->get_scheduled_time() - fc::time_point::now() ));
task_ready.wait_until( lock, std::chrono::system_clock::time_point(std::chrono::microseconds( (*_scheduled.begin())->get_scheduled_time().time_since_epoch().count())) );
// wlog( "done waiting... " );
}
}
}
}
// ilog( "exec done" );
_running = false;
}
void move_newly_scheduled_tasks_to_task_queue()
{
// first, if there are any new tasks on 'task_in_queue', which is tasks that
// have been just been async or scheduled, but we haven't processed them.
// move them into the task_sch_queue or task_pqueue, as appropriate
//DLN: changed from memory_order_consume for boost 1.55.
//This appears to be safest replacement for now, maybe
//can be changed to relaxed later, but needs analysis.
auto pending_list = task_in_queue.exchange(0, std::memory_order_seq_cst);
if( !pending_list ) return;
/** reverse the list */
detail::task* cur = pending_list;
detail::task* prev = nullptr;
detail::task* next = nullptr;
detail::task* new_end = cur;
while( cur != nullptr ) {
next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
/** insert the list to the current queue */
if( !_queue ) {
_queue = prev;
} else {
_queue_end->next = prev;
}
_queue_end = new_end;
}
};
namespace detail {
void thread_detail_cancel( thread& t, scheduled_task* stask ) {
t.async( [tptr=&t,stask](){
for( auto itr = tptr->my->_scheduled.begin(); itr != tptr->my->_scheduled.end(); ++itr ) {
if( itr->get() == stask ) {
tptr->my->_scheduled.erase(itr);
return;
}
}
});
}
}
thread::thread( const std::string& name ) {
boost::fibers::promise<void> prom;
auto stdt = new std::thread( [this,name,&prom]() {
my = new thread_detail( *this );
my->name = name;
prom.set_value();
set_thread_name( name.c_str() );
this->exec();
//elog( "exit thread" );
my->exit_promise.set_value();
});
prom.get_future().wait();
my->std_thread = stdt;
}
thread::thread( thread&& mv )
:my(mv.my){
mv.my = nullptr;
}
thread::thread( thread_detail* d ) {
my = new thread_detail(*this);
}
thread::~thread() {
delete my->std_thread;
delete my;
}
const string& thread::name()const {
return my->name;
}
thread& thread::current() {
auto cur = current_thread();
if( cur ) return *cur;
return *(new thread( (thread_detail*)nullptr ));
}
void thread::quit() {
if( !my->_done && my->_running )
async( [&](){ my->_done = true; }, "thread::quit" ).wait();
}
bool thread::is_running()const {
return !my->_done;
}
bool thread::is_current()const {
return this == &current();
}
void thread::set_name( const string& n ) {
//this->async( [=]() {
my->name = n;
set_thread_name( my->name.c_str() );
//}).wait();
}
void thread::exec() {
if( this != &current() ) elog( "exec called from wrong thread" );
else my->exec();
}
void thread::async_task( detail::task* t ) {
my->async_task(t);
if( !my->_running /*&& this == &current()*/ ) {
my->_running = true;
boost::fibers::async( boost::fibers::launch::post, [this](){ my->exec(); } );
/*
my->exec();
boost::fibers::fiber fib( boost::fibers::launch::post, [&](){
elog( "STARTING FIBER to call exec()" );
exec();
elog( "EXITING FIBER CALLING EXEC" );
} );
fib.detach();
*/
}
}
void thread::schedule( const std::shared_ptr<scheduled_task>& stask ) {
async( [=]() {
my->_scheduled.insert( stask );
});
}
void thread::join() {
quit();
if( my->std_thread ) {
my->exit_promise.get_future().wait();
my->std_thread->join();
}
}
} // namespace fc
file(GLOB HEADERS "include/eos/net/*.hpp")
set(SOURCES node.cpp
stcp_socket.cpp
core_messages.cpp
peer_database.cpp
peer_connection.cpp
message_oriented_connection.cpp)
add_library( eos_net ${SOURCES} ${HEADERS} )
target_link_libraries( eos_net
PUBLIC fc chainbase appbase eos_types)
target_include_directories( eos_net
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
PRIVATE "${CMAKE_SOURCE_DIR}/libraries/chain/include"
)
if(MSVC)
set_source_files_properties( node.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
if (USE_PCH)
set_target_properties(eos_net PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
cotire(eos_net)
endif(USE_PCH)
install( TARGETS
eos_net
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install( FILES ${HEADERS} DESTINATION "include/eos/net" )
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <eos/net/core_messages.hpp>
namespace eos { namespace net {
const core_message_type_enum trx_message::type = core_message_type_enum::trx_message_type;
const core_message_type_enum block_message::type = core_message_type_enum::block_message_type;
const core_message_type_enum item_ids_inventory_message::type = core_message_type_enum::item_ids_inventory_message_type;
const core_message_type_enum blockchain_item_ids_inventory_message::type = core_message_type_enum::blockchain_item_ids_inventory_message_type;
const core_message_type_enum fetch_blockchain_item_ids_message::type = core_message_type_enum::fetch_blockchain_item_ids_message_type;
const core_message_type_enum fetch_items_message::type = core_message_type_enum::fetch_items_message_type;
const core_message_type_enum item_not_available_message::type = core_message_type_enum::item_not_available_message_type;
const core_message_type_enum hello_message::type = core_message_type_enum::hello_message_type;
const core_message_type_enum connection_accepted_message::type = core_message_type_enum::connection_accepted_message_type;
const core_message_type_enum connection_rejected_message::type = core_message_type_enum::connection_rejected_message_type;
const core_message_type_enum address_request_message::type = core_message_type_enum::address_request_message_type;
const core_message_type_enum address_message::type = core_message_type_enum::address_message_type;
const core_message_type_enum closing_connection_message::type = core_message_type_enum::closing_connection_message_type;
const core_message_type_enum current_time_request_message::type = core_message_type_enum::current_time_request_message_type;
const core_message_type_enum current_time_reply_message::type = core_message_type_enum::current_time_reply_message_type;
const core_message_type_enum check_firewall_message::type = core_message_type_enum::check_firewall_message_type;
const core_message_type_enum check_firewall_reply_message::type = core_message_type_enum::check_firewall_reply_message_type;
const core_message_type_enum get_current_connections_request_message::type = core_message_type_enum::get_current_connections_request_message_type;
const core_message_type_enum get_current_connections_reply_message::type = core_message_type_enum::get_current_connections_reply_message_type;
} } // eos::net
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#define EOS_NET_PROTOCOL_VERSION 106
/**
* Define this to enable debugging code in the p2p network interface.
* This is code that would never be executed in normal operation, but is
* used for automated testing (creating artificial net splits,
* tracking where messages came from and when)
*/
#define ENABLE_P2P_DEBUGGING_API 1
/**
* 2MiB
*/
#define MAX_MESSAGE_SIZE 1024*1024*2
#define EOS_NET_DEFAULT_PEER_CONNECTION_RETRY_TIME 30 // seconds
/**
* AFter trying all peers, how long to wait before we check to
* see if there are peers we can try again.
*/
#define EOS_PEER_DATABASE_RETRY_DELAY 15 // seconds
#define EOS_NET_PEER_HANDSHAKE_INACTIVITY_TIMEOUT 5
#define EOS_NET_PEER_DISCONNECT_TIMEOUT 20
#define EOS_NET_TEST_SEED_IP "104.236.44.210" // autogenerated
#define EOS_NET_TEST_P2P_PORT 1700
#define EOS_NET_DEFAULT_P2P_PORT 1776
#define EOS_NET_DEFAULT_DESIRED_CONNECTIONS 20
#define EOS_NET_DEFAULT_MAX_CONNECTIONS 200
#define EOS_NET_MAXIMUM_QUEUED_MESSAGES_IN_BYTES (1024 * 1024)
/**
* When we receive a message from the network, we advertise it to
* our peers and save a copy in a cache were we will find it if
* a peer requests it. We expire out old items out of the cache
* after this number of blocks go by.
*
* Recently lowered from 30 to match the default expiration time
* the web wallet imposes on transactions.
*/
#define EOS_NET_MESSAGE_CACHE_DURATION_IN_BLOCKS 5
/**
* We prevent a peer from offering us a list of blocks which, if we fetched them
* all, would result in a blockchain that extended into the future.
* This parameter gives us some wiggle room, allowing a peer to give us blocks
* that would put our blockchain up to an hour in the future, just in case
* our clock is a bit off.
*/
#define EOS_NET_FUTURE_SYNC_BLOCKS_GRACE_PERIOD_SEC (60 * 60)
#define EOS_NET_MAX_INVENTORY_SIZE_IN_MINUTES 2
#define EOS_NET_MAX_BLOCKS_PER_PEER_DURING_SYNCING 200
/**
* During normal operation, how many items will be fetched from each
* peer at a time. This will only come into play when the network
* is being flooded -- typically transactions will be fetched as soon
* as we find out about them, so only one item will be requested
* at a time.
*
* No tests have been done to find the optimal value for this
* parameter, so consider increasing or decreasing it if performance
* during flooding is lacking.
*/
#define EOS_NET_MAX_ITEMS_PER_PEER_DURING_NORMAL_OPERATION 1
/**
* Instead of fetching all item IDs from a peer, then fetching all blocks
* from a peer, we will interleave them. Fetch at least this many block IDs,
* then switch into block-fetching mode until the number of blocks we know about
* but haven't yet fetched drops below this
*/
#define EOS_NET_MIN_BLOCK_IDS_TO_PREFETCH 10000
#define EOS_NET_MAX_TRX_PER_SECOND 1000
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <eos/net/config.hpp>
#include <eos/chain/block.hpp>
#include <fc/crypto/ripemd160.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/network/ip.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/time.hpp>
#include <fc/variant_object.hpp>
#include <fc/exception/exception.hpp>
#include <fc/io/enum_type.hpp>
#include <vector>
namespace eos { namespace net {
using eos::chain::SignedTransaction;
using eos::chain::block_id_type;
using eos::chain::transaction_id_type;
using eos::chain::signed_block;
typedef fc::ecc::public_key_data node_id_t;
typedef fc::sha256 item_hash_t;
struct item_id
{
uint32_t item_type;
item_hash_t item_hash;
item_id() {}
item_id(uint32_t type, const item_hash_t& hash) :
item_type(type),
item_hash(hash)
{}
bool operator==(const item_id& other) const
{
return item_type == other.item_type &&
item_hash == other.item_hash;
}
};
enum core_message_type_enum
{
trx_message_type = 1000,
block_message_type = 1001,
core_message_type_first = 5000,
item_ids_inventory_message_type = 5001,
blockchain_item_ids_inventory_message_type = 5002,
fetch_blockchain_item_ids_message_type = 5003,
fetch_items_message_type = 5004,
item_not_available_message_type = 5005,
hello_message_type = 5006,
connection_accepted_message_type = 5007,
connection_rejected_message_type = 5008,
address_request_message_type = 5009,
address_message_type = 5010,
closing_connection_message_type = 5011,
current_time_request_message_type = 5012,
current_time_reply_message_type = 5013,
check_firewall_message_type = 5014,
check_firewall_reply_message_type = 5015,
get_current_connections_request_message_type = 5016,
get_current_connections_reply_message_type = 5017,
core_message_type_last = 5099
};
const uint32_t core_protocol_version = EOS_NET_PROTOCOL_VERSION;
struct trx_message
{
static const core_message_type_enum type;
SignedTransaction trx;
trx_message() {}
trx_message(SignedTransaction transaction) :
trx(std::move(transaction))
{}
};
struct block_message
{
static const core_message_type_enum type;
block_message(){}
block_message(const signed_block& blk )
:block(blk),block_id(blk.id()){}
signed_block block;
block_id_type block_id;
};
struct item_ids_inventory_message
{
static const core_message_type_enum type;
uint32_t item_type;
std::vector<item_hash_t> item_hashes_available;
item_ids_inventory_message() {}
item_ids_inventory_message(uint32_t item_type, const std::vector<item_hash_t>& item_hashes_available) :
item_type(item_type),
item_hashes_available(item_hashes_available)
{}
};
struct blockchain_item_ids_inventory_message
{
static const core_message_type_enum type;
uint32_t total_remaining_item_count;
uint32_t item_type;
std::vector<item_hash_t> item_hashes_available;
blockchain_item_ids_inventory_message() {}
blockchain_item_ids_inventory_message(uint32_t total_remaining_item_count,
uint32_t item_type,
const std::vector<item_hash_t>& item_hashes_available) :
total_remaining_item_count(total_remaining_item_count),
item_type(item_type),
item_hashes_available(item_hashes_available)
{}
};
struct fetch_blockchain_item_ids_message
{
static const core_message_type_enum type;
uint32_t item_type;
std::vector<item_hash_t> blockchain_synopsis;
fetch_blockchain_item_ids_message() {}
fetch_blockchain_item_ids_message(uint32_t item_type, const std::vector<item_hash_t>& blockchain_synopsis) :
item_type(item_type),
blockchain_synopsis(blockchain_synopsis)
{}
};
struct fetch_items_message
{
static const core_message_type_enum type;
uint32_t item_type;
std::vector<item_hash_t> items_to_fetch;
fetch_items_message() {}
fetch_items_message(uint32_t item_type, const std::vector<item_hash_t>& items_to_fetch) :
item_type(item_type),
items_to_fetch(items_to_fetch)
{}
};
struct item_not_available_message
{
static const core_message_type_enum type;
item_id requested_item;
item_not_available_message() {}
item_not_available_message(const item_id& requested_item) :
requested_item(requested_item)
{}
};
struct hello_message
{
static const core_message_type_enum type;
std::string user_agent;
uint32_t core_protocol_version;
fc::ip::address inbound_address;
uint16_t inbound_port;
uint16_t outbound_port;
node_id_t node_public_key;
fc::ecc::compact_signature signed_shared_secret;
fc::sha256 chain_id;
fc::variant_object user_data;
hello_message() {}
hello_message(const std::string& user_agent,
uint32_t core_protocol_version,
const fc::ip::address& inbound_address,
uint16_t inbound_port,
uint16_t outbound_port,
const node_id_t& node_public_key,
const fc::ecc::compact_signature& signed_shared_secret,
const fc::sha256& chain_id_arg,
const fc::variant_object& user_data ) :
user_agent(user_agent),
core_protocol_version(core_protocol_version),
inbound_address(inbound_address),
inbound_port(inbound_port),
outbound_port(outbound_port),
node_public_key(node_public_key),
signed_shared_secret(signed_shared_secret),
chain_id(chain_id_arg),
user_data(user_data)
{}
};
struct connection_accepted_message
{
static const core_message_type_enum type;
connection_accepted_message() {}
};
enum class rejection_reason_code { unspecified,
different_chain,
already_connected,
connected_to_self,
not_accepting_connections,
blocked,
invalid_hello_message,
client_too_old };
struct connection_rejected_message
{
static const core_message_type_enum type;
std::string user_agent;
uint32_t core_protocol_version;
fc::ip::endpoint remote_endpoint;
std::string reason_string;
fc::enum_type<uint8_t, rejection_reason_code> reason_code;
connection_rejected_message() {}
connection_rejected_message(const std::string& user_agent, uint32_t core_protocol_version,
const fc::ip::endpoint& remote_endpoint, rejection_reason_code reason_code,
const std::string& reason_string) :
user_agent(user_agent),
core_protocol_version(core_protocol_version),
remote_endpoint(remote_endpoint),
reason_string(reason_string),
reason_code(reason_code)
{}
};
struct address_request_message
{
static const core_message_type_enum type;
address_request_message() {}
};
enum class peer_connection_direction { unknown, inbound, outbound };
enum class firewalled_state { unknown, firewalled, not_firewalled };
struct address_info
{
fc::ip::endpoint remote_endpoint;
fc::time_point_sec last_seen_time;
fc::microseconds latency;
node_id_t node_id;
fc::enum_type<uint8_t, peer_connection_direction> direction;
fc::enum_type<uint8_t, firewalled_state> firewalled;
address_info() {}
address_info(const fc::ip::endpoint& remote_endpoint,
const fc::time_point_sec last_seen_time,
const fc::microseconds latency,
const node_id_t& node_id,
peer_connection_direction direction,
firewalled_state firewalled) :
remote_endpoint(remote_endpoint),
last_seen_time(last_seen_time),
latency(latency),
node_id(node_id),
direction(direction),
firewalled(firewalled)
{}
};
struct address_message
{
static const core_message_type_enum type;
std::vector<address_info> addresses;
};
struct closing_connection_message
{
static const core_message_type_enum type;
std::string reason_for_closing;
bool closing_due_to_error;
fc::oexception error;
closing_connection_message() : closing_due_to_error(false) {}
closing_connection_message(const std::string& reason_for_closing,
bool closing_due_to_error = false,
const fc::oexception& error = fc::oexception()) :
reason_for_closing(reason_for_closing),
closing_due_to_error(closing_due_to_error),
error(error)
{}
};
struct current_time_request_message
{
static const core_message_type_enum type;
fc::time_point request_sent_time;
current_time_request_message(){}
current_time_request_message(const fc::time_point request_sent_time) :
request_sent_time(request_sent_time)
{}
};
struct current_time_reply_message
{
static const core_message_type_enum type;
fc::time_point request_sent_time;
fc::time_point request_received_time;
fc::time_point reply_transmitted_time;
current_time_reply_message(){}
current_time_reply_message(const fc::time_point request_sent_time,
const fc::time_point request_received_time,
const fc::time_point reply_transmitted_time = fc::time_point()) :
request_sent_time(request_sent_time),
request_received_time(request_received_time),
reply_transmitted_time(reply_transmitted_time)
{}
};
struct check_firewall_message
{
static const core_message_type_enum type;
node_id_t node_id;
fc::ip::endpoint endpoint_to_check;
};
enum class firewall_check_result
{
unable_to_check,
unable_to_connect,
connection_successful
};
struct check_firewall_reply_message
{
static const core_message_type_enum type;
node_id_t node_id;
fc::ip::endpoint endpoint_checked;
fc::enum_type<uint8_t, firewall_check_result> result;
};
struct get_current_connections_request_message
{
static const core_message_type_enum type;
};
struct current_connection_data
{
uint32_t connection_duration; // in seconds
fc::ip::endpoint remote_endpoint;
node_id_t node_id;
fc::microseconds clock_offset;
fc::microseconds round_trip_delay;
fc::enum_type<uint8_t, peer_connection_direction> connection_direction;
fc::enum_type<uint8_t, firewalled_state> firewalled;
fc::variant_object user_data;
};
struct get_current_connections_reply_message
{
static const core_message_type_enum type;
uint32_t upload_rate_one_minute;
uint32_t download_rate_one_minute;
uint32_t upload_rate_fifteen_minutes;
uint32_t download_rate_fifteen_minutes;
uint32_t upload_rate_one_hour;
uint32_t download_rate_one_hour;
std::vector<current_connection_data> current_connections;
};
} } // eos::net
FC_REFLECT_ENUM( eos::net::core_message_type_enum,
(trx_message_type)
(block_message_type)
(core_message_type_first)
(item_ids_inventory_message_type)
(blockchain_item_ids_inventory_message_type)
(fetch_blockchain_item_ids_message_type)
(fetch_items_message_type)
(item_not_available_message_type)
(hello_message_type)
(connection_accepted_message_type)
(connection_rejected_message_type)
(address_request_message_type)
(address_message_type)
(closing_connection_message_type)
(current_time_request_message_type)
(current_time_reply_message_type)
(check_firewall_message_type)
(check_firewall_reply_message_type)
(get_current_connections_request_message_type)
(get_current_connections_reply_message_type)
(core_message_type_last) )
FC_REFLECT( eos::net::trx_message, (trx) )
FC_REFLECT( eos::net::block_message, (block)(block_id) )
FC_REFLECT( eos::net::item_id, (item_type)
(item_hash) )
FC_REFLECT( eos::net::item_ids_inventory_message, (item_type)
(item_hashes_available) )
FC_REFLECT( eos::net::blockchain_item_ids_inventory_message, (total_remaining_item_count)
(item_type)
(item_hashes_available) )
FC_REFLECT( eos::net::fetch_blockchain_item_ids_message, (item_type)
(blockchain_synopsis) )
FC_REFLECT( eos::net::fetch_items_message, (item_type)
(items_to_fetch) )
FC_REFLECT( eos::net::item_not_available_message, (requested_item) )
FC_REFLECT( eos::net::hello_message, (user_agent)
(core_protocol_version)
(inbound_address)
(inbound_port)
(outbound_port)
(node_public_key)
(signed_shared_secret)
(chain_id)
(user_data) )
FC_REFLECT_EMPTY( eos::net::connection_accepted_message )
FC_REFLECT_ENUM(eos::net::rejection_reason_code, (unspecified)
(different_chain)
(already_connected)
(connected_to_self)
(not_accepting_connections)
(blocked)
(invalid_hello_message)
(client_too_old))
FC_REFLECT( eos::net::connection_rejected_message, (user_agent)
(core_protocol_version)
(remote_endpoint)
(reason_code)
(reason_string))
FC_REFLECT_EMPTY( eos::net::address_request_message )
FC_REFLECT( eos::net::address_info, (remote_endpoint)
(last_seen_time)
(latency)
(node_id)
(direction)
(firewalled) )
FC_REFLECT( eos::net::address_message, (addresses) )
FC_REFLECT( eos::net::closing_connection_message, (reason_for_closing)
(closing_due_to_error)
(error) )
FC_REFLECT_ENUM(eos::net::peer_connection_direction, (unknown)
(inbound)
(outbound))
FC_REFLECT_ENUM(eos::net::firewalled_state, (unknown)
(firewalled)
(not_firewalled))
FC_REFLECT(eos::net::current_time_request_message, (request_sent_time))
FC_REFLECT(eos::net::current_time_reply_message, (request_sent_time)
(request_received_time)
(reply_transmitted_time))
FC_REFLECT_ENUM(eos::net::firewall_check_result, (unable_to_check)
(unable_to_connect)
(connection_successful))
FC_REFLECT(eos::net::check_firewall_message, (node_id)(endpoint_to_check))
FC_REFLECT(eos::net::check_firewall_reply_message, (node_id)(endpoint_checked)(result))
FC_REFLECT_EMPTY(eos::net::get_current_connections_request_message)
FC_REFLECT(eos::net::current_connection_data, (connection_duration)
(remote_endpoint)
(node_id)
(clock_offset)
(round_trip_delay)
(connection_direction)
(firewalled)
(user_data))
FC_REFLECT(eos::net::get_current_connections_reply_message, (upload_rate_one_minute)
(download_rate_one_minute)
(upload_rate_fifteen_minutes)
(download_rate_fifteen_minutes)
(upload_rate_one_hour)
(download_rate_one_hour)
(current_connections))
#include <unordered_map>
#include <fc/crypto/city.hpp>
#include <fc/crypto/sha224.hpp>
namespace std
{
template<>
struct hash<eos::net::item_id>
{
size_t operator()(const eos::net::item_id& item_to_hash) const
{
return fc::city_hash_size_t((char*)&item_to_hash, sizeof(item_to_hash));
}
};
}
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/exception/exception.hpp>
namespace eos { namespace net {
// registered in node.cpp
FC_DECLARE_EXCEPTION( net_exception, 90000, "P2P Networking Exception" );
FC_DECLARE_DERIVED_EXCEPTION( send_queue_overflow, eos::net::net_exception, 90001, "send queue for this peer exceeded maximum size" );
FC_DECLARE_DERIVED_EXCEPTION( insufficient_relay_fee, eos::net::net_exception, 90002, "insufficient relay fee" );
FC_DECLARE_DERIVED_EXCEPTION( already_connected_to_requested_peer, eos::net::net_exception, 90003, "already connected to requested peer" );
FC_DECLARE_DERIVED_EXCEPTION( block_older_than_undo_history, eos::net::net_exception, 90004, "block is older than our undo history allows us to process" );
FC_DECLARE_DERIVED_EXCEPTION( peer_is_on_an_unreachable_fork, eos::net::net_exception, 90005, "peer is on another fork" );
FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, eos::net::net_exception, 90006, "unlinkable block" )
} }
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/array.hpp>
#include <fc/io/varint.hpp>
#include <fc/network/ip.hpp>
#include <fc/io/raw.hpp>
#include <fc/crypto/sha512.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/reflect/variant.hpp>
namespace eos { namespace net {
/**
* Defines an 8 byte header that is always present because the minimum encrypted packet
* size is 8 bytes (blowfish). The maximum message size is defined in config.hpp. The channel,
* and message type is also included because almost every channel will have a message type
* field and we might as well include it in the 8 byte header to save space.
*/
struct message_header
{
uint32_t size; // number of bytes in message, capped at MAX_MESSAGE_SIZE
uint32_t msg_type; // every channel gets a 16 bit message type specifier
};
typedef fc::sha256 message_hash_type;
/**
* Abstracts the process of packing/unpacking a message for a
* particular channel.
*/
struct message : public message_header
{
std::vector<char> data;
message(){}
message( message&& m )
:message_header(m),data( std::move(m.data) ){}
message( const message& m )
:message_header(m),data( m.data ){}
/**
* Assumes that T::type specifies the message type
*/
template<typename T>
message( const T& m )
{
msg_type = T::type;
data = fc::raw::pack(m);
size = (uint32_t)data.size();
}
fc::sha256 id()const
{
return fc::sha256::hash( data.data(), (uint32_t)data.size() );
}
/**
* Automatically checks the type and deserializes T in the
* opposite process from the constructor.
*/
template<typename T>
T as()const
{
try {
FC_ASSERT( msg_type == T::type );
T tmp;
if( data.size() )
{
fc::datastream<const char*> ds( data.data(), data.size() );
fc::raw::unpack( ds, tmp );
}
else
{
// just to make sure that tmp shouldn't have any data
fc::datastream<const char*> ds( nullptr, 0 );
fc::raw::unpack( ds, tmp );
}
return tmp;
} FC_RETHROW_EXCEPTIONS( warn,
"error unpacking network message as a '${type}' ${x} !=? ${msg_type}",
("type", fc::get_typename<T>::name() )
("x", T::type)
("msg_type", msg_type)
);
}
};
} } // eos::net
FC_REFLECT( eos::net::message_header, (size)(msg_type) )
FC_REFLECT_DERIVED( eos::net::message, (eos::net::message_header), (data) )
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/network/tcp_socket.hpp>
#include <eos/net/message.hpp>
namespace eos { namespace net {
namespace detail { class message_oriented_connection_impl; }
class message_oriented_connection;
/** receives incoming messages from a message_oriented_connection object */
class message_oriented_connection_delegate
{
public:
virtual void on_message(message_oriented_connection* originating_connection, const message& received_message) = 0;
virtual void on_connection_closed(message_oriented_connection* originating_connection) = 0;
};
/** uses a secure socket to create a connection that reads and writes a stream of `fc::net::message` objects */
class message_oriented_connection
{
public:
message_oriented_connection(message_oriented_connection_delegate* delegate = nullptr);
~message_oriented_connection();
fc::tcp_socket& get_socket();
void accept();
void bind(const fc::ip::endpoint& local_endpoint);
void connect_to(const fc::ip::endpoint& remote_endpoint);
void send_message(const message& message_to_send);
void close_connection();
void destroy_connection();
uint64_t get_total_bytes_sent() const;
uint64_t get_total_bytes_received() const;
fc::time_point get_last_message_sent_time() const;
fc::time_point get_last_message_received_time() const;
fc::time_point get_connection_time() const;
fc::sha512 get_shared_secret() const;
private:
std::unique_ptr<detail::message_oriented_connection_impl> my;
};
typedef std::shared_ptr<message_oriented_connection> message_oriented_connection_ptr;
} } // eos::net
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <eos/net/core_messages.hpp>
#include <eos/net/message.hpp>
#include <eos/net/peer_database.hpp>
#include <eos/chain/types.hpp>
#include <list>
namespace eos { namespace net {
using fc::variant_object;
using eos::chain::chain_id_type;
namespace detail
{
class node_impl;
struct node_impl_deleter
{
void operator()(node_impl*);
};
}
// during network development, we need to track message propagation across the network
// using a structure like this:
struct message_propagation_data
{
fc::time_point received_time;
fc::time_point validated_time;
node_id_t originating_peer;
};
/**
* @class node_delegate
* @brief used by node reports status to client or fetch data from client
*/
class node_delegate
{
public:
virtual ~node_delegate(){}
/**
* If delegate has the item, the network has no need to fetch it.
*/
virtual bool has_item( const net::item_id& id ) = 0;
/**
* @brief Called when a new block comes in from the network
*
* @param sync_mode true if the message was fetched through the sync process, false during normal operation
* @returns true if this message caused the blockchain to switch forks, false if it did not
*
* @throws exception if error validating the item, otherwise the item is
* safe to broadcast on.
*/
virtual bool handle_block( const eos::net::block_message& blk_msg, bool sync_mode,
std::vector<fc::sha256>& contained_transaction_message_ids ) = 0;
/**
* @brief Called when a new transaction comes in from the network
*
* @throws exception if error validating the item, otherwise the item is
* safe to broadcast on.
*/
virtual void handle_transaction( const eos::net::trx_message& trx_msg ) = 0;
/**
* @brief Called when a new message comes in from the network other than a
* block or a transaction. Currently there are no other possible
* messages, so this should never be called.
*
* @throws exception if error validating the item, otherwise the item is
* safe to broadcast on.
*/
virtual void handle_message( const message& message_to_process ) = 0;
/**
* Assuming all data elements are ordered in some way, this method should
* return up to limit ids that occur *after* from_id.
* On return, remaining_item_count will be set to the number of items
* in our blockchain after the last item returned in the result,
* or 0 if the result contains the last item in the blockchain
*/
virtual std::vector<item_hash_t> get_block_ids(const std::vector<item_hash_t>& blockchain_synopsis,
uint32_t& remaining_item_count,
uint32_t limit = 2000) = 0;
/**
* Given the hash of the requested data, fetch the body.
*/
virtual message get_item( const item_id& id ) = 0;
virtual chain_id_type get_chain_id()const = 0;
/**
* Returns a synopsis of the blockchain used for syncing.
* This consists of a list of selected item hashes from our current preferred
* blockchain, exponentially falling off into the past. Horrible explanation.
*
* If the blockchain is empty, it will return the empty list.
* If the blockchain has one block, it will return a list containing just that block.
* If it contains more than one block:
* the first element in the list will be the hash of the highest numbered block that
* we cannot undo
* the second element will be the hash of an item at the half way point in the undoable
* segment of the blockchain
* the third will be ~3/4 of the way through the undoable segment of the block chain
* the fourth will be at ~7/8...
* &c.
* the last item in the list will be the hash of the most recent block on our preferred chain
*/
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
uint32_t number_of_blocks_after_reference_point) = 0;
/**
* Call this after the call to handle_message succeeds.
*
* @param item_type the type of the item we're synchronizing, will be the same as item passed to the sync_from() call
* @param item_count the number of items known to the node that haven't been sent to handle_item() yet.
* After `item_count` more calls to handle_item(), the node will be in sync
*/
virtual void sync_status( uint32_t item_type, uint32_t item_count ) = 0;
/**
* Call any time the number of connected peers changes.
*/
virtual void connection_count_changed( uint32_t c ) = 0;
virtual uint32_t get_block_number(const item_hash_t& block_id) = 0;
/**
* Returns the time a block was produced (if block_id = 0, returns genesis time).
* If we don't know about the block, returns time_point_sec::min()
*/
virtual fc::time_point_sec get_block_time(const item_hash_t& block_id) = 0;
virtual item_hash_t get_head_block_id() const = 0;
virtual uint32_t estimate_last_known_fork_from_git_revision_timestamp(uint32_t unix_timestamp) const = 0;
virtual void error_encountered(const std::string& message, const fc::oexception& error) = 0;
virtual uint8_t get_current_block_interval_in_seconds() const = 0;
};
/**
* Information about connected peers that the client may want to make
* available to the user.
*/
struct peer_status
{
uint32_t version;
fc::ip::endpoint host;
/** info contains the fields required by bitcoin-rpc's getpeerinfo call, we will likely
extend it with our own fields. */
fc::variant_object info;
};
/**
* @class node
* @brief provides application independent P2P broadcast and data synchronization
*
* Unanswered questions:
* when does the node start establishing network connections and accepting peers?
* we don't have enough info to start synchronizing until sync_from() is called,
* would we have any reason to connect before that?
*/
class node : public std::enable_shared_from_this<node>
{
public:
node(const std::string& user_agent);
~node();
void close();
void set_node_delegate( node_delegate* del );
void load_configuration( const fc::path& configuration_directory );
virtual void listen_to_p2p_network();
virtual void connect_to_p2p_network();
/**
* Add endpoint to internal level_map database of potential nodes
* to attempt to connect to. This database is consulted any time
* the number connected peers falls below the target.
*/
void add_node( const fc::ip::endpoint& ep );
/**
* Attempt to connect to the specified endpoint immediately.
*/
virtual void connect_to_endpoint( const fc::ip::endpoint& ep );
/**
* Specifies the network interface and port upon which incoming
* connections should be accepted.
*/
void listen_on_endpoint( const fc::ip::endpoint& ep, bool wait_if_not_available );
/**
* Call with true to enable listening for incoming connections
*/
void accept_incoming_connections(bool accept);
/**
* Specifies the port upon which incoming connections should be accepted.
* @param port the port to listen on
* @param wait_if_not_available if true and the port is not available, enter a
* sleep and retry loop to wait for it to become
* available. If false and the port is not available,
* just choose a random available port
*/
void listen_on_port(uint16_t port, bool wait_if_not_available);
/**
* Returns the endpoint the node is listening on. This is usually the same
* as the value previously passed in to listen_on_endpoint, unless we
* were unable to bind to that port.
*/
virtual fc::ip::endpoint get_actual_listening_endpoint() const;
/**
* @return a list of peers that are currently connected.
*/
std::vector<peer_status> get_connected_peers() const;
/** return the number of peers we're actively connected to */
virtual uint32_t get_connection_count() const;
/**
* Add message to outgoing inventory list, notify peers that
* I have a message ready.
*/
virtual void broadcast( const message& item_to_broadcast );
virtual void broadcast_transaction( const SignedTransaction& trx )
{
broadcast( trx_message(trx) );
}
/**
* Node starts the process of fetching all items after item_id of the
* given item_type. During this process messages are not broadcast.
*/
virtual void sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers);
bool is_connected() const;
void set_advanced_node_parameters(const fc::variant_object& params);
fc::variant_object get_advanced_node_parameters();
message_propagation_data get_transaction_propagation_data(const eos::chain::transaction_id_type& transaction_id);
message_propagation_data get_block_propagation_data(const eos::chain::block_id_type& block_id);
node_id_t get_node_id() const;
void set_allowed_peers(const std::vector<node_id_t>& allowed_peers);
/**
* Instructs the node to forget everything in its peer database, mostly for debugging
* problems where nodes are failing to connect to the network
*/
void clear_peer_database();
void set_total_bandwidth_limit(uint32_t upload_bytes_per_second, uint32_t download_bytes_per_second);
fc::variant_object network_get_info() const;
fc::variant_object network_get_usage_stats() const;
std::vector<potential_peer_record> get_potential_peers() const;
void disable_peer_advertising();
fc::variant_object get_call_statistics() const;
private:
std::unique_ptr<detail::node_impl, detail::node_impl_deleter> my;
};
class simulated_network : public node
{
public:
~simulated_network();
simulated_network(const std::string& user_agent) : node(user_agent) {}
void listen_to_p2p_network() override {}
void connect_to_p2p_network() override {}
void connect_to_endpoint(const fc::ip::endpoint& ep) override {}
fc::ip::endpoint get_actual_listening_endpoint() const override { return fc::ip::endpoint(); }
void sync_from(const item_id& current_head_block, const std::vector<uint32_t>& hard_fork_block_numbers) override {}
void broadcast(const message& item_to_broadcast) override;
void add_node_delegate(node_delegate* node_delegate_to_add);
virtual uint32_t get_connection_count() const override { return 8; }
private:
struct node_info;
void message_sender(node_info* destination_node);
std::list<node_info*> network_nodes;
};
typedef std::shared_ptr<node> node_ptr;
typedef std::shared_ptr<simulated_network> simulated_network_ptr;
} } // eos::net
FC_REFLECT(eos::net::message_propagation_data, (received_time)(validated_time)(originating_peer));
FC_REFLECT( eos::net::peer_status, (version)(host)(info) );
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <eos/net/node.hpp>
#include <eos/net/peer_database.hpp>
#include <eos/net/message_oriented_connection.hpp>
#include <eos/net/stcp_socket.hpp>
#include <eos/net/config.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <queue>
#include <boost/container/deque.hpp>
#include <fc/thread/future.hpp>
namespace eos { namespace net
{
struct firewall_check_state_data
{
node_id_t expected_node_id;
fc::ip::endpoint endpoint_to_test;
// if we're coordinating a firewall check for another node, these are the helper
// nodes we've already had do the test (if this structure is still relevant, that
// that means they have all had indeterminate results
std::set<node_id_t> nodes_already_tested;
// If we're a just a helper node, this is the node we report back to
// when we have a result
node_id_t requesting_peer;
};
class peer_connection;
class peer_connection_delegate
{
public:
virtual void on_message(peer_connection* originating_peer,
const message& received_message) = 0;
virtual void on_connection_closed(peer_connection* originating_peer) = 0;
virtual message get_message_for_item(const item_id& item) = 0;
};
class peer_connection;
typedef std::shared_ptr<peer_connection> peer_connection_ptr;
class peer_connection : public message_oriented_connection_delegate,
public std::enable_shared_from_this<peer_connection>
{
public:
enum class our_connection_state
{
disconnected,
just_connected, // if in this state, we have sent a hello_message
connection_accepted, // remote side has sent us a connection_accepted, we're operating normally with them
connection_rejected // remote side has sent us a connection_rejected, we may be exchanging address with them or may just be waiting for them to close
};
enum class their_connection_state
{
disconnected,
just_connected, // we have not yet received a hello_message
connection_accepted, // we have sent them a connection_accepted
connection_rejected // we have sent them a connection_rejected
};
enum class connection_negotiation_status
{
disconnected,
connecting,
connected,
accepting,
accepted,
hello_sent,
peer_connection_accepted,
peer_connection_rejected,
negotiation_complete,
closing,
closed
};
private:
peer_connection_delegate* _node;
fc::optional<fc::ip::endpoint> _remote_endpoint;
message_oriented_connection _message_connection;
/* a base class for messages on the queue, to hide the fact that some
* messages are complete messages and some are only hashes of messages.
*/
struct queued_message
{
fc::time_point enqueue_time;
fc::time_point transmission_start_time;
fc::time_point transmission_finish_time;
queued_message(fc::time_point enqueue_time = fc::time_point::now()) :
enqueue_time(enqueue_time)
{}
virtual message get_message(peer_connection_delegate* node) = 0;
/** returns roughly the number of bytes of memory the message is consuming while
* it is sitting on the queue
*/
virtual size_t get_size_in_queue() = 0;
virtual ~queued_message() {}
};
/* when you queue up a 'real_queued_message', a full copy of the message is
* stored on the heap until it is sent
*/
struct real_queued_message : queued_message
{
message message_to_send;
size_t message_send_time_field_offset;
real_queued_message(message message_to_send,
size_t message_send_time_field_offset = (size_t)-1) :
message_to_send(std::move(message_to_send)),
message_send_time_field_offset(message_send_time_field_offset)
{}
message get_message(peer_connection_delegate* node) override;
size_t get_size_in_queue() override;
};
/* when you queue up a 'virtual_queued_message', we just queue up the hash of the
* item we want to send. When it reaches the top of the queue, we make a callback
* to the node to generate the message.
*/
struct virtual_queued_message : queued_message
{
item_id item_to_send;
virtual_queued_message(item_id item_to_send) :
item_to_send(std::move(item_to_send))
{}
message get_message(peer_connection_delegate* node) override;
size_t get_size_in_queue() override;
};
size_t _total_queued_messages_size;
std::queue<std::unique_ptr<queued_message>, std::list<std::unique_ptr<queued_message> > > _queued_messages;
fc::future<void> _send_queued_messages_done;
public:
fc::time_point connection_initiation_time;
fc::time_point connection_closed_time;
fc::time_point connection_terminated_time;
peer_connection_direction direction;
//connection_state state;
firewalled_state is_firewalled;
fc::microseconds clock_offset;
fc::microseconds round_trip_delay;
our_connection_state our_state;
bool they_have_requested_close;
their_connection_state their_state;
bool we_have_requested_close;
connection_negotiation_status negotiation_status;
fc::oexception connection_closed_error;
fc::time_point get_connection_time()const { return _message_connection.get_connection_time(); }
fc::time_point get_connection_terminated_time()const { return connection_terminated_time; }
/// data about the peer node
/// @{
/** node_public_key from the hello message, zero-initialized before we get the hello */
node_id_t node_public_key;
/** the unique identifier we'll use to refer to the node with. zero-initialized before
* we receive the hello message, at which time it will be filled with either the "node_id"
* from the user_data field of the hello, or if none is present it will be filled with a
* copy of node_public_key */
node_id_t node_id;
uint32_t core_protocol_version;
std::string user_agent;
fc::optional<std::string> eos_git_revision_sha;
fc::optional<fc::time_point_sec> eos_git_revision_unix_timestamp;
fc::optional<std::string> fc_git_revision_sha;
fc::optional<fc::time_point_sec> fc_git_revision_unix_timestamp;
fc::optional<std::string> platform;
fc::optional<uint32_t> bitness;
// for inbound connections, these fields record what the peer sent us in
// its hello message. For outbound, they record what we sent the peer
// in our hello message
fc::ip::address inbound_address;
uint16_t inbound_port;
uint16_t outbound_port;
/// @}
typedef std::unordered_map<item_id, fc::time_point> item_to_time_map_type;
/// blockchain synchronization state data
/// @{
boost::container::deque<item_hash_t> ids_of_items_to_get; /// id of items in the blockchain that this peer has told us about
std::set<item_hash_t> ids_of_items_being_processed; /// list of all items this peer has offered use that we've already handed to the client but the client hasn't finished processing
uint32_t number_of_unfetched_item_ids; /// number of items in the blockchain that follow ids_of_items_to_get but the peer hasn't yet told us their ids
bool peer_needs_sync_items_from_us;
bool we_need_sync_items_from_peer;
fc::optional<boost::tuple<std::vector<item_hash_t>, fc::time_point> > item_ids_requested_from_peer; /// we check this to detect a timed-out request and in busy()
item_to_time_map_type sync_items_requested_from_peer; /// ids of blocks we've requested from this peer during sync. fetch from another peer if this peer disconnects
item_hash_t last_block_delegate_has_seen; /// the hash of the last block this peer has told us about that the peer knows
fc::time_point_sec last_block_time_delegate_has_seen;
bool inhibit_fetching_sync_blocks;
/// @}
/// non-synchronization state data
/// @{
struct timestamped_item_id
{
item_id item;
fc::time_point_sec timestamp;
timestamped_item_id(const item_id& item, const fc::time_point_sec timestamp) :
item(item),
timestamp(timestamp)
{}
};
struct timestamp_index{};
typedef boost::multi_index_container<timestamped_item_id,
boost::multi_index::indexed_by<boost::multi_index::hashed_unique<boost::multi_index::member<timestamped_item_id, item_id, &timestamped_item_id::item>,
std::hash<item_id> >,
boost::multi_index::ordered_non_unique<boost::multi_index::tag<timestamp_index>,
boost::multi_index::member<timestamped_item_id, fc::time_point_sec, &timestamped_item_id::timestamp> > > > timestamped_items_set_type;
timestamped_items_set_type inventory_peer_advertised_to_us;
timestamped_items_set_type inventory_advertised_to_peer;
item_to_time_map_type items_requested_from_peer; /// items we've requested from this peer during normal operation. fetch from another peer if this peer disconnects
/// @}
// if they're flooding us with transactions, we set this to avoid fetching for a few seconds to let the
// blockchain catch up
fc::time_point transaction_fetching_inhibited_until;
uint32_t last_known_fork_block_number;
fc::future<void> accept_or_connect_task_done;
firewall_check_state_data *firewall_check_state;
#ifndef NDEBUG
private:
fc::thread* _thread;
unsigned _send_message_queue_tasks_running; // temporary debugging
#endif
private:
peer_connection(peer_connection_delegate* delegate);
void destroy();
public:
static peer_connection_ptr make_shared(peer_connection_delegate* delegate); // use this instead of the constructor
virtual ~peer_connection();
fc::tcp_socket& get_socket();
void accept_connection();
void connect_to(const fc::ip::endpoint& remote_endpoint, fc::optional<fc::ip::endpoint> local_endpoint = fc::optional<fc::ip::endpoint>());
void on_message(message_oriented_connection* originating_connection, const message& received_message) override;
void on_connection_closed(message_oriented_connection* originating_connection) override;
void send_queueable_message(std::unique_ptr<queued_message>&& message_to_send);
void send_message(const message& message_to_send, size_t message_send_time_field_offset = (size_t)-1);
void send_item(const item_id& item_to_send);
void close_connection();
void destroy_connection();
uint64_t get_total_bytes_sent() const;
uint64_t get_total_bytes_received() const;
fc::time_point get_last_message_sent_time() const;
fc::time_point get_last_message_received_time() const;
fc::optional<fc::ip::endpoint> get_remote_endpoint();
fc::ip::endpoint get_local_endpoint();
void set_remote_endpoint(fc::optional<fc::ip::endpoint> new_remote_endpoint);
bool busy();
bool idle();
bool is_transaction_fetching_inhibited() const;
fc::sha512 get_shared_secret() const;
void clear_old_inventory();
bool is_inventory_advertised_to_us_list_full_for_transactions() const;
bool is_inventory_advertised_to_us_list_full() const;
bool performing_firewall_check() const;
fc::optional<fc::ip::endpoint> get_endpoint_for_connecting() const;
private:
void send_queued_messages_task();
void accept_connection_task();
void connect_to_task(const fc::ip::endpoint& remote_endpoint);
};
typedef std::shared_ptr<peer_connection> peer_connection_ptr;
} } // end namespace eos::net
// not sent over the wire, just reflected for logging
FC_REFLECT_ENUM(eos::net::peer_connection::our_connection_state, (disconnected)
(just_connected)
(connection_accepted)
(connection_rejected))
FC_REFLECT_ENUM(eos::net::peer_connection::their_connection_state, (disconnected)
(just_connected)
(connection_accepted)
(connection_rejected))
FC_REFLECT_ENUM(eos::net::peer_connection::connection_negotiation_status, (disconnected)
(connecting)
(connected)
(accepting)
(accepted)
(hello_sent)
(peer_connection_accepted)
(peer_connection_rejected)
(negotiation_complete)
(closing)
(closed) )
FC_REFLECT( eos::net::peer_connection::timestamped_item_id, (item)(timestamp));
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <boost/iterator/iterator_facade.hpp>
#include <fc/network/ip.hpp>
#include <fc/time.hpp>
#include <fc/io/enum_type.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/reflect/variant.hpp>
#include <fc/exception/exception.hpp>
#include <fc/io/raw.hpp>
namespace eos { namespace net {
enum potential_peer_last_connection_disposition
{
never_attempted_to_connect,
last_connection_failed,
last_connection_rejected,
last_connection_handshaking_failed,
last_connection_succeeded
};
struct potential_peer_record
{
fc::ip::endpoint endpoint;
fc::time_point_sec last_seen_time;
fc::enum_type<uint8_t,potential_peer_last_connection_disposition> last_connection_disposition;
fc::time_point_sec last_connection_attempt_time;
uint32_t number_of_successful_connection_attempts;
uint32_t number_of_failed_connection_attempts;
fc::optional<fc::exception> last_error;
potential_peer_record() :
number_of_successful_connection_attempts(0),
number_of_failed_connection_attempts(0){}
potential_peer_record(fc::ip::endpoint endpoint,
fc::time_point_sec last_seen_time = fc::time_point_sec(),
potential_peer_last_connection_disposition last_connection_disposition = never_attempted_to_connect) :
endpoint(endpoint),
last_seen_time(last_seen_time),
last_connection_disposition(last_connection_disposition),
number_of_successful_connection_attempts(0),
number_of_failed_connection_attempts(0)
{}
};
namespace detail
{
class peer_database_impl;
class peer_database_iterator_impl;
class peer_database_iterator : public boost::iterator_facade<peer_database_iterator, const potential_peer_record, boost::forward_traversal_tag>
{
public:
peer_database_iterator();
~peer_database_iterator();
explicit peer_database_iterator(peer_database_iterator_impl* impl);
peer_database_iterator( const peer_database_iterator& c );
private:
friend class boost::iterator_core_access;
void increment();
bool equal(const peer_database_iterator& other) const;
const potential_peer_record& dereference() const;
private:
std::unique_ptr<peer_database_iterator_impl> my;
};
}
class peer_database
{
public:
peer_database();
~peer_database();
void open(const fc::path& databaseFilename);
void close();
void clear();
void erase(const fc::ip::endpoint& endpointToErase);
void update_entry(const potential_peer_record& updatedRecord);
potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);
fc::optional<potential_peer_record> lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);
typedef detail::peer_database_iterator iterator;
iterator begin() const;
iterator end() const;
size_t size() const;
private:
std::unique_ptr<detail::peer_database_impl> my;
};
} } // end namespace eos::net
FC_REFLECT_ENUM(eos::net::potential_peer_last_connection_disposition, (never_attempted_to_connect)(last_connection_failed)(last_connection_rejected)(last_connection_handshaking_failed)(last_connection_succeeded))
FC_REFLECT(eos::net::potential_peer_record, (endpoint)(last_seen_time)(last_connection_disposition)(last_connection_attempt_time)(number_of_successful_connection_attempts)(number_of_failed_connection_attempts)(last_error) )
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
#include <fc/network/tcp_socket.hpp>
#include <fc/crypto/aes.hpp>
#include <fc/crypto/elliptic.hpp>
namespace eos { namespace net {
/**
* Uses ECDH to negotiate a aes key for communicating
* with other nodes on the network.
*/
class stcp_socket : public virtual fc::iostream
{
public:
stcp_socket();
~stcp_socket();
fc::tcp_socket& get_socket() { return _sock; }
void accept();
void connect_to( const fc::ip::endpoint& remote_endpoint );
void bind( const fc::ip::endpoint& local_endpoint );
virtual size_t readsome( char* buffer, size_t max );
virtual size_t readsome( const std::shared_ptr<char>& buf, size_t len, size_t offset );
virtual bool eof()const;
virtual size_t writesome( const char* buffer, size_t len );
virtual size_t writesome( const std::shared_ptr<const char>& buf, size_t len, size_t offset );
virtual void flush();
virtual void close();
using istream::get;
void get( char& c ) { read( &c, 1 ); }
fc::sha512 get_shared_secret() const { return _shared_secret; }
private:
void do_key_exchange();
fc::sha512 _shared_secret;
fc::ecc::private_key _priv_key;
fc::array<char,8> _buf;
//uint32_t _buf_len;
fc::tcp_socket _sock;
fc::aes_encoder _send_aes;
fc::aes_decoder _recv_aes;
std::shared_ptr<char> _read_buffer;
std::shared_ptr<char> _write_buffer;
#ifndef NDEBUG
bool _read_buffer_in_use;
bool _write_buffer_in_use;
#endif
};
typedef std::shared_ptr<stcp_socket> stcp_socket_ptr;
} } // eos::net
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fc/thread/thread.hpp>
#include <fc/thread/mutex.hpp>
#include <fc/thread/scoped_lock.hpp>
#include <fc/thread/future.hpp>
#include <fc/log/logger.hpp>
#include <fc/io/enum_type.hpp>
#include <eos/net/message_oriented_connection.hpp>
#include <eos/net/stcp_socket.hpp>
#include <eos/net/config.hpp>
#ifdef DEFAULT_LOGGER
# undef DEFAULT_LOGGER
#endif
#define DEFAULT_LOGGER "p2p"
#ifndef NDEBUG
# define VERIFY_CORRECT_THREAD() assert(_thread->is_current())
#else
# define VERIFY_CORRECT_THREAD() do {} while (0)
#endif
namespace eos { namespace net {
namespace detail
{
class message_oriented_connection_impl
{
private:
message_oriented_connection* _self;
message_oriented_connection_delegate *_delegate;
stcp_socket _sock;
fc::future<void> _read_loop_done;
uint64_t _bytes_received;
uint64_t _bytes_sent;
fc::time_point _connected_time;
fc::time_point _last_message_received_time;
fc::time_point _last_message_sent_time;
bool _send_message_in_progress;
#ifndef NDEBUG
fc::thread* _thread;
#endif
void read_loop();
void start_read_loop();
public:
fc::tcp_socket& get_socket();
void accept();
void connect_to(const fc::ip::endpoint& remote_endpoint);
void bind(const fc::ip::endpoint& local_endpoint);
message_oriented_connection_impl(message_oriented_connection* self,
message_oriented_connection_delegate* delegate = nullptr);
~message_oriented_connection_impl();
void send_message(const message& message_to_send);
void close_connection();
void destroy_connection();
uint64_t get_total_bytes_sent() const;
uint64_t get_total_bytes_received() const;
fc::time_point get_last_message_sent_time() const;
fc::time_point get_last_message_received_time() const;
fc::time_point get_connection_time() const { return _connected_time; }
fc::sha512 get_shared_secret() const;
};
message_oriented_connection_impl::message_oriented_connection_impl(message_oriented_connection* self,
message_oriented_connection_delegate* delegate)
: _self(self),
_delegate(delegate),
_bytes_received(0),
_bytes_sent(0),
_send_message_in_progress(false)
#ifndef NDEBUG
,_thread(&fc::thread::current())
#endif
{
}
message_oriented_connection_impl::~message_oriented_connection_impl()
{
VERIFY_CORRECT_THREAD();
destroy_connection();
}
fc::tcp_socket& message_oriented_connection_impl::get_socket()
{
VERIFY_CORRECT_THREAD();
return _sock.get_socket();
}
void message_oriented_connection_impl::accept()
{
VERIFY_CORRECT_THREAD();
_sock.accept();
assert(!_read_loop_done.valid()); // check to be sure we never launch two read loops
_read_loop_done = fc::async([=](){ read_loop(); }, "message read_loop");
}
void message_oriented_connection_impl::connect_to(const fc::ip::endpoint& remote_endpoint)
{
VERIFY_CORRECT_THREAD();
_sock.connect_to(remote_endpoint);
assert(!_read_loop_done.valid()); // check to be sure we never launch two read loops
_read_loop_done = fc::async([=](){ read_loop(); }, "message read_loop");
}
void message_oriented_connection_impl::bind(const fc::ip::endpoint& local_endpoint)
{
VERIFY_CORRECT_THREAD();
_sock.bind(local_endpoint);
}
void message_oriented_connection_impl::read_loop()
{
VERIFY_CORRECT_THREAD();
const int BUFFER_SIZE = 16;
const int LEFTOVER = BUFFER_SIZE - sizeof(message_header);
static_assert(BUFFER_SIZE >= sizeof(message_header), "insufficient buffer");
_connected_time = fc::time_point::now();
fc::oexception exception_to_rethrow;
bool call_on_connection_closed = false;
try
{
message m;
while( true )
{
char buffer[BUFFER_SIZE];
_sock.read(buffer, BUFFER_SIZE);
_bytes_received += BUFFER_SIZE;
memcpy((char*)&m, buffer, sizeof(message_header));
FC_ASSERT( m.size <= MAX_MESSAGE_SIZE, "", ("m.size",m.size)("MAX_MESSAGE_SIZE",MAX_MESSAGE_SIZE) );
size_t remaining_bytes_with_padding = 16 * ((m.size - LEFTOVER + 15) / 16);
m.data.resize(LEFTOVER + remaining_bytes_with_padding); //give extra 16 bytes to allow for padding added in send call
std::copy(buffer + sizeof(message_header), buffer + sizeof(buffer), m.data.begin());
if (remaining_bytes_with_padding)
{
_sock.read(&m.data[LEFTOVER], remaining_bytes_with_padding);
_bytes_received += remaining_bytes_with_padding;
}
m.data.resize(m.size); // truncate off the padding bytes
_last_message_received_time = fc::time_point::now();
try
{
// message handling errors are warnings...
_delegate->on_message(_self, m);
}
/// Dedicated catches needed to distinguish from general fc::exception
catch ( const fc::canceled_exception& e ) { throw e; }
catch ( const fc::eof_exception& e ) { throw e; }
catch ( const fc::exception& e)
{
/// Here loop should be continued so exception should be just caught locally.
wlog( "message transmission failed ${er}", ("er", e.to_detail_string() ) );
throw;
}
}
}
catch ( const fc::canceled_exception& e )
{
wlog( "caught a canceled_exception in read_loop. this should mean we're in the process of deleting this object already, so there's no need to notify the delegate: ${e}", ("e", e.to_detail_string() ) );
throw;
}
catch ( const fc::eof_exception& e )
{
wlog( "disconnected ${e}", ("e", e.to_detail_string() ) );
call_on_connection_closed = true;
}
catch ( const fc::exception& e )
{
elog( "disconnected ${er}", ("er", e.to_detail_string() ) );
call_on_connection_closed = true;
exception_to_rethrow = fc::unhandled_exception(FC_LOG_MESSAGE(warn, "disconnected: ${e}", ("e", e.to_detail_string())));
}
catch ( const std::exception& e )
{
elog( "disconnected ${er}", ("er", e.what() ) );
call_on_connection_closed = true;
exception_to_rethrow = fc::unhandled_exception(FC_LOG_MESSAGE(warn, "disconnected: ${e}", ("e", e.what())));
}
catch ( ... )
{
elog( "unexpected exception" );
call_on_connection_closed = true;
exception_to_rethrow = fc::unhandled_exception(FC_LOG_MESSAGE(warn, "disconnected: ${e}", ("e", fc::except_str())));
}
if (call_on_connection_closed)
_delegate->on_connection_closed(_self);
if (exception_to_rethrow)
throw *exception_to_rethrow;
}
void message_oriented_connection_impl::send_message(const message& message_to_send)
{
VERIFY_CORRECT_THREAD();
#if 0 // this gets too verbose
#ifndef NDEBUG
fc::optional<fc::ip::endpoint> remote_endpoint;
if (_sock.get_socket().is_open())
remote_endpoint = _sock.get_socket().remote_endpoint();
struct scope_logger {
const fc::optional<fc::ip::endpoint>& endpoint;
scope_logger(const fc::optional<fc::ip::endpoint>& endpoint) : endpoint(endpoint) { dlog("entering message_oriented_connection::send_message() for peer ${endpoint}", ("endpoint", endpoint)); }
~scope_logger() { dlog("leaving message_oriented_connection::send_message() for peer ${endpoint}", ("endpoint", endpoint)); }
} send_message_scope_logger(remote_endpoint);
#endif
#endif
struct verify_no_send_in_progress {
bool& var;
verify_no_send_in_progress(bool& var) : var(var)
{
if (var)
elog("Error: two tasks are calling message_oriented_connection::send_message() at the same time");
assert(!var);
var = true;
}
~verify_no_send_in_progress() { var = false; }
} _verify_no_send_in_progress(_send_message_in_progress);
try
{
size_t size_of_message_and_header = sizeof(message_header) + message_to_send.size;
if( message_to_send.size > MAX_MESSAGE_SIZE )
elog("Trying to send a message larger than MAX_MESSAGE_SIZE. This probably won't work...");
//pad the message we send to a multiple of 16 bytes
size_t size_with_padding = 16 * ((size_of_message_and_header + 15) / 16);
std::unique_ptr<char[]> padded_message(new char[size_with_padding]);
memcpy(padded_message.get(), (char*)&message_to_send, sizeof(message_header));
memcpy(padded_message.get() + sizeof(message_header), message_to_send.data.data(), message_to_send.size );
_sock.write(padded_message.get(), size_with_padding);
_sock.flush();
_bytes_sent += size_with_padding;
_last_message_sent_time = fc::time_point::now();
} FC_RETHROW_EXCEPTIONS( warn, "unable to send message" );
}
void message_oriented_connection_impl::close_connection()
{
VERIFY_CORRECT_THREAD();
_sock.close();
}
void message_oriented_connection_impl::destroy_connection()
{
VERIFY_CORRECT_THREAD();
fc::optional<fc::ip::endpoint> remote_endpoint;
if (_sock.get_socket().is_open())
remote_endpoint = _sock.get_socket().remote_endpoint();
ilog( "in destroy_connection() for ${endpoint}", ("endpoint", remote_endpoint) );
if (_send_message_in_progress)
elog("Error: message_oriented_connection is being destroyed while a send_message is in progress. "
"The task calling send_message() should have been canceled already");
assert(!_send_message_in_progress);
try
{
_read_loop_done.cancel_and_wait(__FUNCTION__);
}
catch ( const fc::exception& e )
{
wlog( "Exception thrown while canceling message_oriented_connection's read_loop, ignoring: ${e}", ("e",e) );
}
catch (...)
{
wlog( "Exception thrown while canceling message_oriented_connection's read_loop, ignoring" );
}
}
uint64_t message_oriented_connection_impl::get_total_bytes_sent() const
{
VERIFY_CORRECT_THREAD();
return _bytes_sent;
}
uint64_t message_oriented_connection_impl::get_total_bytes_received() const
{
VERIFY_CORRECT_THREAD();
return _bytes_received;
}
fc::time_point message_oriented_connection_impl::get_last_message_sent_time() const
{
VERIFY_CORRECT_THREAD();
return _last_message_sent_time;
}
fc::time_point message_oriented_connection_impl::get_last_message_received_time() const
{
VERIFY_CORRECT_THREAD();
return _last_message_received_time;
}
fc::sha512 message_oriented_connection_impl::get_shared_secret() const
{
VERIFY_CORRECT_THREAD();
return _sock.get_shared_secret();
}
} // end namespace eos::net::detail
message_oriented_connection::message_oriented_connection(message_oriented_connection_delegate* delegate) :
my(new detail::message_oriented_connection_impl(this, delegate))
{
}
message_oriented_connection::~message_oriented_connection()
{
}
fc::tcp_socket& message_oriented_connection::get_socket()
{
return my->get_socket();
}
void message_oriented_connection::accept()
{
my->accept();
}
void message_oriented_connection::connect_to(const fc::ip::endpoint& remote_endpoint)
{
my->connect_to(remote_endpoint);
}
void message_oriented_connection::bind(const fc::ip::endpoint& local_endpoint)
{
my->bind(local_endpoint);
}
void message_oriented_connection::send_message(const message& message_to_send)
{
my->send_message(message_to_send);
}
void message_oriented_connection::close_connection()
{
my->close_connection();
}
void message_oriented_connection::destroy_connection()
{
my->destroy_connection();
}
uint64_t message_oriented_connection::get_total_bytes_sent() const
{
return my->get_total_bytes_sent();
}
uint64_t message_oriented_connection::get_total_bytes_received() const
{
return my->get_total_bytes_received();
}
fc::time_point message_oriented_connection::get_last_message_sent_time() const
{
return my->get_last_message_sent_time();
}
fc::time_point message_oriented_connection::get_last_message_received_time() const
{
return my->get_last_message_received_time();
}
fc::time_point message_oriented_connection::get_connection_time() const
{
return my->get_connection_time();
}
fc::sha512 message_oriented_connection::get_shared_secret() const
{
return my->get_shared_secret();
}
} } // end namespace eos::net
此差异已折叠。
此差异已折叠。
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/tag.hpp>
#include <fc/io/raw.hpp>
#include <fc/io/raw_variant.hpp>
#include <fc/log/logger.hpp>
#include <fc/io/json.hpp>
#include <eos/net/peer_database.hpp>
namespace eos { namespace net {
namespace detail
{
using namespace boost::multi_index;
class peer_database_impl
{
public:
struct last_seen_time_index {};
struct endpoint_index {};
typedef boost::multi_index_container<potential_peer_record,
indexed_by<ordered_non_unique<tag<last_seen_time_index>,
member<potential_peer_record,
fc::time_point_sec,
&potential_peer_record::last_seen_time> >,
hashed_unique<tag<endpoint_index>,
member<potential_peer_record,
fc::ip::endpoint,
&potential_peer_record::endpoint>,
std::hash<fc::ip::endpoint> > > > potential_peer_set;
private:
potential_peer_set _potential_peer_set;
fc::path _peer_database_filename;
public:
void open(const fc::path& databaseFilename);
void close();
void clear();
void erase(const fc::ip::endpoint& endpointToErase);
void update_entry(const potential_peer_record& updatedRecord);
potential_peer_record lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);
fc::optional<potential_peer_record> lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup);
peer_database::iterator begin() const;
peer_database::iterator end() const;
size_t size() const;
};
class peer_database_iterator_impl
{
public:
typedef peer_database_impl::potential_peer_set::index<peer_database_impl::last_seen_time_index>::type::iterator last_seen_time_index_iterator;
last_seen_time_index_iterator _iterator;
peer_database_iterator_impl(const last_seen_time_index_iterator& iterator) :
_iterator(iterator)
{}
};
peer_database_iterator::peer_database_iterator( const peer_database_iterator& c ) :
boost::iterator_facade<peer_database_iterator, const potential_peer_record, boost::forward_traversal_tag>(c){}
void peer_database_impl::open(const fc::path& peer_database_filename)
{
_peer_database_filename = peer_database_filename;
if (fc::exists(_peer_database_filename))
{
try
{
std::vector<potential_peer_record> peer_records = fc::json::from_file(_peer_database_filename).as<std::vector<potential_peer_record> >();
std::copy(peer_records.begin(), peer_records.end(), std::inserter(_potential_peer_set, _potential_peer_set.end()));
#define MAXIMUM_PEERDB_SIZE 1000
if (_potential_peer_set.size() > MAXIMUM_PEERDB_SIZE)
{
// prune database to a reasonable size
auto iter = _potential_peer_set.begin();
std::advance(iter, MAXIMUM_PEERDB_SIZE);
_potential_peer_set.erase(iter, _potential_peer_set.end());
}
}
catch (const fc::exception& e)
{
elog("error opening peer database file ${peer_database_filename}, starting with a clean database",
("peer_database_filename", _peer_database_filename));
}
}
}
void peer_database_impl::close()
{
std::vector<potential_peer_record> peer_records;
peer_records.reserve(_potential_peer_set.size());
std::copy(_potential_peer_set.begin(), _potential_peer_set.end(), std::back_inserter(peer_records));
try
{
fc::path peer_database_filename_dir = _peer_database_filename.parent_path();
if (!fc::exists(peer_database_filename_dir))
fc::create_directories(peer_database_filename_dir);
fc::json::save_to_file(peer_records, _peer_database_filename);
}
catch (const fc::exception& e)
{
elog("error saving peer database to file ${peer_database_filename}",
("peer_database_filename", _peer_database_filename));
}
_potential_peer_set.clear();
}
void peer_database_impl::clear()
{
_potential_peer_set.clear();
}
void peer_database_impl::erase(const fc::ip::endpoint& endpointToErase)
{
auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToErase);
if (iter != _potential_peer_set.get<endpoint_index>().end())
_potential_peer_set.get<endpoint_index>().erase(iter);
}
void peer_database_impl::update_entry(const potential_peer_record& updatedRecord)
{
auto iter = _potential_peer_set.get<endpoint_index>().find(updatedRecord.endpoint);
if (iter != _potential_peer_set.get<endpoint_index>().end())
_potential_peer_set.get<endpoint_index>().modify(iter, [&updatedRecord](potential_peer_record& record) { record = updatedRecord; });
else
_potential_peer_set.get<endpoint_index>().insert(updatedRecord);
}
potential_peer_record peer_database_impl::lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup)
{
auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToLookup);
if (iter != _potential_peer_set.get<endpoint_index>().end())
return *iter;
return potential_peer_record(endpointToLookup);
}
fc::optional<potential_peer_record> peer_database_impl::lookup_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup)
{
auto iter = _potential_peer_set.get<endpoint_index>().find(endpointToLookup);
if (iter != _potential_peer_set.get<endpoint_index>().end())
return *iter;
return fc::optional<potential_peer_record>();
}
peer_database::iterator peer_database_impl::begin() const
{
return peer_database::iterator(new peer_database_iterator_impl(_potential_peer_set.get<last_seen_time_index>().begin()));
}
peer_database::iterator peer_database_impl::end() const
{
return peer_database::iterator(new peer_database_iterator_impl(_potential_peer_set.get<last_seen_time_index>().end()));
}
size_t peer_database_impl::size() const
{
return _potential_peer_set.size();
}
peer_database_iterator::peer_database_iterator()
{
}
peer_database_iterator::~peer_database_iterator()
{
}
peer_database_iterator::peer_database_iterator(peer_database_iterator_impl* impl) :
my(impl)
{
}
void peer_database_iterator::increment()
{
++my->_iterator;
}
bool peer_database_iterator::equal(const peer_database_iterator& other) const
{
return my->_iterator == other.my->_iterator;
}
const potential_peer_record& peer_database_iterator::dereference() const
{
return *my->_iterator;
}
} // end namespace detail
peer_database::peer_database() :
my(new detail::peer_database_impl)
{
}
peer_database::~peer_database()
{}
void peer_database::open(const fc::path& databaseFilename)
{
my->open(databaseFilename);
}
void peer_database::close()
{
my->close();
}
void peer_database::clear()
{
my->clear();
}
void peer_database::erase(const fc::ip::endpoint& endpointToErase)
{
my->erase(endpointToErase);
}
void peer_database::update_entry(const potential_peer_record& updatedRecord)
{
my->update_entry(updatedRecord);
}
potential_peer_record peer_database::lookup_or_create_entry_for_endpoint(const fc::ip::endpoint& endpointToLookup)
{
return my->lookup_or_create_entry_for_endpoint(endpointToLookup);
}
fc::optional<potential_peer_record> peer_database::lookup_entry_for_endpoint(const fc::ip::endpoint& endpoint_to_lookup)
{
return my->lookup_entry_for_endpoint(endpoint_to_lookup);
}
peer_database::iterator peer_database::begin() const
{
return my->begin();
}
peer_database::iterator peer_database::end() const
{
return my->end();
}
size_t peer_database::size() const
{
return my->size();
}
} } // end namespace eos::net
/*
* Copyright (c) 2017, Respective Authors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <assert.h>
#include <algorithm>
#include <fc/crypto/hex.hpp>
#include <fc/crypto/aes.hpp>
#include <fc/crypto/city.hpp>
#include <fc/log/logger.hpp>
#include <fc/network/ip.hpp>
#include <fc/exception/exception.hpp>
#include <eos/net/stcp_socket.hpp>
namespace eos { namespace net {
stcp_socket::stcp_socket()
//:_buf_len(0)
#ifndef NDEBUG
: _read_buffer_in_use(false),
_write_buffer_in_use(false)
#endif
{
}
stcp_socket::~stcp_socket()
{
}
void stcp_socket::do_key_exchange()
{
_priv_key = fc::ecc::private_key::generate();
fc::ecc::public_key pub = _priv_key.get_public_key();
fc::ecc::public_key_data s = pub.serialize();
std::shared_ptr<char> serialized_key_buffer(new char[sizeof(fc::ecc::public_key_data)], [](char* p){ delete[] p; });
memcpy(serialized_key_buffer.get(), (char*)&s, sizeof(fc::ecc::public_key_data));
_sock.write( serialized_key_buffer, sizeof(fc::ecc::public_key_data) );
_sock.read( serialized_key_buffer, sizeof(fc::ecc::public_key_data) );
fc::ecc::public_key_data rpub;
memcpy((char*)&rpub, serialized_key_buffer.get(), sizeof(fc::ecc::public_key_data));
_shared_secret = _priv_key.get_shared_secret( rpub );
// ilog("shared secret ${s}", ("s", shared_secret) );
_send_aes.init( fc::sha256::hash( (char*)&_shared_secret, sizeof(_shared_secret) ),
fc::city_hash_crc_128((char*)&_shared_secret,sizeof(_shared_secret) ) );
_recv_aes.init( fc::sha256::hash( (char*)&_shared_secret, sizeof(_shared_secret) ),
fc::city_hash_crc_128((char*)&_shared_secret,sizeof(_shared_secret) ) );
}
void stcp_socket::connect_to( const fc::ip::endpoint& remote_endpoint )
{
_sock.connect_to( remote_endpoint );
do_key_exchange();
}
void stcp_socket::bind( const fc::ip::endpoint& local_endpoint )
{
_sock.bind(local_endpoint);
}
/**
* This method must read at least 16 bytes at a time from
* the underlying TCP socket so that it can decrypt them. It
* will buffer any left-over.
*/
size_t stcp_socket::readsome( char* buffer, size_t len )
{ try {
assert( len > 0 && (len % 16) == 0 );
#ifndef NDEBUG
// This code was written with the assumption that you'd only be making one call to readsome
// at a time so it reuses _read_buffer. If you really need to make concurrent calls to
// readsome(), you'll need to prevent reusing _read_buffer here
struct check_buffer_in_use {
bool& _buffer_in_use;
check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; }
~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; }
} buffer_in_use_checker(_read_buffer_in_use);
#endif
const size_t read_buffer_length = 4096;
if (!_read_buffer)
_read_buffer.reset(new char[read_buffer_length], [](char* p){ delete[] p; });
len = std::min<size_t>(read_buffer_length, len);
size_t s = _sock.readsome( _read_buffer, len, 0 );
if( s % 16 )
{
_sock.read(_read_buffer, 16 - (s%16), s);
s += 16-(s%16);
}
_recv_aes.decode( _read_buffer.get(), s, buffer );
return s;
} FC_RETHROW_EXCEPTIONS( warn, "", ("len",len) ) }
size_t stcp_socket::readsome( const std::shared_ptr<char>& buf, size_t len, size_t offset )
{
return readsome(buf.get() + offset, len);
}
bool stcp_socket::eof()const
{
return _sock.eof();
}
size_t stcp_socket::writesome( const char* buffer, size_t len )
{ try {
assert( len > 0 && (len % 16) == 0 );
#ifndef NDEBUG
// This code was written with the assumption that you'd only be making one call to writesome
// at a time so it reuses _write_buffer. If you really need to make concurrent calls to
// writesome(), you'll need to prevent reusing _write_buffer here
struct check_buffer_in_use {
bool& _buffer_in_use;
check_buffer_in_use(bool& buffer_in_use) : _buffer_in_use(buffer_in_use) { assert(!_buffer_in_use); _buffer_in_use = true; }
~check_buffer_in_use() { assert(_buffer_in_use); _buffer_in_use = false; }
} buffer_in_use_checker(_write_buffer_in_use);
#endif
const std::size_t write_buffer_length = 4096;
if (!_write_buffer)
_write_buffer.reset(new char[write_buffer_length], [](char* p){ delete[] p; });
len = std::min<size_t>(write_buffer_length, len);
memset(_write_buffer.get(), 0, len); // just in case aes.encode screws up
/**
* every sizeof(crypt_buf) bytes the aes channel
* has an error and doesn't decrypt properly... disable
* for now because we are going to upgrade to something
* better.
*/
uint32_t ciphertext_len = _send_aes.encode( buffer, len, _write_buffer.get() );
assert(ciphertext_len == len);
_sock.write( _write_buffer, ciphertext_len );
return ciphertext_len;
} FC_RETHROW_EXCEPTIONS( warn, "", ("len",len) ) }
size_t stcp_socket::writesome( const std::shared_ptr<const char>& buf, size_t len, size_t offset )
{
return writesome(buf.get() + offset, len);
}
void stcp_socket::flush()
{
_sock.flush();
}
void stcp_socket::close()
{
try
{
_sock.close();
}FC_RETHROW_EXCEPTIONS( warn, "error closing stcp socket" );
}
void stcp_socket::accept()
{
do_key_exchange();
}
}} // namespace eos::net
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册