From 9347a1ab8be04459f6792eb44818915d7270e660 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 15 Jan 2014 01:52:10 +0000 Subject: [PATCH] Move over to DB/overlay-based state. --- libethereum/BlockInfo.cpp | 11 ++- libethereum/BlockInfo.h | 1 + libethereum/Exceptions.h | 1 + libethereum/RLP.cpp | 6 +- libethereum/RLP.h | 18 +++- libethereum/State.cpp | 187 ++++++++++++++++++++++++-------------- libethereum/State.h | 35 +++++-- libethereum/Transaction.h | 4 +- libethereum/Trie.cpp | 2 + libethereum/Trie.h | 56 ++++++++++++ libethereum/vector_ref.h | 2 +- 11 files changed, 233 insertions(+), 90 deletions(-) diff --git a/libethereum/BlockInfo.cpp b/libethereum/BlockInfo.cpp index 7b451cbe7..c6e9a5374 100644 --- a/libethereum/BlockInfo.cpp +++ b/libethereum/BlockInfo.cpp @@ -42,7 +42,7 @@ bytes BlockInfo::createGenesisBlock() { RLPStream block(3); auto sha3EmptyList = sha3(RLPEmptyList); - block.appendList(8) << (uint)0 << sha3EmptyList << (uint)0 << sha3(RLPNull) << sha3EmptyList << ((uint)1 << 36) << (uint)0 << (uint)0; + block.appendList(9) << (uint)0 << sha3EmptyList << (uint)0 << sha3(RLPNull) << sha3EmptyList << ((uint)1 << 36) << (uint)0 << (uint)0 << (uint)0; block.appendRaw(RLPEmptyList); block.appendRaw(RLPEmptyList); return block.out(); @@ -50,12 +50,14 @@ bytes BlockInfo::createGenesisBlock() h256 BlockInfo::headerHashWithoutNonce() const { - return sha3((RLPStream(7) << parentHash << sha3Uncles << coinbaseAddress << stateRoot << sha3Transactions << difficulty << timestamp).out()); + RLPStream s; + fillStream(s, false); + return sha3(s.out()); } void BlockInfo::fillStream(RLPStream& _s, bool _nonce) const { - _s.appendList(_nonce ? 8 : 7) << parentHash << sha3Uncles << coinbaseAddress << stateRoot << sha3Transactions << difficulty << timestamp; + _s.appendList(_nonce ? 9 : 8) << parentHash << sha3Uncles << coinbaseAddress << stateRoot << sha3Transactions << difficulty << timestamp << extraData; if (_nonce) _s << nonce; } @@ -80,7 +82,8 @@ void BlockInfo::populate(bytesConstRef _block) sha3Transactions = header[4].toHash(); difficulty = header[5].toInt(); timestamp = header[6].toInt(); - nonce = header[7].toInt(); + extraData = header[7].toHash(); + nonce = header[8].toInt(); } catch (RLP::BadCast) { diff --git a/libethereum/BlockInfo.h b/libethereum/BlockInfo.h index 1fbd8e2fe..2bf341d26 100644 --- a/libethereum/BlockInfo.h +++ b/libethereum/BlockInfo.h @@ -38,6 +38,7 @@ public: h256 sha3Transactions; u256 difficulty; u256 timestamp; + h256 extraData; u256 nonce; BlockInfo(); diff --git a/libethereum/Exceptions.h b/libethereum/Exceptions.h index 70caed2b3..58be587ab 100644 --- a/libethereum/Exceptions.h +++ b/libethereum/Exceptions.h @@ -27,5 +27,6 @@ class InvalidDifficulty: public std::exception {}; class InvalidTimestamp: public std::exception {}; 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 {}; +class InvalidContractAddress: public std::exception {}; } diff --git a/libethereum/RLP.cpp b/libethereum/RLP.cpp index b2748522d..40cc74859 100644 --- a/libethereum/RLP.cpp +++ b/libethereum/RLP.cpp @@ -113,7 +113,7 @@ eth::uint RLP::items() const return ret; } -RLPStream& RLPStream::append(std::string const& _s) +RLPStream& RLPStream::appendString(bytesConstRef _s) { if (_s.size() < 0x38) m_out.push_back(_s.size() | 0x40); @@ -125,7 +125,7 @@ RLPStream& RLPStream::append(std::string const& _s) return *this; } -RLPStream& RLPStream::appendString(bytes const& _s) +RLPStream& RLPStream::appendString(string const& _s) { if (_s.size() < 0x38) m_out.push_back(_s.size() | 0x40); @@ -137,7 +137,7 @@ RLPStream& RLPStream::appendString(bytes const& _s) return *this; } -RLPStream& RLPStream::appendRaw(bytes const& _s) +RLPStream& RLPStream::appendRaw(bytesConstRef _s) { uint os = m_out.size(); m_out.resize(os + _s.size()); diff --git a/libethereum/RLP.h b/libethereum/RLP.h index 39da78a5a..6399206ae 100644 --- a/libethereum/RLP.h +++ b/libethereum/RLP.h @@ -163,6 +163,10 @@ public: explicit operator bigint() const { return toBigInt(); } template explicit operator FixedHash<_N>() const { return toHash<_N>(); } + /// Converts to bytearray. @returns the empty byte array if not a string. + bytes toBytes() const { if (!isString()) return bytes(); bytes(payload().data(), payload().data() + items()); } + /// Converts to bytearray. @returns the empty byte array if not a string. + bytesConstRef toBytesConstRef() const { if (!isString()) return bytesConstRef(); payload().cropped(0, items()); } /// Converts to string. @returns the empty string if not a string. std::string toString() const { if (!isString()) return std::string(); return payload().cropped(0, items()).toString(); } /// Converts to string. @throws BadCast if not a string. @@ -296,10 +300,13 @@ public: RLPStream& append(h160 _s, bool _compact = false) { return appendFixed(_s, _compact); } RLPStream& append(h256 _s, bool _compact = false) { return appendFixed(_s, _compact); } RLPStream& append(bigint _s); - RLPStream& append(std::string const& _s); RLPStream& appendList(uint _count); - RLPStream& appendRaw(bytes const& _rlp); - RLPStream& appendString(bytes const& _rlp); + RLPStream& appendString(bytesConstRef _s); + RLPStream& appendString(bytes const& _s) { return appendString(bytesConstRef(&_s)); } + RLPStream& appendString(std::string const& _s); + RLPStream& appendRaw(bytesConstRef _rlp); + RLPStream& appendRaw(bytes const& _rlp) { return appendRaw(&_rlp); } + RLPStream& appendRaw(RLP const& _rlp) { return appendRaw(_rlp.data()); } /// Shift operators for appending data items. RLPStream& operator<<(uint _i) { return append(_i); } @@ -308,8 +315,9 @@ public: RLPStream& operator<<(h160 _i) { return append(_i); } RLPStream& operator<<(h256 _i) { return append(_i); } RLPStream& operator<<(bigint _i) { return append(_i); } - RLPStream& operator<<(char const* _s) { return append(std::string(_s)); } - RLPStream& operator<<(std::string const& _s) { return append(_s); } + RLPStream& operator<<(char const* _s) { return appendString(std::string(_s)); } + RLPStream& operator<<(std::string const& _s) { return appendString(_s); } + RLPStream& operator<<(RLP const& _i) { return appendRaw(_i); } template RLPStream& operator<<(std::vector<_T> const& _s) { appendList(_s.size()); for (auto const& i: _s) append(i); return *this; } /// Read the byte stream. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index cbdde75a5..485e18b1a 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -23,11 +23,13 @@ #include #include #include +#include #include #include "Trie.h" #include "BlockChain.h" #include "Instruction.h" #include "Exceptions.h" +#include "Dagger.h" #include "State.h" using namespace std; using namespace eth; @@ -46,6 +48,10 @@ State::State(Address _coinbaseAddress): m_ourAddress(_coinbaseAddress) secp256k1_start(); m_previousBlock = BlockInfo::genesis(); m_currentBlock.coinbaseAddress = m_ourAddress; + + ldb::Options o; + ldb::DB::Open(o, "state", &m_db); + m_state.open(m_db, m_currentBlock.stateRoot, &m_over); } void State::sync(BlockChain const& _bc) @@ -75,11 +81,8 @@ void State::sync(BlockChain const& _bc, h256 _block) // We mined the last block. // Our state is good - we just need to move on to next. m_previousBlock = m_currentBlock; - m_current.clear(); - m_transactions.clear(); - m_currentBlock = BlockInfo(); - m_currentBlock.coinbaseAddress = m_ourAddress; - ++m_currentNumber; + resetCurrent(); + m_currentNumber++; } else if (bi == m_previousBlock) { @@ -107,14 +110,18 @@ void State::sync(BlockChain const& _bc, h256 _block) for (auto it = next(l.cbegin()); it != l.cend(); ++it) playback(_bc.block(*it)); - m_transactions.clear(); - m_current.clear(); - m_currentBlock = BlockInfo(); m_currentNumber = _bc.details(_bc.currentHash()).number + 1; - m_currentBlock.coinbaseAddress = m_ourAddress; + resetCurrent(); } } +void State::resetCurrent() +{ + m_transactions.clear(); + m_currentBlock = BlockInfo(); + m_currentBlock.coinbaseAddress = m_ourAddress; + m_currentBlock.stateRoot = m_previousBlock.stateRoot; +} void State::sync(TransactionQueue& _tq) { @@ -195,15 +202,14 @@ u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent) throw InvalidStateRoot(); m_previousBlock = m_currentBlock; - m_currentBlock = BlockInfo(); - m_currentBlock.coinbaseAddress = m_ourAddress; + resetCurrent(); return tdIncrease; } // @returns the block that represents the difference between m_previousBlock and m_currentBlock. // (i.e. all the transactions we executed). -bytes State::compileBlock(BlockChain const& _bc) +void State::prepareToMine(BlockChain const& _bc) { RLPStream uncles; if (m_previousBlock != BlockInfo::genesis()) @@ -226,78 +232,126 @@ bytes State::compileBlock(BlockChain const& _bc) m_currentBlock.sha3Transactions = sha3(m_currentTxs); m_currentBlock.sha3Uncles = sha3(m_currentUncles); - - RLPStream ret; - ret.appendList(3); - m_currentBlock.fillStream(ret, true); - ret.appendRaw(m_currentTxs); - ret.appendRaw(m_currentUncles); - return ret.out(); } -h256 State::rootHash() const +bool State::mine(uint _msTimeout) { - // TODO! - return h256(); -} + // Update timestamp according to clock. + m_currentBlock.timestamp = time(0); + + // Update difficulty according to timestamp. + m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); + + // TODO: Miner class that keeps dagger between mine calls (or just non-polling mining). + + Dagger d(m_currentBlock.headerHashWithoutNonce()); + m_currentBlock.nonce = d.search(_msTimeout, m_currentBlock.difficulty); + if (m_currentBlock.nonce) + { + // Got it! Compile block: + RLPStream ret; + ret.appendList(3); + m_currentBlock.fillStream(ret, true); + ret.appendRaw(m_currentTxs); + ret.appendRaw(m_currentUncles); + ret.swapOut(m_currentBytes); + return true; + } -bool State::mine(uint _msTimeout) const -{ - // TODO: update timestamp according to clock. - // TODO: update difficulty according to timestamp. - // TODO: look for a nonce that makes a good hash. - // ...but don't take longer than _msTimeout ms. return false; } -bool State::isNormalAddress(Address _address) const +bool State::isNormalAddress(Address _id) const { - auto it = m_current.find(_address); - return it != m_current.end() && it->second.type() == AddressType::Normal; + return RLP(m_state[_id]).itemCount() == 2; } -bool State::isContractAddress(Address _address) const +bool State::isContractAddress(Address _id) const { - auto it = m_current.find(_address); - return it != m_current.end() && it->second.type() == AddressType::Contract; + return RLP(m_state[_id]).itemCount() == 3; } u256 State::balance(Address _id) const { - auto it = m_current.find(_id); - return it == m_current.end() ? 0 : it->second.balance(); + RLP rlp(m_state[_id]); + if (rlp.isList()) + return rlp[0].toInt(); + else + return 0; +} + +void State::noteSending(Address _id) +{ + RLP rlp(m_state[_id]); + if (rlp.isList()) + if (rlp.itemCount() == 2) + m_state.insert(_id, rlpList(rlp[0], rlp[1].toInt() + 1)); + else + m_state.insert(_id, rlpList(rlp[0], rlp[1].toInt() + 1, rlp[2])); + else + m_state.insert(_id, rlpList(0, 1)); } void State::addBalance(Address _id, u256 _amount) { - auto it = m_current.find(_id); - if (it == m_current.end()) - it->second.balance() = _amount; + RLP rlp(m_state[_id]); + if (rlp.isList()) + if (rlp.itemCount() == 2) + m_state.insert(_id, rlpList(rlp[0].toInt() + _amount, rlp[1])); + else + m_state.insert(_id, rlpList(rlp[0].toInt() + _amount, rlp[1], rlp[2])); else - it->second.balance() += _amount; + m_state.insert(_id, rlpList(_amount, 0)); } void State::subBalance(Address _id, bigint _amount) { - auto it = m_current.find(_id); - if (it == m_current.end() || (bigint)it->second.balance() < _amount) + RLP rlp(m_state[_id]); + if (rlp.isList()) + { + bigint bal = rlp[0].toInt(); + if (bal < _amount) + throw NotEnoughCash(); + bal -= _amount; + if (rlp.itemCount() == 2) + m_state.insert(_id, rlpList(bal, rlp[1])); + else + m_state.insert(_id, rlpList(bal, rlp[1], rlp[2])); + } + else throw NotEnoughCash(); - it->second.balance() = (u256)((bigint)it->second.balance() - _amount); } -u256 State::transactionsFrom(Address _address) const +u256 State::transactionsFrom(Address _id) const +{ + RLP rlp(m_state[_id]); + if (rlp.isList()) + return rlp[0].toInt(RLP::LaisezFaire); + else + return 0; +} + +u256 State::contractMemory(Address _id, u256 _memory) const { - auto it = m_current.find(_address); - return it == m_current.end() ? 0 : it->second.nonce(); + RLP rlp(m_state[_id]); + if (rlp.itemCount() != 3) + throw InvalidContractAddress(); + return fromBigEndian(TrieDB(m_db, rlp[2].toHash(), (std::map*)&m_over)[_memory]); } -u256 State::contractMemory(Address _contract, u256 _memory) const +void State::setContractMemory(Address _contract, u256 _memory, u256 _value) { - auto m = m_current.find(_contract); - if (m == m_current.end()) - return 0; - auto i = m->second.memory().find(_memory); - return i == m->second.memory().end() ? 0 : i->second; + RLP rlp(m_state[_contract]); + TrieDB c(m_db, &m_over); + std::string s = toBigEndianString(_value); + if (rlp.itemCount() == 3) + { + c.setRoot(rlp[2].toHash()); + c.insert(_memory, bytesConstRef(s)); + m_state.insert(_contract, rlpList(rlp[0], rlp[1], c.root())); + } + else + throw InvalidContractAddress(); } bool State::execute(bytesConstRef _rlp) @@ -346,6 +400,9 @@ void State::execute(Transaction const& _t, Address _sender) if (balance(_sender) < _t.value + _t.fee) throw NotEnoughCash(); + // Increment associated nonce for sender. + noteSending(_sender); + if (_t.receiveAddress) { subBalance(_sender, _t.value + _t.fee); @@ -368,9 +425,8 @@ void State::execute(Transaction const& _t, Address _sender) if (isContractAddress(newAddress)) throw ContractAddressCollision(); - auto& mem = m_current[newAddress].memory(); for (uint i = 0; i < _t.data.size(); ++i) - mem[i] = _t.data[i]; + setContractMemory(newAddress, i, _t.data[i]); subBalance(_sender, _t.value + _t.fee); addBalance(newAddress, _t.value); addBalance(m_currentBlock.coinbaseAddress, _t.fee); @@ -381,12 +437,6 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ { std::vector stack; - // Find our memory. - auto m = m_current.find(_myAddress); - if (m == m_current.end()) - throw NoSuchContract(); - auto& myMemory = m->second.memory(); - // Set up some local functions. auto require = [&](u256 _n) { @@ -395,15 +445,17 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ }; auto mem = [&](u256 _n) -> u256 { - auto i = myMemory.find(_n); - return i == myMemory.end() ? 0 : i->second; + return contractMemory(_myAddress, _n); +// auto i = myMemory.find(_n); +// return i == myMemory.end() ? 0 : i->second; }; auto setMem = [&](u256 _n, u256 _v) { - if (_v) + setContractMemory(_myAddress, _n, _v); +/* if (_v) myMemory[_n] = _v; else - myMemory.erase(_n); + myMemory.erase(_n);*/ }; u256 curPC = 0; @@ -868,9 +920,10 @@ void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _ { require(1); Address dest = left160(stack.back()); - u256 minusVoidFee = m_current[_myAddress].memory().size() * c_memoryFee; + // TODO: easy once we have the local cache of memory in place. + u256 minusVoidFee = 0;//m_current[_myAddress].memory().size() * c_memoryFee; addBalance(dest, balance(_myAddress) + minusVoidFee); - m_current.erase(_myAddress); + m_state.remove(_myAddress); // ...follow through to... } case Instruction::STOP: diff --git a/libethereum/State.h b/libethereum/State.h index 98d3ae539..4ea93c1a4 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -31,6 +31,7 @@ #include "BlockInfo.h" #include "AddressState.h" #include "Transaction.h" +#include "Trie.h" namespace eth { @@ -47,15 +48,18 @@ class State { public: /// Construct null state object. - State() {} +// State() {} /// Construct state object. explicit State(Address _coinbaseAddress); + /// Compiles uncles and transactions list, and puts hashes into the current block header. + void prepareToMine(BlockChain const& _bc); + /// Attempt to find valid nonce for block that this state represents. /// @param _msTimeout Timeout before return in milliseconds. /// @returns true if it got lucky. - bool mine(uint _msTimeout = 1000) const; + bool mine(uint _msTimeout = 1000); /// Get the complete current block, including valid nonce. bytes const& blockData() const { return m_currentBytes; } @@ -98,12 +102,21 @@ public: /// @returns 0 if no contract exists at that address. u256 contractMemory(Address _contract, u256 _memory) const; + /// Set the value of a memory position of a contract. + void setContractMemory(Address _contract, u256 _memory, u256 _value); + + /// Get the full memory of a contract. +// std::map contractMemory(Address _contract) const; + + /// Note that the given address is sending a transaction and thus increment the associated ticker. + void noteSending(Address _id); + /// Get the number of transactions a particular address has sent (used for the transaction nonce). /// @returns 0 if the address has never been used. u256 transactionsFrom(Address _address) const; /// The hash of the root of our state tree. - h256 rootHash() const; + h256 rootHash() const { return m_state.root(); } /// Finalise the block, applying the earned rewards. void applyRewards(Addresses const& _uncleAddresses); @@ -114,8 +127,6 @@ public: /// This might throw. u256 playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent); - bytes compileBlock(BlockChain const& _bc); - private: /// Fee-adder on destruction RAII class. struct MinerFeeAdder @@ -140,8 +151,16 @@ private: /// Execute a contract transaction. void execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* o_totalFee); - // TODO: move over to Trie in leveldb; specify a snapshot for . - std::map m_current; ///< The current state. We work with a C++ hash map rather than a Trie. + /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). + void resetCurrent(); + + void mergeOverlay() { 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(); } + void dropOverlay() { m_over.clear(); } + + ldb::DB* m_db; ///< The DB, holding all of our Tries' backend data. + ldb::WriteOptions m_writeOptions; + std::map m_over; ///< The current overlay onto the state DB. + TrieDB
m_state; ///< Our state tree. std::map m_transactions; ///< The current list of transactions that we've included in the state. BlockInfo m_previousBlock; ///< The previous block's information. @@ -152,7 +171,7 @@ private: bytes m_currentTxs; bytes m_currentUncles; - Address m_ourAddress; ///< Our address (i.e. the address to which fees go). + Address m_ourAddress; ///< Our address (i.e. the address to which fees go). /// The fee structure. Values yet to be agreed on... static const u256 c_stepFee; diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index c289da204..2c3e0991d 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -30,8 +30,8 @@ namespace eth struct Signature { byte v; - u256 r; - u256 s; + h256 r; + h256 s; }; // [ nonce, receiving_address, value, fee, [ data item 0, data item 1 ... data item n ], v, r, s ] diff --git a/libethereum/Trie.cpp b/libethereum/Trie.cpp index 12ed53831..be3a1c265 100644 --- a/libethereum/Trie.cpp +++ b/libethereum/Trie.cpp @@ -665,4 +665,6 @@ void Trie::remove(std::string const& _key) } } +h256 const GenericTrieDB::c_null = sha3(RLPNull); + } diff --git a/libethereum/Trie.h b/libethereum/Trie.h index c80b30790..b03bca15c 100644 --- a/libethereum/Trie.h +++ b/libethereum/Trie.h @@ -22,7 +22,9 @@ #pragma once #include +#include #include "RLP.h" +namespace ldb = leveldb; namespace eth { @@ -56,6 +58,60 @@ private: TrieNode* m_root; }; +/** + * @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! + */ +class GenericTrieDB +{ +public: + GenericTrieDB() {} + GenericTrieDB(ldb::DB* _db, std::map* _overlay = nullptr): GenericTrieDB() { open(_db, c_null, _overlay); } + GenericTrieDB(ldb::DB* _db, h256 _root, std::map* _overlay = nullptr): GenericTrieDB() { open(_db, _root, _overlay); } + ~GenericTrieDB() {} + + void open(ldb::DB* _db, h256 _root, std::map* _overlay = nullptr) { m_root = _root; m_db = _db; m_over = _overlay; } + + void setRoot(h256 _root) { m_root = _root; } + h256 root() const { return m_root; } + + void debugPrint() {} + + std::string at(bytesConstRef _key) const { return std::string(); } + 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; } + + static const h256 c_null; + h256 m_root = c_null; + + ldb::DB* m_db = nullptr; + std::map* m_over = nullptr; + + ldb::ReadOptions m_readOptions; +}; + +template +class TrieDB: public GenericTrieDB +{ +public: + TrieDB() {} + TrieDB(ldb::DB* _db, std::map* _overlay = nullptr): GenericTrieDB(_db, _overlay) {} + TrieDB(ldb::DB* _db, h256 _root, std::map* _overlay = nullptr): GenericTrieDB() { open(_db, _root, _overlay); } + + std::string operator[](KeyType _k) const { return at(_k); } + + std::string at(KeyType _k) const { return GenericTrieDB::at(bytesConstRef((byte const*)&_k, sizeof(KeyType))); } + void insert(KeyType _k, bytesConstRef _value) { GenericTrieDB::insert(bytesConstRef((byte const*)&_k, sizeof(KeyType)), _value); } + void insert(KeyType _k, bytes const& _value) { insert(_k, bytesConstRef(&_value)); } + void remove(KeyType _k) { GenericTrieDB::remove(bytesConstRef((byte const*)&_k, sizeof(KeyType))); } +}; + } diff --git a/libethereum/vector_ref.h b/libethereum/vector_ref.h index 3f05c0f1a..e68dac999 100644 --- a/libethereum/vector_ref.h +++ b/libethereum/vector_ref.h @@ -20,7 +20,7 @@ public: vector_ref(_T* _data, unsigned _count): m_data(_data), m_count(_count) {} vector_ref(std::string* _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} vector_ref(typename std::conditional::value, std::vector::type> const*, std::vector<_T>*>::type _data): m_data(_data->data()), m_count(_data->size()) {} - vector_ref(typename std::conditional::value, std::string const&, std::string&>::type _data): m_data((_T*)_data->data()), m_count(_data->size() / sizeof(_T)) {} + vector_ref(typename std::conditional::value, std::string const&, std::string&>::type _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; }