From 720df0509ef276c1932f401a501a07bd24f77012 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 9 Jan 2014 01:29:16 +0000 Subject: [PATCH] Additional framework stuff - block verification, playback &c. --- eth/CMakeLists.txt | 1 + libethereum/BlockChain.cpp | 57 ++++++++++++++++++++------------ libethereum/BlockChain.h | 25 ++++---------- libethereum/BlockInfo.cpp | 38 +++++++++++++-------- libethereum/BlockInfo.h | 9 +++-- libethereum/CMakeLists.txt | 1 + libethereum/Common.h | 3 ++ libethereum/Dagger.cpp | 9 +++-- libethereum/Dagger.h | 2 ++ libethereum/Exceptions.h | 4 ++- libethereum/State.cpp | 55 ++++++++++++++++++++++++------ libethereum/State.h | 9 +++-- libethereum/Transaction.cpp | 2 +- libethereum/Transaction.h | 3 +- libethereum/TransactionQueue.cpp | 6 ++-- libethereum/TransactionQueue.h | 4 +-- libethereum/vector_ref.h | 9 +++++ 17 files changed, 158 insertions(+), 79 deletions(-) diff --git a/eth/CMakeLists.txt b/eth/CMakeLists.txt index a3f218485..b1cbfe931 100644 --- a/eth/CMakeLists.txt +++ b/eth/CMakeLists.txt @@ -14,6 +14,7 @@ link_directories(../libethereum) add_executable(eth ${SRC_LIST}) target_link_libraries(eth ethereum) +target_link_libraries(eth leveldb) target_link_libraries(eth secp256k1) target_link_libraries(eth cryptopp) target_link_libraries(eth gmp) diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 571d2822c..a5401dbbf 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -20,6 +20,9 @@ */ #include "Common.h" +#include "RLP.h" +#include "Exceptions.h" +#include "Dagger.h" #include "BlockInfo.h" #include "BlockChain.h" using namespace std; @@ -27,6 +30,9 @@ using namespace eth; BlockChain::BlockChain() { + ldb::Options o; + auto s = ldb::DB::Open(o, "blockchain", &m_db); + // Initialise with the genesis as the last block on the longest chain. m_lastBlockHash = m_genesisHash = BlockInfo::genesis().hash; m_genesisBlock = BlockInfo::createGenesisBlock(); @@ -36,11 +42,13 @@ BlockChain::~BlockChain() { } -std::vector blockChain() const +u256s BlockChain::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(); + u256s ret; + + return ret; } void BlockChain::import(bytes const& _block) @@ -65,17 +73,35 @@ void BlockChain::import(bytes const& _block) return; bi.number = it->second.first + 1; - // CHECK ANCESTRY: - // TODO: check it hashes according to proof of work. - // TODO: check timestamp is after previous timestamp. + // Check Ancestry: + // Check timestamp is after previous timestamp. + if (bi.timestamp <= BlockInfo(block(bi.parentHash)).timestamp) + throw InvalidTimestamp(); + // TODO: check difficulty is correct given the two timestamps. +// if (bi.timestamp ) + + // TODO: check transactions are valid and that they result in a state equivalent to our state_root. + // this saves us from an embarrassing exit later. + + // Check uncles. + for (auto const& i: RLP(_block)[2]) + { + auto it = m_numberAndParent.find(i.toInt()); + if (it == m_numberAndParent.end()) + return; // Don't (yet) have the uncle in our list. + if (it->second.second != bi.parentHash) + throw InvalidUncle(); + } // Insert into DB m_numberAndParent[newHash] = make_pair(bi.number, bi.parentHash); m_children.insert(make_pair(bi.parentHash, newHash)); - // TODO: put _block onto disk and load into cache. + ldb::WriteOptions o; + m_db->Put(o, ldb::Slice(toBigEndianString(newHash)), (ldb::Slice)ref(_block)); // This might be the new last block; count back through ancestors to common shared ancestor and compare to current. + // TODO: Use GHOST algorithm. } catch (...) { @@ -86,22 +112,9 @@ void BlockChain::import(bytes const& _block) bytesConstRef BlockChain::block(u256 _hash) const { - auto it = m_cache.find(_hash); - if (it == m_cache.end()) - { - // Load block from disk. - pair> loaded; - it = m_cache.insert(loaded).first; - } - return it->second->data(); -} - -bytesConstRef BlockChain::lastBlock() const -{ - if (m_lastBlockHash == m_genesisHash) - return bytesConstRef((bytes*)&m_genesisBlock); - - return block(m_lastBlockHash); + if (_hash == m_genesisHash) + return &m_genesisBlock; + return &m_genesisBlock; } u256 BlockChain::lastBlockNumber() const diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 07cd511f5..f85708a44 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -22,23 +22,12 @@ #pragma once #include "Common.h" +#include +namespace ldb = leveldb; namespace eth { -class MappedBlock -{ -public: - MappedBlock() {} - MappedBlock(u256 _hash) {} // TODO: map memory from disk. - ~MappedBlock() {} // TODO: unmap memory from disk - - bytesConstRef data() const { return bytesConstRef(); } - -private: - // TODO: memory mapping. -}; - /** * @brief Implements the blockchain database. All data this gives is disk-backed. */ @@ -53,15 +42,16 @@ public: void process(); /// Attempt to import the given block. - bool attemptImport(bytes const& _block) { try { import(_bytes); return true; } catch (...) { return false; } } + bool attemptImport(bytes const& _block) { try { import(_block); 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 + bytesConstRef lastBlock() const { return block(m_lastBlockHash); } - std::vector blockChain() + /// Get the full block chain, according to the GHOST algo and the blocks available in the db. + u256s blockChain() const; /// Get the number of the last block of the longest chain. u256 lastBlockNumber() const; @@ -73,8 +63,7 @@ private: mutable std::map> m_numberAndParent; mutable std::multimap m_children; - /// Gets populated on demand. Inactive nodes are pruned after a while. - mutable std::map> m_cache; + ldb::DB* m_db; /// Hash of the last (valid) block on the longest chain. u256 m_lastBlockHash; diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index 062cdb08d..bb3b162b0 100644 --- a/libethereum/BlockInfo.cpp +++ b/libethereum/BlockInfo.cpp @@ -20,6 +20,7 @@ */ #include "Common.h" +#include "Dagger.h" #include "Exceptions.h" #include "RLP.h" #include "BlockInfo.h" @@ -33,16 +34,26 @@ BlockInfo::BlockInfo() number = Invalid256; } +BlockInfo::BlockInfo(bytesConstRef _block, u256 _number) +{ + populate(_block, _number); +} + bytes BlockInfo::createGenesisBlock() { RLPStream block(3); - auto sha256EmptyList = sha3(RLPEmptyList); - block.appendList(7) << (uint)0 << sha256EmptyList << (uint)0 << sha256EmptyList << ((uint)1 << 36) << (uint)0 << (uint)0; + auto sha3EmptyList = sha3(RLPEmptyList); + block.appendList(8) << (uint)0 << sha3EmptyList << (uint)0 << sha3(RLPNull) << sha3EmptyList << ((uint)1 << 36) << (uint)0 << (uint)0; block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList); return block.out(); } +u256 BlockInfo::headerHashWithoutNonce() const +{ + return sha3((RLPStream(7) << toBigEndianString(parentHash) << toBigEndianString(sha3Uncles) << coinbaseAddress << toBigEndianString(stateRoot) << toBigEndianString(sha3Transactions) << difficulty << timestamp).out()); +} + void BlockInfo::populateGenesis() { bytes genesisBlock = createGenesisBlock(); @@ -59,12 +70,13 @@ void BlockInfo::populate(bytesConstRef _block, u256 _number) RLP header = root[0]; hash = eth::sha3(_block); parentHash = header[0].toInt(); - sha256Uncles = header[1].toInt(); + sha3Uncles = header[1].toInt(); coinbaseAddress = header[2].toInt(); - sha256Transactions = header[3].toInt(); - difficulty = header[4].toInt(); - timestamp = header[5].toInt(); - nonce = header[6].toInt(); + stateRoot = header[3].toInt(); + sha3Transactions = header[4].toInt(); + difficulty = header[5].toInt(); + timestamp = header[6].toInt(); + nonce = header[7].toInt(); } catch (RLP::BadCast) { @@ -76,14 +88,14 @@ void BlockInfo::verifyInternals(bytesConstRef _block) { RLP root(_block); - if (sha256Transactions != sha3(root[1].data())) + if (sha3Transactions != sha3(root[1].data())) throw InvalidTransactionsHash(); - if (sha256Uncles != sha3(root[2].data())) + if (sha3Uncles != sha3(root[2].data())) throw InvalidUnclesHash(); - // TODO: check difficulty against timestamp. - // TODO: check proof of work. - - // TODO: check each transaction - allow coinbaseAddress for the miner fees, but everything else must be exactly how we would do it. + // check it hashes according to proof of work. + Dagger d(headerHashWithoutNonce()); + if (d.eval(nonce) >= difficulty) + throw InvalidNonce(); } diff --git a/libethereum/BlockInfo.h b/libethereum/BlockInfo.h index d0d3a1edd..96f72be5e 100644 --- a/libethereum/BlockInfo.h +++ b/libethereum/BlockInfo.h @@ -31,15 +31,17 @@ struct BlockInfo public: u256 hash; u256 parentHash; - u256 sha256Uncles; + u256 sha3Uncles; u256 coinbaseAddress; - u256 sha256Transactions; + u256 stateRoot; + u256 sha3Transactions; u256 difficulty; u256 timestamp; u256 nonce; u256 number; BlockInfo(); + explicit BlockInfo(bytesConstRef _block, u256 _number = 0); explicit operator bool() { return number != Invalid256; } @@ -49,6 +51,9 @@ public: void populate(bytesConstRef _block, u256 _number = 0); void verifyInternals(bytesConstRef _block); + /// No-nonce sha3 of the header only. + u256 headerHashWithoutNonce() const; + static bytes createGenesisBlock(); private: diff --git a/libethereum/CMakeLists.txt b/libethereum/CMakeLists.txt index b224b650a..843d53ca3 100644 --- a/libethereum/CMakeLists.txt +++ b/libethereum/CMakeLists.txt @@ -10,5 +10,6 @@ aux_source_directory(. SRC_LIST) add_library(ethereum ${SRC_LIST}) target_link_libraries(ethereum secp256k1) +target_link_libraries(ethereum leveldb) target_link_libraries(ethereum cryptopp) target_link_libraries(ethereum gmp) diff --git a/libethereum/Common.h b/libethereum/Common.h index 00a52c2e5..705ea4350 100644 --- a/libethereum/Common.h +++ b/libethereum/Common.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include #include @@ -51,6 +52,8 @@ using uint = uint64_t; using sint = int64_t; using u256s = std::vector; using u160s = std::vector; +using u256Set = std::set; +using u160Set = std::set; // Map types. using StringMap = std::map; diff --git a/libethereum/Dagger.cpp b/libethereum/Dagger.cpp index bb87b4916..c33c56a55 100644 --- a/libethereum/Dagger.cpp +++ b/libethereum/Dagger.cpp @@ -17,16 +17,21 @@ Dagger::~Dagger() { } +u256 Dagger::bound(u256 _diff) +{ + return (u256)((bigint(1) << 256) / _diff); +} + u256 Dagger::search(uint _msTimeout, u256 _diff) { static mt19937_64 s_engine((std::random_device())()); - u256 bound = (u256)((bigint(1) << 256) / _diff); + u256 b = bound(_diff); auto start = steady_clock::now(); while (steady_clock::now() - start < milliseconds(_msTimeout)) for (uint sp = std::uniform_int_distribution()(s_engine), j = 0; j < 1000; ++j, ++sp) - if (eval(sp) < bound) + if (eval(sp) < b) return sp; return 0; } diff --git a/libethereum/Dagger.h b/libethereum/Dagger.h index 59884ded5..942c12b90 100644 --- a/libethereum/Dagger.h +++ b/libethereum/Dagger.h @@ -16,6 +16,8 @@ public: u256 eval(u256 _N); u256 search(uint _msTimeout, u256 _diff); + static u256 bound(u256 _diff); + private: u256 m_hash; u256 m_xn; diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index a9614855c..70caed2b3 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -19,11 +19,13 @@ class InvalidSignature: public std::exception {}; class InvalidTransactionFormat: public std::exception {}; class InvalidBlockFormat: public std::exception {}; class InvalidUnclesHash: public std::exception {}; +class InvalidUncle: public std::exception {}; +class InvalidStateRoot: public std::exception {}; 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 { public: InvalidNonce(u256 _required, u256 _candidate): required(_required), candidate(_candidate) {} u256 required; u256 candidate; }; +class InvalidNonce: public std::exception { public: InvalidNonce(u256 _required = 0, u256 _candidate = 0): required(_required), candidate(_candidate) {} u256 required; u256 candidate; }; class InvalidParentHash: public std::exception {}; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 29dc6fb4e..3b550214b 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -87,7 +87,6 @@ void State::sync(BlockChain const& _bc, TransactionQueue& _tq) if (l.back() == BlockInfo::genesis().hash) { // Reset to genesis block. - m_current.clear(); m_previousBlock = BlockInfo::genesis(); } else @@ -100,6 +99,9 @@ void State::sync(BlockChain const& _bc, TransactionQueue& _tq) playback(_bc.block(*it)); m_transactions.clear(); + m_current.clear(); + m_currentBlock = BlockInfo(); + m_currentBlock.number = m_previousBlock.number + 1; } @@ -127,18 +129,23 @@ void State::sync(BlockChain const& _bc, TransactionQueue& _tq) void State::playback(bytesConstRef _block) { - BlockInfo bi; try { - bi.populate(_block, m_previousBlock.number + 1); - bi.verifyInternals(_block); - if (bi.parentHash != m_previousBlock.hash) + m_currentBlock.populate(_block, m_previousBlock.number + 1); + m_currentBlock.verifyInternals(_block); + if (m_currentBlock.parentHash != m_previousBlock.hash) throw InvalidParentHash(); // All ok with the block generally. Play back the transactions now... - RLP txs = _block[1]; + RLP txs = RLP(_block)[1]; for (auto const& i: txs) execute(i.data()); + + // Hash the state trie and check against the state_root hash in m_currentBlock. + if (m_currentBlock.stateRoot != currentHash()) + throw InvalidStateRoot(); + + m_previousBlock = m_currentBlock; } catch (...) { @@ -148,6 +155,12 @@ void State::playback(bytesConstRef _block) } } +u256 State::currentHash() const +{ + // TODO! + return 0; +} + bool State::mine(uint _msTimeout) const { // TODO: update timestamp according to clock. @@ -207,21 +220,41 @@ u256 State::contractMemory(Address _contract, u256 _memory) const return i == m->second.memory().end() ? 0 : i->second; } +bool State::execute(bytesConstRef _rlp) +{ + // Entry point for a user-executed transaction. + try + { + Transaction t(_rlp); + execute(t, t.sender()); + + // Add to the user-originated transactions that we've executed. + // NOTE: Here, contract-originated transactions will not get added to the transaction list. + // If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and + // don't forget to allow unsigned transactions in the tx list if they concur with the script execution. + m_transactions.insert(make_pair(t.sha3(), t)); + + return true; + } + catch (...) + { + return false; + } +} + void State::execute(Transaction const& _t, Address _sender) { // Entry point for a contract-originated transaction. // Ignore invalid transactions. - if (_t.nonce != transactionsFrom(_sender)) - throw InvalidNonce(); + auto nonceReq = transactionsFrom(_sender); + if (_t.nonce != nonceReq) + throw InvalidNonce(nonceReq, _t.nonce); // 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 45cb6afe1..452e38cbe 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -59,11 +59,12 @@ public: /// Sync our state with the block chain. /// This basically involves wiping ourselves if we've been superceded and rebuilding from the transaction queue. - void sync(BlockChain const& _bc, TransactionQueue const& _tq); + /// We also sync our transactions, killing those from the queue that we have and assimilating those that we don't. + void sync(BlockChain const& _bc, TransactionQueue& _tq); /// Execute a given transaction. - bool execute(bytes const& _rlp) { try { Transaction t(_rlp); execute(t, t.sender()); } catch (...) { return false; } } - + bool execute(bytes const& _rlp) { return execute(&_rlp); } + bool execute(bytesConstRef _rlp); /// Check if the address is a valid normal (non-contract) account address. bool isNormalAddress(Address _address) const; @@ -111,6 +112,8 @@ private: /// Execute all transactions within a given block. void playback(bytesConstRef _block); + u256 currentHash() const; + // 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. diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index 780a45a83..5659d42f9 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -25,7 +25,7 @@ using namespace std; using namespace eth; -Transaction::Transaction(bytes const& _rlpData) +Transaction::Transaction(bytesConstRef _rlpData) { RLP rlp(_rlpData); nonce = rlp[0].toInt(RLP::StrictlyInt); diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 6ad5d926b..c9989fb22 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -41,7 +41,8 @@ struct Signature struct Transaction { Transaction() {} - Transaction(bytes const& _rlp); + Transaction(bytesConstRef _rlp); + Transaction(bytes const& _rlp): Transaction(&_rlp) {} u256 nonce; Address receiveAddress; diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 33cff63e1..5afc7a5e6 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -28,14 +28,14 @@ void TransactionQueue::import(bytes const& _block) { // Check if we already know this transaction. u256 h = sha3(_block); - if (m_hashes.count(h)) + if (m_data.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(); + t.sender(); // If valid, append to blocks. - m_data[h] = m_data; + m_data[h] = _block; } diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 7c2a8dfe7..1c5b9300a 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -34,13 +34,13 @@ class BlockChain; class TransactionQueue { public: - bool attemptImport(bytes const& _block) { try { import(_bytes); return true; } catch (...) { return false; } } + bool attemptImport(bytes const& _block) { try { import(_block); return true; } catch (...) { return false; } } void import(bytes const& _block); void drop(u256 _txHash) { m_data.erase(_txHash); } - std::map const& transactions() const; + std::map const& transactions() const { return m_data; } private: std::map m_data; ///< the queue. diff --git a/libethereum/vector_ref.h b/libethereum/vector_ref.h index 0f2ddfd9e..61193c10d 100644 --- a/libethereum/vector_ref.h +++ b/libethereum/vector_ref.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace eth { @@ -21,6 +22,7 @@ public: vector_ref(std::string* _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} vector_ref(std::vector::value, typename std::remove_const<_T>::type>::type> const* _data): m_data(_data->data()), m_count(_data->size()) {} vector_ref(std::enable_if::value, std::string const&> _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} + vector_ref(leveldb::Slice const& _s): m_data(_s.data()), m_count(_s.size() / sizeof(_T)) {} explicit operator bool() const { return m_data && m_count; } @@ -48,6 +50,8 @@ public: bool operator==(vector_ref<_T> const& _cmp) const { return m_data == _cmp.m_data && m_count == _cmp.m_count; } bool operator!=(vector_ref<_T> const& _cmp) const { return !operator==(); } + operator leveldb::Slice() const { return leveldb::Slice((char const*)m_data, m_count * sizeof(_T)); } + void reset() { m_data = nullptr; m_count = 0; } private: @@ -55,4 +59,9 @@ private: unsigned m_count; }; +template vector_ref<_T const> ref(_T const& _t) { return vector_ref<_T const>(&_t, 1); } +template vector_ref<_T> ref(_T& _t) { return vector_ref<_T>(&_t, 1); } +template vector_ref<_T const> ref(std::vector<_T> const& _t) { return vector_ref<_T const>(&_t); } +template vector_ref<_T> ref(std::vector<_T>& _t) { return vector_ref<_T>(&_t); } + }