From e7ebf1e6b67887deabbdafe507d1558fcec93582 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 8 Jan 2014 21:17:58 +0000 Subject: [PATCH] More work on big picture stuff. --- eth/main.cpp | 4 +- libethereum/BlockChain.cpp | 8 ++++ libethereum/BlockChain.h | 5 +++ libethereum/Exceptions.h | 3 +- libethereum/PeerNetwork.cpp | 20 +++------ libethereum/State.cpp | 74 ++++++++++++++++++++++++++++++-- libethereum/State.h | 5 ++- libethereum/TransactionQueue.cpp | 17 ++++++++ libethereum/TransactionQueue.h | 10 ++++- test/main.cpp | 2 +- 10 files changed, 122 insertions(+), 26 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 309a4853a..dc73e7205 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -31,9 +31,9 @@ int main() // Our address. Address us; // TODO: should be loaded from config file/set at command-line. - BlockChain bc; // TODO: Implement - should look for block database. + BlockChain bc; // TODO: Implement - should maintain block database. TransactionQueue tq; // TODO: Implement. - State s(us); + State s(us); // TODO: Switch to disk-backed state (leveldb) // s.restore(); // TODO: Implement - key optimisation. // Synchronise the state according to the block chain - i.e. replay all transactions, in order. Will take a while if the state isn't restored. diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index fc928651c..571d2822c 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -36,6 +36,13 @@ BlockChain::~BlockChain() { } +std::vector blockChain() const +{ + // TODO: return the current valid block chain from most recent to genesis. + // TODO: arguments for specifying a set of early-ends + return std::vector(); +} + void BlockChain::import(bytes const& _block) { BlockInfo bi; @@ -59,6 +66,7 @@ void BlockChain::import(bytes const& _block) bi.number = it->second.first + 1; // CHECK ANCESTRY: + // TODO: check it hashes according to proof of work. // TODO: check timestamp is after previous timestamp. // TODO: check difficulty is correct given the two timestamps. diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 3fbaac9ae..07cd511f5 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -52,12 +52,17 @@ public: /// To be called from main loop every 100ms or so. void process(); + /// Attempt to import the given block. + bool attemptImport(bytes const& _block) { try { import(_bytes); return true; } catch (...) { return false; } } + /// Import block into disk-backed DB void import(bytes const& _block); /// Get the last block of the longest chain. bytesConstRef lastBlock() const; // TODO: switch to return MappedBlock or add the lock into vector_ref + std::vector blockChain() + /// Get the number of the last block of the longest chain. u256 lastBlockNumber() const; diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index 7678c2bbf..a9614855c 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -23,6 +23,7 @@ class InvalidTransactionsHash: public std::exception {}; class InvalidTransaction: public std::exception {}; class InvalidDifficulty: public std::exception {}; class InvalidTimestamp: public std::exception {}; -class InvalidNonce: public std::exception {}; +class InvalidNonce: public std::exception { public: InvalidNonce(u256 _required, u256 _candidate): required(_required), candidate(_candidate) {} u256 required; u256 candidate; }; +class InvalidParentHash: public std::exception {}; } diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index 91a930bba..47c43af9c 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -39,23 +39,13 @@ void PeerNetwork::process() void PeerNetwork::sync(BlockChain& _bc, TransactionQueue const& _tq) { /* - while (incomingBlock()) + while (incomingData()) { // import new block - bytes const& block = net.incomingBlock(); - _bc.import(block); - net.popIncomingBlock(); - - // check block chain and make longest given all available blocks. - bc.rejig(); - } - - while (incomingTransaction()) - { - bytes const& tx = net.incomingTransaction(); - _tq.import(tx); - net.popIncomingTransaction(); + bytes const& data = net.incomingData(); + if (!tq.attemptImport(data) && !_bc.attemptImport(data)) + handleMessage(data); + popIncoming(); } */ } - diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 7ebfb0882..29dc6fb4e 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -45,8 +45,10 @@ State::State(Address _minerAddress): m_minerAddress(_minerAddress) m_currentBlock.number = 1; } -void State::sync(BlockChain const& _bc, TransactionQueue const& _tq) +void State::sync(BlockChain const& _bc, TransactionQueue& _tq) { + // BLOCK + BlockInfo bi; try { @@ -55,6 +57,7 @@ void State::sync(BlockChain const& _bc, TransactionQueue const& _tq) } catch (...) { + // TODO: Slightly nicer handling? :-) cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; exit(1); } @@ -79,6 +82,69 @@ void State::sync(BlockChain const& _bc, TransactionQueue const& _tq) // New blocks available, or we've switched to a different branch. All change. // TODO: Find most recent state dump and replay what's left. // (Most recent state dump might end up being genesis.) + std::vector l = _bc.blockChain(); // TODO: take u256Set "restorePoints" argument so it needs only give us the chain up until some restore point in the past where we stored the state. + + if (l.back() == BlockInfo::genesis().hash) + { + // Reset to genesis block. + m_current.clear(); + m_previousBlock = BlockInfo::genesis(); + } + else + { + // TODO: Begin at a restore point. + } + + // Iterate through in reverse, playing back each of the blocks. + for (auto it = next(l.cbegin()); it != l.cend(); ++it) + playback(_bc.block(*it)); + + m_transactions.clear(); + } + + + // TRANSACTIONS + + auto ts = _tq.transactions(); + for (auto const& i: ts) + if (!m_transactions.count(i.first)) + // don't have it yet! Execute it now. + try + { + Transaction t(i.second); + execute(t, t.sender()); + } + catch (InvalidNonce in) + { + if (in.required > in.candidate) + // too old + _tq.drop(i.first); + } + catch (...) + { + } +} + +void State::playback(bytesConstRef _block) +{ + BlockInfo bi; + try + { + bi.populate(_block, m_previousBlock.number + 1); + bi.verifyInternals(_block); + if (bi.parentHash != m_previousBlock.hash) + throw InvalidParentHash(); + + // All ok with the block generally. Play back the transactions now... + RLP txs = _block[1]; + for (auto const& i: txs) + execute(i.data()); + } + catch (...) + { + // TODO: Slightly nicer handling? :-) + cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; + exit(1); } } @@ -149,13 +215,13 @@ void State::execute(Transaction const& _t, Address _sender) if (_t.nonce != transactionsFrom(_sender)) throw InvalidNonce(); - // Add to the transactions in - m_transactions.push_back(_t); - // Not considered invalid - just pointless. if (balance(_sender) < _t.value + _t.fee) throw NotEnoughCash(); + // Add to the transactions in + m_transactions.push_back(_t.sha3(), _t); + if (_t.receiveAddress) { subBalance(_sender, _t.value + _t.fee); diff --git a/libethereum/State.h b/libethereum/State.h index 86f229427..45cb6afe1 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -108,10 +108,13 @@ private: /// Execute a contract transaction. void execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); + /// Execute all transactions within a given block. + void playback(bytesConstRef _block); + // TODO: std::hash
and then move to unordered_map. // Will need to sort on hash construction. std::map m_current; ///< The current state. We work with a C++ hash map rather than a Trie. - std::vector m_transactions; ///< The current list of transactions that we've included in the state. + std::map m_transactions; ///< The current list of transactions that we've included in the state. BlockInfo m_previousBlock; ///< The previous block's information. BlockInfo m_currentBlock; ///< The current block's information. diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 340f5802a..33cff63e1 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -19,6 +19,23 @@ * @date 2014 */ +#include "Transaction.h" #include "TransactionQueue.h" using namespace std; using namespace eth; + +void TransactionQueue::import(bytes const& _block) +{ + // Check if we already know this transaction. + u256 h = sha3(_block); + if (m_hashes.count(h)) + return; + + // 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); + u160 sender = t.sender(); + + // If valid, append to blocks. + m_data[h] = m_data; +} diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index f76709c42..7c2a8dfe7 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -34,10 +34,16 @@ class BlockChain; class TransactionQueue { public: - void sync(BlockChain const& _bc) {} + bool attemptImport(bytes const& _block) { try { import(_bytes); return true; } catch (...) { return false; } } + + void import(bytes const& _block); + + void drop(u256 _txHash) { m_data.erase(_txHash); } + + std::map const& transactions() const; private: - std::vector m_data; + std::map m_data; ///< the queue. }; } diff --git a/test/main.cpp b/test/main.cpp index 2216e8776..100b9e6d1 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -38,7 +38,7 @@ int main() { // Test dagger { - Dagger d(1); + Dagger d(0); auto s = steady_clock::now(); cout << hex << d.eval(0); cout << " " << dec << duration_cast(steady_clock::now() - s).count() << " ms" << endl;