Browse Source

Basic client.

cl-refactor
Gav Wood 11 years ago
parent
commit
6e3f6e3fd1
  1. 27
      eth/main.cpp
  2. 56
      libethereum/BlockChain.cpp
  3. 2
      libethereum/BlockChain.h
  4. 2
      libethereum/Client.cpp
  5. 1
      libethereum/Common.h
  6. 17
      libethereum/Dagger.cpp
  7. 4
      libethereum/Dagger.h
  8. 98
      libethereum/PeerNetwork.cpp
  9. 4
      libethereum/PeerNetwork.h
  10. 2
      libethereum/RLP.cpp
  11. 8
      libethereum/RLP.h
  12. 4
      libethereum/State.cpp
  13. 2
      libethereum/TrieDB.h
  14. 4
      test/state.cpp

27
eth/main.cpp

@ -34,9 +34,10 @@ int main(int argc, char** argv)
short remotePort = 30303;
bool interactive = false;
string dbPath;
eth::uint mining = ~(eth::uint)0;
// Our address.
Address us; // TODO: should be loaded from config file
Address us; // TODO: load from config file?
for (int i = 1; i < argc; ++i)
{
@ -51,8 +52,15 @@ int main(int argc, char** argv)
us = h160(fromUserHex(argv[++i]));
else if (arg == "-i")
interactive = true;
else if (arg == "-d")
dbPath = arg;
else if (arg == "-d" && i + 1 < argc)
dbPath = argv[++i];
else if (arg == "-m" && i + 1 < argc)
if (string(argv[++i]) == "on")
mining = ~(eth::uint)0;
else if (string(argv[i]) == "off")
mining = 0;
else
mining = atoi(argv[i]);
else
remoteHost = argv[i];
}
@ -61,7 +69,8 @@ int main(int argc, char** argv)
if (interactive)
{
cout << "Ethereum (++)" << endl;
cout << " By Gav Wood, Tim Hughes & team Ethereum, (c) 2013, 2014" << endl << endl;
cout << " Code by Gav Wood, (c) 2013, 2014." << endl;
cout << " Based on a design by Vitalik Buterin." << endl << endl;
while (true)
{
@ -109,9 +118,15 @@ int main(int argc, char** argv)
else
{
c.startNetwork(listenPort, remoteHost, remotePort);
c.startMining();
eth::uint n = c.blockChain().details().number;
while (true)
sleep(1);
{
if (c.blockChain().details().number - n > mining)
c.stopMining();
else
c.startMining();
usleep(100000);
}
}

56
libethereum/BlockChain.cpp

@ -82,8 +82,15 @@ BlockChain::BlockChain(std::string _path, bool _killExisting)
m_genesisHash = BlockInfo::genesis().hash;
m_genesisBlock = BlockInfo::createGenesisBlock();
// Insert details of genesis block.
m_details[m_genesisHash] = BlockDetails(0, (u256)1 << 36, h256(), {});
if (!details(m_genesisHash))
{
// Insert details of genesis block.
m_details[m_genesisHash] = BlockDetails(0, (u256)1 << 36, h256(), {});
auto r = m_details[m_genesisHash].rlp();
m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&m_genesisHash, 32), (ldb::Slice)eth::ref(r));
}
checkConsistency();
// TODO: Implement ability to rebuild details map from DB.
std::string l;
@ -95,6 +102,15 @@ BlockChain::~BlockChain()
{
}
template <class T, class V>
bool contains(T const& _t, V const& _v)
{
for (auto const& i: _t)
if (i == _v)
return true;
return false;
}
void BlockChain::import(bytes const& _block, Overlay const& _db)
{
// VERIFY: populates from the block and checks the block is internally coherent.
@ -102,16 +118,23 @@ void BlockChain::import(bytes const& _block, Overlay const& _db)
bi.verifyInternals(&_block);
auto newHash = eth::sha3(_block);
cout << "Attempting import of " << newHash << "..." << endl;
// Check block doesn't already exist first!
if (details(newHash))
{
cout << " Not new." << endl;
throw AlreadyHaveBlock();
}
// Work out its number as the parent's number + 1
auto pd = details(bi.parentHash);
if (!pd)
{
cout << " Unknown parent " << bi.parentHash << endl;
// 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));
@ -128,6 +151,8 @@ void BlockChain::import(bytes const& _block, Overlay const& _db)
auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true);
u256 td = pd.totalDifficulty + tdIncrease;
checkConsistency();
// 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()));
@ -137,11 +162,16 @@ void BlockChain::import(bytes const& _block, Overlay const& _db)
m_db->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)ref(_block));
checkConsistency();
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;
}
else
{
@ -149,6 +179,25 @@ void BlockChain::import(bytes const& _block, Overlay const& _db)
}
}
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())
{
auto dp = details(p);
assert(contains(dp.children, h));
assert(dp.number == dh.number - 1);
}
}
delete it;
}
bytesConstRef BlockChain::block(h256 _hash) const
{
if (_hash == m_genesisHash)
@ -166,7 +215,10 @@ BlockDetails const& BlockChain::details(h256 _h) const
std::string s;
m_detailsDB->Get(m_readOptions, ldb::Slice((char const*)&_h, 32), &s);
if (s.empty())
{
cout << "Not found in DB: " << _h << endl;
return NullBlockDetails;
}
bool ok;
tie(it, ok) = m_details.insert(make_pair(_h, BlockDetails(RLP(s))));
}

2
libethereum/BlockChain.h

@ -103,6 +103,8 @@ public:
h256 genesisHash() const { return m_genesisHash; }
private:
void checkConsistency();
/// Get fully populated from disk DB.
mutable std::map<h256, BlockDetails> m_details;
mutable std::map<h256, std::string> m_cache;

2
libethereum/Client.cpp

@ -123,7 +123,7 @@ void Client::work()
m_mineProgress.current = mineInfo.best;
m_mineProgress.requirement = mineInfo.requirement;
if (mineInfo.completed())
if (mineInfo.completed)
{
// Import block.
m_lock.lock();

1
libethereum/Common.h

@ -75,6 +75,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(), std::min<uint>(_b.size(), N)); }
explicit FixedHash(byte const* _bs) { memcpy(m_data.data(), _bs, N); }
operator Arith() const { return fromBigEndian<Arith>(m_data); }

17
libethereum/Dagger.cpp

@ -24,16 +24,23 @@ namespace eth
MineInfo Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool const& _continue)
{
MineInfo ret{toLog2(_difficulty), 0};
MineInfo ret{toLog2(_difficulty), 0, false};
static std::mt19937_64 s_eng((time(0)));
o_solution = std::uniform_int_distribution<uint>(0, ~(uint)0)(s_eng);
u256 d = ((u256)1 << 235) - _difficulty;
ret.requirement = toLog2(d);
// 2^ 0 32 64 128 256
// [--------*-------------------------]
//
// evaluate until we run out of time
for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout) && _continue; o_solution += 1)
for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout) && _continue && !ret.completed; o_solution += 1)
{
auto e = (u256)eval(_root, o_solution);
ret.best = max(ret.best, (uint)log2((double)e));
if (e > _difficulty)
return ret;
ret.best = max(ret.best, 256 - toLog2(e));
if (e < d)
ret.completed = true;
}
return ret;
}

4
libethereum/Dagger.h

@ -16,7 +16,7 @@ struct MineInfo
{
uint requirement;
uint best;
bool completed() { return best > requirement; }
bool completed;
};
#if FAKE_DAGGER
@ -25,7 +25,7 @@ class Dagger
{
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; }
static bool verify(h256 const& _root, u256 const& _nonce, u256 const& _difficulty) { return (u256)eval(_root, _nonce) > _difficulty * 1000; }
MineInfo mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100, bool const& _continue = bool(true));
};

98
libethereum/PeerNetwork.cpp

@ -40,9 +40,11 @@ PeerSession::~PeerSession()
bool PeerSession::interpret(RLP const& _r)
{
::operator<<(cout, _r) << endl;
switch (_r[0].toInt<unsigned>())
{
case Hello:
cout << std::setw(2) << m_socket.native_handle() << " | Hello" << endl;
m_protocolVersion = _r[1].toInt<uint>();
m_networkId = _r[2].toInt<uint>();
m_clientVersion = _r[3].toString();
@ -52,14 +54,23 @@ bool PeerSession::interpret(RLP const& _r)
return false;
}
cout << std::setw(2) << m_socket.native_handle() << " | Client version: " << m_clientVersion << endl;
// Grab their block chain off them.
{
RLPStream s;
prep(s);
s.appendList(3) << (uint)GetChain << m_server->m_latestBlockSent << 256;
sealAndSend(s);
}
break;
case Disconnect:
m_socket.close();
return false;
case Ping:
{
cout << std::setw(2) << m_socket.native_handle() << " | Ping" << endl;
RLPStream s;
sealAndSend(prep(s).appendList(1) << Pong);
sealAndSend(prep(s).appendList(1) << (uint)Pong);
break;
}
case Pong:
@ -68,10 +79,11 @@ bool PeerSession::interpret(RLP const& _r)
break;
case GetPeers:
{
cout << std::setw(2) << m_socket.native_handle() << " | GetPeers" << endl;
std::vector<bi::tcp::endpoint> peers = m_server->potentialPeers();
RLPStream s;
prep(s).appendList(2);
s << Peers;
s << (uint)Peers;
s.appendList(peers.size());
for (auto i: peers)
s.appendList(2) << i.address().to_v4().to_bytes() << i.port();
@ -79,6 +91,7 @@ bool PeerSession::interpret(RLP const& _r)
break;
}
case Peers:
cout << std::setw(2) << m_socket.native_handle() << " | Peers (" << _r[1].itemCount() << " entries)" << endl;
for (auto i: _r[1])
{
auto ep = bi::tcp::endpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<short>());
@ -87,10 +100,12 @@ bool PeerSession::interpret(RLP const& _r)
}
break;
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());
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());
break;
@ -99,6 +114,7 @@ bool PeerSession::interpret(RLP const& _r)
h256 parent = _r[1].toHash<h256>();
// return 256 block max.
uint count = (uint)min<bigint>(_r[1].toInt<bigint>(), 256);
cout << std::setw(2) << m_socket.native_handle() << " | GetChain (" << count << " max, from " << parent << ")" << endl;
h256 latest = m_server->m_chain->currentHash();
uint latestNumber = 0;
uint parentNumber = 0;
@ -110,17 +126,40 @@ bool PeerSession::interpret(RLP const& _r)
count = min<uint>(latestNumber - parentNumber, count);
RLPStream s;
prep(s);
s.appendList(2);
s.append(Blocks);
s.appendList(2) << (uint)Blocks;
s.appendList(count);
uint startNumber = m_server->m_chain->details(parent).number + count;
uint endNumber = m_server->m_chain->details(parent).number;
uint startNumber = endNumber + count;
auto h = m_server->m_chain->currentHash();
for (uint n = latestNumber; h != parent; n--, h = m_server->m_chain->details(h).parent)
if (m_server->m_chain->details(h).number <= startNumber)
s.appendRaw(m_server->m_chain->block(h));
uint n = latestNumber;
for (; n > startNumber; n--, h = m_server->m_chain->details(h).parent) {}
for (; h != parent && n > endNumber; n--, h = m_server->m_chain->details(h).parent)
s.appendRaw(m_server->m_chain->block(h));
if (h != parent)
{
cout << std::setw(2) << m_socket.native_handle() << " | GetChain failed; not in chain" << endl;
// No good - must have been on a different branch.
s.clear();
prep(s).appendList(2) << (uint)NotInChain << parent;
}
sealAndSend(s);
break;
}
case NotInChain:
{
h256 noGood = _r[1].toHash<h256>();
cout << std::setw(2) << m_socket.native_handle() << " | NotInChain (" << noGood << ")" << endl;
if (noGood != m_server->m_chain->genesisHash())
{
RLPStream s;
prep(s).appendList(3);
s << (uint)GetChain << m_server->m_chain->details(noGood).parent << 256;
sealAndSend(s);
}
// else our peer obviously knows nothing if they're unable to give the descendents of the genesis!
break;
}
default:
break;
}
@ -156,6 +195,7 @@ void PeerSession::sealAndSend(RLPStream& _s)
{
bytes b;
_s.swapOut(b);
seal(b);
sendDestroy(b);
}
@ -163,20 +203,30 @@ 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) {});
assert((*buffer)[0] == 0x22);
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;
});
}
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) {});
assert((*buffer)[0] == 0x22);
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;
});
}
void PeerSession::disconnect()
{
RLPStream s;
prep(s);
s.appendList(1) << Disconnect;
s.appendList(1) << (uint)Disconnect;
sealAndSend(s);
sleep(1);
m_socket.close();
@ -185,13 +235,15 @@ 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;
sealAndSend(s);
ping();
doRead();
// TODO: ask for latest block chain.
}
void PeerSession::doRead()
@ -205,9 +257,19 @@ void PeerSession::doRead()
memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length);
while (m_incoming.size() > 8)
{
uint32_t len = fromBigEndian<uint32_t>(bytesConstRef(m_incoming.data() + 4, 4));
if (m_incoming.size() - 8 >= len)
if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91)
{
cout << "*** Out of alignment: skipping: " << hex << showbase << (int)m_incoming[0] << endl;
memmove(m_incoming.data(), m_incoming.data() + 1, m_incoming.size() - 1);
m_incoming.resize(m_incoming.size() - 1);
}
else
{
uint32_t len = fromBigEndian<uint32_t>(bytesConstRef(m_incoming.data() + 4, 4));
cout << "Received packet of " << len << " bytes" << endl;
if (m_incoming.size() - 8 < len)
break;
// enough has come in.
RLP r(bytesConstRef(m_incoming.data() + 8, len));
if (!interpret(r))
@ -216,8 +278,6 @@ void PeerSession::doRead()
memmove(m_incoming.data(), m_incoming.data() + len + 8, m_incoming.size() - (len + 8));
m_incoming.resize(m_incoming.size() - (len + 8));
}
else
break;
}
}
doRead();
@ -299,7 +359,7 @@ void PeerServer::process(BlockChain& _bc)
m_ioService.poll();
for (auto i = m_peers.begin(); i != m_peers.end();)
if (auto j = i->lock())
{}
++i;
else
i = m_peers.erase(i);
}
@ -316,12 +376,13 @@ void PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o)
process(_bc);
for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end();)
for (auto it = m_incomingTransactions.begin(); it != m_incomingTransactions.end(); ++it)
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.
if (m_peers.size())
{
bytes b;
uint n = 0;
@ -361,6 +422,7 @@ void PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o)
if (auto p = j.lock())
p->send(&b);
}
m_latestBlockSent = h;
}
for (bool accepted = 1; accepted;)

4
libethereum/PeerNetwork.h

@ -36,7 +36,6 @@ namespace eth
class BlockChain;
class TransactionQueue;
class BlockChain;
enum PacketType
{
@ -48,7 +47,8 @@ enum PacketType
Peers,
Transactions,
Blocks,
GetChain
GetChain,
NotInChain
};
class PeerServer;

2
libethereum/RLP.cpp

@ -228,7 +228,7 @@ void RLPStream::pushCount(uint _count, byte _base)
pushInt(_count, br);
}
std::ostream& operator<<(std::ostream& _out, eth::RLP _d)
std::ostream& eth::operator<<(std::ostream& _out, eth::RLP const& _d)
{
if (_d.isNull())
_out << "null";

8
libethereum/RLP.h

@ -327,6 +327,8 @@ public:
template <class _T> RLPStream& operator<<(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
template <class _T, size_t S> RLPStream& operator<<(std::array<_T, S> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; }
void clear() { m_out.clear(); }
/// Read the byte stream.
bytes const& out() const { return m_out; }
@ -398,7 +400,7 @@ extern bytes RLPNull;
/// The empty list in RLP format.
extern bytes RLPEmptyList;
}
/// Human readable version of RLP.
std::ostream& operator<<(std::ostream& _out, eth::RLP _d);
std::ostream& operator<<(std::ostream& _out, eth::RLP const& _d);
}

4
libethereum/State.cpp

@ -338,6 +338,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;
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.
@ -380,7 +381,7 @@ MineInfo State::mine(uint _msTimeout)
// TODO: Miner class that keeps dagger between mine calls (or just non-polling mining).
MineInfo ret = m_dagger.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout);
if (ret.completed())
if (ret.completed)
{
// Got it!
@ -395,6 +396,7 @@ MineInfo State::mine(uint _msTimeout)
ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes);
m_currentBlock.hash = sha3(m_currentBytes);
cout << "*** SUCCESS: Mined " << m_currentBlock.hash << " (parent: " << m_currentBlock.parentHash << ")" << endl;
}
else
m_currentBytes.clear();

2
libethereum/TrieDB.h

@ -60,7 +60,7 @@ inline std::ostream& operator<<(std::ostream& _out, BasicMap const& _m)
for (auto i: _m.get())
{
_out << i.first << ": ";
::operator<<(_out, RLP(i.second));
_out << RLP(i.second);
_out << " " << asHex(i.second);
_out << std::endl;
}

4
test/state.cpp

@ -47,7 +47,7 @@ int stateTest()
// Mine to get some ether!
s.commitToMine(bc);
while (s.mine(100).completed()) {}
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).completed()) {}
while (s.mine(100).completed) {}
bc.attemptImport(s.blockData(), stateDB);
cout << bc;

Loading…
Cancel
Save