diff --git a/README.md b/README.md index 7591a04073c..f73d0b72074 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ Welcome to the EOS.IO source code repository! EOS.IO software enables developers to create and deploy high-performance, horizontally scalable, blockchain infrastructure upon which decentralized applications -can be built. +can be built. This code is currently alpha-quality and under rapid development. That said, there is plenty early experimenters can do including, running a private multi-node test network and -develop applications (smart contracts). +develop applications (smart contracts). # Resources 1. [EOS.IO Website](https://eos.io) @@ -59,7 +59,7 @@ For Ubuntu 16.10 and MacOS Sierra, there is an automated build script that can i It is called build.sh with following inputs. - architecture [ubuntu|darwin] -- optional mode [full|build] +- optional mode [full|build] The second optional input can be full or build where full implies that it installs dependencies and builds eos. If you omit this input then build script always installs dependencies and then builds eos. @@ -69,13 +69,13 @@ The second optional input can be full or build where full implies that it instal Clone EOS repository recursively as below and run build.sh located in root `eos` folder. -#### Clean install Ubuntu 16.10 +#### Clean install Ubuntu 16.10 ```bash git clone https://github.com/eosio/eos --recursive cd eos -./build.sh ubuntu +./build.sh ubuntu ``` Now you can proceed to the next step - [Creating and launching a single-node testnet](#singlenode) @@ -100,10 +100,10 @@ cd eos Now you can proceed to the next step - [Creating and launching a single-node testnet](#singlenode) -## Building EOS and running a node +## Building EOS and running a node -### Getting the code +### Getting the code To download all of the code, download EOS source code and a recursion or two of submodules. The easiest way to get all of this is to do a recursive clone: @@ -118,7 +118,7 @@ git submodule update --init --recursive ``` -### Building from source code +### Building from source code The *WASM_LLVM_CONFIG* environment variable is used to find our recently built WASM compiler. This is needed to compile the example contracts inside `eos/contracts` folder and their respective tests. @@ -147,7 +147,7 @@ EOS comes with a number of programs you can find in `~/eos/build/programs`. They * launcher - application for nodes network composing and deployment; [more on launcher](https://github.com/EOSIO/eos/blob/master/testnet.md) -### Creating and launching a single-node testnet +### Creating and launching a single-node testnet After successfully building the project, the `eosd` binary should be present in the `build/programs/eosd` directory. Go ahead and run `eosd` -- it will probably exit with an error, but if not, close it immediately with Ctrl-C. Note that `eosd` created a directory named `data-dir` containing the default configuration (`config.ini`) and some other internals. This default data storage path can be overridden by passing `--data-dir /path/to/data` to `eosd`. @@ -204,7 +204,7 @@ When running `eosd` you should get log messages similar to below. It means the b ## Example "Currency" Contract Walkthrough -EOS comes with example contracts that can be uploaded and run for testing purposes. Next we demonstrate how to upload and interact with the sample contract "currency". +EOS comes with example contracts that can be uploaded and run for testing purposes. Next we demonstrate how to upload and interact with the sample contract "currency". ### Example smart contracts @@ -217,7 +217,7 @@ cd ~/eos/build/programs/eosd/ ``` -### Setting up a wallet and importing account key +### Setting up a wallet and importing account key As you've previously added `plugin = eosio::wallet_api_plugin` into `config.ini`, EOS wallet will be running as a part of `eosd` process. Every contract requires an associated account, so first, create a wallet. @@ -226,7 +226,7 @@ cd ~/eos/build/programs/eosc/ ./eosc wallet create # Outputs a password that you need to save to be able to lock/unlock the wallet ``` -For the purpose of this walkthrough, import the private key of the `inita` account, a test account included within genesis.json, so that you're able to issue API commands under authority of an existing account. The private key referenced below is found within your `config.ini` and is provided to you for testing purposes. +For the purpose of this walkthrough, import the private key of the `inita` account, a test account included within genesis.json, so that you're able to issue API commands under authority of an existing account. The private key referenced below is found within your `config.ini` and is provided to you for testing purposes. ```bash ./eosc wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 @@ -256,7 +256,7 @@ Save the values for future reference. Run the `create` command where `inita` is the account authorizing the creation of the `currency` account and `PUBLIC_KEY_1` and `PUBLIC_KEY_2` are the values generated by the `create key` command ```bash -./eosc create account inita currency PUBLIC_KEY_1 PUBLIC_KEY_2 +./eosc create account inita currency PUBLIC_KEY_1 PUBLIC_KEY_2 ``` You should then get a json response back with a transaction ID confirming it was executed successfully. @@ -290,12 +290,12 @@ Now import the active private key generated previously in the wallet: ``` -### Upload sample "currency" contract to blockchain +### Upload sample "currency" contract to blockchain Before uploading a contract, verify that there is no current contract: ```bash -./eosc get code currency +./eosc get code currency code hash: 0000000000000000000000000000000000000000000000000000000000000000 ``` @@ -333,11 +333,11 @@ Next verify the currency contract has the proper initial balance: ``` -### Transfering funds with the sample "currency" contract +### Transfering funds with the sample "currency" contract Anyone can send any message to any contract at any time, but the contracts may reject messages which are not given necessary permission. Messages are not sent "from" anyone, they are sent "with permission of" one or more accounts and permission levels. The following commands shows a "transfer" message being -sent to the "currency" contract. +sent to the "currency" contract. The content of the message is `'{"from":"currency","to":"inita","quantity":50}'`. In this case we are asking the currency contract to transfer funds from itself to someone else. This requires the permission of the currency contract. @@ -361,14 +361,14 @@ As a confirmation of a successfully submitted transaction you will receive json ### Reading sample "currency" contract balance -So now check the state of both of the accounts involved in the previous transaction. +So now check the state of both of the accounts involved in the previous transaction. ```bash ./eosc get table inita currency account { "rows": [{ "key": "account", - "balance": 50 + "balance": 50 } ], "more": false @@ -384,10 +384,10 @@ So now check the state of both of the accounts involved in the previous transact } ``` -As expected, the receiving account **inita** now has a balance of **50** tokens, and the sending account now has **50** less tokens than its initial supply. +As expected, the receiving account **inita** now has a balance of **50** tokens, and the sending account now has **50** less tokens than its initial supply. -## Running multi-node local testnet +## Running multi-node local testnet To run a local testnet you can use a `launcher` application provided in `~/eos/build/programs/launcher` folder. @@ -404,9 +404,6 @@ This command will generate 2 data folders for each instance of the node: `tn_dat You should see the following response: ```bash -adding hostname ip-XXX-XXX-XXX -found interface 127.0.0.1 -found interface XXX.XX.XX.XX spawning child, programs/eosd/eosd --skip-transaction-signatures --data-dir tn_data_0 spawning child, programs/eosd/eosd --skip-transaction-signatures --data-dir tn_data_1 ``` @@ -468,12 +465,12 @@ This eosd instance listens on 127.0.0.1:8888 for http requests, on all interface for p2p requests, and includes the wallet plugins. -## Doxygen documentation +## Doxygen documentation You can find more detailed API documentation in Doxygen reference: https://eosio.github.io/eos/ -## Running EOS in Docker +## Running EOS in Docker You can find up to date information about EOS Docker in the [Docker Readme](https://github.com/EOSIO/eos/blob/master/Docker/README.md) @@ -497,7 +494,7 @@ Dependencies: * [binaryen](https://github.com/WebAssembly/binaryen.git) -### Clean install Ubuntu 16.10 +### Clean install Ubuntu 16.10 Install the development toolkit: @@ -526,7 +523,7 @@ source ~/.bash_profile ``` Install [secp256k1-zkp (Cryptonomex branch)](https://github.com/cryptonomex/secp256k1-zkp.git): - + ```bash cd ~ git clone https://github.com/cryptonomex/secp256k1-zkp.git @@ -570,10 +567,10 @@ cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=.. -DLLVM_TARGETS_TO_BUILD= -DL make -j4 install ``` -Your environment is set up. Now you can build EOS and run a node. +Your environment is set up. Now you can build EOS and run a node. -### MacOS Sierra 10.12.6 +### MacOS Sierra 10.12.6 macOS additional Dependencies: @@ -601,7 +598,7 @@ brew link gettext --force ``` Install [secp256k1-zkp (Cryptonomex branch)](https://github.com/cryptonomex/secp256k1-zkp.git): - + ```bash cd ~ git clone https://github.com/cryptonomex/secp256k1-zkp.git diff --git a/plugins/net_plugin/include/eos/net_plugin/protocol.hpp b/plugins/net_plugin/include/eos/net_plugin/protocol.hpp index 36d4a98a65b..0049fdb2b8c 100644 --- a/plugins/net_plugin/include/eos/net_plugin/protocol.hpp +++ b/plugins/net_plugin/include/eos/net_plugin/protocol.hpp @@ -43,7 +43,8 @@ namespace eosio { bad_transaction, ///< the peer sent a transaction that failed verification validation, ///< the peer sent a block that failed validation benign_other, ///< reasons such as a timeout. not fatal but warrant resetting - fatal_other ///< a catch-all for errors we don't have discriminated + fatal_other, ///< a catch-all for errors we don't have discriminated + authentication ///< peer failed authenicatio }; constexpr auto reason_str( go_away_reason rsn ) { @@ -57,6 +58,7 @@ namespace eosio { case unlinkable : return "unlinkable block received"; case bad_transaction : return "bad transaction"; case validation : return "invalid block"; + case authentication : return "authentication failure"; case fatal_other : return "some other failure"; case benign_other : return "some other non-fatal condition"; default : return "some crazy reason"; diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 1c9ba09e45a..8fee0c55ad0 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -176,7 +176,6 @@ namespace eosio { int started_sessions = 0; node_transaction_index local_txns; - notice_message pending_notify; shared_ptr resolver; @@ -286,7 +285,7 @@ namespace eosio { constexpr auto def_txn_expire_wait = std::chrono::seconds(3); constexpr auto def_resp_expected_wait = std::chrono::seconds(1); constexpr auto def_sync_fetch_span = 100; - constexpr auto def_max_just_send = 1300 * 3; // "mtu" * 3 + constexpr auto def_max_just_send = 1500 * 3; // "mtu" * 3 constexpr auto def_send_whole_blocks = true; constexpr auto message_header_size = 4; @@ -1190,17 +1189,17 @@ namespace eosio { if( end > sync_known_lib_num ) end = sync_known_lib_num; if( end > 0 && end >= start ) { - ilog("conn ${n} recv blks ${s} to ${e}",("n",source->peer_name() )("s",start)("e",end)); + fc_dlog(logger,"conn ${n} recv blks ${s} to ${e}",("n",source->peer_name() )("s",start)("e",end)); source->sync_receiving.reset(new sync_state( start, end, sync_last_requested_num ) ); } } else { - ilog("conn ${n} resetting sync recv",("n",source->peer_name() )); + fc_dlog(logger, "conn ${n} resetting sync recv",("n",source->peer_name() )); source->sync_receiving.reset( ); } if(source->sync_receiving && source->sync_receiving->start_block == head_block + 1) { - ilog("conn ${n} requesting range ${rs} to ${re} should be ${s} to ${e}", + fc_dlog(logger, "conn ${n} requesting range ${rs} to ${re} should be ${s} to ${e}", ("n",source->peer_name())("rs",source->sync_receiving->start_block)("re",source->sync_receiving->end_block)("s",start)("e",end)); source->enqueue( (sync_request_message){source->sync_receiving->start_block, source->sync_receiving->end_block}); @@ -1210,7 +1209,7 @@ namespace eosio { void sync_manager::take_chunk( connection_ptr c) { if( !c->sync_receiving) { - elog( "take_chunk called, but sync_receiving is empty"); + fc_dlog(logger, "take_chunk called, but sync_receiving is empty"); return; } sync_state_ptr ss; @@ -1271,24 +1270,32 @@ namespace eosio { fc_dlog( logger, "Skipping connect due to go_away reason ${r}",("r", reason_str( c->no_retry ))); return; } - auto host = c->peer_addr.substr( 0, c->peer_addr.find(':') ); - auto port = c->peer_addr.substr( host.size()+1, host.size() ); - idump((host)(port)); - tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str() ); - // Note: need to add support for IPv6 too - resolver->async_resolve( query, - [c, this]( const boost::system::error_code& err, - tcp::resolver::iterator endpoint_itr ){ - if( !err ) { - connect( c, endpoint_itr ); - } else { - elog( "Unable to resolve ${peer_addr}: ${error}", - ( "peer_addr", c->peer_name() )("error", err.message() ) ); - } - }); + auto colon = c->peer_addr.find(':'); + + if (colon == std::string::npos || colon == 0) { + elog ("Invalid peer address. must be \"host:port\": ${p}", ("p",c->peer_addr)); + return; } + auto host = c->peer_addr.substr( 0, colon ); + auto port = c->peer_addr.substr( colon + 1); + idump((host)(port)); + tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str() ); + // Note: need to add support for IPv6 too + + resolver->async_resolve( query, + [c, this]( const boost::system::error_code& err, + tcp::resolver::iterator endpoint_itr ){ + if( !err ) { + connect( c, endpoint_itr ); + } else { + elog( "Unable to resolve ${peer_addr}: ${error}", + ( "peer_addr", c->peer_name() )("error", err.message() ) ); + } + }); + } + void net_plugin_impl::connect( connection_ptr c, tcp::resolver::iterator endpoint_itr ) { if( c->no_retry != go_away_reason::no_reason) { string rsn = reason_str(c->no_retry); @@ -1302,6 +1309,7 @@ namespace eosio { ( const boost::system::error_code& err ) { if( !err ) { start_session( c ); + c->send_handshake (); } else { if( endpoint_itr != tcp::resolver::iterator() ) { c->close(); @@ -1322,7 +1330,6 @@ namespace eosio { con->socket->set_option( nodelay ); start_read_message( con ); ++started_sessions; - con->send_handshake(); // for now, we can just use the application main loop. // con->readloop_complete = bf::async( [=](){ read_loop( con ); } ); @@ -1450,12 +1457,16 @@ namespace eosio { void net_plugin_impl::handle_message( connection_ptr c, const handshake_message &msg) { ilog("got a handshake_message from ${p} ${h}", ("p",c->peer_addr)("h",msg.p2p_address)); + chain_controller& cc = chain_plug->chain(); + uint32_t lib_num = cc.last_irreversible_block_num( ); + uint32_t peer_lib = msg.last_irreversible_block_num; if( c->connecting ) { c->connecting = false; - + } + if (msg.generation == 1) { if( msg.node_id == node_id) { elog( "Self connection detected. Closing connection"); - c->enqueue( go_away_message( go_away_reason::self ) ); + c->enqueue( go_away_message( self ) ); return; } @@ -1469,7 +1480,7 @@ namespace eosio { go_away_message gam(go_away_reason::duplicate); gam.node_id = node_id; c->enqueue(gam); - c->no_retry = go_away_reason::duplicate; + c->no_retry = duplicate; return; } } @@ -1487,7 +1498,7 @@ namespace eosio { if (network_version_match) { elog("Peer network version does not match expected ${nv} but got ${mnv}", ("nv", network_version)("mnv", msg.network_version)); - c->enqueue(go_away_message(go_away_reason::wrong_version)); + c->enqueue(go_away_message(wrong_version)); return; } else { wlog("Peer network version does not match expected ${nv} but got ${mnv}", @@ -1500,34 +1511,35 @@ namespace eosio { } if(!authenticate_peer(msg)) { - dlog("Peer not authenticated. Closing connection."); - close(c); + elog("Peer not authenticated. Closing connection."); + c->enqueue(go_away_message(authentication)); return; } - } - chain_controller& cc = chain_plug->chain(); - uint32_t lib_num = cc.last_irreversible_block_num( ); - uint32_t peer_lib = msg.last_irreversible_block_num; - bool on_fork = false; - fc_dlog(logger, "lib_num = ${ln} peer_lib = ${pl}",("ln",lib_num)("pl",peer_lib)); + bool on_fork = false; + fc_dlog(logger, "lib_num = ${ln} peer_lib = ${pl}",("ln",lib_num)("pl",peer_lib)); - if( peer_lib <= lib_num && peer_lib > 0) { - try { - block_id_type peer_lib_id = cc.get_block_id_for_num( peer_lib); - on_fork =( msg.last_irreversible_block_id != peer_lib_id); - } - catch( ...) { - wlog( "caught an exception getting block id for ${pl}",("pl",peer_lib)); - on_fork = true; - } - if( on_fork) { - elog( "Peer chain is forked"); - c->enqueue( go_away_message( go_away_reason::forked )); - return; + if( peer_lib <= lib_num && peer_lib > 0) { + try { + block_id_type peer_lib_id = cc.get_block_id_for_num( peer_lib); + on_fork =( msg.last_irreversible_block_id != peer_lib_id); + } + catch( ...) { + wlog( "caught an exception getting block id for ${pl}",("pl",peer_lib)); + on_fork = true; + } + if( on_fork) { + elog( "Peer chain is forked"); + c->enqueue( go_away_message( forked )); + return; + } } + if (c->sent_handshake_count == 0) { + c->send_handshake(); + } } + c->last_handshake = msg; sync_master->reset_lib_num(); c->syncing = false; @@ -1552,10 +1564,12 @@ namespace eosio { note.known_blocks.pending = lib_num; } note.known_trx.mode = id_list_modes::catch_up; - note.known_trx.pending = local_txns.size(); // cc.pending().size(); + note.known_trx.pending = local_txns.size(); if( note.known_trx.pending > 0 || note.known_blocks.pending > 0) { - fc_dlog(logger, "sending ${m} notice to ${n} about ${t} txns and ${b} blocks",("m",modes_str(note.known_blocks.mode))("n",c->peer_name())("t",note.known_trx.pending)("b",note.known_blocks.pending)); - c->enqueue( note ); + if (msg.generation > 1) { + fc_dlog(logger, "sending ${m} notice to ${n} about ${t} txns and ${b} blocks",("m",modes_str(note.known_blocks.mode))("n",c->peer_name())("t",note.known_trx.pending)("b",note.known_blocks.pending)); + c->enqueue( note ); + } c->syncing = true; } } @@ -2090,11 +2104,11 @@ namespace eosio { }); } else { - fc_dlog(logger, "pending_notify, mode = ${m}, pending count = ${p}",("m",modes_str(pending_notify.known_trx.mode))("p",pending_notify.known_trx.pending)); + notice_message pending_notify; pending_notify.known_trx.mode = normal; pending_notify.known_trx.ids.push_back( txnid ); pending_notify.known_blocks.mode = none; - send_all(pending_notify, [txn, txnid](connection_ptr c) -> bool { + send_all(pending_notify, [txnid](connection_ptr c) -> bool { const auto& bs = c->trx_state.find(txnid); bool unknown = bs == c->trx_state.end(); if( unknown) { @@ -2105,8 +2119,6 @@ namespace eosio { } return unknown; }); - - pending_notify.known_trx.ids.clear(); } } @@ -2118,62 +2130,29 @@ namespace eosio { } void net_plugin_impl::broadcast_block_impl( const chain::signed_block &sb) { - send_all( sb,[](connection_ptr c) -> bool { return true; }); - return; -#if 0 - if( send_whole_blocks) { + net_message msg(sb); + uint32_t packsiz = fc::raw::pack_size(msg); + uint32_t msgsiz = packsiz + sizeof(packsiz); + + if (msgsiz <= just_send_it_max) { send_all( sb,[](connection_ptr c) -> bool { return true; }); - return; } - - block_summary_message bsm = {sb, vector()}; - vector &trxs = bsm.trx_ids; - if( !sb.cycles.empty()) { - for( const auto& cyc : sb.cycles) { - fc_dlog(logger, "cyc.size = ${cs}",( "cs", cyc.size())); - if( cyc.empty() ) { - continue; - } - trxs.emplace_back(cycle_ids()); - cycle_ids &cycs = trxs.back(); - fc_dlog(logger, "trxs.size = ${ts} cycles.size = ${cs}",("ts", trxs.size())("cs", cycs.size())); - for( const auto& thr : cyc) { - fc_dlog(logger, "user txns = ${ui} generated = ${gi}",("ui",thr.user_input.size( ))("gi",thr.generated_input.size( ))); - if( thr.user_input.size( ) == 0 ) { - continue; - } - cycs.emplace_back(thread_ids()); - thread_ids &thd_ids = cycs.back(); - - for( auto gi : thr.generated_input ) { - thd_ids.gen_trx.emplace_back( gi.id ); - } - - for( auto &ui : thr.user_input) { - processed_trans_summary pts({ui.id( ),ui.output }); - thd_ids.user_trx.emplace_back( pts ); - fc_dlog(logger, "user txn has ${m} messages, summary has ${ms}", ("m",ui.output.size())("ms",pts.outmsgs.size())); + else { + notice_message pending_notify; + block_id_type bid = sb.id(); + pending_notify.known_blocks.mode = normal; + pending_notify.known_blocks.ids.push_back( bid ); + pending_notify.known_trx.mode = none; + send_all(pending_notify, [bid](connection_ptr c) -> bool { + const auto& bs = c->block_state.find(bid); + bool unknown = bs == c->block_state.end(); + if( unknown) { + fc_dlog(logger, "sending block notice to ${n}", ("n",c->peer_name() ) ); + c->block_state.insert(block_state({bid,false,true,fc::time_point() })); } - } - } - } - - fc_dlog(logger, "sending bsm with ${c} transactions",("c",trxs.size())); - fc_dlog(logger, "bsm header = ${h} txns = ${t}",("h",bsm.block_header)("t",bsm.trx_ids.size())); - if(bsm.trx_ids.size() > 0) { - fc_dlog(logger, "cycles.size = ${cs}",("cs", bsm.trx_ids[0].size())); - if (bsm.trx_ids[0].size()) { - } + return unknown; + }); } - send_all( bsm,[sb](connection_ptr c) -> bool { - const auto& bs = c->block_state.find(sb.id()); - if( bs == c->block_state.end()) { - c->block_state.insert( (block_state){sb.id(),true,true,fc::time_point()}); - return true; - } - return false; - }); -#endif } bool net_plugin_impl::authenticate_peer(const handshake_message& msg) const { @@ -2466,9 +2445,6 @@ namespace eosio { my->keepalive_timer.reset(new boost::asio::steady_timer(app().get_io_service())); my->ticker(); - my->pending_notify.known_trx.mode = id_list_modes::normal; - my->pending_notify.known_trx.pending = 0; - } void net_plugin::plugin_startup() { @@ -2525,9 +2501,10 @@ namespace eosio { return "already connected"; connection_ptr c = std::make_shared(host); + fc_dlog(my->logger,"adding new connection to the list"); my->connections.insert( c ); + fc_dlog(my->logger,"calling active connector"); my->connect( c ); - return "added connection"; }