Browse Source

State updater tested and working pretty well.

cl-refactor
Gav Wood 11 years ago
parent
commit
8fb99152ee
  1. 19
      eth/main.cpp
  2. 103
      libethereum/BlockChain.cpp
  3. 42
      libethereum/BlockChain.h
  4. 15
      libethereum/BlockInfo.h
  5. 11
      libethereum/Dagger.cpp
  6. 4
      libethereum/Dagger.h
  7. 4
      libethereum/RLP.h
  8. 92
      libethereum/State.cpp
  9. 45
      libethereum/State.h
  10. 7
      libethereum/TrieDB.h
  11. 8
      test/main.cpp
  12. 41
      test/state.cpp

19
eth/main.cpp

@ -32,9 +32,10 @@ int main()
h256 privkey = sha3("123"); h256 privkey = sha3("123");
Address us = toAddress(privkey); // TODO: should be loaded from config file/set at command-line. Address us = toAddress(privkey); // TODO: should be loaded from config file/set at command-line.
BlockChain bc; // Maintains block database. BlockChain bc; // Maintains block database.
TransactionQueue tq; // Maintains list of incoming transactions not yet on the block chain. TransactionQueue tq; // Maintains list of incoming transactions not yet on the block chain.
State s(us); 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. // 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. // 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.
@ -42,7 +43,7 @@ int main()
s.sync(bc); s.sync(bc);
s.sync(tq); s.sync(tq);
PeerNetwork net; // TODO: Implement - should run in background and send us events when blocks found and allow us to send blocks as required. PeerNetwork net; // TODO: Implement - should run in background and send us events when blocks found and allow us to send blocks as required.
while (true) while (true)
{ {
// Process network events. // Process network events.
@ -62,14 +63,10 @@ int main()
s.sync(tq); s.sync(tq);
// Mine for a while. // Mine for a while.
if (s.mine(100)) bytes b = s.mine(100);
{ if (b.size())
// Mined block
bytes b = s.blockData();
// Import block. // Import block.
bc.import(b); bc.attemptImport(b, stateDB);
}
} }
return 0; return 0;

103
libethereum/BlockChain.cpp

@ -30,44 +30,72 @@
using namespace std; using namespace std;
using namespace eth; using namespace eth;
std::string Defaults::s_dbPath = string(getenv("HOME")) + "/.ethereum";
namespace eth
{
std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc)
{
string cmp = toBigEndianString(_bc.m_lastBlockHash);
auto it = _bc.m_detailsDB->NewIterator(_bc.m_readOptions);
for (it->SeekToFirst(); it->Valid(); it->Next())
if (it->key().ToString() != "best")
{
BlockDetails d(RLP(it->value().ToString()));
_out << asHex(it->key().ToString()) << ": " << d.number << " @ " << d.parent << (cmp == it->key().ToString() ? " BEST" : "") << std::endl;
}
delete it;
return _out;
}
}
BlockDetails::BlockDetails(RLP const& _r)
{
number = _r[0].toInt<uint>();
totalDifficulty = _r[1].toInt<u256>();
parent = _r[2].toHash<h256>();
children = _r[3].toVector<h256>();
}
bytes BlockDetails::rlp() const
{
return rlpList(number, totalDifficulty, parent, children);
}
BlockChain::BlockChain(std::string _path, bool _killExisting) BlockChain::BlockChain(std::string _path, bool _killExisting)
{ {
if (_path.empty()) if (_path.empty())
_path = string(getenv("HOME")) + "/.ethereum"; _path = Defaults::s_dbPath;
boost::filesystem::create_directory(_path); boost::filesystem::create_directory(_path);
if (_killExisting) if (_killExisting)
{
boost::filesystem::remove_all(_path + "/blocks"); boost::filesystem::remove_all(_path + "/blocks");
boost::filesystem::remove_all(_path + "/details");
}
ldb::Options o; ldb::Options o;
o.create_if_missing = true; o.create_if_missing = true;
auto s = ldb::DB::Open(o, _path + "/blocks", &m_db); auto s = ldb::DB::Open(o, _path + "/blocks", &m_db);
s = ldb::DB::Open(o, _path + "/details", &m_detailsDB);
// Initialise with the genesis as the last block on the longest chain. // Initialise with the genesis as the last block on the longest chain.
m_lastBlockHash = m_genesisHash = BlockInfo::genesis().hash; m_genesisHash = BlockInfo::genesis().hash;
m_genesisBlock = BlockInfo::createGenesisBlock(); m_genesisBlock = BlockInfo::createGenesisBlock();
// Insert details of genesis block. // Insert details of genesis block.
m_details[m_genesisHash] = { 0, (u256)1 << 36, h256(), {} }; m_details[m_genesisHash] = BlockDetails(0, (u256)1 << 36, h256(), {});
}
BlockChain::~BlockChain() // TODO: Implement ability to rebuild details map from DB.
{ std::string l;
m_detailsDB->Get(m_readOptions, ldb::Slice("best"), &l);
m_lastBlockHash = l.empty() ? m_genesisHash : *(h256*)l.data();
} }
h256s BlockChain::blockChain(h256Set const& _earlyExit) const BlockChain::~BlockChain()
{ {
// Return the current valid block chain from most recent to genesis.
// Arguments for specifying a set of early-ends
h256s ret;
ret.reserve(m_details[m_lastBlockHash].number + 1);
auto i = m_lastBlockHash;
for (; i != m_genesisHash && !_earlyExit.count(i); i = m_details[i].parent)
ret.push_back(i);
ret.push_back(i);
return ret;
} }
void BlockChain::import(bytes const& _block) void BlockChain::import(bytes const& _block, Overlay const& _db)
{ {
try try
{ {
@ -78,12 +106,12 @@ void BlockChain::import(bytes const& _block)
auto newHash = eth::sha3(_block); auto newHash = eth::sha3(_block);
// Check block doesn't already exist first! // Check block doesn't already exist first!
if (m_details.count(newHash)) if (details(newHash))
return; return;
// Work out its number as the parent's number + 1 // Work out its number as the parent's number + 1
auto it = m_details.find(bi.parentHash); auto pd = details(bi.parentHash);
if (it == m_details.end()) 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. // 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; return;
@ -92,23 +120,35 @@ void BlockChain::import(bytes const& _block)
bi.verifyParent(biParent); bi.verifyParent(biParent);
// Check transactions are valid and that they result in a state equivalent to our state_root. // Check transactions are valid and that they result in a state equivalent to our state_root.
State s(bi.coinbaseAddress); State s(bi.coinbaseAddress, _db);
s.sync(*this, bi.parentHash); s.sync(*this, bi.parentHash);
// Get total difficulty increase and update state, checking it. // Get total difficulty increase and update state, checking it.
BlockInfo biGrandParent; BlockInfo biGrandParent;
if (it->second.number) if (pd.number)
biGrandParent.populate(block(it->second.parent)); biGrandParent.populate(block(pd.parent));
u256 td = it->second.totalDifficulty + s.playback(&_block, bi, biParent, biGrandParent, true); auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true);
u256 td = pd.totalDifficulty + tdIncrease;
// All ok - insert into DB // All ok - insert into DB
m_details[newHash] = BlockDetails{(uint)it->second.number + 1, bi.parentHash, td}; 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_details[bi.parentHash].children.push_back(newHash);
m_db->Put(m_writeOptions, ldb::Slice(toBigEndianString(newHash)), (ldb::Slice)ref(_block)); 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));
// This might be the new last block... // This might be the new last block...
if (td > m_details[m_lastBlockHash].totalDifficulty) if (td > m_details[m_lastBlockHash].totalDifficulty)
{
m_lastBlockHash = newHash; 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;
}
} }
catch (...) catch (...)
{ {
@ -122,7 +162,7 @@ bytesConstRef BlockChain::block(h256 _hash) const
if (_hash == m_genesisHash) if (_hash == m_genesisHash)
return &m_genesisBlock; return &m_genesisBlock;
m_db->Get(m_readOptions, ldb::Slice(toBigEndianString(_hash)), &m_cache[_hash]); m_db->Get(m_readOptions, ldb::Slice((char const*)&_hash, 32), &m_cache[_hash]);
return bytesConstRef(&m_cache[_hash]); return bytesConstRef(&m_cache[_hash]);
} }
@ -130,6 +170,13 @@ BlockDetails const& BlockChain::details(h256 _h) const
{ {
auto it = m_details.find(_h); auto it = m_details.find(_h);
if (it == m_details.end()) if (it == m_details.end())
return NullBlockDetails; {
std::string s;
m_detailsDB->Get(m_readOptions, ldb::Slice((char const*)&_h, 32), &s);
if (s.empty())
return NullBlockDetails;
bool ok;
tie(it, ok) = m_details.insert(make_pair(_h, BlockDetails(RLP(s))));
}
return it->second; return it->second;
} }

42
libethereum/BlockChain.h

@ -27,24 +27,49 @@ namespace ldb = leveldb;
namespace eth namespace eth
{ {
struct Defaults
{
friend class BlockChain;
friend class State;
public:
static void setDBPath(std::string _dbPath) { s_dbPath = _dbPath; }
private:
static std::string s_dbPath;
};
class RLP;
class RLPStream;
struct BlockDetails struct BlockDetails
{ {
BlockDetails(): number(0), totalDifficulty(0) {}
BlockDetails(uint _n, u256 _tD, h256 _p, h256s _c): number(_n), totalDifficulty(_tD), parent(_p), children(_c) {}
BlockDetails(RLP const& _r);
bytes rlp() const;
bool isNull() const { return !totalDifficulty; }
explicit operator bool() const { return !isNull(); }
uint number; uint number;
u256 totalDifficulty; u256 totalDifficulty;
h256 parent; h256 parent;
h256s children; h256s children;
}; };
static const BlockDetails NullBlockDetails({ 0, 0, h256(), {} }); static const BlockDetails NullBlockDetails;
static const h256s NullH256s; static const h256s NullH256s;
class Overlay;
/** /**
* @brief Implements the blockchain database. All data this gives is disk-backed. * @brief Implements the blockchain database. All data this gives is disk-backed.
*/ */
class BlockChain class BlockChain
{ {
public: public:
BlockChain(std::string _path = std::string(), bool _killExisting = false); BlockChain(bool _killExisting = false): BlockChain(std::string(), _killExisting) {}
BlockChain(std::string _path, bool _killExisting = false);
~BlockChain(); ~BlockChain();
/// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock.
@ -52,13 +77,10 @@ public:
void process(); void process();
/// Attempt to import the given block. /// Attempt to import the given block.
bool attemptImport(bytes const& _block) { try { import(_block); return true; } catch (...) { return false; } } bool attemptImport(bytes const& _block, Overlay const& _stateDB) { try { import(_block, _stateDB); return true; } catch (...) { return false; } }
/// Import block into disk-backed DB /// Import block into disk-backed DB
void import(bytes const& _block); void import(bytes const& _block, Overlay const& _stateDB);
/// Get the full block chain, according to the GHOST algo and the blocks available in the db.
h256s blockChain(h256Set const& _earlyExit) const;
/// Get the number of the last block of the longest chain. /// Get the number of the last block of the longest chain.
BlockDetails const& details(h256 _hash) const; BlockDetails const& details(h256 _hash) const;
@ -75,10 +97,10 @@ public:
private: private:
/// Get fully populated from disk DB. /// Get fully populated from disk DB.
mutable std::map<h256, BlockDetails> m_details; mutable std::map<h256, BlockDetails> m_details;
mutable std::map<h256, std::string> m_cache; mutable std::map<h256, std::string> m_cache;
ldb::DB* m_db; ldb::DB* m_db;
ldb::DB* m_detailsDB;
/// Hash of the last (valid) block on the longest chain. /// Hash of the last (valid) block on the longest chain.
h256 m_lastBlockHash; h256 m_lastBlockHash;
@ -87,6 +109,10 @@ private:
ldb::ReadOptions m_readOptions; ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions; ldb::WriteOptions m_writeOptions;
friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
}; };
std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
} }

15
libethereum/BlockInfo.h

@ -30,7 +30,7 @@ namespace eth
struct BlockInfo struct BlockInfo
{ {
public: public:
h256 hash; h256 hash; ///< SHA3 hash of the entire block!
h256 parentHash; h256 parentHash;
h256 sha3Uncles; h256 sha3Uncles;
Address coinbaseAddress; Address coinbaseAddress;
@ -46,7 +46,18 @@ public:
explicit operator bool() const { return timestamp != Invalid256; } explicit operator bool() const { return timestamp != Invalid256; }
bool operator==(BlockInfo const& _cmp) const { return parentHash == _cmp.parentHash && nonce == _cmp.nonce; } bool operator==(BlockInfo const& _cmp) const
{
return parentHash == _cmp.parentHash &&
sha3Uncles == _cmp.sha3Uncles &&
coinbaseAddress == _cmp.coinbaseAddress &&
stateRoot == _cmp.stateRoot &&
sha3Transactions == _cmp.sha3Transactions &&
difficulty == _cmp.difficulty &&
timestamp == _cmp.timestamp &&
extraData == _cmp.extraData &&
nonce == _cmp.nonce;
}
bool operator!=(BlockInfo const& _cmp) const { return !operator==(_cmp); } bool operator!=(BlockInfo const& _cmp) const { return !operator==(_cmp); }
static BlockInfo const& genesis() { if (!s_genesis) (s_genesis = new BlockInfo)->populateGenesis(); return *s_genesis; } static BlockInfo const& genesis() { if (!s_genesis) (s_genesis = new BlockInfo)->populateGenesis(); return *s_genesis; }

11
libethereum/Dagger.cpp

@ -22,11 +22,12 @@ namespace eth
#if FAKE_DAGGER #if FAKE_DAGGER
bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout) bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool const& _continue)
{ {
o_solution = 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 // evaluate until we run out of time
for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout); o_solution += 1) for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout) && _continue; o_solution += 1)
if (verify(_root, o_solution, _difficulty)) if (verify(_root, o_solution, _difficulty))
return true; return true;
return false; return false;
@ -52,7 +53,7 @@ bool Dagger::verify(h256 const& _root, u256 const& _nonce, u256 const& _difficul
return eval(_root, _nonce) < bound(_difficulty); return eval(_root, _nonce) < bound(_difficulty);
} }
bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout) bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout, bool const& _continue)
{ {
// restart search if root has changed // restart search if root has changed
if (m_root != _root) if (m_root != _root)
@ -65,7 +66,7 @@ bool Dagger::mine(u256& o_solution, h256 const& _root, u256 const& _difficulty,
u256 const b = bound(_difficulty); u256 const b = bound(_difficulty);
// evaluate until we run out of time // evaluate until we run out of time
for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout); m_nonce += 1) for (auto startTime = steady_clock::now(); (steady_clock::now() - startTime) < milliseconds(_msTimeout) && _continue; m_nonce += 1)
{ {
if (eval(_root, m_nonce) < b) if (eval(_root, m_nonce) < b)
{ {

4
libethereum/Dagger.h

@ -15,7 +15,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 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; }
bool mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100); bool mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100, bool const& _continue = bool(true));
}; };
#else #else
@ -31,7 +31,7 @@ public:
static h256 eval(h256 const& _root, u256 const& _nonce); static h256 eval(h256 const& _root, u256 const& _nonce);
static bool verify(h256 const& _root, u256 const& _nonce, u256 const& _difficulty); static bool verify(h256 const& _root, u256 const& _nonce, u256 const& _difficulty);
bool mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100); bool mine(u256& o_solution, h256 const& _root, u256 const& _difficulty, uint _msTimeout = 100, bool const& _continue = bool(true));
private: private:

4
libethereum/RLP.h

@ -161,7 +161,7 @@ public:
explicit operator uint() const { return toSlimInt(); } explicit operator uint() const { return toSlimInt(); }
explicit operator u256() const { return toFatInt(); } explicit operator u256() const { return toFatInt(); }
explicit operator bigint() const { return toBigInt(); } explicit operator bigint() const { return toBigInt(); }
template <unsigned _N> explicit operator FixedHash<_N>() const { return toHash<_N>(); } template <unsigned _N> explicit operator FixedHash<_N>() const { return toHash<FixedHash<_N>>(); }
/// Converts to bytearray. @returns the empty byte array if not a string. /// Converts to bytearray. @returns the empty byte array if not a string.
bytes toBytes() const { if (!isString()) return bytes(); return bytes(payload().data(), payload().data() + items()); } bytes toBytes() const { if (!isString()) return bytes(); return bytes(payload().data(), payload().data() + items()); }
@ -172,6 +172,8 @@ public:
/// Converts to string. @throws BadCast if not a string. /// Converts to string. @throws BadCast if not a string.
std::string toStringStrict() const { if (!isString()) throw BadCast(); return payload().cropped(0, items()).toString(); } std::string toStringStrict() const { if (!isString()) throw BadCast(); return payload().cropped(0, items()).toString(); }
template <class T> std::vector<T> toVector() const { std::vector<T> ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; }
/// Int conversion flags /// Int conversion flags
enum enum
{ {

92
libethereum/State.cpp

@ -53,12 +53,10 @@ u256 const State::c_newContractFee = 60000;
u256 const State::c_txFee = 0; u256 const State::c_txFee = 0;
u256 const State::c_blockReward = 1000000000; u256 const State::c_blockReward = 1000000000;
State::State(Address _coinbaseAddress, std::string _path, bool _killExisting): m_state(&m_db), m_ourAddress(_coinbaseAddress) Overlay State::openDB(std::string _path, bool _killExisting)
{ {
secp256k1_start();
if (_path.empty()) if (_path.empty())
_path = string(getenv("HOME")) + "/.ethereum"; _path = Defaults::s_dbPath;
boost::filesystem::create_directory(_path); boost::filesystem::create_directory(_path);
if (_killExisting) if (_killExisting)
boost::filesystem::remove_all(_path + "/state"); boost::filesystem::remove_all(_path + "/state");
@ -67,21 +65,26 @@ State::State(Address _coinbaseAddress, std::string _path, bool _killExisting): m
o.create_if_missing = true; o.create_if_missing = true;
ldb::DB* db = nullptr; ldb::DB* db = nullptr;
ldb::DB::Open(o, _path + "/state", &db); ldb::DB::Open(o, _path + "/state", &db);
return Overlay(db);
}
m_db.setDB(db); State::State(Address _coinbaseAddress, Overlay const& _db): m_db(_db), m_state(&m_db), m_ourAddress(_coinbaseAddress)
{
secp256k1_start();
m_state.init(); m_state.init();
m_previousBlock = BlockInfo::genesis(); m_previousBlock = BlockInfo::genesis();
resetCurrent(); resetCurrent();
} }
void State::ensureCached(Address _a, bool _requireMemory) const void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const
{ {
auto it = m_cache.find(_a); auto it = m_cache.find(_a);
if (it == m_cache.end()) if (it == m_cache.end())
{ {
// populate basic info. // populate basic info.
string stateBack = m_state.at(_a); string stateBack = m_state.at(_a);
if (stateBack.empty() && !_forceCreate)
return;
RLP state(stateBack); RLP state(stateBack);
AddressState s; AddressState s;
if (state.isNull()) if (state.isNull())
@ -176,23 +179,22 @@ void State::sync(BlockChain const& _bc, h256 _block)
// New blocks available, or we've switched to a different branch. All change. // New blocks available, or we've switched to a different branch. All change.
// Find most recent state dump and replay what's left. // Find most recent state dump and replay what's left.
// (Most recent state dump might end up being genesis.) // (Most recent state dump might end up being genesis.)
std::vector<h256> l = _bc.blockChain(h256Set());
if (l.back() == BlockInfo::genesis().hash) std::vector<h256> chain;
{ while (bi.stateRoot != BlockInfo::genesis().hash && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block...
// Reset to genesis block.
m_previousBlock = BlockInfo::genesis();
}
else
{ {
// TODO: Begin at a restore point. chain.push_back(bi.hash); // push back for later replay.
bi.populate(_bc.block(bi.parentHash)); // move to parent.
} }
m_previousBlock = bi;
resetCurrent();
// Iterate through in reverse, playing back each of the blocks. // Iterate through in reverse, playing back each of the blocks.
for (auto it = next(l.cbegin()); it != l.cend(); ++it) for (auto it = chain.rbegin(); it != chain.rend(); ++it)
playback(_bc.block(*it), true); playback(_bc.block(*it), true);
m_currentNumber = _bc.details(_bc.currentHash()).number + 1; m_currentNumber = _bc.details(_block).number + 1;
resetCurrent(); resetCurrent();
} }
} }
@ -289,6 +291,8 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _
// Commit all cached state changes to the state trie. // Commit all cached state changes to the state trie.
commit(); commit();
// cout << m_state << endl << TrieDB<Address, Overlay>(&m_db, m_currentBlock.stateRoot);
// Hash the state trie and check against the state_root hash in m_currentBlock. // Hash the state trie and check against the state_root hash in m_currentBlock.
if (m_currentBlock.stateRoot != rootHash()) if (m_currentBlock.stateRoot != rootHash())
{ {
@ -325,13 +329,15 @@ void State::commitToMine(BlockChain const& _bc)
{ {
// Find uncles if we're not a direct child of the genesis. // Find uncles if we're not a direct child of the genesis.
auto us = _bc.details(m_previousBlock.parentHash).children; auto us = _bc.details(m_previousBlock.parentHash).children;
uncles.appendList(us.size()); 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.
for (auto const& u: us) for (auto const& u: us)
{ if (u != m_previousBlock.hash) // ignore our own parent - it's not an uncle.
BlockInfo ubi(_bc.block(u)); {
ubi.fillStream(uncles, true); BlockInfo ubi(_bc.block(u));
uncleAddresses.push_back(ubi.coinbaseAddress); ubi.fillStream(uncles, true);
} uncleAddresses.push_back(ubi.coinbaseAddress);
}
} }
else else
uncles.appendList(0); uncles.appendList(0);
@ -354,7 +360,7 @@ void State::commitToMine(BlockChain const& _bc)
m_currentBlock.parentHash = m_previousBlock.hash; m_currentBlock.parentHash = m_previousBlock.hash;
} }
bool State::mine(uint _msTimeout) bytes const& State::mine(uint _msTimeout)
{ {
// Update timestamp according to clock. // Update timestamp according to clock.
m_currentBlock.timestamp = time(0); m_currentBlock.timestamp = time(0);
@ -365,27 +371,29 @@ bool State::mine(uint _msTimeout)
// TODO: Miner class that keeps dagger between mine calls (or just non-polling mining). // 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)) if (m_dagger.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout))
{ {
// Got it! Compile block: // 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?
m_db.commit();
// Compile block:
RLPStream ret; RLPStream ret;
ret.appendList(3); ret.appendList(3);
{ m_currentBlock.fillStream(ret, true);
RLPStream s;
m_currentBlock.fillStream(s, true);
m_currentBlock.hash = sha3(s.out());
ret.appendRaw(s.out());
}
ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentTxs);
ret.appendRaw(m_currentUncles); ret.appendRaw(m_currentUncles);
ret.swapOut(m_currentBytes); ret.swapOut(m_currentBytes);
return true; m_currentBlock.hash = sha3(m_currentBytes);
} }
else
m_currentBytes.clear();
return false; return m_currentBytes;
} }
bool State::isNormalAddress(Address _id) const bool State::isNormalAddress(Address _id) const
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end()) if (it == m_cache.end())
return false; return false;
@ -394,7 +402,7 @@ bool State::isNormalAddress(Address _id) const
bool State::isContractAddress(Address _id) const bool State::isContractAddress(Address _id) const
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end()) if (it == m_cache.end())
return false; return false;
@ -403,7 +411,7 @@ bool State::isContractAddress(Address _id) const
u256 State::balance(Address _id) const u256 State::balance(Address _id) const
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end()) if (it == m_cache.end())
return 0; return 0;
@ -412,7 +420,7 @@ u256 State::balance(Address _id) const
void State::noteSending(Address _id) void State::noteSending(Address _id)
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end()) if (it == m_cache.end())
m_cache[_id] = AddressState(0, 1); m_cache[_id] = AddressState(0, 1);
@ -422,7 +430,7 @@ void State::noteSending(Address _id)
void State::addBalance(Address _id, u256 _amount) void State::addBalance(Address _id, u256 _amount)
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end()) if (it == m_cache.end())
m_cache[_id] = AddressState(_amount, 0); m_cache[_id] = AddressState(_amount, 0);
@ -432,7 +440,7 @@ void State::addBalance(Address _id, u256 _amount)
void State::subBalance(Address _id, bigint _amount) void State::subBalance(Address _id, bigint _amount)
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end() || (bigint)it->second.balance() < _amount) if (it == m_cache.end() || (bigint)it->second.balance() < _amount)
throw NotEnoughCash(); throw NotEnoughCash();
@ -442,7 +450,7 @@ void State::subBalance(Address _id, bigint _amount)
u256 State::transactionsFrom(Address _id) const u256 State::transactionsFrom(Address _id) const
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end()) if (it == m_cache.end())
return 0; return 0;
@ -452,7 +460,7 @@ u256 State::transactionsFrom(Address _id) const
u256 State::contractMemory(Address _id, u256 _memory) const u256 State::contractMemory(Address _id, u256 _memory) const
{ {
ensureCached(_id); ensureCached(_id, false, false);
auto it = m_cache.find(_id); auto it = m_cache.find(_id);
if (it == m_cache.end() || it->second.type() != AddressType::Contract) if (it == m_cache.end() || it->second.type() != AddressType::Contract)
return 0; return 0;
@ -571,7 +579,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _
if (stack.size() < _n) if (stack.size() < _n)
throw StackTooSmall(_n, stack.size()); throw StackTooSmall(_n, stack.size());
}; };
ensureCached(_myAddress, true); ensureCached(_myAddress, true, true);
auto& myMemory = m_cache[_myAddress].takeMemory(); auto& myMemory = m_cache[_myAddress].takeMemory();
auto mem = [&](u256 _n) -> u256 auto mem = [&](u256 _n) -> u256

45
libethereum/State.h

@ -43,13 +43,16 @@ class BlockChain;
* @brief Model of the current state of the ledger. * @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). * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block).
* Should maintain ledger as of last N blocks, also, in case we end up on the wrong branch. * Should maintain ledger as of last N blocks, also, in case we end up on the wrong branch.
* TODO: Block database I/O class.
*/ */
class State class State
{ {
public: public:
/// Construct state object. /// Construct state object.
explicit State(Address _coinbaseAddress, std::string _path = std::string(), bool _killExisting = false); State(Address _coinbaseAddress, Overlay const& _db);
/// 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); }
/// Cancels transactions and rolls back the state to the end of the previous block. /// Cancels transactions and rolls back the state to the end of the previous block.
/// @warning This will only work for on any transactions after you called the last commitToMine(). /// @warning This will only work for on any transactions after you called the last commitToMine().
@ -65,9 +68,9 @@ public:
/// Attempt to find valid nonce for block that this state represents. /// Attempt to find valid nonce for block that this state represents.
/// @param _msTimeout Timeout before return in milliseconds. /// @param _msTimeout Timeout before return in milliseconds.
/// @returns true if it got lucky. In this case, call blockData() to get the block for /// @returns a non-empty byte array containing the block if it got lucky. In this case, call blockData()
/// spreading far and wide. /// to get the block if you need it later.
bool mine(uint _msTimeout = 1000); bytes const& mine(uint _msTimeout = 1000);
/// Get the complete current block, including valid nonce. /// Get the complete current block, including valid nonce.
/// Only valid after mine() returns true. /// Only valid after mine() returns true.
@ -140,7 +143,10 @@ private:
}; };
/// Retrieve all information about a given address into the cache. /// Retrieve all information about a given address into the cache.
void ensureCached(Address _a, bool _requireMemory = false) const; /// If _requireMemory is true, grab the full memory should it be a contract item.
/// If _forceCreate is true, then insert a default item into the cache, in the case it doesn't
/// exist in the DB.
void ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const;
/// Commit all changes waiting in the address cache. /// Commit all changes waiting in the address cache.
void commit(); void commit();
@ -190,8 +196,35 @@ private:
static const u256 c_newContractFee; static const u256 c_newContractFee;
static const u256 c_txFee; static const u256 c_txFee;
static const u256 c_blockReward; static const u256 c_blockReward;
static std::string c_defaultPath;
friend std::ostream& operator<<(std::ostream& _out, State const& _s);
}; };
inline std::ostream& operator<<(std::ostream& _out, State const& _s)
{
_out << "--- " << _s.rootHash() << std::endl;
std::set<Address> d;
for (auto const& i: TrieDB<Address, Overlay>(const_cast<Overlay*>(&_s.m_db), _s.m_currentBlock.stateRoot))
{
auto it = _s.m_cache.find(i.first);
if (it == _s.m_cache.end())
{
RLP r(i.second);
_out << "[ " << (r.itemCount() == 3 ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << r[1].toInt<u256>() << "@" << r[0].toInt<u256>() << std::endl;
}
else
d.insert(i.first);
}
for (auto i: _s.m_cache)
if (i.second.type() == AddressType::Dead)
_out << "[XXX " << i.first << std::endl;
else
_out << (d.count(i.first) ? "[ ! " : "[ * ") << (i.second.type() == AddressType::Contract ? "CONTRACT] " : " NORMAL] ") << i.first << ": " << std::dec << i.second.nonce() << "@" << i.second.balance() << std::endl;
return _out;
}
} }

7
libethereum/TrieDB.h

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <memory>
#include <leveldb/db.h> #include <leveldb/db.h>
#include "TrieCommon.h" #include "TrieCommon.h"
namespace ldb = leveldb; namespace ldb = leveldb;
@ -71,8 +72,8 @@ class Overlay: public BasicMap
public: public:
Overlay(ldb::DB* _db = nullptr): m_db(_db) {} Overlay(ldb::DB* _db = nullptr): m_db(_db) {}
ldb::DB* db() const { return m_db; } ldb::DB* db() const { return m_db.get(); }
void setDB(ldb::DB* _db, bool _clearOverlay = true) { m_db = _db; if (_clearOverlay) m_over.clear(); } void setDB(ldb::DB* _db, bool _clearOverlay = true) { m_db = std::shared_ptr<ldb::DB>(_db); if (_clearOverlay) m_over.clear(); }
void commit() { for (auto const& i: m_over) m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size())); m_over.clear(); m_refCount.clear(); } void commit() { for (auto const& i: m_over) m_db->Put(m_writeOptions, ldb::Slice((char const*)i.first.data(), i.first.size), ldb::Slice(i.second.data(), i.second.size())); m_over.clear(); m_refCount.clear(); }
void rollback() { m_over.clear(); m_refCount.clear(); } void rollback() { m_over.clear(); m_refCount.clear(); }
@ -82,7 +83,7 @@ public:
private: private:
using BasicMap::clear; using BasicMap::clear;
ldb::DB* m_db = nullptr; std::shared_ptr<ldb::DB> m_db;
ldb::ReadOptions m_readOptions; ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions; ldb::WriteOptions m_writeOptions;

8
test/main.cpp

@ -31,11 +31,11 @@ int hexPrefixTest();
int main() int main()
{ {
hexPrefixTest(); // hexPrefixTest();
rlpTest(); // rlpTest();
trieTest(); // trieTest();
// daggerTest(); // daggerTest();
cryptoTest(); // cryptoTest();
stateTest(); stateTest();
return 0; return 0;
} }

41
test/state.cpp

@ -32,21 +32,31 @@ int stateTest()
KeyPair myMiner = sha3("Gav's Miner"); KeyPair myMiner = sha3("Gav's Miner");
// KeyPair you = sha3("123"); // KeyPair you = sha3("123");
BlockChain bc("/tmp"); Defaults::setDBPath("/tmp");
State s(myMiner.address(), "/tmp");
cout << dec << "me: " << s.balance(me.address()) << endl; Overlay stateDB = State::openDB();
cout << "myMiner: " << s.balance(myMiner.address()) << endl; BlockChain bc;
State s(myMiner.address(), stateDB);
cout << bc;
// Sync up - this won't do much until we use the last state.
s.sync(bc);
cout << s;
// Mine to get some ether! // Mine to get some ether!
s.commitToMine(bc); s.commitToMine(bc);
while (!s.mine(100)) {} while (s.mine(100).empty()) {}
bc.attemptImport(s.blockData()); bc.attemptImport(s.blockData(), stateDB);
cout << bc;
s.sync(bc); s.sync(bc);
cout << "me: " << s.balance(me.address()) << endl; cout << s;
cout << "myMiner: " << s.balance(myMiner.address()) << endl;
// Inject a transaction to transfer funds from miner to me.
bytes tx; bytes tx;
{ {
Transaction t; Transaction t;
@ -60,17 +70,18 @@ int stateTest()
} }
s.execute(tx); s.execute(tx);
cout << "me: " << s.balance(me.address()) << endl; cout << s;
cout << "myMiner: " << s.balance(myMiner.address()) << endl;
// Mine to get some ether and set in stone.
s.commitToMine(bc); s.commitToMine(bc);
while (!s.mine(100)) {} while (s.mine(100).empty()) {}
bc.attemptImport(s.blockData()); bc.attemptImport(s.blockData(), stateDB);
cout << bc;
s.sync(bc); s.sync(bc);
cout << "me: " << s.balance(me.address()) << endl; cout << s;
cout << "myMiner: " << s.balance(myMiner.address()) << endl;
// s.dumpAccounts();
return 0; return 0;
} }

Loading…
Cancel
Save