Merge commit '0053613b38e81719c198f36c748e181cc44635b2' into betting

This commit is contained in:
Eric Frias 2017-06-20 18:57:23 -04:00
commit 5a99abc3de
40 changed files with 642 additions and 1883 deletions

2
.gitignore vendored
View file

@ -8,6 +8,8 @@ Makefile
compile_commands.json
moc_*
*.moc
genesis.json
hardfork.hpp
libraries/utilities/git_revision.cpp

View file

@ -143,8 +143,6 @@ else( WIN32 ) # Apple AND Linux
endif( WIN32 )
find_package( BerkeleyDB )
set(ENABLE_COVERAGE_TESTING FALSE CACHE BOOL "Build BitShares for code coverage analysis")
if(ENABLE_COVERAGE_TESTING)

View file

@ -1,98 +0,0 @@
# Find the BerkeleyDB includes and library
# Customizable variables:
# BDB_ROOT_DIR
# This variable points to the BerkeleyDB root directory. On Windows the
# library location typically will have to be provided explicitly using the
# -D command-line option. Alternatively, the DBROOTDIR environment variable
# can be set.
#
# BDB_STATIC_LIBS
# Should be set to 1 if static version of libraries should be found. Defaults to 0 (shared libs).
#
# This module defines
# BDB_INCLUDE_DIR, where to find db.h, etc.
# BDB_LIBRARIES, the libraries needed to use BerkeleyDB.
IF (NOT DEFINED BDB_ROOT_DIR)
SET (BDB_ROOT_DIR $ENV{DBROOTDIR})
ENDIF()
MESSAGE (STATUS "Using ${BDB_ROOT_DIR} as BerkeleyDB root")
IF(NOT DEFINED BDB_STATIC_LIBS)
SET (BDB_STATIC_LIBS 0)
ENDIF()
FIND_PATH(BDB_INCLUDE_DIR NAMES db.h db_cxx.h
HINTS "${BDB_ROOT_DIR}/include"
PATHS ${BDB_ROOT_DIR}
/usr/include/libdb5
/usr/include/db5
/usr/include/libdb4
/usr/include/db4
/usr/local/include/libdb5
/usr/local/include/db5
/usr/local/include/libdb4
/usr/local/include/db4
PATH_SUFFIXES include
)
IF (WIN32)
IF(NOT DEFINED BDB_VERSION)
SET (DB_VERSION "60")
ENDIF ()
SET (BDB_LIB_BASENAME "libdb")
IF (${BDB_STATIC_LIBS} EQUAL 1)
SET (BDB_LIBS_SUFFIX_RELEASE "s")
SET (BDB_LIBS_SUFFIX_DEBUG "sD")
ELSE()
SET (BDB_LIBS_SUFFIX_RELEASE "")
SET (BDB_LIBS_SUFFIX_DEBUG "D")
ENDIF()
ELSE (WIN32)
IF(NOT DEFINED BDB_VERSION)
SET (DB_VERSION "-6.0")
ENDIF ()
# On unix library in all versions have the same names.
SET (BDB_LIBS_SUFFIX_RELEASE "")
SET (BDB_LIBS_SUFFIX_DEBUG "")
SET (BDB_LIB_BASENAME "db_cxx")
ENDIF (WIN32)
message (STATUS "Looking for: ${BDB_LIB_BASENAME}${DB_VERSION}${BDB_LIBS_SUFFIX_RELEASE}")
FIND_LIBRARY(BDB_LIBRARY_RELEASE "${BDB_LIB_BASENAME}${DB_VERSION}${BDB_LIBS_SUFFIX_RELEASE}" "${BDB_LIB_BASENAME}"
HINTS "${BDB_ROOT_DIR}/lib" PATHS ${BDB_ROOT_DIR} ${BDB_INCLUDE_DIR} "/usr/local/lib" PATH_SUFFIXES lib
)
FIND_LIBRARY(BDB_LIBRARY_DEBUG "${BDB_LIB_BASENAME}${DB_VERSION}${BDB_LIBS_SUFFIX_DEBUG}" "${BDB_LIB_BASENAME}"
HINTS "${BDB_ROOT_DIR}/lib" PATHS ${BDB_ROOT_DIR} ${BDB_INCLUDE_DIR} "/usr/local/lib" PATH_SUFFIXES lib
)
IF (BDB_LIBRARY_RELEASE AND BDB_LIBRARY_DEBUG )
SET (_BDB_LIBRARY
debug ${BDB_LIBRARY_DEBUG}
optimized ${BDB_LIBRARY_RELEASE}
)
ELSEIF(BDB_LIBRARY_RELEASE)
SET (_BDB_LIBRARY ${BDB_LIBRARY_RELEASE})
ELSEIF(BDB_LIBRARY_DEBUG)
SET (_BDB_LIBRARY ${BDB_LIBRARY_DEBUG})
ENDIF()
MESSAGE (STATUS ${_BDB_LIBRARY})
IF(_BDB_LIBRARY)
LIST (APPEND BDB_LIBRARIES ${_BDB_LIBRARY})
ENDIF()
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(BerkeleyDB
FOUND_VAR BerkeleyDB_FOUND
REQUIRED_VARS BDB_INCLUDE_DIR BDB_LIBRARIES
FAIL_MESSAGE "Could not find Berkeley DB >= 4.1" )

View file

@ -1,8 +0,0 @@
find_program(NPM_EXECUTABLE npm)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("NPM" DEFAULT_MSG NPM_EXECUTABLE)
find_program(LINEMAN_EXECUTABLE lineman)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("Lineman" DEFAULT_MSG LINEMAN_EXECUTABLE)

View file

@ -1,4 +0,0 @@
find_program(NODEJS_EXECUTABLE node)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("NodeJs" DEFAULT_MSG NODEJS_EXECUTABLE)

File diff suppressed because it is too large Load diff

View file

@ -3,9 +3,9 @@
Intro for new developers
------------------------
This is a quick introduction to get new developers up to speed on Graphene.
This is a quick introduction to get new developers up to speed on BitShares.
Starting Graphene
Starting BitShares
-----------------
For Ubuntu 14.04 LTS users, see this link first:
@ -13,10 +13,10 @@ For Ubuntu 14.04 LTS users, see this link first:
and then proceed with:
git clone https://github.com/cryptonomex/graphene.git
cd graphene
git clone https://github.com/bitshares/bitshares-2
cd bitshares-2
git submodule update --init --recursive
cmake -DCMAKE_BUILD_TYPE=Debug .
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .
make
./programs/witness_node/witness_node
@ -42,7 +42,7 @@ To import your initial balance:
If you send private keys over this connection, `rpc-endpoint` should be bound to localhost for security.
A list of CLI wallet commands is available
[here](https://github.com/cryptonomex/graphene/blob/master/libraries/wallet/include/graphene/wallet/wallet.hpp).
[here](https://github.com/bitshares/bitshares-2/blob/bitshares/libraries/wallet/include/graphene/wallet/wallet.hpp).
Code coverage testing
---------------------
@ -146,7 +146,7 @@ necessary to use the wallet:
]
}
Passwords are stored in `base64` as as salted `sha256` hashes. A simple Python script, `saltpass.py` is avaliable to obtain hash and salt values from a password.
Passwords are stored in `base64` as salted `sha256` hashes. A simple Python script, `saltpass.py` is avaliable to obtain hash and salt values from a password.
A single asterisk `"*"` may be specified as username or password hash to accept any value.
With the above configuration, here is an example of how to call `add_node` from the `network_node` API:
@ -209,7 +209,7 @@ Questions
The second number specifies the *type*. The type of the object determines what fields it has. For a
complete list of type ID's, see `enum object_type` and `enum impl_object_type` in
[types.hpp](https://github.com/cryptonomex/graphene/blob/master/libraries/chain/include/graphene/chain/protocol/types.hpp).
[types.hpp](https://github.com/bitshares/bitshares-2/blob/bitshares/libraries/chain/include/graphene/chain/protocol/types.hpp).
The third number specifies the *instance*. The instance of the object is different for each individual
object.
@ -239,4 +239,4 @@ Questions
less fine if your `witness_node` allows the general public to control which p2p nodes it's
connecting to. Therefore the API to add p2p connections needs to be set up with proper access
controls.

View file

@ -69,6 +69,6 @@ appenders=stderr
# route messages sent to the "p2p" logger to the p2p appender declared above
[logger.p2p]
level=debug
level=info
appenders=p2p

2
docs

@ -1 +1 @@
Subproject commit 51c3fccda7cb39656d27b94c5274b2aa11c7b598
Subproject commit 8d8b69d82482101279460fa02f814d0e4030966f

View file

@ -1 +1 @@
2.0.160829
2.0.161031

View file

@ -4,7 +4,6 @@ add_subdirectory( deterministic_openssl_rand )
add_subdirectory( chain )
add_subdirectory( egenesis )
add_subdirectory( net )
#add_subdirectory( p2p )
add_subdirectory( time )
add_subdirectory( utilities )
add_subdirectory( app )

View file

@ -162,23 +162,19 @@ namespace detail {
else
{
vector<string> seeds = {
"faucet.bitshares.org:1776",
"bitshares.openledger.info:1776",
"bts-seed1.abit-more.com:62015", // abit
"seed.blocktrades.us:1776",
"seed.bitsharesnodes.com:1776", // wackou
"seed04.bitsharesnodes.com:1776", // thom
"seed05.bitsharesnodes.com:1776", // thom
"seed06.bitsharesnodes.com:1776", // thom
"seed07.bitsharesnodes.com:1776", // thom
"seed.cubeconnex.com:1777", // cube
"54.85.252.77:39705", // lafona
"104.236.144.84:1777", // puppies
"40.127.190.171:1777", // betax
"185.25.22.21:1776", // liondani (greece)
"212.47.249.84:50696", // iHashFury (France)
"104.168.154.160:50696", // iHashFury (USA)
"128.199.143.47:2015" // Harvey
"104.236.144.84:1777", // puppies
"128.199.143.47:2015", // Harvey
"185.25.22.21:1776", // liondani (Greece)
"bitshares.openledger.info:1776", // OpenLedger
"bts-seed1.abit-more.com:62015", // abit
"seed.bitsharesnodes.com:1776", // wackou
"seed.blocktrades.us:1776", // BlockTrades
"seed.roelandp.nl:1776", // roelandp (Canada)
"seed02.bitsharesnodes.com:1776",
"seed04.bitsharesnodes.com:1776", // Thom
"seed05.bitsharesnodes.com:1776", // Thom
"seed06.bitsharesnodes.com:1776", // Thom
"seed07.bitsharesnodes.com:1776" // Thom
};
for( const string& endpoint_string : seeds )
{
@ -241,9 +237,7 @@ namespace detail {
if( !_options->count("rpc-endpoint") )
return;
bool enable_deflate_compression = _options->count("enable-permessage-deflate") != 0;
_websocket_server = std::make_shared<fc::http::websocket_server>(enable_deflate_compression);
_websocket_server = std::make_shared<fc::http::websocket_server>();
_websocket_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
@ -270,8 +264,7 @@ namespace detail {
}
string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as<string>() : "";
bool enable_deflate_compression = _options->count("enable-permessage-deflate") != 0;
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at("server-pem").as<string>(), password, enable_deflate_compression );
_websocket_tls_server = std::make_shared<fc::http::websocket_tls_server>( _options->at("server-pem").as<string>(), password );
_websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){
auto wsc = std::make_shared<fc::rpc::websocket_api_connection>(*c);
@ -530,7 +523,7 @@ namespace detail {
const auto& witness = blk_msg.block.witness(*_chain_db);
const auto& witness_account = witness.witness_account(*_chain_db);
auto last_irr = _chain_db->get_dynamic_global_properties().last_irreversible_block_num;
ilog("Got block: #${n} time: ${t} latency: ${l} ms from: ${w} irreversible: ${i} (-${d})",
ilog("Got block: #${n} time: ${t} latency: ${l} ms from: ${w} irreversible: ${i} (-${d})",
("t",blk_msg.block.timestamp)
("n", blk_msg.block.block_num())
("l", (latency.count()/1000))
@ -625,7 +618,7 @@ namespace detail {
result.reserve(limit);
block_id_type last_known_block_id;
if (blockchain_synopsis.empty() ||
(blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type()))
{
@ -686,13 +679,13 @@ namespace detail {
}
/**
* Returns a synopsis of the blockchain used for syncing. This consists of a list of
* Returns a synopsis of the blockchain used for syncing. This consists of a list of
* block hashes at intervals exponentially increasing towards the genesis block.
* When syncing to a peer, the peer uses this data to determine if we're on the same
* fork as they are, and if not, what blocks they need to send us to get us on their
* fork.
*
* In the over-simplified case, this is a straighforward synopsis of our current
* In the over-simplified case, this is a straighforward synopsis of our current
* preferred blockchain; when we first connect up to a peer, this is what we will be sending.
* It looks like this:
* If the blockchain is empty, it will return the empty list.
@ -708,7 +701,7 @@ namespace detail {
* the last item in the list will be the hash of the most recent block on our preferred chain
* so if the blockchain had 26 blocks labeled a - z, the synopsis would be:
* a n u x z
* the idea being that by sending a small (<30) number of block ids, we can summarize a huge
* the idea being that by sending a small (<30) number of block ids, we can summarize a huge
* blockchain. The block ids are more dense near the end of the chain where because we are
* more likely to be almost in sync when we first connect, and forks are likely to be short.
* If the peer we're syncing with in our example is on a fork that started at block 'v',
@ -723,27 +716,27 @@ namespace detail {
* no reason to fetch the blocks.
*
* Second, when a peer replies to our initial synopsis and gives us a list of the blocks they think
* we are missing, they only send a chunk of a few thousand blocks at once. After we get those
* we are missing, they only send a chunk of a few thousand blocks at once. After we get those
* block ids, we need to request more blocks by sending another synopsis (we can't just say "send me
* the next 2000 ids" because they may have switched forks themselves and they don't track what
* they've sent us). For faster performance, we want to get a fairly long list of block ids first,
* then start downloading the blocks.
* The peer doesn't handle these follow-up block id requests any different from the initial request;
* it treats the synopsis we send as our blockchain and bases its response entirely off that. So to
* get the response we want (the next chunk of block ids following the last one they sent us, or,
* get the response we want (the next chunk of block ids following the last one they sent us, or,
* failing that, the shortest fork off of the last list of block ids they sent), we need to construct
* a synopsis as if our blockchain was made up of:
* 1. the blocks in our block chain up to the fork point (if there is a fork) or the head block (if no fork)
* 2. the blocks we've already pushed from their fork (if there's a fork)
* 3. the block ids they've previously sent us
* Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
* Segment 3 is handled in the p2p code, it just tells us the number of blocks it has (in
* number_of_blocks_after_reference_point) so we can leave space in the synopsis for them.
* We're responsible for constructing the synopsis of Segments 1 and 2 from our active blockchain and
* fork database. The reference_point parameter is the last block from that peer that has been
* successfully pushed to the blockchain, so that tells us whether the peer is on a fork or on
* the main chain.
*/
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
virtual std::vector<item_hash_t> get_blockchain_synopsis(const item_hash_t& reference_point,
uint32_t number_of_blocks_after_reference_point) override
{ try {
std::vector<item_hash_t> synopsis;
@ -768,13 +761,13 @@ namespace detail {
if (reference_point_block_num < low_block_num)
{
// we're on the same fork (at least as far as reference_point) but we've passed
// reference point and could no longer undo that far if we diverged after that
// we're on the same fork (at least as far as reference_point) but we've passed
// reference point and could no longer undo that far if we diverged after that
// block. This should probably only happen due to a race condition where
// the network thread calls this function, and then immediately pushes a bunch of blocks,
// the network thread calls this function, and then immediately pushes a bunch of blocks,
// then the main thread finally processes this function.
// with the current framework, there's not much we can do to tell the network
// thread what our current head block is, so we'll just pretend that
// thread what our current head block is, so we'll just pretend that
// our head is actually the reference point.
// this *may* enable us to fetch blocks that we're unable to push, but that should
// be a rare case (and correctly handled)
@ -818,7 +811,7 @@ namespace detail {
if (non_fork_high_block_num < low_block_num)
{
wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago "
"(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
"(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})",
("low_block_num", low_block_num)
("non_fork_high_block_num", non_fork_high_block_num));
FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to");
@ -835,11 +828,11 @@ namespace detail {
}
// at this point:
// low_block_num is the block before the first block we can undo,
// low_block_num is the block before the first block we can undo,
// non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num)
// high_block_num is the block number of the reference block, or the end of the chain if no reference provided
// true_high_block_num is the ending block number after the network code appends any item ids it
// true_high_block_num is the ending block number after the network code appends any item ids it
// knows about that we don't
uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point;
do
@ -966,8 +959,6 @@ void application::set_program_options(boost::program_options::options_descriptio
("checkpoint,c", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
("rpc-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8090"), "Endpoint for websocket RPC to listen on")
("rpc-tls-endpoint", bpo::value<string>()->implicit_value("127.0.0.1:8089"), "Endpoint for TLS websocket RPC to listen on")
("enable-permessage-deflate", "Enable support for per-message deflate compression in the websocket servers "
"(--rpc-endpoint and --rpc-tls-endpoint), disabled by default")
("server-pem,p", bpo::value<string>()->implicit_value("server.pem"), "The TLS certificate file for this server")
("server-pem-password,P", bpo::value<string>()->implicit_value(""), "Password for this certificate")
("genesis-json", bpo::value<boost::filesystem::path>(), "File to read Genesis State from")

View file

@ -244,10 +244,6 @@ fc::variants database_api_impl::get_objects(const vector<object_id_type>& ids)co
this->subscribe_to_item( id );
}
}
else
{
elog( "getObjects without subscribe callback??" );
}
fc::variants result;
result.reserve(ids.size());

View file

@ -29,6 +29,8 @@
#include <graphene/chain/fba_accumulator_id.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_operation& o )

View file

@ -66,7 +66,7 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
_undo_db.disable();
for( uint32_t i = 1; i <= last_block_num; ++i )
{
if( i % 2000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "<<i << " of " <<last_block_num<<" \n";
if( i % 5000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "<<i << " of " <<last_block_num<<" \n";
fc::optional< signed_block > block = _block_id_to_block.fetch_by_number(i);
if( !block.valid() )
{

View file

@ -146,7 +146,7 @@ bool maybe_cull_small_order( database& db, const limit_order_object& order )
*/
if( order.amount_to_receive().amount == 0 )
{
ilog( "applied epsilon logic" );
//ilog( "applied epsilon logic" );
db.cancel_order(order);
return true;
}

View file

@ -35,6 +35,7 @@
#include <graphene/chain/protocol/market.hpp>
#include <fc/uint128.hpp>
#include <fc/smart_ref_impl.hpp>
namespace graphene { namespace chain {
void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_operation& op)

View file

@ -80,6 +80,8 @@ namespace graphene { namespace chain {
virtual const object* find( object_id_type id )const override
{
static_assert(std::is_same<typename MultiIndexType::key_type, object_id_type>::value,
"First index of MultiIndexType MUST be object_id_type!");
auto itr = _indices.find( id );
if( itr == _indices.end() ) return nullptr;
return &*itr;

View file

@ -165,9 +165,10 @@ namespace graphene { namespace db {
void on_modify( const object& obj );
template<typename T>
void add_secondary_index()
T* add_secondary_index()
{
_sindex.emplace_back( new T() );
return static_cast<T*>(_sindex.back().get());
}
template<typename T>

View file

@ -979,6 +979,7 @@ namespace graphene { namespace net { namespace detail {
{
elog("${e}", ("e", e));
}
FC_CAPTURE_AND_LOG( () )
}// while(!canceled)
}
@ -4192,7 +4193,7 @@ namespace graphene { namespace net { namespace detail {
// limit the rate at which we accept connections to mitigate DOS attacks
fc::usleep( fc::milliseconds(10) );
} FC_CAPTURE_AND_RETHROW()
} FC_CAPTURE_AND_LOG( () )
}
} // accept_loop()

View file

@ -1,33 +0,0 @@
file(GLOB HEADERS "include/graphene/p2p/*.hpp")
set(SOURCES node.cpp
stcp_socket.cpp
peer_connection.cpp
message_oriented_connection.cpp)
add_library( graphene_p2p ${SOURCES} ${HEADERS} )
target_link_libraries( graphene_p2p
PUBLIC fc graphene_db )
target_include_directories( graphene_p2p
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(graphene_p2p PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
# cotire(graphene_p2p )
#endif(USE_PCH)
install( TARGETS
graphene_p2p
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
install( FILES ${HEADERS} DESTINATION "include/graphene/p2p" )

View file

@ -1,96 +0,0 @@
# Network Protocol 2
Building a low-latency network requires P2P nodes that have low-latency
connections and a protocol designed to minimize latency. for the purpose
of this document we will assume that two nodes are located on opposite
sides of the globe with a ping time of 250ms.
## Announce, Request, Send Protocol
Under the prior network archtiecture, transactions and blocks were broadcast
in a manner similar to the Bitcoin protocol: inventory messages notify peers of
transactions and blocks, then peers fetch the transaction or block from one
peer. After validating the item a node will broadcast an inventory message to
its peers.
Under this model it will take 0.75 seconds for a peer to communicate a transaction
or block to another peer even if their size was 0 and there was no processing overhead.
This level of performance is unacceptable for a network attempting to produce one block
every second.
This prior protocol also sent every transaction twice: initial broadcast, and again as
part of a block.
## Push Protocol
To minimize latency each node needs to immediately broadcast the data it receives
to its peers after validating it. Given the average transaction size is less than
100 bytes, it is almost as effecient to send the transaction as it is to send
the notice (assuming a 20 byte transaction id)
Each node implements the following protocol:
onReceiveTransaction( from_peer, transaction )
if( isKnown( transaction.id() ) )
return
markKnown( transaction.id() )
if( !validate( transaction ) )
return
for( peer : peers )
if( peer != from_peer )
send( peer, transaction )
onReceiveBlock( from_peer, block_summary )
if( isKnown( block_summary )
return
full_block = reconstructFullBlcok( from_peer, block_summary )
if( !full_block ) disconnect from_peer
markKnown( block_summary )
if( !pushBlock( full_block ) ) disconnect from_peer
for( peer : peers )
if( peer != from_peer )
send( peer, block_summary )
onHello( new_peer, new_peer_head_block_num )
replyHello( new_peer ) // ack the hello message with our timestamp to measure latency
if( peers.size() >= max_peers )
send( new_peer, peers )
disconnect( new_peer )
return
while( new_peer_head_block_num < our_head_block_num )
sendFullBlock( new_peer, ++new_peer_head_block_num )
new_peer.synced = true
for( peer : peers )
send( peer, new_peer )
onHelloReply( from_peer, hello_reply )
update_latency_measure, disconnect if too slow
onReceivePeers( from_peer, peers )
addToPotentialPeers( peers )
onUpdateConnectionsTimer
if( peers.size() < desired_peers )
connect( random_potential_peer )
onFullBlock( from_peer, full_block )
if( !pushBlock( full_block ) ) disconnect from_peer
onStartup
init_potential_peers from config
start onUpdateConnectionsTimer

View file

@ -1,183 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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/ripemd160.hpp>
#include <fc/reflect/variant.hpp>
namespace graphene { namespace p2p {
using namespace graphene::chain;
struct message_header
{
uint32_t size; // number of bytes in message, capped at MAX_MESSAGE_SIZE
uint32_t msg_type;
};
typedef fc::uint160_t 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::uint160_t id()const
{
return fc::ripemd160::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)
);
}
};
enum core_message_type_enum {
hello_message_type = 1000,
transaction_message_type = 1001,
block_message_type = 1002,
peer_message_type = 1003,
error_message_type = 1004
};
struct hello_message
{
static const core_message_type_enum type;
std::string user_agent;
uint16_t version;
fc::time_point timestamp;
fc::ip::address inbound_address;
uint16_t inbound_port;
uint16_t outbound_port;
public_key_type node_public_key;
fc::sha256 chain_id;
fc::variant_object user_data;
block_id_type head_block;
};
struct hello_reply_message
{
static const core_message_type_enum type;
fc::time_point hello_timestamp;
fc::time_point reply_timestamp;
};
struct transaction_message
{
static const core_message_type_enum type;
signed_transaction trx;
};
struct block_summary_message
{
static const core_message_type_enum type;
signed_block_header header;
vector<transaction_id_type> transaction_ids;
};
struct full_block_message
{
static const core_message_type_enum type;
signed_block block;
};
struct peers_message
{
static const core_message_type_enum type;
vector<fc::ip::endpoint> peers;
};
struct error_message
{
static const core_message_type_enum type;
string message;
};
} } // graphene::p2p
FC_REFLECT( graphene::p2p::message_header, (size)(msg_type) )
FC_REFLECT_DERIVED( graphene::p2p::message, (graphene::p2p::message_header), (data) )
FC_REFLECT_ENUM( graphene::p2p::core_message_type_enum,
(hello_message_type)
(transaction_message_type)
(block_message_type)
(peer_message_type)
(error_message_type)
)

View file

@ -1,71 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 <graphene/p2p/message.hpp>
namespace graphene { namespace p2p {
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::p2p::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;
} } // graphene::net

View file

@ -1,96 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 <graphene/chain/database.hpp>
#include <graphene/p2p/peer_connection.hpp>
namespace graphene { namespace p2p {
using namespace graphene::chain;
struct node_config
{
fc::ip::endpoint server_endpoint;
bool wait_if_not_available = true;
uint32_t desired_peers;
uint32_t max_peers;
/** receive, but don't rebroadcast data */
bool subscribe_only = false;
public_key_type node_id;
vector<fc::ip::endpoint> seed_nodes;
};
struct by_remote_endpoint;
struct by_peer_id;
/**
* @ingroup object_index
*/
typedef multi_index_container<
peer_connection_ptr,
indexed_by<
ordered_unique< tag<by_remote_endpoint>,
const_mem_fun< peer_connection, fc::ip::endpoint, &peer_connection::get_remote_endpoint > >,
ordered_unique< tag<by_peer_id>, member< peer_connection, public_key_type, &peer_connection::node_id > >
>
> peer_connection_index;
class node : public std::enable_shared_from_this<node>
{
public:
server( chain_database& db );
void add_peer( const fc::ip::endpoint& ep );
void configure( const node_config& cfg );
void on_incomming_connection( peer_connection_ptr new_peer );
void on_hello( peer_connection_ptr new_peer, hello_message m );
void on_transaction( peer_connection_ptr from_peer, transaction_message m );
void on_block( peer_connection_ptr from_peer, block_message m );
void on_peers( peer_connection_ptr from_peer, peers_message m );
void on_error( peer_connection_ptr from_peer, error_message m );
void on_full_block( peer_connection_ptr from_peer, full_block_message m );
void on_update_connections();
private:
/**
* Specifies the network interface and port upon which incoming
* connections should be accepted.
*/
void listen_on_endpoint( fc::ip::endpoint ep, bool wait_if_not_available );
void accept_loop();
graphene::chain::database& _db;
fc::tcp_server _tcp_server;
fc::ip::endpoint _actual_listening_endpoint;
fc::future<void> _accept_loop_complete;
peer_connection_index _peers;
};
} } /// graphene::p2p

View file

@ -1,195 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 <graphene/p2p/node.hpp>
#include <graphene/p2p/message.hpp>
#include <graphene/p2p/message_oriented_connection.hpp>
#include <graphene/p2p/stcp_socket.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 graphene { namespace p2p {
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;
};
class peer_connection;
typedef std::shared_ptr<peer_connection> peer_connection_ptr;
/**
* Each connection maintains its own queue of messages to be sent, when an item
* is first pushed to the queue it starts an async fiber that will sequentially write
* all items until there is nothing left to be sent.
*
* If a particular connection is unable to keep up with the real-time stream of
* messages to be sent then it will be disconnected. The backlog will be measured in
* seconds.
*
* A multi-index container that tracks the
*/
class peer_connection : public message_oriented_connection_delegate,
public std::enable_shared_from_this<peer_connection>
{
public:
enum direction_type { inbound, outbound };
enum connection_state {
connecting = 0,
syncing = 1,
synced = 2
};
fc::time_point connection_initiation_time;
fc::time_point connection_closed_time;
fc::time_point connection_terminated_time;
direction_type direction = outbound;
connection_state state = connecting;
bool is_firewalled = true
//connection_state state;
fc::microseconds clock_offset;
fc::microseconds round_trip_delay;
/// data about the peer node
/// @{
/** 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 */
public_key_type node_id;
uint32_t core_protocol_version;
std::string user_agent;
fc::optional<std::string> graphene_git_revision_sha;
fc::optional<fc::time_point_sec> graphene_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;
/// @}
void send( transaction_message_ptr msg )
{
// if not in sent_or_received then insert into _pending_send
// if process_send_queue is invalid or complete then
// async process_send_queue
}
void received_transaction( const transaction_id_type& id )
{
_sent_or_received.insert(id);
}
void process_send_queue()
{
// while _pending_send.size() || _pending_blocks.size()
// while there are pending blocks, then take the oldest
// for each transaction id, verify that it exists in _sent_or_received
// else find it in the _pending_send queue and send it
// send one from _pending_send
}
std::unordered_map<transaction_id_type, transaction_message_ptr> _pending_send;
/// todo: make multi-index that tracks how long items have been cached and removes them
/// after a resasonable period of time (say 10 seconds)
std::unordered_set<transaction_id_type> _sent_or_received;
std::map<uint32_t,block_message_ptr> _pending_blocks;
fc::ip::endpoint get_remote_endpoint()const
{ return get_socket().get_remote_endpoint(); }
void on_message(message_oriented_connection* originating_connection,
const message& received_message) override
{
switch( core_message_type_enum( received_message.type ) )
{
case hello_message_type:
_node->on_hello( shared_from_this(),
received_message.as<hello_message>() );
break;
case transaction_message_type:
_node->on_transaction( shared_from_this(),
received_message.as<transaction_message>() );
break;
case block_message_type:
_node->on_block( shared_from_this(),
received_message.as<block_message>() );
break;
case peer_message_type:
_node->on_peers( shared_from_this(),
received_message.as<peers_message>() );
break;
}
}
void on_connection_closed(message_oriented_connection* originating_connection) override
{
_node->on_close( shared_from_this() );
}
fc::tcp_socket& get_socket() { return _message_connection.get_socket(); }
private:
peer_connection_delegate* _node;
fc::optional<fc::ip::endpoint> _remote_endpoint;
message_oriented_connection _message_connection;
};
typedef std::shared_ptr<peer_connection> peer_connection_ptr;
} } // end namespace graphene::p2p
// not sent over the wire, just reflected for logging
FC_REFLECT_ENUM(graphene::p2p::peer_connection::connection_state, (connecting)(syncing)(synced) )
FC_REFLECT_ENUM(graphene::p2p::peer_connection::direction_type, (inbound)(outbound) )

View file

@ -1,79 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 graphene { namespace p2p {
/**
* 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;
} } // graphene::p2p

View file

@ -1,412 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 <graphene/p2p/message_oriented_connection.hpp>
#include <graphene/p2p/stcp_socket.hpp>
#include <graphene/p2p/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 graphene { namespace p2p {
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 graphene::p2p::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 graphene::p2p

View file

@ -1,164 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 <graphene/p2p/node.hpp>
namespace graphene { namespace p2p {
node::node( chain_database& db )
:_db(db)
{
}
node::~node()
{
}
void node::add_peer( const fc::ip::endpoint& ep )
{
}
void node::configure( const node_config& cfg )
{
listen_on_endpoint( cfg.server_endpoint, wait_if_not_available );
/** don't allow node to go out of scope until accept loop exits */
auto self = shared_from_this();
_accept_loop_complete = fc::async( [self](){ self->accept_loop(); } )
}
void node::accept_loop()
{
auto self = shared_from_this();
while( !_accept_loop_complete.canceled() )
{
try {
auto new_peer = std::make_shared<peer_connection>(self);
_tcp_server.accept( new_peer.get_socket() );
if( _accept_loop_complete.canceled() )
return;
_peers.insert( new_peer );
// limit the rate at which we accept connections to mitigate DOS attacks
fc::usleep( fc::milliseconds(10) );
} FC_CAPTURE_AND_RETHROW()
}
} // accept_loop()
void node::listen_on_endpoint( fc::ip::endpoint ep, bool wait_if_not_available )
{
if( ep.port() != 0 )
{
// if the user specified a port, we only want to bind to it if it's not already
// being used by another application. During normal operation, we set the
// SO_REUSEADDR/SO_REUSEPORT flags so that we can bind outbound sockets to the
// same local endpoint as we're listening on here. On some platforms, setting
// those flags will prevent us from detecting that other applications are
// listening on that port. We'd like to detect that, so we'll set up a temporary
// tcp server without that flag to see if we can listen on that port.
bool first = true;
for( ;; )
{
bool listen_failed = false;
try
{
fc::tcp_server temporary_server;
if( listen_endpoint.get_address() != fc::ip::address() )
temporary_server.listen( ep );
else
temporary_server.listen( ep.port() );
break;
}
catch ( const fc::exception&)
{
listen_failed = true;
}
if (listen_failed)
{
if( wait_if_endpoint_is_busy )
{
std::ostringstream error_message_stream;
if( first )
{
error_message_stream << "Unable to listen for connections on port "
<< ep.port()
<< ", retrying in a few seconds\n";
error_message_stream << "You can wait for it to become available, or restart "
"this program using\n";
error_message_stream << "the --p2p-port option to specify another port\n";
first = false;
}
else
{
error_message_stream << "\nStill waiting for port " << listen_endpoint.port() << " to become available\n";
}
std::string error_message = error_message_stream.str();
ulog(error_message);
fc::usleep( fc::seconds(5 ) );
}
else // don't wait, just find a random port
{
wlog( "unable to bind on the requested endpoint ${endpoint}, "
"which probably means that endpoint is already in use",
( "endpoint", ep ) );
ep.set_port( 0 );
}
} // if (listen_failed)
} // for(;;)
} // if (listen_endpoint.port() != 0)
_tcp_server.set_reuse_address();
try
{
if( ep.get_address() != fc::ip::address() )
_tcp_server.listen( ep );
else
_tcp_server.listen( ep.port() );
_actual_listening_endpoint = _tcp_server.get_local_endpoint();
ilog( "listening for connections on endpoint ${endpoint} (our first choice)",
( "endpoint", _actual_listening_endpoint ) );
}
catch ( fc::exception& e )
{
FC_RETHROW_EXCEPTION( e, error,
"unable to listen on ${endpoint}", ("endpoint",listen_endpoint ) );
}
}
} }

View file

@ -1,30 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 <graphene/p2p/peer_connection.hpp>
namespace graphene { namespace p2p {
} } //graphene::p2p

View file

@ -1,187 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* 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 <graphene/p2p/stcp_socket.hpp>
namespace graphene { namespace p2p {
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 graphene::p2p

View file

@ -19,3 +19,5 @@ install( TARGETS
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/account_history" )

View file

@ -2,6 +2,7 @@
#include <fc/filesystem.hpp>
#include <fc/optional.hpp>
#include <fc/variant_object.hpp>
#include <fc/smart_ref_impl.hpp>
#include <graphene/app/application.hpp>

View file

@ -19,3 +19,5 @@ install( TARGETS
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/market_history" )

View file

@ -191,10 +191,10 @@ block_production_condition::block_production_condition_enum witness_plugin::bloc
ilog("Not producing block because production is disabled until we receive a recent block (see: --enable-stale-production)");
break;
case block_production_condition::not_my_turn:
ilog("Not producing block because it isn't my turn");
//ilog("Not producing block because it isn't my turn");
break;
case block_production_condition::not_time_yet:
ilog("Not producing block because slot has not yet arrived");
//ilog("Not producing block because slot has not yet arrived");
break;
case block_production_condition::no_private_key:
ilog("Not producing block because I don't have the private key for ${scheduled_key}", (capture) );

View file

@ -7,14 +7,14 @@ if(NOT GRAPHENE_GIT_REVISION_DESCRIPTION)
set(GRAPHENE_GIT_REVISION_DESCRIPTION "unknown")
endif(NOT GRAPHENE_GIT_REVISION_DESCRIPTION)
file(GLOB headers "include/graphene/utilities/*.hpp")
file(GLOB HEADERS "include/graphene/utilities/*.hpp")
set(sources
key_conversion.cpp
string_escape.cpp
tempdir.cpp
words.cpp
${headers})
${HEADERS})
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/git_revision.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp" @ONLY)
list(APPEND sources "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp")

View file

@ -0,0 +1,104 @@
Introduction
------------
The `debug_node` is a tool to allow developers to run many interesting sorts of "what-if" tests using state from a production blockchain.
Like "what happens if I produce enough blocks for the next hardfork time to arrive?" or "what would happen if this account (which I don't have a private key for) did this transaction?"
Setup
-----
Be sure you've built the right build targets:
$ make get_dev_key debug_node cli_wallet witness_node
Use the `get_dev_key` utility to generate a keypair:
$ programs/genesis_util/get_dev_key "" nathan
[{"private_key":"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3","public_key":"BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV","address":"BTSFAbAx7yuxt725qSZvfwWqkdCwp9ZnUama"}]
Obtain a copy of the blockchain in `block_db` directory:
$ programs/witness_node/witness_node --data-dir data/mydatadir
# ... wait for chain to sync
^C
$ cp -Rp data/mydatadir/blockchain/database/block_num_to_block ./block_db
Set up a new datadir with the following `config.ini` settings:
# setup API endpoint
rpc-endpoint = 127.0.0.1:8090
# setting this to empty effectively disables the p2p network
seed-nodes = []
# set apiaccess.json so we can set up
api-access = "data/debug_datadir/api-access.json"
Then set up `data/debug_datadir/api-access.json` to allow access to the debug API like this:
{
"permission_map" :
[
[
"bytemaster",
{
"password_hash_b64" : "9e9GF7ooXVb9k4BoSfNIPTelXeGOZ5DrgOYMj94elaY=",
"password_salt_b64" : "INDdM6iCi/8=",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api", "network_node_api", "debug_api"]
}
],
[
"*",
{
"password_hash_b64" : "*",
"password_salt_b64" : "*",
"allowed_apis" : ["database_api", "network_broadcast_api", "history_api"]
}
]
]
}
See [here](https://github.com/cryptonomex/graphene#accessing-restricted-apis) for more detail on the `api-access.json` format.
Once that is set up, run `debug_node` against your newly prepared datadir:
programs/debug_node/debug_node --data-dir data/debug_datadir
Run `cli_wallet` to connect to the `debug_node` port, using the username and password to access the new `debug_api` (and also a different wallet file):
programs/cli_wallet/cli_wallet -s 127.0.0.1:8090 -w debug.wallet -u bytemaster -p supersecret
Example usage
-------------
Load some blocks from the datadir:
dbg_push_blocks block_db 20000
Note, when pushing a very large number of blocks sometimes `cli_wallet` hangs and you must Ctrl+C and restart it (leaving the `debug_node` running).
Generate (fake) blocks with our own private key:
dbg_generate_blocks 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 1000
Update `angel` account to be controlled by our own private key and generate a (fake) transfer:
dbg_update_object {"_action":"update", "id":"1.2.1090", "active":{"weight_threshold":1,"key_auths":[["BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",1]]}}
import_key angel 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
transfer angel init0 999999 BTS "" true
How it works
------------
The commands work by creating diff(s) from the main chain that are applied to the local chain at specified block height(s). It lets you easily check out "what-if"
scenarios in a fantasy debug toy world forked from the real chain, e.g. "if we take all of the blocks until today, then generate a bunch more until a hardfork time
in the future arrives, does the chain stay up? Can I do transactions X, Y, and Z in the wallet after the hardfork?" Anyone connecting to this node sees the same
fantasy world, so you can e.g. make changes with the `cli_wallet` and see them exist in other `cli_wallet` instances (or GUI wallets or API scripts).
Limitations
-----------
The main limitations are:
- No export format for the diffs, so you can't really [1] connect multiple `debug_node` to each other.
- Once faked block(s) or tx(s) have been produced on your chain, you can't really [1] stream blocks or tx's from the main network to your chain.
[1] It should theoretically be possible, but it's non-trivial and totally untested.

View file

@ -222,7 +222,7 @@ void write_default_logging_config_to_stream(std::ostream& out)
"appenders=stderr\n\n"
"# route messages sent to the \"p2p\" logger to the p2p appender declared above\n"
"[logger.p2p]\n"
"level=debug\n"
"level=info\n"
"appenders=p2p\n\n";
}

View file

@ -207,7 +207,7 @@ void write_default_logging_config_to_stream(std::ostream& out)
"appenders=stderr\n\n"
"# route messages sent to the \"p2p\" logger to the p2p appender declared above\n"
"[logger.p2p]\n"
"level=debug\n"
"level=info\n"
"appenders=p2p\n\n";
}

View file

@ -222,7 +222,7 @@ void write_default_logging_config_to_stream(std::ostream& out)
"appenders=stderr\n\n"
"# route messages sent to the \"p2p\" logger to the p2p appender declared above\n"
"[logger.p2p]\n"
"level=debug\n"
"level=info\n"
"appenders=p2p\n\n";
}