diff --git a/eth/main.cpp b/eth/main.cpp index 222859b35..c7a549a84 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -32,9 +32,10 @@ int main() h256 privkey = sha3("123"); Address us = toAddress(privkey); // TODO: should be loaded from config file/set at command-line. - BlockChain bc; // Maintains block database. - TransactionQueue tq; // Maintains list of incoming transactions not yet on the block chain. - State s(us); + 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. @@ -42,7 +43,7 @@ int main() s.sync(bc); 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) { // Process network events. @@ -62,14 +63,10 @@ int main() s.sync(tq); // Mine for a while. - if (s.mine(100)) - { - // Mined block - bytes b = s.blockData(); - + bytes b = s.mine(100); + if (b.size()) // Import block. - bc.import(b); - } + bc.attemptImport(b, stateDB); } return 0; diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 63abf3fc3..e3589964a 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -30,44 +30,72 @@ using namespace std; 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(); + totalDifficulty = _r[1].toInt(); + parent = _r[2].toHash(); + children = _r[3].toVector(); +} + +bytes BlockDetails::rlp() const +{ + return rlpList(number, totalDifficulty, parent, children); +} + BlockChain::BlockChain(std::string _path, bool _killExisting) { if (_path.empty()) - _path = string(getenv("HOME")) + "/.ethereum"; + _path = Defaults::s_dbPath; boost::filesystem::create_directory(_path); if (_killExisting) + { boost::filesystem::remove_all(_path + "/blocks"); + boost::filesystem::remove_all(_path + "/details"); + } ldb::Options o; o.create_if_missing = true; 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. - m_lastBlockHash = m_genesisHash = BlockInfo::genesis().hash; + m_genesisHash = BlockInfo::genesis().hash; m_genesisBlock = BlockInfo::createGenesisBlock(); // 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 { @@ -78,12 +106,12 @@ void BlockChain::import(bytes const& _block) auto newHash = eth::sha3(_block); // Check block doesn't already exist first! - if (m_details.count(newHash)) + if (details(newHash)) return; // Work out its number as the parent's number + 1 - auto it = m_details.find(bi.parentHash); - if (it == m_details.end()) + 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; @@ -92,23 +120,35 @@ void BlockChain::import(bytes const& _block) bi.verifyParent(biParent); // 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); // Get total difficulty increase and update state, checking it. BlockInfo biGrandParent; - if (it->second.number) - biGrandParent.populate(block(it->second.parent)); - u256 td = it->second.totalDifficulty + s.playback(&_block, bi, biParent, biGrandParent, true); + 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)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_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... 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; + } } catch (...) { @@ -122,7 +162,7 @@ bytesConstRef BlockChain::block(h256 _hash) const if (_hash == m_genesisHash) 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]); } @@ -130,6 +170,13 @@ BlockDetails const& BlockChain::details(h256 _h) const { auto it = m_details.find(_h); 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; } diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 7c422ec4c..08478d526 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -27,24 +27,49 @@ namespace ldb = leveldb; 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 { + 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; u256 totalDifficulty; h256 parent; h256s children; }; -static const BlockDetails NullBlockDetails({ 0, 0, h256(), {} }); +static const BlockDetails NullBlockDetails; static const h256s NullH256s; +class Overlay; + /** * @brief Implements the blockchain database. All data this gives is disk-backed. */ class BlockChain { 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(); /// (Potentially) renders invalid existing bytesConstRef returned by lastBlock. @@ -52,13 +77,10 @@ public: void process(); /// 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 - void import(bytes const& _block); - - /// Get the full block chain, according to the GHOST algo and the blocks available in the db. - h256s blockChain(h256Set const& _earlyExit) const; + void import(bytes const& _block, Overlay const& _stateDB); /// Get the number of the last block of the longest chain. BlockDetails const& details(h256 _hash) const; @@ -75,10 +97,10 @@ public: private: /// Get fully populated from disk DB. mutable std::map m_details; - mutable std::map m_cache; ldb::DB* m_db; + ldb::DB* m_detailsDB; /// Hash of the last (valid) block on the longest chain. h256 m_lastBlockHash; @@ -87,6 +109,10 @@ private: ldb::ReadOptions m_readOptions; ldb::WriteOptions m_writeOptions; + + friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); }; +std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc); + } diff --git a/libethereum/BlockInfo.h b/libethereum/BlockInfo.h index 5f1897d8b..784874630 100644 --- a/libethereum/BlockInfo.h +++ b/libethereum/BlockInfo.h @@ -30,7 +30,7 @@ namespace eth struct BlockInfo { public: - h256 hash; + h256 hash; ///< SHA3 hash of the entire block! h256 parentHash; h256 sha3Uncles; Address coinbaseAddress; @@ -46,7 +46,18 @@ public: 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); } static BlockInfo const& genesis() { if (!s_genesis) (s_genesis = new BlockInfo)->populateGenesis(); return *s_genesis; } diff --git a/libethereum/Dagger.cpp b/libethereum/Dagger.cpp index 6aeee956a..177ec0696 100644 --- a/libethereum/Dagger.cpp +++ b/libethereum/Dagger.cpp @@ -22,11 +22,12 @@ namespace eth #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(0, ~(uint)0)(s_eng); // 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)) return true; return false; @@ -52,7 +53,7 @@ bool Dagger::verify(h256 const& _root, u256 const& _nonce, u256 const& _difficul 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 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); // 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) { diff --git a/libethereum/Dagger.h b/libethereum/Dagger.h index d4a456088..2d9e1bba4 100644 --- a/libethereum/Dagger.h +++ b/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 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 @@ -31,7 +31,7 @@ public: static h256 eval(h256 const& _root, u256 const& _nonce); 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: diff --git a/libethereum/RLP.h b/libethereum/RLP.h index e7e3a84fe..2b0f59eec 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -161,7 +161,7 @@ public: explicit operator uint() const { return toSlimInt(); } explicit operator u256() const { return toFatInt(); } explicit operator bigint() const { return toBigInt(); } - template explicit operator FixedHash<_N>() const { return toHash<_N>(); } + template explicit operator FixedHash<_N>() const { return toHash>(); } /// 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()); } @@ -172,6 +172,8 @@ public: /// Converts to string. @throws BadCast if not a string. std::string toStringStrict() const { if (!isString()) throw BadCast(); return payload().cropped(0, items()).toString(); } + template std::vector toVector() const { std::vector ret; if (isList()) { ret.reserve(itemCount()); for (auto const& i: *this) ret.push_back((T)i); } return ret; } + /// Int conversion flags enum { diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 3c7f36bd7..4a7bd099d 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -53,12 +53,10 @@ u256 const State::c_newContractFee = 60000; u256 const State::c_txFee = 0; 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()) - _path = string(getenv("HOME")) + "/.ethereum"; + _path = Defaults::s_dbPath; boost::filesystem::create_directory(_path); if (_killExisting) 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; ldb::DB* db = nullptr; 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_previousBlock = BlockInfo::genesis(); 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); if (it == m_cache.end()) { // populate basic info. string stateBack = m_state.at(_a); + if (stateBack.empty() && !_forceCreate) + return; RLP state(stateBack); AddressState s; 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. // Find most recent state dump and replay what's left. // (Most recent state dump might end up being genesis.) - std::vector l = _bc.blockChain(h256Set()); - if (l.back() == BlockInfo::genesis().hash) - { - // Reset to genesis block. - m_previousBlock = BlockInfo::genesis(); - } - else + std::vector 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... { - // 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. - for (auto it = next(l.cbegin()); it != l.cend(); ++it) + for (auto it = chain.rbegin(); it != chain.rend(); ++it) playback(_bc.block(*it), true); - m_currentNumber = _bc.details(_bc.currentHash()).number + 1; + m_currentNumber = _bc.details(_block).number + 1; resetCurrent(); } } @@ -289,6 +291,8 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _ // Commit all cached state changes to the state trie. commit(); +// cout << m_state << endl << TrieDB(&m_db, m_currentBlock.stateRoot); + // Hash the state trie and check against the state_root hash in m_currentBlock. 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. 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) - { - BlockInfo ubi(_bc.block(u)); - ubi.fillStream(uncles, true); - uncleAddresses.push_back(ubi.coinbaseAddress); - } + if (u != m_previousBlock.hash) // ignore our own parent - it's not an uncle. + { + BlockInfo ubi(_bc.block(u)); + ubi.fillStream(uncles, true); + uncleAddresses.push_back(ubi.coinbaseAddress); + } } else uncles.appendList(0); @@ -354,7 +360,7 @@ void State::commitToMine(BlockChain const& _bc) m_currentBlock.parentHash = m_previousBlock.hash; } -bool State::mine(uint _msTimeout) +bytes const& State::mine(uint _msTimeout) { // Update timestamp according to clock. 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). 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; ret.appendList(3); - { - RLPStream s; - m_currentBlock.fillStream(s, true); - m_currentBlock.hash = sha3(s.out()); - ret.appendRaw(s.out()); - } + m_currentBlock.fillStream(ret, true); ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); 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 { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return false; @@ -394,7 +402,7 @@ bool State::isNormalAddress(Address _id) const bool State::isContractAddress(Address _id) const { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return false; @@ -403,7 +411,7 @@ bool State::isContractAddress(Address _id) const u256 State::balance(Address _id) const { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return 0; @@ -412,7 +420,7 @@ u256 State::balance(Address _id) const void State::noteSending(Address _id) { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) m_cache[_id] = AddressState(0, 1); @@ -422,7 +430,7 @@ void State::noteSending(Address _id) void State::addBalance(Address _id, u256 _amount) { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) m_cache[_id] = AddressState(_amount, 0); @@ -432,7 +440,7 @@ void State::addBalance(Address _id, u256 _amount) void State::subBalance(Address _id, bigint _amount) { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end() || (bigint)it->second.balance() < _amount) throw NotEnoughCash(); @@ -442,7 +450,7 @@ void State::subBalance(Address _id, bigint _amount) u256 State::transactionsFrom(Address _id) const { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return 0; @@ -452,7 +460,7 @@ u256 State::transactionsFrom(Address _id) const u256 State::contractMemory(Address _id, u256 _memory) const { - ensureCached(_id); + ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end() || it->second.type() != AddressType::Contract) return 0; @@ -571,7 +579,7 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ if (stack.size() < _n) throw StackTooSmall(_n, stack.size()); }; - ensureCached(_myAddress, true); + ensureCached(_myAddress, true, true); auto& myMemory = m_cache[_myAddress].takeMemory(); auto mem = [&](u256 _n) -> u256 diff --git a/libethereum/State.h b/libethereum/State.h index e902378b1..8e73934a3 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -43,13 +43,16 @@ class BlockChain; * @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). * 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 { public: /// 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. /// @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. /// @param _msTimeout Timeout before return in milliseconds. - /// @returns true if it got lucky. In this case, call blockData() to get the block for - /// spreading far and wide. - bool mine(uint _msTimeout = 1000); + /// @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); /// Get the complete current block, including valid nonce. /// Only valid after mine() returns true. @@ -140,7 +143,10 @@ private: }; /// 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. void commit(); @@ -190,8 +196,35 @@ private: static const u256 c_newContractFee; static const u256 c_txFee; 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
d; + for (auto const& i: TrieDB(const_cast(&_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() << "@" << r[0].toInt() << 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; +} + } diff --git a/libethereum/TrieDB.h b/libethereum/TrieDB.h index 39ddf622e..ecb5da66b 100644 --- a/libethereum/TrieDB.h +++ b/libethereum/TrieDB.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include "TrieCommon.h" namespace ldb = leveldb; @@ -71,8 +72,8 @@ class Overlay: public BasicMap public: Overlay(ldb::DB* _db = nullptr): m_db(_db) {} - ldb::DB* db() const { return m_db; } - void setDB(ldb::DB* _db, bool _clearOverlay = true) { m_db = _db; if (_clearOverlay) m_over.clear(); } + ldb::DB* db() const { return m_db.get(); } + void setDB(ldb::DB* _db, bool _clearOverlay = true) { m_db = std::shared_ptr(_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 rollback() { m_over.clear(); m_refCount.clear(); } @@ -82,7 +83,7 @@ public: private: using BasicMap::clear; - ldb::DB* m_db = nullptr; + std::shared_ptr m_db; ldb::ReadOptions m_readOptions; ldb::WriteOptions m_writeOptions; diff --git a/test/main.cpp b/test/main.cpp index ff2be2a59..e082b6cac 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -31,11 +31,11 @@ int hexPrefixTest(); int main() { - hexPrefixTest(); - rlpTest(); - trieTest(); +// hexPrefixTest(); +// rlpTest(); +// trieTest(); // daggerTest(); - cryptoTest(); +// cryptoTest(); stateTest(); return 0; } diff --git a/test/state.cpp b/test/state.cpp index ce12baaf9..d4201c570 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -32,21 +32,31 @@ int stateTest() KeyPair myMiner = sha3("Gav's Miner"); // KeyPair you = sha3("123"); - BlockChain bc("/tmp"); - State s(myMiner.address(), "/tmp"); + Defaults::setDBPath("/tmp"); - cout << dec << "me: " << s.balance(me.address()) << endl; - cout << "myMiner: " << s.balance(myMiner.address()) << endl; + Overlay stateDB = State::openDB(); + 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! s.commitToMine(bc); - while (!s.mine(100)) {} - bc.attemptImport(s.blockData()); + while (s.mine(100).empty()) {} + bc.attemptImport(s.blockData(), stateDB); + + cout << bc; + s.sync(bc); - cout << "me: " << s.balance(me.address()) << endl; - cout << "myMiner: " << s.balance(myMiner.address()) << endl; + cout << s; + // Inject a transaction to transfer funds from miner to me. bytes tx; { Transaction t; @@ -60,17 +70,18 @@ int stateTest() } s.execute(tx); - cout << "me: " << s.balance(me.address()) << endl; - cout << "myMiner: " << s.balance(myMiner.address()) << endl; + cout << s; + // Mine to get some ether and set in stone. s.commitToMine(bc); - while (!s.mine(100)) {} - bc.attemptImport(s.blockData()); + while (s.mine(100).empty()) {} + bc.attemptImport(s.blockData(), stateDB); + + cout << bc; + s.sync(bc); - cout << "me: " << s.balance(me.address()) << endl; - cout << "myMiner: " << s.balance(myMiner.address()) << endl; -// s.dumpAccounts(); + cout << s; return 0; }