diff --git a/TODO b/TODO index d59112a69..c5ebaeea2 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +### UP FOR GRABS + Tests - Use standard tests. @@ -5,20 +7,29 @@ Config file & command line options. Peer network. +Crypto stuff: +- kFromMessage +- Check all the tweak instructions. + +Better handling of corrupt blocks. + + +### GAV + Trie on DB. - Modularise overlay and DB. - Iterate. -- Kill all the restore point stuff. +- Move the restore point stuff into block restore points + - i.e. keep all nodes from last 127 blocks with counter, at 128, kill but keep every (60*24*7)th or so i.e. one per week as a restore point. + - maybe allow this to be configured. Cache some state - Contract memory, balances - for single commit into Trie. -Better handling of corrupt blocks. + +### TIM Stateful Miner class. Better Mod-Exp. -Crypto stuff: -- kFromMessage -- Check all the tweak instructions. diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 3883aadb2..c8c73aec4 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -240,6 +240,9 @@ public: /// Converts to RLPs collection object. Useful if you need random access to sub items or will iterate over multiple times. RLPs toList() const; + /// @returns the data payload. Valid for all types. + bytesConstRef payload() const { auto n = (m_data[0] & 0x3f); return m_data.cropped(1 + (n < 0x38 ? 0 : (n - 0x37))); } + private: /// Direct value integer. bool isDirectValueInt() const { assert(!isNull()); return m_data[0] < 0x18; } @@ -269,9 +272,6 @@ private: /// @returns the number of data items (bytes in the case of strings & ints, items in the case of lists). Valid for all types. uint items() const; - /// @returns the data payload. Valid for all types. - bytesConstRef payload() const { auto n = (m_data[0] & 0x3f); return m_data.cropped(1 + (n < 0x38 ? 0 : (n - 0x37))); } - /// Our byte data. bytesConstRef m_data; @@ -297,8 +297,8 @@ public: RLPStream& append(uint _s); RLPStream& append(u160 _s); RLPStream& append(u256 _s); - RLPStream& append(h160 _s, bool _compact = false) { return appendFixed(_s, _compact); } - RLPStream& append(h256 _s, bool _compact = false) { return appendFixed(_s, _compact); } + RLPStream& append(h160 _s, bool _compact = true) { return appendFixed(_s, _compact); } + RLPStream& append(h256 _s, bool _compact = true) { return appendFixed(_s, _compact); } RLPStream& append(bigint _s); RLPStream& appendList(uint _count); RLPStream& appendString(bytesConstRef _s); diff --git a/libethereum/Trie.cpp b/libethereum/Trie.cpp index be3a1c265..49ae4a1c3 100644 --- a/libethereum/Trie.cpp +++ b/libethereum/Trie.cpp @@ -667,4 +667,138 @@ void Trie::remove(std::string const& _key) h256 const GenericTrieDB::c_null = sha3(RLPNull); +inline byte nibble(bytesConstRef _data, uint _i) +{ + return (i & 1) ? (_data[i / 2] & 15) : (_data[i / 2] >> 4); +} + +std::string hexPrefixEncode(bytesConstRef _data, bool _terminated, int _beginNibble, int _endNibble) +{ + uint begin = _begin; + uint end = _end < 0 ? _data.size() * 2 + 1 + _end : _end; + bool odd = (end - begin) & 1; + + std::string ret(1, ((_terminated ? 2 : 0) | (odd ? 1 : 0)) * 16); + ret.reserve((end - begin) / 2 + 1); + + uint d = odd ? 1 : 2; + for (auto i = begin; i < end; ++i, ++d) + { + byte n = nibble(_data, i); + if (d & 1) // odd + ret.back() |= n; // or the nibble onto the back + else + ret.push_back(n << 4); // push the nibble on to the back << 4 + } + return ret; +} + +uint sharedNibbles(bytesConstRef _a, uint _ab, uint _ae, bytesConstRef _b, uint _bb, uint _be) +{ + uint ret = 0; + for (uint ai = _ab, bi = _bb; ai < _ae && bi < _be && nibble(_a, ai) == nibble(_b, bi); ++ai, ++bi) {} + return ret; +} + +// alters given RLP such that the given key[_begin:_end]/value exists under it, and puts the new RLP into _s. +// _s's size could be anything. +void GenericTrieDB::insertHelper(RLPStream& _s, RLP const& _node, bytesConstRef _key, bytesConstRef _value, uint _begin, uint _end) +{ + if (_node.isNull() || _node.isEmpty()) + { + _s.insertList(2); + _s << hexPrefixEncode(_key, true, _begin, _end); + _s.appendString(_value); + } + else if (_node.itemCount() == 2) + { + // split [k,v] into branch + auto pl = _node[0].payload(); + auto plb = (_node[0] & 0x10) ? 1 : 2; + auto ple = pl.size() - plb; + uint pivot = sharedNibbles(_key, _begin, _end, pl, plb, ple); + // special case for pivot == first nibble, pivot == last nibble. if it's anything else, need stem, branch, 2 stems (old & new) + if (pivot == _end - _begin && pivot == ple - plb) + { + _s.insertList(2); + _s << _node[0]; + _s.appendString(_value); + } + else + { + if (pivot == 0) + { + // branch immediately + _s.insertList(17); + if (_begin == _end) + { + // as value + for (auto i = 0; i < 16; ++i) + _s << _node[i]; + _s << _value; + } + } + else + } + } + else if (_node.itemCount() == 17) + { + // insert into branch + _s.insertList(17); + if (_begin == _end) + { + // as value + for (auto i = 0; i < 16; ++i) + _s << _node[i]; + _s << _value; + } + else + { + // underneath + auto n = nibble(_key, _begin); + for (auto i = 0; i < 17; ++i) + if (n == i) + insertItem(s, _node[i], _key, _value, _begin, _end); + else + _s << _node[i]; + } + } +} + +// Inserts the given item into an RLPStream, either inline (if RLP < 32) or creating a node and inserting the hash. +void GenericTrieDB::insertItem(RLPStream& _s, RLP const& _node, bytesConstRef _k, bytesConstRef _v, uint _begin, uint _end) +{ + // Kill old node. + if (_node.isString() || _node.isInt()) + killNode(_node.toHash()); + + RLPStream s; + insertHelper(s, _node, _k, _v, _begin, _end); + if (s.size() < 32) + _s.insertRaw(s.out()); + else + _s << insertNode(s.out()); +} + +void GenericTrieDB::insert(bytesConstRef _key, bytesConstRef _value, ) +{ + string rv = node(m_root); + killNode(m_root); + + RLPStream s; + insertHelper(s, RLP(rv), _key, _value, 0, _key.size() * 2); + m_root = insertNode(s.out()); +} + +void GenericTrieDB::remove(bytesConstRef _key) +{ + +} + +std::string GenericTrieDB::at(bytesConstRef _key) const +{ + return std::string(); +} + + } diff --git a/libethereum/Trie.h b/libethereum/Trie.h index fa632ac7f..b380075dc 100644 --- a/libethereum/Trie.h +++ b/libethereum/Trie.h @@ -58,10 +58,28 @@ private: TrieNode* m_root; }; +/*class HashDBFace +{ +public: + virtual void insert(h256 _key, bytesConstRef _value) = 0; + virtual void remove(h256 _key) = 0; + virtual std::string at(h256 _key) const = 0; +}; + +class HashDBOverlay +{ +public: + + virtual void insert(h256 _key, bytesConstRef _value) = 0; + virtual void remove(h256 _key) = 0; + virtual std::string at(h256 _key) const = 0; +};*/ + /** * @brief Merkle Patricia Tree "Trie": a modifed base-16 Radix tree. * This version uses an LDB backend - TODO: split off m_db & m_over into opaque key/value map layer and allow caching & testing without DB. * TODO: Implement! + * TODO: Init function that inserts the SHA(emptyRLP) -> emptyRLP into the DB and sets m_root to SHA(emptyRLP). */ class GenericTrieDB { @@ -79,15 +97,18 @@ public: void debugPrint() {} std::string at(bytesConstRef _key) const { return std::string(); } - void insert(bytesConstRef _key, bytesConstRef _value) {} + void insert(bytesConstRef _key, bytesConstRef _value); void remove(bytesConstRef _key) {} // TODO: iterators. private: - std::string node(h256 _h) const { if (_h == c_null) return std::string(); if (m_over) { auto it = m_over->find(_h); if (it != m_over->end()) return it->second; } std::string ret; m_db->Get(m_readOptions, ldb::Slice((char const*)&m_root, 32), &ret); return ret; } - void insertNode(h256 _h, bytesConstRef _v) const {} - void killNode(h256 _h) const {} // only from overlay - no killing from DB proper. + void insertHelper(bytesConstRef _key, bytesConstRef _value, uint _begin, uint _end); + + std::string node(h256 _h) const { if (_h == c_null) return std::string(); if (m_over) { auto it = m_over->find(_h); if (it != m_over->end()) return it->second; } std::string ret; if (m_db) m_db->Get(m_readOptions, ldb::Slice((char const*)&m_root, 32), &ret); return ret; } + void insertNode(h256 _h, bytesConstRef _v) const { m_over[_h] = _v; } + h256 insertNode(bytesConstRef _v) const { auto h = sha3(_v); m_over[h] = _v; return h; } + void killNode(h256 _h) const { m_over->erase(_h); } // only from overlay - no killing from DB proper. static const h256 c_null; h256 m_root = c_null;