diff --git a/TODO b/TODO index 4d0250543..1c85782f7 100644 --- a/TODO +++ b/TODO @@ -3,10 +3,6 @@ Tests - Use standard tests. -Config file & command line options. - -Peer network. - Crypto stuff: - kFromMessage - Check all the tweak instructions. @@ -22,16 +18,17 @@ Network: ### GAV +Network: +** Don't crash when a peer leaves! +** Check peer utility (useful transactions & blocks passed) for when dismissing. +** Respect peer count & dismiss/collect/stop listening for peers as necessary. +** Manage GetBlocks properly; should work for when > 256 blocks away. + Trie on DB. -- Modularise overlay and DB. -- Iterate. - Move the restore point stuff into block restore points - i.e. keep all nodes from last 127 blocks with counter, at 128, kill but keep every (60*24*7)th or so i.e. one per week as a restore point. - maybe allow this to be configured. -Cache some state -- Contract memory, balances - for single commit into Trie. - ### TIM diff --git a/eth/main.cpp b/eth/main.cpp index db6f49184..56389f778 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -20,6 +20,7 @@ * Ethereum client. */ +#include #include "Client.h" #include "PeerNetwork.h" #include "BlockChain.h" @@ -27,6 +28,26 @@ using namespace std; using namespace eth; +bytes contents(std::string const& _file) +{ + std::ifstream is(_file, std::ifstream::binary); + if (!is) + return bytes(); + // get length of file: + is.seekg (0, is.end); + int length = is.tellg(); + is.seekg (0, is.beg); + bytes ret(length); + is.read((char*)ret.data(), length); + is.close(); + return ret; +} + +void writeFile(std::string const& _file, bytes const& _data) +{ + ofstream(_file, ios::trunc).write((char const*)_data.data(), _data.size()); +} + int main(int argc, char** argv) { short listenPort = 30303; @@ -37,7 +58,23 @@ int main(int argc, char** argv) eth::uint mining = ~(eth::uint)0; // Our address. - Address us; // TODO: load from config file? + KeyPair us = KeyPair::create(); + Address coinbase = us.address(); + + string configFile = string(getenv("HOME")) + "/.ethereum/config.rlp"; + bytes b = contents(configFile); + if (b.size()) + { + RLP config(b); + us = KeyPair(config[0].toHash()); + coinbase = config[1].toHash
(); + } + else + { + RLPStream config(2); + config << us.secret() << coinbase; + writeFile(configFile, config.out()); + } for (int i = 1; i < argc; ++i) { @@ -49,7 +86,9 @@ int main(int argc, char** argv) else if (arg == "-p" && i + 1 < argc) remotePort = atoi(argv[++i]); else if (arg == "-a" && i + 1 < argc) - us = h160(fromUserHex(argv[++i])); + coinbase = h160(fromUserHex(argv[++i])); + else if (arg == "-s" && i + 1 < argc) + us = KeyPair(h256(fromUserHex(argv[++i]))); else if (arg == "-i") interactive = true; else if (arg == "-d" && i + 1 < argc) @@ -65,7 +104,7 @@ int main(int argc, char** argv) remoteHost = argv[i]; } - Client c("Ethereum(++)/v0.1", us, dbPath); + Client c("Ethereum(++)/v0.1", coinbase, dbPath); if (interactive) { cout << "Ethereum (++)" << endl; @@ -113,6 +152,15 @@ int main(int argc, char** argv) Address dest = h160(fromUserHex(rechex)); c.transact(secret, dest, amount, fee); } + else if (cmd == "send") + { + string rechex; + u256 amount; + u256 fee; + cin >> rechex >> amount >> fee; + Address dest = h160(fromUserHex(rechex)); + c.transact(us.secret(), dest, amount, fee); + } } } else diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 727197652..9e9b48292 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -164,18 +164,19 @@ void BlockChain::import(bytes const& _block, Overlay const& _db) checkConsistency(); - cout << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children." << endl; +// cout << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children." << endl; // This might be the new last block... if (td > m_details[m_lastBlockHash].totalDifficulty) { m_lastBlockHash = newHash; m_detailsDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); - cout << "Block " << newHash << " is best." << endl; + cout << " Imported and best." << endl; } else { - cerr << "*** WARNING: Imported block not newest (otd=" << m_details[m_lastBlockHash].totalDifficulty << ", td=" << td << ")" << endl; + cout << " Imported." << endl; +// cerr << "*** WARNING: Imported block not newest (otd=" << m_details[m_lastBlockHash].totalDifficulty << ", td=" << td << ")" << endl; } } @@ -184,17 +185,18 @@ void BlockChain::checkConsistency() m_details.clear(); ldb::Iterator* it = m_detailsDB->NewIterator(m_readOptions); for (it->SeekToFirst(); it->Valid(); it->Next()) - { - h256 h((byte const*)it->key().data()); - auto dh = details(h); - auto p = dh.parent; - if (p != h256()) + if (it->key().size() == 32) { - auto dp = details(p); - assert(contains(dp.children, h)); - assert(dp.number == dh.number - 1); + h256 h((byte const*)it->key().data()); + auto dh = details(h); + auto p = dh.parent; + if (p != h256()) + { + auto dp = details(p); + assert(contains(dp.children, h)); + assert(dp.number == dh.number - 1); + } } - } delete it; } @@ -216,7 +218,7 @@ BlockDetails const& BlockChain::details(h256 _h) const m_detailsDB->Get(m_readOptions, ldb::Slice((char const*)&_h, 32), &s); if (s.empty()) { - cout << "Not found in DB: " << _h << endl; +// cout << "Not found in DB: " << _h << endl; return NullBlockDetails; } bool ok; diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index 91c8cb9b1..5dd8d2446 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -40,7 +40,6 @@ PeerSession::~PeerSession() bool PeerSession::interpret(RLP const& _r) { - ::operator<<(cout, _r) << endl; switch (_r[0].toInt()) { case Hello: @@ -68,14 +67,14 @@ bool PeerSession::interpret(RLP const& _r) return false; case Ping: { - cout << std::setw(2) << m_socket.native_handle() << " | Ping" << endl; +// cout << std::setw(2) << m_socket.native_handle() << " | Ping" << endl; RLPStream s; sealAndSend(prep(s).appendList(1) << (uint)Pong); break; } case Pong: m_lastPing = std::chrono::steady_clock::now() - m_ping; - cout << "Latency: " << chrono::duration_cast(m_lastPing).count() << " ms" << endl; +// cout << "Latency: " << chrono::duration_cast(m_lastPing).count() << " ms" << endl; break; case GetPeers: { @@ -102,12 +101,18 @@ bool PeerSession::interpret(RLP const& _r) case Transactions: cout << std::setw(2) << m_socket.native_handle() << " | Transactions (" << _r[1].itemCount() << " entries)" << endl; for (auto i: _r[1]) + { m_server->m_incomingTransactions.push_back(i.data().toBytes()); + m_knownTransactions.insert(sha3(i.data())); + } break; case Blocks: cout << std::setw(2) << m_socket.native_handle() << " | Blocks (" << _r[1].itemCount() << " entries)" << endl; for (auto i: _r[1]) + { m_server->m_incomingBlocks.push_back(i.data().toBytes()); + m_knownBlocks.insert(sha3(i.data())); + } break; case GetChain: { @@ -204,10 +209,12 @@ void PeerSession::sendDestroy(bytes& _msg) std::shared_ptr buffer = std::make_shared(); swap(*buffer, _msg); assert((*buffer)[0] == 0x22); - cout << "Sending " << RLP(bytesConstRef(buffer.get()).cropped(8)) << endl; +// cout << "Sending " << RLP(bytesConstRef(buffer.get()).cropped(8)) << endl; ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) { - cout << length << " bytes written (EC: " << ec << ")" << endl; + if (ec) + disconnect(); +// cout << length << " bytes written (EC: " << ec << ")" << endl; }); } @@ -215,10 +222,12 @@ void PeerSession::send(bytesConstRef _msg) { std::shared_ptr buffer = std::make_shared(_msg.toBytes()); assert((*buffer)[0] == 0x22); - cout << "Sending " << RLP(bytesConstRef(buffer.get()).cropped(8)) << endl; +// cout << "Sending " << RLP(bytesConstRef(buffer.get()).cropped(8)) << endl; ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) { - cout << length << " bytes written (EC: " << ec << ")" << endl; + if (ec) + disconnect(); +// cout << length << " bytes written (EC: " << ec << ")" << endl; }); } @@ -234,8 +243,6 @@ void PeerSession::disconnect() void PeerSession::start() { - cout << "Starting session." << endl; - RLPStream s; prep(s); s.appendList(4) << (uint)Hello << (uint)0 << (uint)0 << m_server->m_clientVersion; @@ -251,7 +258,9 @@ void PeerSession::doRead() auto self(shared_from_this()); m_socket.async_read_some(boost::asio::buffer(m_data), [this, self](boost::system::error_code ec, std::size_t length) { - if (!ec) + if (ec) + disconnect(); + else { m_incoming.resize(m_incoming.size() + length); memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); @@ -266,7 +275,7 @@ void PeerSession::doRead() else { uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); - cout << "Received packet of " << len << " bytes" << endl; +// cout << "Received packet of " << len << " bytes" << endl; if (m_incoming.size() - 8 < len) break; @@ -354,12 +363,36 @@ bool PeerServer::connect(string const& _addr, uint _port) } } +bool PeerServer::connect(bi::tcp::endpoint _ep) +{ + bi::tcp::resolver resolver(m_ioService); + cout << "Attempting connection to " << _ep << endl; + try + { + bi::tcp::socket s(m_ioService); + boost::asio::connect(s, resolver.resolve(_ep)); + auto p = make_shared(this, std::move(s), m_requiredNetworkId); + m_peers.push_back(p); + cout << "Connected." << endl; + p->start(); + return true; + } + catch (exception& _e) + { + cout << "Connection refused (" << _e.what() << ")" << endl; + return false; + } +} + void PeerServer::process(BlockChain& _bc) { m_ioService.poll(); for (auto i = m_peers.begin(); i != m_peers.end();) if (auto j = i->lock()) - ++i; + if (j->m_socket.is_open()) + ++i; + else + i = m_peers.erase(i); else i = m_peers.erase(i); } @@ -382,29 +415,29 @@ void PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) m_incomingTransactions.clear(); // Send any new transactions. - if (m_peers.size()) - { - bytes b; - uint n = 0; - for (auto const& i: _tq.transactions()) - if (!m_transactionsSent.count(i.first)) + for (auto j: m_peers) + if (auto p = j.lock()) + { + bytes b; + uint n = 0; + for (auto const& i: _tq.transactions()) + if (!m_transactionsSent.count(i.first) && !p->m_knownTransactions.count(i.first)) + { + b += i.second; + ++n; + m_transactionsSent.insert(i.first); + } + if (n) { - b += i.second; - ++n; - m_transactionsSent.insert(i.first); + RLPStream ts; + PeerSession::prep(ts); + ts.appendList(2) << Transactions; + ts.appendList(n).appendRaw(b).swapOut(b); + PeerSession::seal(b); + p->send(&b); } - if (n) - { - RLPStream ts; - PeerSession::prep(ts); - ts.appendList(2) << Transactions; - ts.appendList(n).appendRaw(b).swapOut(b); - PeerSession::seal(b); - for (auto j: m_peers) - if (auto p = j.lock()) - p->send(&b); + p->m_knownTransactions.clear(); } - } // Send any new blocks. { @@ -420,7 +453,11 @@ void PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) PeerSession::seal(b); for (auto j: m_peers) if (auto p = j.lock()) - p->send(&b); + { + if (!p->m_knownBlocks.count(_bc.currentHash())) + p->send(&b); + p->m_knownBlocks.clear(); + } } m_latestBlockSent = h; } @@ -448,6 +485,13 @@ void PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) } } } + + // Connect to additional peers + while (m_peers.size() < m_idealPeerCount && m_incomingPeers.size()) + { + connect(m_incomingPeers.back()); + m_incomingPeers.pop_back(); + } } std::vector PeerServer::peers() const diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 1d6ff2b73..a26cb8d48 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -89,6 +89,9 @@ private: std::chrono::steady_clock::time_point m_ping; std::chrono::steady_clock::duration m_lastPing; + + std::set m_knownBlocks; + std::set m_knownTransactions; }; struct PeerInfo @@ -110,6 +113,7 @@ public: /// Connect to a peer explicitly. bool connect(std::string const& _addr = "127.0.0.1", uint _port = 30303); + bool connect(bi::tcp::endpoint _ep); /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. /// Conduct I/O, polling, syncing, whatever. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 691b5ee38..19e399823 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -223,27 +223,23 @@ void State::sync(TransactionQueue& _tq) // TRANSACTIONS auto ts = _tq.transactions(); for (auto const& i: ts) - { if (!m_transactions.count(i.first)) - { // don't have it yet! Execute it now. try { execute(i.second); } - catch (InvalidNonce in) + catch (InvalidNonce const& in) { if (in.required > in.candidate) // too old _tq.drop(i.first); } - catch (...) + catch (std::exception const&) { // Something else went wrong - drop it. _tq.drop(i.first); } - } - } } u256 State::playback(bytesConstRef _block, bool _fullCommit) @@ -338,7 +334,7 @@ void State::commitToMine(BlockChain const& _bc) if (m_previousBlock != BlockInfo::genesis()) { // Find uncles if we're not a direct child of the genesis. - cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; +// cout << "Checking " << m_previousBlock.hash << ", parent=" << m_previousBlock.parentHash << endl; auto us = _bc.details(m_previousBlock.parentHash).children; assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent! uncles.appendList(us.size() - 1); // one fewer - uncles precludes our parent from the list of grandparent's children. @@ -489,26 +485,17 @@ u256 State::contractMemory(Address _id, u256 _memory) const return RLP(memdb.at(_memory)).toInt(); // TODO: CHECK: check if this is actually an RLP decode } -bool State::execute(bytesConstRef _rlp) +void State::execute(bytesConstRef _rlp) { // Entry point for a user-executed transaction. - try - { - Transaction t(_rlp); - executeBare(t, t.sender()); - - // Add to the user-originated transactions that we've executed. - // NOTE: Here, contract-originated transactions will not get added to the transaction list. - // If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and - // don't forget to allow unsigned transactions in the tx list if they concur with the script execution. - m_transactions.insert(make_pair(t.sha3(), t)); - - return true; - } - catch (...) - { - return false; - } + Transaction t(_rlp); + executeBare(t, t.sender()); + + // Add to the user-originated transactions that we've executed. + // NOTE: Here, contract-originated transactions will not get added to the transaction list. + // If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and + // don't forget to allow unsigned transactions in the tx list if they concur with the script execution. + m_transactions.insert(make_pair(t.sha3(), t)); } void State::applyRewards(Addresses const& _uncleAddresses) diff --git a/libethereum/State.h b/libethereum/State.h index 1edb7cf71..66608d725 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -97,8 +97,8 @@ public: void sync(TransactionQueue& _tq); /// Execute a given transaction. - bool execute(bytes const& _rlp) { return execute(&_rlp); } - bool execute(bytesConstRef _rlp); + void execute(bytes const& _rlp) { return execute(&_rlp); } + void execute(bytesConstRef _rlp); /// Check if the address is a valid normal (non-contract) account address. bool isNormalAddress(Address _address) const;