diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 97545b36d..331155559 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -277,7 +277,7 @@ - 349 + 408 251 @@ -298,26 +298,6 @@ 4 - - - - - 1 - 0 - - - - - - - - Send - - - - - - @@ -325,6 +305,9 @@ + + + @@ -338,6 +321,16 @@ + + + + Data + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + @@ -351,44 +344,45 @@ - - - - - - - - - - 430000000 - - - 100 + + + + + + + Send - - - - - - - Fee + + + + + 1 + 0 + + + + (Create Contract) - - + + - Data + - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 98e8e22b5..351f4b77f 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -24,8 +24,6 @@ Main::Main(QWidget *parent) : { setWindowFlags(Qt::Window); ui->setupUi(this); - initUnits(ui->valueUnits); - initUnits(ui->feeUnits); g_logPost = [=](std::string const& s, char const*) { ui->log->addItem(QString::fromStdString(s)); }; m_client = new Client("AlethZero/v" ADD_QUOTES(ETH_VERSION) "/" ADD_QUOTES(ETH_BUILD_TYPE) "/" ADD_QUOTES(ETH_BUILD_PLATFORM)); @@ -51,6 +49,7 @@ Main::Main(QWidget *parent) : on_verbosity_sliderMoved(); + initUnits(ui->valueUnits); statusBar()->addPermanentWidget(ui->balance); statusBar()->addPermanentWidget(ui->peerCount); statusBar()->addPermanentWidget(ui->blockChain); @@ -137,9 +136,8 @@ void Main::refresh() for (pair const& i: m_client->transactionQueue().transactions()) { Transaction t(i.second); - ui->transactionQueue->addItem(QString("%1 (%2 fee) @ %3 <- %4") + ui->transactionQueue->addItem(QString("%1 @ %2 <- %3") .arg(formatBalance(t.value).c_str()) - .arg(formatBalance(t.fee).c_str()) .arg(asHex(t.receiveAddress.asArray()).c_str()) .arg(asHex(t.sender().asArray()).c_str()) ); } @@ -153,9 +151,8 @@ void Main::refresh() for (auto const& i: RLP(bc.block(h))[1]) { Transaction t(i.data()); - ui->transactions->addItem(QString("%1 (%2) @ %3 <- %4") + ui->transactions->addItem(QString("%1 @ %2 <- %3") .arg(formatBalance(t.value).c_str()) - .arg(formatBalance(t.fee).c_str()) .arg(asHex(t.receiveAddress.asArray()).c_str()) .arg(asHex(t.sender().asArray()).c_str()) ); } @@ -189,6 +186,51 @@ void Main::on_accounts_doubleClicked() qApp->clipboard()->setText(ui->accounts->currentItem()->text().section(" @ ", 1)); } +void Main::on_destination_textChanged() +{ + updateFee(); +} + +void Main::on_data_textChanged() +{ + m_data = ui->data->toPlainText().split(QRegExp("[^0-9a-fA-Fx]+"), QString::SkipEmptyParts); + updateFee(); +} + +u256 Main::fee() const +{ + return (ui->destination->text().isEmpty() || !ui->destination->text().toInt()) ? m_client->state().fee(m_data.size()) : m_client->state().fee(); +} + +u256 Main::value() const +{ + return ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; +} + +u256 Main::total() const +{ + return value() + fee(); +} + +void Main::updateFee() +{ + ui->fee->setText(QString("(fee: %1)").arg(formatBalance(fee()).c_str())); + auto totalReq = total(); + ui->total->setText(QString("Total: %1").arg(formatBalance(totalReq).c_str())); + + bool ok = false; + for (auto i: m_myKeys) + if (m_client->state().balance(i.address()) >= totalReq) + { + ok = true; + break; + } + ui->send->setEnabled(ok); + QPalette p = ui->total->palette(); + p.setColor(QPalette::WindowText, QColor(ok ? 0x00 : 0x80, 0x00, 0x00)); + ui->total->setPalette(p); +} + void Main::on_net_triggered() { ui->port->setEnabled(!ui->net->isChecked()); @@ -233,9 +275,7 @@ void Main::on_mine_triggered() void Main::on_send_clicked() { - u256 value = ui->value->value() * units()[units().size() - 1 - ui->valueUnits->currentIndex()].first; - u256 fee = ui->fee->value() * units()[units().size() - 1 - ui->feeUnits->currentIndex()].first; - u256 totalReq = value + fee; + u256 totalReq = value() + fee(); m_client->lock(); for (auto i: m_myKeys) if (m_client->state().balance(i.address()) >= totalReq) @@ -243,12 +283,11 @@ void Main::on_send_clicked() m_client->unlock(); Secret s = m_myKeys.front().secret(); Address r = Address(fromUserHex(ui->destination->text().toStdString())); - auto ds = ui->data->toPlainText().split(QRegExp("[^0-9a-fA-Fx]+")); u256s data; - data.reserve(ds.size()); - for (QString const& i: ds) + data.reserve(m_data.size()); + for (QString const& i: m_data) data.push_back(u256(i.toStdString())); - m_client->transact(s, r, value, fee, data); + m_client->transact(s, r, value(), data); refresh(); return; } diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index cafbb5652..016e00274 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -32,6 +32,10 @@ private slots: void on_verbosity_sliderMoved(); void on_ourAccounts_doubleClicked(); void on_accounts_doubleClicked(); + void on_destination_textChanged(); + void on_data_textChanged(); + void on_value_valueChanged() { updateFee(); } + void on_valueUnits_currentIndexChanged() { updateFee(); } void on_log_doubleClicked(); void on_about_triggered(); void on_quit_triggered() { close(); } @@ -39,9 +43,14 @@ private slots: void refresh(); private: + void updateFee(); void readSettings(); void writeSettings(); + eth::u256 fee() const; + eth::u256 total() const; + eth::u256 value() const; + Ui::Main *ui; eth::Client* m_client; @@ -50,6 +59,7 @@ private: QTimer* m_refresh; QStringList m_servers; QVector m_myKeys; + QStringList m_data; QNetworkAccessManager m_webCtrl; }; diff --git a/eth/main.cpp b/eth/main.cpp index a6f55acc8..2bd7f865a 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -207,20 +207,18 @@ int main(int argc, char** argv) string sechex; string rechex; u256 amount; - u256 fee; - cin >> sechex >> rechex >> amount >> fee; + cin >> sechex >> rechex >> amount; Secret secret = h256(fromUserHex(sechex)); Address dest = h160(fromUserHex(rechex)); - c.transact(secret, dest, amount, fee); + c.transact(secret, dest, amount); } else if (cmd == "send") { string rechex; u256 amount; - u256 fee; - cin >> rechex >> amount >> fee; + cin >> rechex >> amount; Address dest = h160(fromUserHex(rechex)); - c.transact(us.secret(), dest, amount, fee); + c.transact(us.secret(), dest, amount); } else if (cmd == "exit") { diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index cd7f53de6..b053fa9c1 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -32,7 +32,8 @@ Client::Client(std::string const& _clientVersion, Address _us, std::string const m_clientVersion(_clientVersion), m_bc(_dbPath), m_stateDB(State::openDB(_dbPath)), - m_s(_us, m_stateDB) + m_s(_us, m_stateDB), + m_mined(_us, m_stateDB) { Defaults::setDBPath(_dbPath); @@ -95,6 +96,7 @@ void Client::stopNetwork() void Client::startMining() { m_doMine = true; + m_miningStarted = true; } void Client::stopMining() @@ -102,14 +104,13 @@ void Client::stopMining() m_doMine = false; } -void Client::transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u256s _data) +void Client::transact(Secret _secret, Address _dest, u256 _amount, u256s _data) { m_lock.lock(); Transaction t; t.nonce = m_s.transactionsFrom(toAddress(_secret)); t.receiveAddress = _dest; t.value = _amount; - t.fee = _fee; t.data = _data; t.sign(_secret); m_tq.attemptImport(t.rlp()); @@ -120,12 +121,14 @@ void Client::transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u2 void Client::work() { m_lock.lock(); + bool changed = false; + // Process network events. // Synchronise block chain with network. // Will broadcast any of our (new) transactions and blocks, and collect & add any of their (new) transactions and blocks. if (m_net) if (m_net->process(m_bc, m_tq, m_stateDB)) - m_changed = true; + changed = true; // Synchronise state to block chain. // This should remove any transactions on our queue that are included within our state. @@ -135,16 +138,23 @@ void Client::work() // all blocks. // Resynchronise state with block chain & trans if (m_s.sync(m_bc)) - m_changed = true; + changed = true; if (m_s.sync(m_tq)) - m_changed = true; + changed = true; m_lock.unlock(); if (m_doMine) { + if (changed || m_miningStarted) + { + m_mined = m_s; + m_mined.commitToMine(m_bc); + } + + m_miningStarted = false; + // Mine for a while. - m_s.commitToMine(m_bc); - MineInfo mineInfo = m_s.mine(100); + MineInfo mineInfo = m_mined.mine(100); m_mineProgress.best = max(m_mineProgress.best, mineInfo.best); m_mineProgress.current = mineInfo.best; m_mineProgress.requirement = mineInfo.requirement; @@ -153,7 +163,7 @@ void Client::work() { // Import block. m_lock.lock(); - m_bc.attemptImport(m_s.blockData(), m_stateDB); + m_bc.attemptImport(m_mined.blockData(), m_stateDB); m_mineProgress.best = 0; m_lock.unlock(); m_changed = true; @@ -161,6 +171,8 @@ void Client::work() } else this_thread::sleep_for(chrono::milliseconds(100)); + + m_changed = m_changed || changed; } void Client::lock() diff --git a/libethereum/Client.h b/libethereum/Client.h index be485e768..70b94d84b 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -62,7 +62,7 @@ public: ~Client(); /// Executes the given transaction. - void transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u256s _data = u256s()); + void transact(Secret _secret, Address _dest, u256 _amount, u256s _data = u256s()); /// Requires transactions involving this address be queued for inspection. void setInterest(Address _dest); @@ -131,6 +131,7 @@ private: TransactionQueue m_tq; ///< Maintains list of incoming transactions not yet on the block chain. Overlay m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_s; ///< The present state of the client. + State m_mined; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). PeerServer* m_net = nullptr; ///< Should run in background and send us events when blocks found and allow us to send blocks as required. #if defined(__APPLE__) @@ -143,6 +144,7 @@ private: enum { Active = 0, Deleting, Deleted } m_workState = Active; bool m_doMine = false; ///< Are we supposed to be mining? MineProgress m_mineProgress; + mutable bool m_miningStarted = false; mutable bool m_changed; }; diff --git a/libethereum/Common.cpp b/libethereum/Common.cpp index f5896db95..2041b25bd 100644 --- a/libethereum/Common.cpp +++ b/libethereum/Common.cpp @@ -190,13 +190,50 @@ Address eth::toAddress(Secret _private) KeyPair KeyPair::create() { + secp256k1_start(); static std::mt19937_64 s_eng(time(0)); std::uniform_int_distribution d(0, 255); - KeyPair ret; - for (uint i = 0; i < 32; ++i) - ret.m_secret[i] = d(s_eng); - ret.m_address = toAddress(ret.m_secret); - return ret; + + for (int i = 0; i < 100; ++i) + { + h256 sec; + for (uint i = 0; i < 32; ++i) + sec[i] = d(s_eng); + + KeyPair ret(sec); + if (ret.address()) + return ret; + } + return KeyPair(); +} + +KeyPair::KeyPair(h256 _sec): + m_secret(_sec) +{ + int ok = secp256k1_ecdsa_seckey_verify(m_secret.data()); + if (!ok) + return; + + byte pubkey[65]; + int pubkeylen = 65; + ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0); + if (!ok || pubkeylen != 65) + return; + + ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65); + if (!ok) + return; + + m_secret = m_secret; + memcpy(m_public.data(), &(pubkey[1]), 64); + m_address = right160(eth::sha3(bytesConstRef(&(pubkey[1]), 64))); + +#if ETH_ADDRESS_DEBUG + cout << "---- ADDRESS -------------------------------" << endl; + cout << "SEC: " << m_secret << endl; + cout << "PUB: " << m_public << endl; + cout << "ADR: " << m_address << endl; +#endif } static const vector> g_units = diff --git a/libethereum/Common.h b/libethereum/Common.h index cd4f81af6..9f13dda29 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -127,6 +127,7 @@ inline std::ostream& operator<<(std::ostream& _out, FixedHash const& _h) return _out; } +using h512 = FixedHash<64>; using h256 = FixedHash<32>; using h160 = FixedHash<20>; using h256s = std::vector; @@ -135,6 +136,7 @@ using h256Set = std::set; using h160Set = std::set; using Secret = h256; +using Public = h512; using Address = h160; using Addresses = h160s; @@ -434,15 +436,19 @@ class KeyPair { public: KeyPair() {} - KeyPair(Secret _k): m_secret(_k), m_address(toAddress(_k)) {} + KeyPair(Secret _k); static KeyPair create(); Secret const& secret() const { return m_secret; } + Secret const& sec() const { return m_secret; } + Public const& pub() const { return m_public; } + Address const& address() const { return m_address; } private: Secret m_secret; + Public m_public; Address m_address; }; diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index f12ec4cae..7aa3480c1 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -95,21 +95,29 @@ bool PeerSession::interpret(RLP const& _r) m_protocolVersion = _r[1].toInt(); m_networkId = _r[2].toInt(); auto clientVersion = _r[3].toString(); - m_caps = _r.itemCount() > 4 ? _r[4].toInt() : 0x07; - m_listenPort = _r.itemCount() > 5 ? _r[5].toInt() : 0; + m_caps = _r[4].toInt(); + m_listenPort = _r[5].toInt(); + m_id = _r[6].toHash(); - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << showbase << hex << m_caps << dec << m_listenPort; + clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "/" << m_networkId << "]" << asHex(m_id.ref().cropped(0, 4)) << showbase << hex << m_caps << dec << m_listenPort; - if (m_protocolVersion != 1 || m_networkId != m_reqNetworkId) + if (m_server->m_peers.count(m_id) || !m_id) { - disconnect(); + // Already connected. + disconnect(DuplicatePeer); + } + m_server->m_peers[m_id] = shared_from_this(); + + if (m_protocolVersion != 2 || m_networkId != m_reqNetworkId) + { + disconnect(IncompatibleProtocol); return false; } try { m_info = PeerInfo({clientVersion, m_socket.remote_endpoint().address().to_string(), (short)m_socket.remote_endpoint().port(), std::chrono::steady_clock::duration()}); } catch (...) { - disconnect(); + disconnect(BadProtocol); return false; } @@ -132,13 +140,30 @@ bool PeerSession::interpret(RLP const& _r) break; } case DisconnectPacket: - clogS(NetMessageSummary) << "Disconnect"; + { + string reason = "Unspecified"; + if (_r.itemCount() > 1 && _r[1].isInt()) + switch (_r[1].toInt()) + { + case DisconnectRequested: reason = "Disconnect was requested."; break; + case TCPError: reason = "Low-level TCP communication error."; break; + case BadProtocol: reason = "Data format error."; break; + case UselessPeer: reason = "We had no use to peer."; break; + case TooManyPeers: reason = "Peer had too many connections."; break; + case DuplicatePeer: reason = "Peer was already connected."; break; + case WrongGenesis: reason = "Disagreement over genesis block."; break; + case IncompatibleProtocol: reason = "Peer protocol versions are incompatible."; break; + case ClientQuit: reason = "Peer is exiting."; break; + } + + clogS(NetMessageSummary) << "Disconnect (reason: " << reason << ")"; if (m_socket.is_open()) clogS(NetNote) << "Closing " << m_socket.remote_endpoint(); else clogS(NetNote) << "Remote closed."; m_socket.close(); return false; + } case PingPacket: { // clogS(NetMessageSummary) << "Ping"; @@ -153,14 +178,14 @@ bool PeerSession::interpret(RLP const& _r) case GetPeersPacket: { clogS(NetMessageSummary) << "GetPeers"; - std::vector peers = m_server->potentialPeers(); + auto peers = m_server->potentialPeers(); RLPStream s; prep(s).appendList(peers.size() + 1); s << PeersPacket; for (auto i: peers) { - clogS(NetMessageDetail) << "Sending peer " << i; - s.appendList(2) << i.address().to_v4().to_bytes() << i.port(); + clogS(NetMessageDetail) << "Sending peer " << asHex(i.first.ref().cropped(0, 4)) << i.second; + s.appendList(3) << i.second.address().to_v4().to_bytes() << i.second.port() << i.first; } sealAndSend(s); break; @@ -170,7 +195,16 @@ bool PeerSession::interpret(RLP const& _r) for (unsigned i = 1; i < _r.itemCount(); ++i) { auto ep = bi::tcp::endpoint(bi::address_v4(_r[i][0].toArray()), _r[i][1].toInt()); - clogS(NetAllDetail) << "Checking: " << ep; + Public id; + if (_r[i].itemCount() > 2) + id = _r[i][2].toHash(); + + clogS(NetAllDetail) << "Checking: " << ep << "(" << asHex(id.ref().cropped(0, 4)) << ")"; + + // check that it's not us or one we already know: + if (id && (m_server->m_key.pub() == id || m_server->m_peers.count(id) || m_server->m_incomingPeers.count(id))) + goto CONTINUE; + // check that we're not already connected to addr: if (!ep.port()) goto CONTINUE; @@ -178,16 +212,16 @@ bool PeerSession::interpret(RLP const& _r) if (ep.address() == i && ep.port() == m_server->listenPort()) goto CONTINUE; for (auto i: m_server->m_peers) - if (shared_ptr p = i.lock()) + if (shared_ptr p = i.second.lock()) { clogS(NetAllDetail) << " ...against " << p->endpoint(); if (p->m_socket.is_open() && p->endpoint() == ep) goto CONTINUE; } for (auto i: m_server->m_incomingPeers) - if (i == ep) + if (i.second == ep) goto CONTINUE; - m_server->m_incomingPeers.push_back(ep); + m_server->m_incomingPeers.insert(make_pair(id, ep)); clogS(NetMessageDetail) << "New peer: " << ep; CONTINUE:; } @@ -314,7 +348,7 @@ bool PeerSession::interpret(RLP const& _r) if (noGood == m_server->m_chain->genesisHash()) { clogS(NetWarn) << "Discordance over genesis block! Disconnect."; - disconnect(); + disconnect(WrongGenesis); } else { @@ -366,7 +400,6 @@ void PeerServer::seal(bytes& _b) _b[5] = (len >> 16) & 0xff; _b[6] = (len >> 8) & 0xff; _b[7] = len & 0xff; - cerr << "Sealed " << _b.size() << ": " << asHex(_b) << endl; } void PeerSession::sealAndSend(RLPStream& _s) @@ -412,14 +445,14 @@ void PeerSession::dropped() }catch (...){} m_socket.close(); for (auto i = m_server->m_peers.begin(); i != m_server->m_peers.end(); ++i) - if (i->lock().get() == this) + if (i->second.lock().get() == this) { m_server->m_peers.erase(i); break; } } -void PeerSession::disconnect() +void PeerSession::disconnect(int _reason) { if (m_socket.is_open()) { @@ -427,7 +460,7 @@ void PeerSession::disconnect() { RLPStream s; prep(s); - s.appendList(1) << DisconnectPacket; + s.appendList(1) << DisconnectPacket << _reason; sealAndSend(s); m_disconnect = chrono::steady_clock::now(); } @@ -448,9 +481,7 @@ void PeerSession::start() { RLPStream s; prep(s); - s.appendList(m_server->m_public.port() ? 6 : 5) << HelloPacket << (uint)1 << (uint)m_server->m_requiredNetworkId << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0); - if (m_server->m_public.port()) - s << m_server->m_public.port(); + s.appendList(m_server->m_public.port() ? 6 : 5) << HelloPacket << (uint)1 << (uint)m_server->m_requiredNetworkId << m_server->m_clientVersion << (m_server->m_mode == NodeMode::Full ? 0x07 : m_server->m_mode == NodeMode::PeerServer ? 0x01 : 0) << m_server->m_public.port() << m_server->m_key.pub(); sealAndSend(s); ping(); @@ -518,31 +549,34 @@ PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, m_chain(&_ch), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)), m_socket(m_ioService), + m_key(KeyPair::create()), m_requiredNetworkId(_networkId) { populateAddresses(); determinePublic(_publicAddress, _upnp); ensureAccepting(); - clog(NetNote) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); + clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (_m == NodeMode::PeerServer ? "PeerServer" : "Full"); } -PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId): +PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m): m_clientVersion(_clientVersion), + m_mode(_m), m_listenPort(-1), m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)), m_socket(m_ioService), + m_key(KeyPair::create()), m_requiredNetworkId(_networkId) { // populate addresses. populateAddresses(); - clog(NetNote) << "Genesis: " << m_chain->genesisHash(); + clog(NetNote) << "Id:" << asHex(m_key.address().ref().cropped(0, 4)) << "Mode: " << (m_mode == NodeMode::PeerServer ? "PeerServer" : "Full"); } PeerServer::~PeerServer() { for (auto const& i: m_peers) - if (auto p = i.lock()) - p->disconnect(); + if (auto p = i.second.lock()) + p->disconnect(ClientQuit); delete m_upnp; } @@ -656,17 +690,17 @@ void PeerServer::populateAddresses() #endif } -std::vector PeerServer::potentialPeers() +std::map PeerServer::potentialPeers() { - std::vector ret; + std::map ret; if (!m_public.address().is_unspecified()) - ret.push_back(m_public); + ret.insert(make_pair(m_key.pub(), m_public)); for (auto i: m_peers) - if (auto j = i.lock()) + if (auto j = i.second.lock()) { auto ep = j->endpoint(); - if (ep.port()) - ret.push_back(ep); + if (ep.port() && j->m_id) + ret.insert(make_pair(i.first, ep)); } return ret; } @@ -686,7 +720,6 @@ void PeerServer::ensureAccepting() clog(NetNote) << "Accepted connection from " << m_socket.remote_endpoint(); } catch (...){} auto p = std::make_shared(this, std::move(m_socket), m_requiredNetworkId); - m_peers.push_back(p); p->start(); } catch (std::exception const& _e) @@ -714,7 +747,6 @@ void PeerServer::connect(bi::tcp::endpoint const& _ep) else { auto p = make_shared(this, std::move(*s), m_requiredNetworkId); - m_peers.push_back(p); clog(NetNote) << "Connected to " << p->endpoint(); p->start(); } @@ -735,7 +767,7 @@ bool PeerServer::process(BlockChain& _bc) if (fullProcess) for (auto i = m_peers.begin(); i != m_peers.end();) { - auto p = i->lock(); + auto p = i->second.lock(); if (p && p->m_socket.is_open() && (p->m_disconnect == chrono::steady_clock::time_point::max() || chrono::steady_clock::now() - p->m_disconnect < chrono::seconds(1))) // kill old peers that should be disconnected. ++i; @@ -784,7 +816,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) if (fullProcess) { for (auto j: m_peers) - if (auto p = j.lock()) + if (auto p = j.second.lock()) { bytes b; uint n = 0; @@ -820,7 +852,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) ts.appendRaw(_bc.block(_bc.currentHash())).swapOut(b); seal(b); for (auto j: m_peers) - if (auto p = j.lock()) + if (auto p = j.second.lock()) { if (!p->m_knownBlocks.count(_bc.currentHash())) p->send(&b); @@ -869,7 +901,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) (PeerSession::prep(s).appendList(1) << GetPeersPacket).swapOut(b); seal(b); for (auto const& i: m_peers) - if (auto p = i.lock()) + if (auto p = i.second.lock()) if (p->isOpen()) p->send(&b); m_lastPeersRequest = chrono::steady_clock::now(); @@ -881,8 +913,8 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) break; } - connect(m_incomingPeers.back()); - m_incomingPeers.pop_back(); + connect(m_incomingPeers.begin()->second); + m_incomingPeers.erase(m_incomingPeers.begin()); } } } @@ -902,7 +934,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) shared_ptr worst; unsigned agedPeers = 0; for (auto i: m_peers) - if (auto p = i.lock()) + if (auto p = i.second.lock()) if ((m_mode != NodeMode::PeerServer || p->m_caps != 0x01) && chrono::steady_clock::now() > p->m_connect + chrono::milliseconds(old)) // don't throw off new peers; peer-servers should never kick off other peer-servers. { ++agedPeers; @@ -911,7 +943,7 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) } if (!worst || agedPeers <= m_idealPeerCount) break; - worst->disconnect(); + worst->disconnect(TooManyPeers); } } @@ -924,7 +956,7 @@ std::vector PeerServer::peers() const this_thread::sleep_for(chrono::milliseconds(200)); std::vector ret; for (auto& i: m_peers) - if (auto j = i.lock()) + if (auto j = i.second.lock()) if (j->m_socket.is_open()) ret.push_back(j->m_info); return ret; @@ -933,6 +965,6 @@ std::vector PeerServer::peers() const void PeerServer::pingAll() { for (auto& i: m_peers) - if (auto j = i.lock()) + if (auto j = i.second.lock()) j->ping(); } diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 66d05e02a..f8a28335a 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -60,6 +61,19 @@ enum PacketType GetTransactionsPacket }; +enum DisconnectReason +{ + DisconnectRequested = 0, + TCPError, + BadProtocol, + UselessPeer, + TooManyPeers, + DuplicatePeer, + WrongGenesis, + IncompatibleProtocol, + ClientQuit +}; + class PeerServer; struct PeerInfo @@ -79,7 +93,7 @@ public: ~PeerSession(); void start(); - void disconnect(); + void disconnect(int _reason); void ping(); @@ -102,6 +116,7 @@ private: bi::tcp::socket m_socket; std::array m_data; PeerInfo m_info; + Public m_id; bytes m_incoming; uint m_protocolVersion; @@ -137,7 +152,7 @@ public: /// Start server, listening for connections on the given port. PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, short _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); /// Start server, but don't listen. - PeerServer(std::string const& _clientVersion, uint _networkId); + PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m = NodeMode::Full); ~PeerServer(); /// Connect to a peer explicitly. @@ -172,7 +187,7 @@ private: void populateAddresses(); void determinePublic(std::string const& _publicAddress, bool _upnp); void ensureAccepting(); - std::vector potentialPeers(); + std::map potentialPeers(); std::string m_clientVersion; NodeMode m_mode = NodeMode::Full; @@ -186,13 +201,14 @@ private: UPnP* m_upnp = nullptr; bi::tcp::endpoint m_public; + KeyPair m_key; uint m_requiredNetworkId; - std::vector> m_peers; + std::map> m_peers; std::vector m_incomingTransactions; std::vector m_incomingBlocks; - std::vector m_incomingPeers; + std::multimap m_incomingPeers; h256 m_latestBlockSent; std::set m_transactionsSent; diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 7c3b07afe..86163f0af 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -278,8 +278,7 @@ public: RLPStream& append(bytes const& _s) { return append(bytesConstRef(&_s)); } RLPStream& append(std::string const& _s) { return append(bytesConstRef(_s)); } RLPStream& append(char const* _s) { return append(std::string(_s)); } - RLPStream& append(h160 _s, bool _compact = false) { return append(_s.ref(), _compact); } - RLPStream& append(h256 _s, bool _compact = false) { return append(_s.ref(), _compact); } + template RLPStream& append(FixedHash _s, bool _compact = false) { return append(_s.ref(), _compact); } /// Appends an arbitrary RLP fragment - this *must* be a single item. RLPStream& append(RLP const& _rlp, uint _itemCount = 1) { return appendRaw(_rlp.data(), _itemCount); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index fe1efe576..f2424bec8 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -46,20 +46,15 @@ using namespace std; using namespace eth; -u256 const State::c_stepFee = 10000; -u256 const State::c_dataFee = 20000; -u256 const State::c_memoryFee = 30000; -u256 const State::c_extroFee = 40000; -u256 const State::c_cryptoFee = 50000; -u256 const State::c_newContractFee = 60000; -u256 const State::c_txFee = 0; -u256 const State::c_blockReward = 1000000000000; - -#if NDEBUG -u256 const eth::c_genesisDifficulty = (u256)1 << 22; -#else +u256 const c_stepFee = 1; +u256 const c_dataFee = 20; +u256 const c_memoryFee = 5; +u256 const c_extroFee = 40; +u256 const c_cryptoFee = 20; +u256 const c_newContractFee = 100; +u256 const c_txFee = 100; + u256 const eth::c_genesisDifficulty = (u256)1 << 22; -#endif std::map const& eth::genesisState() { @@ -90,8 +85,14 @@ Overlay State::openDB(std::string _path, bool _killExisting) return Overlay(db); } -State::State(Address _coinbaseAddress, Overlay const& _db): m_db(_db), m_state(&m_db), m_ourAddress(_coinbaseAddress) +State::State(Address _coinbaseAddress, Overlay const& _db): + m_db(_db), + m_state(&m_db), + m_ourAddress(_coinbaseAddress) { + m_blockReward = u256(15000000000) * 100000000; + m_fees.setMultiplier(u256(100000) * 1000000000); + secp256k1_start(); // Initialise to the state entailed by the genesis block; this guarantees the trie is built correctly. @@ -106,6 +107,46 @@ State::State(Address _coinbaseAddress, Overlay const& _db): m_db(_db), m_state(& assert(m_state.root() == m_previousBlock.stateRoot); } +State::State(State const& _s): + m_db(_s.m_db), + m_state(&m_db, _s.m_state.root()), + m_transactions(_s.m_transactions), + m_cache(_s.m_cache), + m_previousBlock(_s.m_previousBlock), + m_currentBlock(_s.m_currentBlock), + m_currentNumber(_s.m_currentNumber), + m_ourAddress(_s.m_ourAddress), + m_fees(_s.m_fees), + m_blockReward(_s.m_blockReward) +{ +} + +State& State::operator=(State const& _s) +{ + m_db = _s.m_db; + m_state.open(&m_db, _s.m_state.root()); + m_transactions = _s.m_transactions; + m_cache = _s.m_cache; + m_previousBlock = _s.m_previousBlock; + m_currentBlock = _s.m_currentBlock; + m_currentNumber = _s.m_currentNumber; + m_ourAddress = _s.m_ourAddress; + m_fees = _s.m_fees; + m_blockReward = _s.m_blockReward; + return *this; +} + +void FeeStructure::setMultiplier(u256 _x) +{ + m_stepFee = c_stepFee * _x; + m_dataFee = c_dataFee * _x; + m_memoryFee = c_memoryFee * _x; + m_extroFee = c_extroFee * _x; + m_cryptoFee = c_cryptoFee * _x; + m_newContractFee = c_newContractFee * _x; + m_txFee = c_txFee * _x; +} + void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const { auto it = m_cache.find(_a); @@ -525,11 +566,11 @@ void State::execute(bytesConstRef _rlp) void State::applyRewards(Addresses const& _uncleAddresses) { - u256 r = c_blockReward; + u256 r = m_blockReward; for (auto const& i: _uncleAddresses) { - addBalance(i, c_blockReward * 4 / 3); - r += c_blockReward / 8; + addBalance(i, m_blockReward * 4 / 3); + r += m_blockReward / 8; } addBalance(m_currentBlock.coinbaseAddress, r); } @@ -544,31 +585,32 @@ void State::executeBare(Transaction const& _t, Address _sender) throw InvalidNonce(nonceReq, _t.nonce); // Not considered invalid - just pointless. - if (balance(_sender) < _t.value + _t.fee) + u256 fee = _t.receiveAddress ? m_fees.m_txFee : (_t.data.size() * m_fees.m_memoryFee + m_fees.m_newContractFee); + if (balance(_sender) < _t.value + fee) throw NotEnoughCash(); - // TODO: check fee is sufficient? - // Increment associated nonce for sender. noteSending(_sender); if (_t.receiveAddress) { - subBalance(_sender, _t.value + _t.fee); + subBalance(_sender, _t.value + fee); addBalance(_t.receiveAddress, _t.value); - addBalance(m_currentBlock.coinbaseAddress, _t.fee); if (isContractAddress(_t.receiveAddress)) { MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction. - execute(_t.receiveAddress, _sender, _t.value, _t.fee, _t.data, &feeAdder.fee); + execute(_t.receiveAddress, _sender, _t.value, _t.data, &feeAdder.fee); } } else { - // Try to make a new contract - if (_t.fee < _t.data.size() * c_memoryFee + c_newContractFee) - throw FeeTooSmall(); +#if ETH_SENDER_PAYS_SETUP + if (balance(_sender) < _t.value + fee) +#else + if (_t.value < fee) +#endif + throw NotEnoughCash(); Address newAddress = low160(_t.sha3()); @@ -584,9 +626,13 @@ void State::executeBare(Transaction const& _t, Address _sender) else mem.at(i) = _t.data[i]; - subBalance(_sender, _t.value + _t.fee); +#if ETH_SENDER_PAYS_SETUP + subBalance(_sender, _t.value + fee); addBalance(newAddress, _t.value); - addBalance(m_currentBlock.coinbaseAddress, _t.fee); +#else + subBalance(_sender, _t.value); + addBalance(newAddress, _t.value - fee); +#endif } } @@ -598,7 +644,7 @@ inline Address asAddress(u256 _item) return left160(h256(_item)); } -void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) +void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* _totalFee) { std::vector stack; @@ -637,7 +683,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ { stepCount++; - bigint minerFee = stepCount > 16 ? c_stepFee : 0; + bigint minerFee = stepCount > 16 ? m_fees.m_stepFee : 0; bigint voidFee = 0; auto rawInst = mem(curPC); @@ -650,21 +696,21 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ case Instruction::STORE: require(2); if (!mem(stack.back()) && stack[stack.size() - 2]) - voidFee += c_memoryFee; + voidFee += m_fees.m_memoryFee; if (mem(stack.back()) && !stack[stack.size() - 2]) - voidFee -= c_memoryFee; + voidFee -= m_fees.m_memoryFee; // continue on to... case Instruction::LOAD: - minerFee += c_dataFee; + minerFee += m_fees.m_dataFee; break; case Instruction::EXTRO: case Instruction::BALANCE: - minerFee += c_extroFee; + minerFee += m_fees.m_extroFee; break; case Instruction::MKTX: - minerFee += c_txFee; + minerFee += m_fees.m_txFee; break; case Instruction::SHA256: @@ -674,7 +720,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ case Instruction::ECSIGN: case Instruction::ECRECOVER: case Instruction::ECVALID: - minerFee += c_cryptoFee; + minerFee += m_fees.m_cryptoFee; break; default: break; @@ -779,9 +825,6 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ case Instruction::TXVALUE: stack.push_back(_txValue); break; - case Instruction::TXFEE: - stack.push_back(_txFee); - break; case Instruction::TXDATAN: stack.push_back(_txData.size()); break; @@ -1069,8 +1112,6 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ stack.pop_back(); t.value = stack.back(); stack.pop_back(); - t.fee = stack.back(); - stack.pop_back(); auto itemCount = stack.back(); stack.pop_back(); @@ -1092,7 +1133,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ { require(1); Address dest = asAddress(stack.back()); - u256 minusVoidFee = myMemory.size() * c_memoryFee; + u256 minusVoidFee = myMemory.size() * m_fees.m_memoryFee; addBalance(dest, balance(_myAddress) + minusVoidFee); m_cache[_myAddress].kill(); // ...follow through to... diff --git a/libethereum/State.h b/libethereum/State.h index 7949dc0ea..1338e3adc 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -42,6 +42,21 @@ class BlockChain; extern const u256 c_genesisDifficulty; std::map const& genesisState(); +#define ETH_SENDER_PAYS_SETUP 1 + +struct FeeStructure +{ + /// The fee structure. Values yet to be agreed on... + void setMultiplier(u256 _x); ///< The current block multiplier. + u256 m_stepFee; + u256 m_dataFee; + u256 m_memoryFee; + u256 m_extroFee; + u256 m_cryptoFee; + u256 m_newContractFee; + u256 m_txFee; +}; + /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -53,6 +68,12 @@ public: /// Construct state object. State(Address _coinbaseAddress, Overlay const& _db); + /// Copy state object. + State(State const& _s); + + /// Copy state object. + State& operator=(State const& _s); + /// Set the coinbase address for any transactions we do. /// This causes a complete reset of current block. void setAddress(Address _coinbaseAddress) { m_ourAddress = _coinbaseAddress; resetCurrent(); } @@ -146,6 +167,12 @@ public: /// This might throw. u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit); + /// Get the fee associated for a contract created with the given data. + u256 fee(uint _dataCount) const { return m_fees.m_memoryFee * _dataCount + m_fees.m_newContractFee; } + + /// Get the fee associated for a normal transaction. + u256 fee() const { return m_fees.m_txFee; } + private: /// Fee-adder on destruction RAII class. struct MinerFeeAdder @@ -177,7 +204,7 @@ private: void executeBare(Transaction const& _t, Address _sender); /// Execute a contract transaction. - void execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); + void execute(Address _myAddress, Address _txSender, u256 _txValue, u256s const& _txData, u256* o_totalFee); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); @@ -199,16 +226,9 @@ private: Address m_ourAddress; ///< Our address (i.e. the address to which fees go). Dagger m_dagger; - - /// The fee structure. Values yet to be agreed on... - static const u256 c_stepFee; - static const u256 c_dataFee; - static const u256 c_memoryFee; - static const u256 c_extroFee; - static const u256 c_cryptoFee; - static const u256 c_newContractFee; - static const u256 c_txFee; - static const u256 c_blockReward; + + FeeStructure m_fees; + u256 m_blockReward; static std::string c_defaultPath; diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 53326cc6d..68afc7844 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -32,8 +32,7 @@ Transaction::Transaction(bytesConstRef _rlpData) nonce = rlp[0].toInt(); receiveAddress = rlp[1].toHash
(); value = rlp[2].toInt(); - fee = rlp[3].toInt(); - data.reserve(rlp[4].itemCountStrict()); + data.reserve(rlp[3].itemCountStrict()); for (auto const& i: rlp[4]) data.push_back(i.toInt()); vrs = Signature{ rlp[5].toInt(), rlp[6].toInt(), rlp[7].toInt() }; @@ -92,7 +91,7 @@ void Transaction::sign(Secret _priv) void Transaction::fillStream(RLPStream& _s, bool _sig) const { _s.appendList(_sig ? 8 : 5); - _s << nonce << receiveAddress << value << fee << data; + _s << nonce << receiveAddress << value << data; if (_sig) _s << vrs.v << vrs.r << vrs.s; } diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 2e077e875..bacd125f8 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -34,7 +34,7 @@ struct Signature u256 s; }; -// [ nonce, receiving_address, value, fee, [ data item 0, data item 1 ... data item n ], v, r, s ] +// [ nonce, receiving_address, value, [ data item 0, data item 1 ... data item n ], v, r, s ] struct Transaction { Transaction() {} @@ -44,7 +44,6 @@ struct Transaction u256 nonce; Address receiveAddress; u256 value; - u256 fee; u256s data; Signature vrs; diff --git a/test/crypto.cpp b/test/crypto.cpp index 71fc41292..215d772ee 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -41,7 +41,6 @@ int cryptoTest() Transaction t; t.nonce = 0; - t.fee = 0; t.value = 1; // 1 wei. t.receiveAddress = toAddress(sha3("123")); diff --git a/test/state.cpp b/test/state.cpp index 25663b851..64bd37acc 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -62,7 +62,6 @@ int stateTest() { Transaction t; t.nonce = s.transactionsFrom(myMiner.address()); - t.fee = 0; t.value = 1000; // 1e3 wei. t.receiveAddress = me.address(); t.sign(myMiner.secret());