Browse Source

GUI & network stuff.

cl-refactor
Gav Wood 11 years ago
parent
commit
baacd4ed5b
  1. 6
      TODO
  2. 98
      alephzero/Main.cpp
  3. 18
      alephzero/Main.h
  4. 93
      alephzero/Main.ui
  5. 6
      alephzero/alephzero.pro
  6. 98
      eth/main.cpp
  7. 82
      libethereum/BlockChain.cpp
  8. 6
      libethereum/BlockChain.h
  9. 65
      libethereum/Client.cpp
  10. 28
      libethereum/Client.h
  11. 11
      libethereum/Common.cpp
  12. 6
      libethereum/Common.h
  13. 13
      libethereum/Dagger.cpp
  14. 14
      libethereum/Dagger.h
  15. 158
      libethereum/PeerNetwork.cpp
  16. 48
      libethereum/PeerNetwork.h
  17. 9
      libethereum/State.cpp
  18. 7
      libethereum/State.h
  19. 25
      libethereum/TransactionQueue.cpp
  20. 2
      libethereum/TransactionQueue.h
  21. 4
      test/peer.cpp
  22. 4
      test/state.cpp

6
TODO

@ -13,6 +13,12 @@ Crypto stuff:
Better handling of corrupt blocks.
Network:
- Firewall-busting PeerConnections.
- Crypto on network.
- Respect idealPeerCount.
- Peer network traversal for peer discovery.
### GAV

98
alephzero/Main.cpp

@ -1,17 +1,111 @@
#include <QtWidgets>
#include <QtCore>
#include <libethereum/Dagger.h>
#include "Main.h"
#include "ui_Main.h"
using namespace std;
using namespace eth;
Main::Main(QWidget *parent) :
QDialog(parent),
ui(new Ui::Main)
ui(new Ui::Main),
m_client("AlephZero/v0.1")
{
setWindowFlags(Qt::Window);
ui->setupUi(this);
ui->transactions->setHtml("Hello world!");
readSettings();
refresh();
m_refresh = new QTimer(this);
connect(m_refresh, SIGNAL(timeout()), SLOT(refresh()));
m_refresh->start(1000);
}
Main::~Main()
{
writeSettings();
delete ui;
}
void Main::writeSettings()
{
QSettings s("ethereum", "alephzero");
QByteArray b;
b.resize(32);
memcpy(b.data(), &m_myKey, 32);
s.setValue("address", b);
}
void Main::readSettings()
{
QSettings s("ethereum", "alephzero");
QByteArray b = s.value("address").toByteArray();
if (b.isEmpty())
m_myKey = KeyPair::create();
else
{
h256 k;
memcpy(&k, b.data(), 32);
m_myKey = KeyPair(k);
}
m_client.setAddress(m_myKey.address());
/*for (uint i = 0; !s.value(QString("peer%1").arg(i)).isNull(); ++i)
{
s.value(QString("peer%1").arg(i)).toString();
}*/
}
void Main::refresh()
{
ui->balance->setText(QString::fromStdString(toString(m_client.state().balance(m_myKey.address()))) + " wei");
ui->peerCount->setText(QString::fromStdString(toString(m_client.peerCount())) + " peer(s)");
ui->address->setText(QString::fromStdString(asHex(m_client.state().address().asArray())));
ui->peers->clear();
for (PeerInfo const& i: m_client.peers())
ui->peers->addItem(QString("%3 ms - %1:%2 - %4").arg(i.endpoint.address().to_string().c_str()).arg(i.endpoint.port()).arg(chrono::duration_cast<chrono::milliseconds>(i.lastPing).count()).arg(i.clientVersion.c_str()));
auto d = m_client.blockChain().details();
auto diff = BlockInfo(m_client.blockChain().block()).difficulty;
ui->blockChain->setText(QString("%1 blocks @ (%3) - %2").arg(d.number).arg(toLog2(d.totalDifficulty)).arg(toLog2(diff)));
}
void Main::on_net_toggled()
{
if (ui->net->isChecked())
m_client.startNetwork(ui->port->value());
else
m_client.stopNetwork();
}
void Main::on_connect_clicked()
{
QString s = QInputDialog::getText(this, "Enter first peer", "Enter a peer to which a connection may be made", QLineEdit::Normal, "127.0.0.1:30303");
string host = s.section(":", 0, 0).toStdString();
short port = s.section(":", 1).toInt();
m_client.connect(host, port);
}
void Main::on_mine_toggled()
{
if (ui->mine->isChecked())
m_client.startMining();
else
m_client.stopMining();
}
void Main::on_send_clicked()
{
Secret s = Secret(fromUserHex(ui->address->text().toStdString()));
Address r = Address(fromUserHex(ui->destination->text().toStdString()));
m_client.transact(s, r, ui->value->value(), ui->fee->value());
refresh();
}
void Main::on_create_clicked()
{
KeyPair p = KeyPair::create();
QString s = QString::fromStdString("New key:\n" + asHex(p.secret().asArray()) + "\nAddress: " + asHex(p.address().asArray()));
QMessageBox::information(this, "New Key", s, QMessageBox::Ok);
}

18
alephzero/Main.h

@ -1,7 +1,9 @@
#ifndef MAIN_H
#define MAIN_H
#include <QAbstractListModel>
#include <QDialog>
#include <libethereum/Client.h>
namespace Ui {
class Main;
@ -17,11 +19,25 @@ public:
private slots:
void on_connect_clicked();
void on_mine_toggled();
void on_send_clicked();
void on_create_clicked();
void on_net_toggled();
void refresh();
private:
Client c;
void readSettings();
void writeSettings();
Ui::Main *ui;
eth::Client m_client;
eth::KeyPair m_myKey;
std::vector<bi::tcp::endpoint> m_peers;
QTimer* m_refresh;
};
#endif // MAIN_H

93
alephzero/Main.ui

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>787</width>
<height>555</height>
<width>978</width>
<height>556</height>
</rect>
</property>
<property name="windowTitle">
@ -29,6 +29,29 @@
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="net">
<property name="text">
<string>Net</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="port">
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
<property name="value">
<number>30303</number>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="connect">
<property name="text">
@ -36,11 +59,30 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="value">
<property name="suffix">
<string> wei</string>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>1000</number>
</property>
</widget>
</item>
<item>
@ -51,6 +93,12 @@
<property name="prefix">
<string>(fee </string>
</property>
<property name="maximum">
<number>430000000</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item>
@ -63,6 +111,13 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="create">
<property name="text">
<string>Create</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@ -70,12 +125,15 @@
</property>
<property name="sizeHint" stdset="0">
<size>
<width>205</width>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QWidget" name="mineProgress" native="true"/>
</item>
<item>
<widget class="QToolButton" name="mine">
<property name="text">
@ -86,9 +144,6 @@
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="mineProgress" native="true"/>
</item>
</layout>
</widget>
</item>
@ -103,16 +158,17 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QListView" name="accounts">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
</widget>
<widget class="QTextEdit" name="transactions">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<widget class="QListWidget" name="peers"/>
<widget class="QListWidget" name="accounts">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
</widget>
</widget>
</item>
<item>
@ -129,10 +185,21 @@
</widget>
</item>
<item>
<widget class="QLineEdit" name="address"/>
<widget class="QLineEdit" name="address">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="blockChain">
<property name="text">
<string>1 block</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="peers">
<widget class="QLabel" name="peerCount">
<property name="text">
<string>0 peers</string>
</property>

6
alephzero/alephzero.pro

@ -6,12 +6,14 @@
QT += core gui widgets
QMAKE_CXXFLAGS += -std=c++11
TARGET = alephzero
TEMPLATE = app
QMAKE_LIBDIR += ../../cpp-ethereum-build/libethereum ../../secp256k1 ../../cryptopp562
LIBS += -lethereum -lsecp256k1 -lleveldb -lcryptopp -lgmp -lboost_system -lboost_filesystem
LIBS += -Wl,-rpath,../../cpp-ethereum-build/libethereum -Wl,-rpath,../../secp256k1 -Wl,-rpath,../../cryptopp562 -lethereum -lsecp256k1 -lleveldb -lcryptopp -lgmp -lboost_filesystem -lboost_system
SOURCES += main.cpp\
Main.cpp
@ -20,6 +22,6 @@ HEADERS += Main.h
FORMS += Main.ui
INCLUDEPATH = ../../secp256k1/include ../../cryptopp562
INCLUDEPATH = ../../secp256k1/include ../../cryptopp562 ../../cpp-ethereum

98
eth/main.cpp

@ -20,17 +20,20 @@
* Ethereum client.
*/
#include "Client.h"
#include "PeerNetwork.h"
#include "BlockChain.h"
#include "State.h"
using namespace std;
using namespace eth;
int main()
int main(int argc, char** argv)
{
short listenPort = 30303;
string remoteHost;
short remotePort = 30303;
bool interactive = false;
string dbPath;
// Our address.
Address us; // TODO: should be loaded from config file
@ -45,47 +48,72 @@ int main()
else if (arg == "-p" && i + 1 < argc)
remotePort = atoi(argv[++i]);
else if (arg == "-a" && i + 1 < argc)
us = h256(fromUserHex(argv[++i]));
us = h160(fromUserHex(argv[++i]));
else if (arg == "-i")
interactive = true;
else if (arg == "-d")
dbPath = arg;
else
remoteHost = argv[i];
}
BlockChain bc; // Maintains block database.
TransactionQueue tq; // Maintains list of incoming transactions not yet on the block chain.
Overlay stateDB = State::openDB(); // Acts as the central point for the state database, so multiple States can share it.
State s(us, stateDB);
// Synchronise the state according to the block chain - i.e. replay all transactions in block chain, in order.
// In practise this won't need to be done since the State DB will contain the keys for the tries for most recent (and many old) blocks.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
s.sync(bc);
s.sync(tq);
PeerServer net(bc, 0, 30303); // TODO: Implement - should run in background and send us events when blocks found and allow us to send blocks as required.
while (true)
Client c("Ethereum(++)/v0.1", us, dbPath);
if (interactive)
{
// Process network events.
net.process();
// Synchronise block chain with network.
// Will broadcast any of our (new) transactions and blocks, and collect & add any of their (new) transactions and blocks.
net.sync(bc, tq);
// Synchronise state to block chain.
// This should remove any transactions on our queue that are included within our state.
// It also guarantees that the state reflects the longest (valid!) chain on the block chain.
// This might mean reverting to an earlier state and replaying some blocks, or, (worst-case:
// if there are no checkpoints before our fork) reverting to the genesis block and replaying
// all blocks.
s.sync(bc); // Resynchronise state with block chain & trans
s.sync(tq);
cout << "Ethereum (++)" << endl;
cout << " By Gav Wood, Tim Hughes & team Ethereum, (c) 2013, 2014" << endl << endl;
// Mine for a while.
bytes b = s.mine(100);
if (b.size())
// Import block.
bc.attemptImport(b, stateDB);
while (true)
{
cout << "> " << flush;
std::string cmd;
cin >> cmd;
if (cmd == "netstart")
{
eth::uint port;
cin >> port;
c.startNetwork(port);
}
else if (cmd == "connect")
{
string addr;
eth::uint port;
cin >> addr >> port;
c.connect(addr, port);
}
else if (cmd == "netstop")
{
c.stopNetwork();
}
else if (cmd == "minestart")
{
c.startMining();
}
else if (cmd == "minestop")
{
c.stopMining();
}
else if (cmd == "transact")
{
string sechex;
string rechex;
u256 amount;
u256 fee;
cin >> sechex >> rechex >> amount >> fee;
Secret secret = h256(fromUserHex(sechex));
Address dest = h160(fromUserHex(rechex));
c.transact(secret, dest, amount, fee);
}
}
}
else
{
c.startNetwork(listenPort, remoteHost, remotePort);
c.startMining();
while (true)
sleep(1);
}
return 0;
}

82
libethereum/BlockChain.cpp

@ -97,63 +97,55 @@ BlockChain::~BlockChain()
void BlockChain::import(bytes const& _block, Overlay const& _db)
{
try
{
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi(&_block);
bi.verifyInternals(&_block);
// VERIFY: populates from the block and checks the block is internally coherent.
BlockInfo bi(&_block);
bi.verifyInternals(&_block);
auto newHash = eth::sha3(_block);
auto newHash = eth::sha3(_block);
// Check block doesn't already exist first!
if (details(newHash))
return;
// Check block doesn't already exist first!
if (details(newHash))
throw AlreadyHaveBlock();
// Work out its number as the parent's number + 1
auto pd = details(bi.parentHash);
if (!pd)
// We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on.
return;
// Work out its number as the parent's number + 1
auto pd = details(bi.parentHash);
if (!pd)
// We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on.
throw UnknownParent();
// Check family:
BlockInfo biParent(block(bi.parentHash));
bi.verifyParent(biParent);
// Check family:
BlockInfo biParent(block(bi.parentHash));
bi.verifyParent(biParent);
// Check transactions are valid and that they result in a state equivalent to our state_root.
State s(bi.coinbaseAddress, _db);
s.sync(*this, bi.parentHash);
// Check transactions are valid and that they result in a state equivalent to our state_root.
State s(bi.coinbaseAddress, _db);
s.sync(*this, bi.parentHash);
// Get total difficulty increase and update state, checking it.
BlockInfo biGrandParent;
if (pd.number)
biGrandParent.populate(block(pd.parent));
auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true);
u256 td = pd.totalDifficulty + tdIncrease;
// Get total difficulty increase and update state, checking it.
BlockInfo biGrandParent;
if (pd.number)
biGrandParent.populate(block(pd.parent));
auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true);
u256 td = pd.totalDifficulty + tdIncrease;
// All ok - insert into DB
m_details[newHash] = BlockDetails((uint)pd.number + 1, td, bi.parentHash, {});
m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)eth::ref(m_details[newHash].rlp()));
// All ok - insert into DB
m_details[newHash] = BlockDetails((uint)pd.number + 1, td, bi.parentHash, {});
m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)eth::ref(m_details[newHash].rlp()));
m_details[bi.parentHash].children.push_back(newHash);
m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&bi.parentHash, 32), (ldb::Slice)eth::ref(m_details[bi.parentHash].rlp()));
m_details[bi.parentHash].children.push_back(newHash);
m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&bi.parentHash, 32), (ldb::Slice)eth::ref(m_details[bi.parentHash].rlp()));
m_db->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)ref(_block));
m_db->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)ref(_block));
// 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));
}
else
{
cerr << "*** WARNING: Imported block not newest (otd=" << m_details[m_lastBlockHash].totalDifficulty << ", td=" << td << ")" << 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));
}
catch (...)
else
{
// Exit silently on exception(?)
return;
cerr << "*** WARNING: Imported block not newest (otd=" << m_details[m_lastBlockHash].totalDifficulty << ", td=" << td << ")" << endl;
}
}

6
libethereum/BlockChain.h

@ -62,6 +62,9 @@ static const h256s NullH256s;
class Overlay;
class AlreadyHaveBlock: public std::exception {};
class UnknownParent: public std::exception {};
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
*/
@ -84,15 +87,18 @@ public:
/// Get the number of the last block of the longest chain.
BlockDetails const& details(h256 _hash) const;
BlockDetails const& details() const { return details(currentHash()); }
/// Get a given block (RLP format).
bytesConstRef block(h256 _hash) const;
bytesConstRef block() const { return block(currentHash()); }
/// Get a given block (RLP format).
h256 currentHash() const { return m_lastBlockHash; }
/// Get the coinbase address of a given block.
Address coinbaseAddress(h256 _hash) const;
Address coinbaseAddress() const { return coinbaseAddress(currentHash()); }
private:
/// Get fully populated from disk DB.

65
libethereum/Client.cpp

@ -24,18 +24,19 @@
using namespace std;
using namespace eth;
Client::Client(std::string const& _dbPath):
Client::Client(std::string const& _clientVersion, Address _us, std::string const& _dbPath):
m_clientVersion(_clientVersion),
m_bc(_dbPath),
m_stateDB(State::openDB(_dbPath)),
m_s(m_stateDB)
m_s(_us, m_stateDB)
{
Defaults::setDBPath(_dbPath);
// Synchronise the state according to the block chain - i.e. replay all transactions in block chain, in order.
// In practise this won't need to be done since the State DB will contain the keys for the tries for most recent (and many old) blocks.
// TODO: currently it contains keys for *all* blocks. Make it remove old ones.
s.sync(bc);
s.sync(tq);
m_s.sync(m_bc);
m_s.sync(m_tq);
m_work = new thread([&](){ while (m_workState != Deleting) work(); m_workState = Deleted; });
}
@ -48,31 +49,22 @@ Client::~Client()
usleep(10000);
}
void Client::transact(Address _dest, u256 _amount, u256 _fee, u256s _data = u256s(), Secret _secret)
{
}
BlockChain const& Client::blockChain() const
{
}
TransactionQueue const& Client::transactionQueue() const
{
}
unsigned Client::peerCount() const
{
}
void Client::startNetwork(short _listenPort = 30303, std::string const& _seedHost, short _port = 30303)
void Client::startNetwork(short _listenPort, std::string const& _seedHost, short _port)
{
if (m_net)
return;
m_net = new PeerServer(m_bc, 0, _listenPort);
m_net = new PeerServer(m_clientVersion, m_bc, 0, _listenPort);
if (_seedHost.size())
m_net->connect(_seedHost, _port);
}
void Client::connect(std::string const& _seedHost, short _port)
{
if (!m_net)
return;
m_net->connect(_seedHost, _port);
}
void Client::stopNetwork()
{
delete m_net;
@ -89,16 +81,25 @@ void Client::stopMining()
m_doMine = false;
}
std::pair<unsigned, unsigned> Client::miningProgress() const
void Client::transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u256s _data)
{
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());
}
void Client::work(string const& _seedHost, short _port)
void Client::work()
{
// 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.
m_net->process(m_bc, m_tq);
if (m_net)
m_net->process(m_bc, m_tq, m_stateDB);
// Synchronise state to block chain.
// This should remove any transactions on our queue that are included within our state.
@ -112,10 +113,18 @@ void Client::work(string const& _seedHost, short _port)
if (m_doMine)
{
// Mine for a while.
bytes b = s.mine(100);
MineInfo mineInfo = m_s.mine(100);
m_mineProgress.best = max(m_mineProgress.best, mineInfo.best);
m_mineProgress.current = mineInfo.best;
m_mineProgress.requirement = mineInfo.requirement;
if (b.size())
if (mineInfo.completed())
{
// Import block.
bc.attemptImport(b, stateDB);
m_bc.attemptImport(m_s.blockData(), m_stateDB);
m_mineProgress.best = 0;
}
}
else
usleep(100000);
}

28
libethereum/Client.h

@ -27,34 +27,47 @@
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "State.h"
#include "Dagger.h"
#include "PeerNetwork.h"
namespace eth
{
struct MineProgress
{
uint requirement;
uint best;
uint current;
};
class Client
{
public:
Client(std::string const& _dbPath);
Client(std::string const& _clientVersion, Address _us = Address(), std::string const& _dbPath = std::string());
~Client();
void transact(Address _dest, u256 _amount, u256 _fee, u256s _data = u256s(), Secret _secret);
void transact(Secret _secret, Address _dest, u256 _amount, u256 _fee, u256s _data = u256s());
BlockChain const& blockChain() const;
TransactionQueue const& transactionQueue() const;
State const& state() const { return m_s; }
BlockChain const& blockChain() const { return m_bc; }
TransactionQueue const& transactionQueue() const { return m_tq; }
unsigned peerCount() const;
std::vector<PeerInfo> peers() { return m_net ? m_net->peers() : std::vector<PeerInfo>(); }
unsigned peerCount() const { return m_net ? m_net->peerCount() : 0; }
void startNetwork(short _listenPort = 30303, std::string const& _seedHost, short _port = 30303);
void startNetwork(short _listenPort = 30303, std::string const& _seedHost = std::string(), short _port = 30303);
void connect(std::string const& _seedHost, short _port = 30303);
void stopNetwork();
void setAddress(Address _us) { m_s.setAddress(_us); }
void startMining();
void stopMining();
std::pair<unsigned, unsigned> miningProgress() const;
MineProgress miningProgress() const { return m_mineProgress; }
private:
void work();
std::string m_clientVersion; ///< Our end-application client's name/version.
BlockChain m_bc; ///< Maintains block database.
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.
@ -63,6 +76,7 @@ private:
std::thread* m_work; ///< The work thread.
enum { Active = 0, Deleting, Deleted } m_workState = Active;
bool m_doMine = false; ///< Are we supposed to be mining?
MineProgress m_mineProgress;
};
}

11
libethereum/Common.cpp

@ -169,3 +169,14 @@ Address eth::toAddress(Secret _private)
#endif
return ret;
}
KeyPair KeyPair::create()
{
static std::mt19937_64 s_eng(time(0));
std::uniform_int_distribution<byte> d(0, 255);
KeyPair ret;
for (uint i = 0; i < 20; ++i)
ret.m_secret[i] = d(s_eng);
ret.m_address = toAddress(ret.m_secret);
return ret;
}

6
libethereum/Common.h

@ -74,7 +74,7 @@ public:
FixedHash() { m_data.fill(0); }
FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); }
explicit FixedHash(bytes const& _b) { memcpy(m_data.data(), _b.data(), min<uint>(_b.size(), N)); }
explicit FixedHash(bytes const& _b) { memcpy(m_data.data(), _b.data(), std::min<uint>(_b.size(), N)); }
operator Arith() const { return fromBigEndian<Arith>(m_data); }
@ -99,6 +99,8 @@ public:
byte const* data() const { return m_data.data(); }
bytes asBytes() const { return bytes(data(), data() + N); }
std::array<byte, N>& asArray() { return m_data; }
std::array<byte, N> const& asArray() const { return m_data; }
private:
std::array<byte, N> m_data;
@ -335,6 +337,8 @@ public:
KeyPair() {}
KeyPair(Secret _k): m_secret(_k), m_address(toAddress(_k)) {}
static KeyPair create();
Secret secret() const { return m_secret; }
Address address() const { return m_address; }

13
libethereum/Dagger.cpp

@ -22,15 +22,20 @@ namespace eth
#if FAKE_DAGGER
bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool const& _continue)
MineInfo Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool const& _continue)
{
MineInfo ret{toLog2(_difficulty), 0};
static std::mt19937_64 s_eng((time(0)));
o_solution = std::uniform_int_distribution<uint>(0, ~(uint)0)(s_eng);
// evaluate until we run out of time
for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout) && _continue; o_solution += 1)
if (verify(_root, o_solution, _difficulty))
return true;
return false;
{
auto e = (u256)eval(_root, o_solution);
ret.best = max(ret.best, (uint)log2((double)e));
if (e > _difficulty)
return ret;
}
return ret;
}
#else

14
libethereum/Dagger.h

@ -7,6 +7,18 @@
namespace eth
{
inline uint toLog2(u256 _d)
{
return (uint)log2((double)_d);
}
struct MineInfo
{
uint requirement;
uint best;
bool completed() { return best > requirement; }
};
#if FAKE_DAGGER
class Dagger
@ -15,7 +27,7 @@ public:
static h256 eval(h256 const& _root, u256 const& _nonce) { h256 b = (h256)((u256)_root ^ _nonce); return sha3(bytesConstRef((byte const*)&b, 32)); }
static bool verify(h256 const& _root, u256 const& _nonce, u256 const& _difficulty) { return (u256)eval(_root, _nonce) > _difficulty; }
bool mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100, bool const& _continue = bool(true));
MineInfo mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100, bool const& _continue = bool(true));
};
#else

158
libethereum/PeerNetwork.cpp

@ -21,11 +21,15 @@
#include "Common.h"
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "PeerNetwork.h"
using namespace std;
using namespace eth;
PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId): m_server(_s), m_socket(std::move(_socket)), m_reqNetworkId(_rNId)
PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId):
m_server(_s),
m_socket(std::move(_socket)),
m_reqNetworkId(_rNId)
{
}
@ -59,11 +63,12 @@ bool PeerSession::interpret(RLP const& _r)
break;
}
case Pong:
cout << "Latency: " << chrono::duration_cast<chrono::milliseconds>(std::chrono::steady_clock::now() - m_ping).count() << " ms" << endl;
m_lastPing = std::chrono::steady_clock::now() - m_ping;
cout << "Latency: " << chrono::duration_cast<chrono::milliseconds>(m_lastPing).count() << " ms" << endl;
break;
case GetPeers:
{
std::vector<bi::tcp::endpoint> peers = m_server->peers();
std::vector<bi::tcp::endpoint> peers = m_server->potentialPeers();
RLPStream s;
prep(s).appendList(2);
s << Peers;
@ -134,29 +139,39 @@ RLPStream& PeerSession::prep(RLPStream& _s)
return _s.appendRaw(bytes(8, 0));
}
void PeerSession::seal(bytes& _b)
{
_b[0] = 0x22;
_b[1] = 0x40;
_b[2] = 0x08;
_b[3] = 0x91;
uint32_t len = _b.size() - 8;
_b[4] = len >> 24;
_b[5] = len >> 16;
_b[6] = len >> 8;
_b[7] = len;
}
void PeerSession::sealAndSend(RLPStream& _s)
{
bytes b;
_s.swapOut(b);
b[0] = 0x22;
b[1] = 0x40;
b[2] = 0x08;
b[3] = 0x91;
uint32_t len = b.size() - 8;
b[4] = len >> 24;
b[5] = len >> 16;
b[6] = len >> 8;
b[7] = len;
send(b);
sendDestroy(b);
}
void PeerSession::send(bytes& _msg)
void PeerSession::sendDestroy(bytes& _msg)
{
std::shared_ptr<bytes> buffer = std::make_shared<bytes>();
swap(*buffer, _msg);
ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) {});
}
void PeerSession::send(bytesConstRef _msg)
{
std::shared_ptr<bytes> buffer = std::make_shared<bytes>(_msg.toBytes());
ba::async_write(m_socket, ba::buffer(*buffer), [=](boost::system::error_code ec, std::size_t length) {});
}
void PeerSession::disconnect()
{
RLPStream s;
@ -172,9 +187,11 @@ void PeerSession::start()
cout << "Starting session." << endl;
RLPStream s;
prep(s);
s.appendList(4) << (uint)Hello << (uint)0 << (uint)0 << "Ethereum++/0.1.0";
s.appendList(4) << (uint)Hello << (uint)0 << (uint)0 << m_server->m_clientVersion;
sealAndSend(s);
ping();
doRead();
// TODO: ask for latest block chain.
}
void PeerSession::doRead()
@ -207,7 +224,8 @@ void PeerSession::doRead()
});
}
PeerServer::PeerServer(BlockChain const& _ch, uint _networkId, short _port):
PeerServer::PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, short _port):
m_clientVersion(_clientVersion),
m_chain(&_ch),
m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), _port)),
m_socket(m_ioService),
@ -216,14 +234,15 @@ PeerServer::PeerServer(BlockChain const& _ch, uint _networkId, short _port):
doAccept();
}
PeerServer::PeerServer(uint _networkId):
PeerServer::PeerServer(std::string const& _clientVersion, uint _networkId):
m_clientVersion(_clientVersion),
m_acceptor(m_ioService, bi::tcp::endpoint(bi::tcp::v4(), 0)),
m_socket(m_ioService),
m_requiredNetworkId(_networkId)
{
}
std::vector<bi::tcp::endpoint> PeerServer::peers()
std::vector<bi::tcp::endpoint> PeerServer::potentialPeers()
{
std::vector<bi::tcp::endpoint> ret;
bool haveLocal = false;
@ -275,7 +294,7 @@ bool PeerServer::connect(string const& _addr, uint _port)
}
}
void PeerServer::process(BlockChain& _bc, TransactionQueue const& _tq)
void PeerServer::process(BlockChain& _bc)
{
m_ioService.poll();
for (auto i = m_peers.begin(); i != m_peers.end();)
@ -283,16 +302,101 @@ void PeerServer::process(BlockChain& _bc, TransactionQueue const& _tq)
{}
else
i = m_peers.erase(i);
/*
while (incomingData())
}
void PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o)
{
if (m_latestBlockSent == h256())
{
// First time - just initialise.
m_latestBlockSent = _bc.currentHash();
for (auto const& i: _tq.transactions())
m_transactionsSent.insert(i.first);
}
process(_bc);
for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end();)
if (!_tq.import(*it))
m_transactionsSent.insert(sha3(*it)); // if we already had the transaction, then don't bother sending it on.
m_incomingTransactions.clear();
// Send any new transactions.
{
bytes b;
uint n = 0;
for (auto const& i: _tq.transactions())
if (!m_transactionsSent.count(i.first))
{
b += i.second;
++n;
m_transactionsSent.insert(i.first);
}
if (n)
{
// import new block
bytes const& data = net.incomingData();
if (!tq.attemptImport(data) && !_bc.attemptImport(data))
handleMessage(data);
popIncoming();
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);
}
*/
}
// Send any new blocks.
{
auto h = _bc.currentHash();
if (h != m_latestBlockSent)
{
// TODO: find where they diverge and send complete new branch.
RLPStream ts;
PeerSession::prep(ts);
ts.appendList(2) << Blocks;
bytes b;
ts.appendList(1).appendRaw(_bc.block(_bc.currentHash())).swapOut(b);
PeerSession::seal(b);
for (auto j: m_peers)
if (auto p = j.lock())
p->send(&b);
}
}
for (bool accepted = 1; accepted;)
{
accepted = 0;
for (auto it = m_incomingBlocks.begin(); it != m_incomingBlocks.end();)
{
try
{
_bc.import(*it, _o);
it = m_incomingBlocks.erase(it);
++accepted;
}
catch (UnknownParent)
{
// Don't (yet) know its parent. Leave it for later.
++it;
}
catch (...)
{
// Some other error - erase it.
it = m_incomingBlocks.erase(it);
}
}
}
}
std::vector<PeerInfo> PeerServer::peers() const
{
const_cast<PeerServer*>(this)->pingAll();
usleep(200000);
std::vector<PeerInfo> ret;
for (auto& i: m_peers)
if (auto j = i.lock())
ret.push_back(PeerInfo{j->m_clientVersion, j->m_socket.remote_endpoint(), j->m_lastPing});
return ret;
}
void PeerServer::pingAll()

48
libethereum/PeerNetwork.h

@ -71,9 +71,11 @@ private:
void doWrite(std::size_t length);
bool interpret(RLP const& _r);
RLPStream& prep(RLPStream& _s);
static RLPStream& prep(RLPStream& _s);
static void seal(bytes& _b);
void sealAndSend(RLPStream& _s);
void send(bytes& _msg);
void sendDestroy(bytes& _msg);
void send(bytesConstRef _msg);
PeerServer* m_server;
bi::tcp::socket m_socket;
@ -86,6 +88,14 @@ private:
uint m_reqNetworkId;
std::chrono::steady_clock::time_point m_ping;
std::chrono::steady_clock::duration m_lastPing;
};
struct PeerInfo
{
std::string clientVersion;
bi::tcp::endpoint endpoint;
std::chrono::steady_clock::duration lastPing;
};
class PeerServer
@ -94,9 +104,9 @@ class PeerServer
public:
/// Start server, listening for connections on the given port.
PeerServer(BlockChain const& _ch, uint _networkId, short _port);
PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, short _port);
/// Start server, but don't listen.
PeerServer(uint _networkId);
PeerServer(std::string const& _clientVersion, uint _networkId);
/// Connect to a peer explicitly.
bool connect(std::string const& _addr = "127.0.0.1", uint _port = 30303);
@ -104,27 +114,26 @@ public:
/// 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.
/// Ideally all time-consuming I/O is done in a background thread, but you get this call every 100ms or so anyway.
void process(BlockChain& _bc, TransactionQueue const&);
/// Get an incoming transaction from the queue. @returns bytes() if nothing waiting.
std::vector<bytes> const& incomingTransactions() { return m_incomingTransactions; }
/// Get an incoming transaction from the queue. @returns bytes() if nothing waiting.
void incomingTransactions(std::vector<bytes>& o_out) { swap(o_out, m_incomingTransactions); m_incomingTransactions.clear(); }
/// Get an incoming transaction from the queue. @returns bytes() if nothing waiting.
void incomingBlocks(std::vector<bytes>& o_out) { swap(o_out, m_blocks); m_blocks.clear(); }
void process(BlockChain& _bc, TransactionQueue&, Overlay& _o);
void process(BlockChain& _bc);
/// Get number of peers connected.
unsigned peerCount() const { return m_peers.size(); }
/// Remove incoming transactions from the queue.
void clearIncomingTransactions() {}
/// Set ideal number of peers.
void setIdealPeerCount(uint _n) { m_idealPeerCount = _n; }
/// Get peer information.
std::vector<PeerInfo> peers() const;
/// Ping the peers.
void pingAll();
private:
void doAccept();
std::vector<bi::tcp::endpoint> peers();
std::vector<bi::tcp::endpoint> potentialPeers();
std::string m_clientVersion;
BlockChain const* m_chain = nullptr;
ba::io_service m_ioService;
@ -137,6 +146,11 @@ private:
std::vector<bytes> m_incomingTransactions;
std::vector<bytes> m_incomingBlocks;
std::vector<bi::tcp::endpoint> m_incomingPeers;
h256 m_latestBlockSent;
std::set<h256> m_transactionsSent;
unsigned m_idealPeerCount;
};

9
libethereum/State.cpp

@ -360,7 +360,7 @@ void State::commitToMine(BlockChain const& _bc)
m_currentBlock.parentHash = m_previousBlock.hash;
}
bytes const& State::mine(uint _msTimeout)
MineInfo State::mine(uint _msTimeout)
{
// Update timestamp according to clock.
m_currentBlock.timestamp = time(0);
@ -369,11 +369,12 @@ bytes const& State::mine(uint _msTimeout)
m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock);
// TODO: Miner class that keeps dagger between mine calls (or just non-polling mining).
if (m_dagger.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout))
MineInfo ret = m_dagger.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout);
if (ret.completed())
{
// Got it!
// Commit our database to disk or nothing other than this state will understand, which would make verifying the state_root rather difficult no?
// Commit to disk.
m_db.commit();
// Compile block:
@ -388,7 +389,7 @@ bytes const& State::mine(uint _msTimeout)
else
m_currentBytes.clear();
return m_currentBytes;
return ret;
}
bool State::isNormalAddress(Address _id) const

7
libethereum/State.h

@ -50,6 +50,11 @@ public:
/// Construct state object.
State(Address _coinbaseAddress, Overlay const& _db);
/// 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(); }
Address address() const { return m_ourAddress; }
/// Open a DB - useful for passing into the constructor & keeping for other states that are necessary.
static Overlay openDB(std::string _path, bool _killExisting = false);
static Overlay openDB(bool _killExisting = false) { return openDB(std::string(), _killExisting); }
@ -70,7 +75,7 @@ public:
/// @param _msTimeout Timeout before return in milliseconds.
/// @returns a non-empty byte array containing the block if it got lucky. In this case, call blockData()
/// to get the block if you need it later.
bytes const& mine(uint _msTimeout = 1000);
MineInfo mine(uint _msTimeout = 1000);
/// Get the complete current block, including valid nonce.
/// Only valid after mine() returns true.

25
libethereum/TransactionQueue.cpp

@ -24,18 +24,27 @@
using namespace std;
using namespace eth;
void TransactionQueue::import(bytes const& _block)
bool TransactionQueue::import(bytes const& _block)
{
// Check if we already know this transaction.
h256 h = sha3(_block);
if (m_data.count(h))
return;
return false;
// Check validity of _block as a transaction. To do this we just deserialise and attempt to determine the sender. If it doesn't work, the signature is bad.
// The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction).
Transaction t(_block);
t.sender();
try
{
// Check validity of _block as a transaction. To do this we just deserialise and attempt to determine the sender. If it doesn't work, the signature is bad.
// The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction).
Transaction t(_block);
t.sender();
// If valid, append to blocks.
m_data[h] = _block;
// If valid, append to blocks.
m_data[h] = _block;
}
catch (...)
{
return false;
}
return true;
}

2
libethereum/TransactionQueue.h

@ -36,7 +36,7 @@ class TransactionQueue
public:
bool attemptImport(bytes const& _block) { try { import(_block); return true; } catch (...) { return false; } }
void import(bytes const& _block);
bool import(bytes const& _block);
void drop(h256 _txHash) { m_data.erase(_txHash); }

4
test/peer.cpp

@ -46,7 +46,7 @@ int peerTest(int argc, char** argv)
}
BlockChain ch("/tmp");
PeerServer pn(ch, 0, listenPort);
PeerServer pn("Test", ch, 0, listenPort);
if (!remoteHost.empty())
pn.connect(remoteHost, remotePort);
@ -54,7 +54,7 @@ int peerTest(int argc, char** argv)
for (int i = 0; ; ++i)
{
usleep(100000);
pn.process();
pn.process(ch);
if (!(i % 10))
pn.pingAll();
}

4
test/state.cpp

@ -47,7 +47,7 @@ int stateTest()
// Mine to get some ether!
s.commitToMine(bc);
while (s.mine(100).empty()) {}
while (s.mine(100).completed()) {}
bc.attemptImport(s.blockData(), stateDB);
cout << bc;
@ -74,7 +74,7 @@ int stateTest()
// Mine to get some ether and set in stone.
s.commitToMine(bc);
while (s.mine(100).empty()) {}
while (s.mine(100).completed()) {}
bc.attemptImport(s.blockData(), stateDB);
cout << bc;

Loading…
Cancel
Save