From 3d009ae161dc6b5d82709731e2e21837c6f32af8 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 25 Mar 2015 10:10:28 +0100 Subject: [PATCH 1/2] ClientBase --- libethereum/BlockChain.h | 6 +- libethereum/Client.cpp | 375 ++--------------------------------- libethereum/Client.h | 135 ++----------- libethereum/ClientBase.cpp | 393 +++++++++++++++++++++++++++++++++++++ libethereum/ClientBase.h | 164 ++++++++++++++++ libethereum/Interface.h | 5 +- libethereum/State.h | 3 +- mix/MixClient.cpp | 326 +++--------------------------- mix/MixClient.h | 70 +++---- 9 files changed, 652 insertions(+), 825 deletions(-) create mode 100644 libethereum/ClientBase.cpp create mode 100644 libethereum/ClientBase.h diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 9676b4a78..235a39267 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -65,6 +65,7 @@ ldb::Slice toSlice(h256 _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; +using UncleHashes = h256s; enum { ExtraDetails = 0, @@ -130,7 +131,10 @@ public: TransactionHashes transactionHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; } TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); } - /// Get a list of transaction hashes for a given block. Thread-safe. + /// Get a list of uncle hashes for a given block. Thread-safe. + UncleHashes uncleHashes(h256 _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; } + UncleHashes uncleHashes() const { return uncleHashes(currentHash()); } + h256 numberHash(u256 _index) const { if (!_index) return genesisHash(); return queryExtras(h256(_index), m_blockHashes, x_blockHashes, NullBlockHash).value; } /** Get the block blooms for a number of blocks. Thread-safe. diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 9e8a611c6..f1ef6df71 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -186,11 +186,6 @@ void Client::doneWorking() m_postMine = m_preMine; } -void Client::flushTransactions() -{ - doWork(); -} - void Client::killChain() { bool wasMining = isMining(); @@ -249,61 +244,6 @@ void Client::clearPending() noteChanged(changeds); } -unsigned Client::installWatch(h256 _h, Reaping _r) -{ - unsigned ret; - { - Guard l(m_filterLock); - ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; - m_watches[ret] = ClientWatch(_h, _r); - cwatch << "+++" << ret << _h.abridged(); - } - auto ch = logs(ret); - if (ch.empty()) - ch.push_back(InitialChange); - { - Guard l(m_filterLock); - swap(m_watches[ret].changes, ch); - } - return ret; -} - -unsigned Client::installWatch(LogFilter const& _f, Reaping _r) -{ - h256 h = _f.sha3(); - { - Guard l(m_filterLock); - if (!m_filters.count(h)) - { - cwatch << "FFF" << _f << h.abridged(); - m_filters.insert(make_pair(h, _f)); - } - } - return installWatch(h, _r); -} - -bool Client::uninstallWatch(unsigned _i) -{ - cwatch << "XXX" << _i; - - Guard l(m_filterLock); - - auto it = m_watches.find(_i); - if (it == m_watches.end()) - return false; - auto id = it->second.id; - m_watches.erase(it); - - auto fit = m_filters.find(id); - if (fit != m_filters.end()) - if (!--fit->second.refCount) - { - cwatch << "*X*" << fit->first << ":" << fit->second.filter; - m_filters.erase(fit); - } - return true; -} - void Client::noteChanged(h256Set const& _filters) { Guard l(m_filterLock); @@ -324,39 +264,6 @@ void Client::noteChanged(h256Set const& _filters) i.second.changes.clear(); } -LocalisedLogEntries Client::peekWatch(unsigned _watchId) const -{ - Guard l(m_filterLock); - -#if ETH_DEBUG - cdebug << "peekWatch" << _watchId; -#endif - auto& w = m_watches.at(_watchId); -#if ETH_DEBUG - cdebug << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); -#endif - w.lastPoll = chrono::system_clock::now(); - return w.changes; -} - -LocalisedLogEntries Client::checkWatch(unsigned _watchId) -{ - Guard l(m_filterLock); - LocalisedLogEntries ret; - -#if ETH_DEBUG && 0 - cdebug << "checkWatch" << _watchId; -#endif - auto& w = m_watches.at(_watchId); -#if ETH_DEBUG && 0 - cdebug << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); -#endif - std::swap(ret, w.changes); - w.lastPoll = chrono::system_clock::now(); - - return ret; -} - void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash) { Guard l(m_filterLock); @@ -475,68 +382,6 @@ void Client::setupState(State& _s) _s.commitToMine(m_bc); } -void Client::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) -{ - startWorking(); - - u256 n; - { - ReadGuard l(x_stateDB); - n = m_postMine.transactionsFrom(toAddress(_secret)); - } - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); -// cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); - cnote << "New transaction " << t; - m_tq.attemptImport(t.rlp()); -} - -ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) -{ - ExecutionResult ret; - try - { - u256 n; - State temp; - // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - { - ReadGuard l(x_stateDB); - temp = asOf(_blockNumber); - n = temp.transactionsFrom(toAddress(_secret)); - } - Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted); - } - catch (...) - { - // TODO: Some sort of notification of failure. - } - return ret; -} - -ExecutionResult Client::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) -{ - ExecutionResult ret; - try - { - u256 n; - State temp; - // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); - { - ReadGuard l(x_stateDB); - temp = asOf(_blockNumber); - n = temp.transactionsFrom(toAddress(_secret)); - } - Transaction t(_value, _gasPrice, _gas, _data, n, _secret); - ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted); - } - catch (...) - { - // TODO: Some sort of notification of failure. - } - return ret; -} - ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice) { ExecutionResult ret; @@ -561,28 +406,6 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 return ret; } -Address Client::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) -{ - startWorking(); - - u256 n; - { - ReadGuard l(x_stateDB); - n = m_postMine.transactionsFrom(toAddress(_secret)); - } - Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); - cnote << "New transaction " << t; - m_tq.attemptImport(t.rlp()); - return right160(sha3(rlpList(t.sender(), t.nonce()))); -} - -void Client::inject(bytesConstRef _rlp) -{ - startWorking(); - - m_tq.attemptImport(_rlp); -} - pair Client::getWork() { Guard l(x_remoteMiner); @@ -730,15 +553,26 @@ void Client::doWork() } } -State Client::asOf(unsigned _h) const +State Client::asOf(BlockNumber _h) const { ReadGuard l(x_stateDB); if (_h == PendingBlock) return m_postMine; else if (_h == LatestBlock) return m_preMine; - else - return State(m_stateDB, m_bc, m_bc.numberHash(_h)); + + return State(m_stateDB, bc(), bc().numberHash(_h)); +} + +State Client::asOf(h256 _block) const +{ + ReadGuard l(x_stateDB); + return State(m_stateDB, bc(), _block); +} + +void Client::prepareForTransaction() +{ + startWorking(); } State Client::state(unsigned _txi, h256 _block) const @@ -759,183 +593,14 @@ eth::State Client::state(unsigned _txi) const return m_postMine.fromPending(_txi); } -StateDiff Client::diff(unsigned _txi, BlockNumber _block) const -{ - State st = asOf(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -StateDiff Client::diff(unsigned _txi, h256 _block) const -{ - State st = state(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -std::vector
Client::addresses(BlockNumber _block) const -{ - vector
ret; - for (auto const& i: asOf(_block).addresses()) - ret.push_back(i.first); - return ret; -} - -u256 Client::balanceAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).balance(_a); -} - -std::map Client::storageAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).storage(_a); -} - -u256 Client::countAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).transactionsFrom(_a); -} - -u256 Client::stateAt(Address _a, u256 _l, BlockNumber _block) const -{ - return asOf(_block).storage(_a, _l); -} - -bytes Client::codeAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).code(_a); -} - -Transaction Client::transaction(h256 _transactionHash) const -{ - return Transaction(m_bc.transaction(_transactionHash), CheckSignature::Range); -} - -Transaction Client::transaction(h256 _blockHash, unsigned _i) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - if (_i < b[1].itemCount()) - return Transaction(b[1][_i].data(), CheckSignature::Range); - else - return Transaction(); -} - -BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - if (_i < b[2].itemCount()) - return BlockInfo::fromHeader(b[2][_i].data()); - else - return BlockInfo(); -} - -unsigned Client::transactionCount(h256 _blockHash) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - return b[1].itemCount(); -} - -unsigned Client::uncleCount(h256 _blockHash) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - return b[2].itemCount(); -} - -Transactions Client::transactions(h256 _blockHash) const -{ - auto bl = m_bc.block(_blockHash); - RLP b(bl); - Transactions res; - for (unsigned i = 0; i < b[1].itemCount(); i++) - res.emplace_back(b[1][i].data(), CheckSignature::Range); - return res; -} - -TransactionHashes Client::transactionHashes(h256 _blockHash) const -{ - return m_bc.transactionHashes(_blockHash); -} - -LocalisedLogEntries Client::logs(unsigned _watchId) const +void Client::inject(bytesConstRef _rlp) { - LogFilter f; - try - { - Guard l(m_filterLock); - f = m_filters.at(m_watches.at(_watchId).id).filter; - } - catch (...) - { - return LocalisedLogEntries(); - } - return logs(f); + startWorking(); + + m_tq.attemptImport(_rlp); } -LocalisedLogEntries Client::logs(LogFilter const& _f) const +void Client::flushTransactions() { - LocalisedLogEntries ret; - unsigned begin = min(m_bc.number() + 1, (unsigned)_f.latest()); - unsigned end = min(m_bc.number(), min(begin, (unsigned)_f.earliest())); - - // Handle pending transactions differently as they're not on the block chain. - if (begin > m_bc.number()) - { - ReadGuard l(x_stateDB); - for (unsigned i = 0; i < m_postMine.pending().size(); ++i) - { - // Might have a transaction that contains a matching log. - TransactionReceipt const& tr = m_postMine.receipt(i); - auto sha3 = m_postMine.pending()[i].sha3(); - LogEntries le = _f.matches(tr); - if (le.size()) - for (unsigned j = 0; j < le.size(); ++j) - ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, sha3)); - } - begin = m_bc.number(); - } - - set matchingBlocks; - for (auto const& i: _f.bloomPossibilities()) - for (auto u: m_bc.withBlockBloom(i, end, begin)) - matchingBlocks.insert(u); - -#if ETH_DEBUG - unsigned falsePos = 0; -#endif - for (auto n: matchingBlocks) - { -#if ETH_DEBUG - int total = 0; -#endif - auto h = m_bc.numberHash(n); - auto receipts = m_bc.receipts(h).receipts; - for (size_t i = 0; i < receipts.size(); i++) - { - TransactionReceipt receipt = receipts[i]; - if (_f.matches(receipt.bloom())) - { - auto info = m_bc.info(h); - auto h = transaction(info.hash, i).sha3(); - LogEntries le = _f.matches(receipt); - if (le.size()) - { -#if ETH_DEBUG - total += le.size(); -#endif - for (unsigned j = 0; j < le.size(); ++j) - ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, h)); - } - } -#if ETH_DEBUG - if (!total) - falsePos++; -#endif - } - } -#if ETH_DEBUG - cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves"; -#endif - return ret; + doWork(); } diff --git a/libethereum/Client.h b/libethereum/Client.h index 6e34dc925..c49181a13 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -40,9 +40,8 @@ #include "TransactionQueue.h" #include "State.h" #include "CommonNet.h" -#include "LogFilter.h" #include "Miner.h" -#include "Interface.h" +#include "ClientBase.h" namespace dev { @@ -74,40 +73,6 @@ private: static const int GenesisBlock = INT_MIN; -struct InstalledFilter -{ - InstalledFilter(LogFilter const& _f): filter(_f) {} - - LogFilter filter; - unsigned refCount = 1; - LocalisedLogEntries changes; -}; - -static const h256 PendingChangedFilter = u256(0); -static const h256 ChainChangedFilter = u256(1); - -static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes()); -static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0); - -struct ClientWatch -{ - ClientWatch(): lastPoll(std::chrono::system_clock::now()) {} - explicit ClientWatch(h256 _id, Reaping _r): id(_id), lastPoll(_r == Reaping::Automatic ? std::chrono::system_clock::now() : std::chrono::system_clock::time_point::max()) {} - - h256 id; - LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange }; - mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now(); -}; - -struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; -#define cwatch dev::LogOutputStream() -struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; -struct WorkOutChannel: public LogChannel { static const char* name() { return "() -#define cworkin dev::LogOutputStream() -#define cworkout dev::LogOutputStream() - template struct ABISerialiser {}; template struct ABISerialiser> { static bytes serialise(FixedHash const& _t) { static_assert(N <= 32, "Cannot serialise hash > 32 bytes."); static_assert(N > 0, "Cannot serialise zero-length hash."); return bytes(32 - N, 0) + _t.asBytes(); } }; template <> struct ABISerialiser { static bytes serialise(u256 const& _t) { return h256(_t).asBytes(); } }; @@ -182,7 +147,7 @@ private: /** * @brief Main API hub for interfacing with Ethereum. */ -class Client: public MinerHost, public Interface, Worker +class Client: public MinerHost, public ClientBase, Worker { friend class Miner; @@ -211,88 +176,20 @@ public: /// Resets the gas pricer to some other object. void setGasPricer(std::shared_ptr _gp) { m_gp = _gp; } - /// Submits the given message-call transaction. - virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; - - /// Submits a new contract-creation transaction. - /// @returns the new contract's address (assuming it all goes through). - virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; - /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. - virtual void inject(bytesConstRef _rlp) override; + virtual void inject(bytesConstRef _rlp); /// Blocks until all pending transactions have been processed. virtual void flushTransactions() override; - /// Makes the given call. Nothing is recorded into the state. - virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; - - /// Does the given creation. Nothing is recorded into the state. - /// @returns the pair of the Address of the created contract together with its code. - virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; - + using Interface::call; // to remove warning about hiding virtual function /// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH. ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether); - // Informational stuff - - // [NEW API] - - using Interface::balanceAt; - using Interface::countAt; - using Interface::stateAt; - using Interface::codeAt; - using Interface::storageAt; - - virtual u256 balanceAt(Address _a, BlockNumber _block) const; - virtual u256 countAt(Address _a, BlockNumber _block) const; - virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const; - virtual bytes codeAt(Address _a, BlockNumber _block) const; - virtual std::map storageAt(Address _a, BlockNumber _block) const; - - virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override; - virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) override; - virtual bool uninstallWatch(unsigned _watchId) override; - virtual LocalisedLogEntries peekWatch(unsigned _watchId) const; - virtual LocalisedLogEntries checkWatch(unsigned _watchId); - - virtual LocalisedLogEntries logs(unsigned _watchId) const; - virtual LocalisedLogEntries logs(LogFilter const& _filter) const; - - // [EXTRA API]: - - /// @returns the length of the chain. - virtual unsigned number() const { return m_bc.number(); } - - /// Get the list of pending transactions. - /// @TODO: Remove in favour of transactions(). - virtual Transactions pending() const { return m_postMine.pending(); } - - virtual h256 hashFromNumber(unsigned _number) const { return m_bc.numberHash(_number); } - virtual BlockInfo blockInfo(h256 _hash) const { return BlockInfo(m_bc.block(_hash)); } - virtual BlockDetails blockDetails(h256 _hash) const { return m_bc.details(_hash); } - virtual Transaction transaction(h256 _transactionHash) const; - virtual Transaction transaction(h256 _blockHash, unsigned _i) const; - virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const; - virtual unsigned transactionCount(h256 _blockHash) const; - virtual unsigned uncleCount(h256 _blockHash) const; - virtual Transactions transactions(h256 _blockHash) const; - virtual TransactionHashes transactionHashes(h256 _blockHash) const; - - /// Differences between transactions. - using Interface::diff; - virtual StateDiff diff(unsigned _txi, h256 _block) const; - virtual StateDiff diff(unsigned _txi, BlockNumber _block) const; - - /// Get a list of all active addresses. - using Interface::addresses; - virtual std::vector
addresses(BlockNumber _block) const; - /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } // [PRIVATE API - only relevant for base clients, not available in general] - dev::eth::State state(unsigned _txi, h256 _block) const; dev::eth::State state(h256 _block) const; dev::eth::State state(unsigned _txi) const; @@ -317,10 +214,6 @@ public: /// Enable/disable fast mining. void setTurboMining(bool _enable = true) { m_turboMining = _enable; } - /// Set the coinbase address. - virtual void setAddress(Address _us) { m_preMine.setAddress(_us); } - /// Get the coinbase address. - virtual Address address() const { return m_preMine.address(); } /// Stops mining and sets the number of mining threads (0 for automatic). virtual void setMiningThreads(unsigned _threads = 0); /// Get the effective number of mining threads. @@ -356,6 +249,17 @@ public: void killChain(); protected: + /// InterfaceStub methods + virtual BlockChain const& bc() const override { return m_bc; } + + /// Returns the state object for the full block (i.e. the terminal state) for index _h. + /// Works properly with LatestBlock and PendingBlock. + virtual State asOf(BlockNumber _h) const override; + virtual State asOf(h256 _block) const override; + virtual State preMine() const override { ReadGuard l(x_stateDB); return m_preMine; } + virtual State postMine() const override { ReadGuard l(x_stateDB); return m_postMine; } + virtual void prepareForTransaction() override; + /// Collate the changed filters for the bloom filter of the given pending transaction. /// Insert any filters that are activated into @a o_changed. void appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _sha3); @@ -380,13 +284,8 @@ private: virtual bool turbo() const { return m_turboMining; } virtual bool force() const { return m_forceMining; } - /// Returns the state object for the full block (i.e. the terminal state) for index _h. - /// Works properly with LatestBlock and PendingBlock. - State asOf(unsigned _h) const; - VersionChecker m_vc; ///< Dummy object to check & update the protocol version. CanonBlockChain m_bc; ///< Maintains block database. - TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. BlockQueue m_bq; ///< Maintains a list of incoming blocks not yet on the blockchain (to be imported). std::shared_ptr m_gp; ///< The gas pricer. @@ -407,10 +306,6 @@ private: bool m_forceMining = false; ///< Mine even when there are no transactions pending? bool m_verifyOwnBlocks = true; ///< Should be verify blocks that we mined? - mutable Mutex m_filterLock; - std::map m_filters; - std::map m_watches; - mutable std::chrono::system_clock::time_point m_lastGarbageCollection; }; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp new file mode 100644 index 000000000..03e74de58 --- /dev/null +++ b/libethereum/ClientBase.cpp @@ -0,0 +1,393 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file ClientBase.cpp + * @author Gav Wood + * @author Marek Kotewicz + * @date 2015 + */ + +#include +#include "ClientBase.h" +#include "BlockChain.h" +#include "Executive.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) +{ + prepareForTransaction(); + + u256 n = postMine().transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + m_tq.attemptImport(t.rlp()); + + StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); + cnote << "New transaction " << t; +} + +Address ClientBase::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) +{ + prepareForTransaction(); + + u256 n = postMine().transactionsFrom(toAddress(_secret)); + Transaction t(_endowment, _gasPrice, _gas, _init, n, _secret); + m_tq.attemptImport(t.rlp()); + + StructuredLogger::transactionReceived(t.sha3().abridged(), t.sender().abridged()); + cnote << "New transaction " << t; + + return right160(sha3(rlpList(t.sender(), t.nonce()))); +} + +// TODO: remove try/catch, allow exceptions +ExecutionResult ClientBase::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) +{ + ExecutionResult ret; + try + { + State temp = asOf(_blockNumber); + u256 n = temp.transactionsFrom(toAddress(_secret)); + Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); + ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + } + catch (...) + { + // TODO: Some sort of notification of failure. + } + return ret; +} + +ExecutionResult ClientBase::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) +{ + ExecutionResult ret; + try + { + State temp = asOf(_blockNumber); + u256 n = temp.transactionsFrom(toAddress(_secret)); + // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); + + Transaction t(_value, _gasPrice, _gas, _data, n, _secret); + ret = temp.execute(bc(), t.rlp(), Permanence::Reverted); + } + catch (...) + { + // TODO: Some sort of notification of failure. + } + return ret; +} + +u256 ClientBase::balanceAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).balance(_a); +} + +u256 ClientBase::countAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).transactionsFrom(_a); +} + +u256 ClientBase::stateAt(Address _a, u256 _l, BlockNumber _block) const +{ + return asOf(_block).storage(_a, _l); +} + +bytes ClientBase::codeAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).code(_a); +} + +map ClientBase::storageAt(Address _a, BlockNumber _block) const +{ + return asOf(_block).storage(_a); +} + +// TODO: remove try/catch, allow exceptions +LocalisedLogEntries ClientBase::logs(unsigned _watchId) const +{ + LogFilter f; + try + { + Guard l(m_filterLock); + f = m_filters.at(m_watches.at(_watchId).id).filter; + } + catch (...) + { + return LocalisedLogEntries(); + } + return logs(f); +} + +LocalisedLogEntries ClientBase::logs(LogFilter const& _f) const +{ + LocalisedLogEntries ret; + unsigned begin = min(bc().number() + 1, (unsigned)_f.latest()); + unsigned end = min(bc().number(), min(begin, (unsigned)_f.earliest())); + + // Handle pending transactions differently as they're not on the block chain. + if (begin > bc().number()) + { + State temp = postMine(); + for (unsigned i = 0; i < temp.pending().size(); ++i) + { + // Might have a transaction that contains a matching log. + TransactionReceipt const& tr = temp.receipt(i); + auto th = temp.pending()[i].sha3(); + LogEntries le = _f.matches(tr); + if (le.size()) + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], begin, th)); + } + begin = bc().number(); + } + + set matchingBlocks; + for (auto const& i: _f.bloomPossibilities()) + for (auto u: bc().withBlockBloom(i, end, begin)) + matchingBlocks.insert(u); + + unsigned falsePos = 0; + for (auto n: matchingBlocks) + { + int total = 0; + auto h = bc().numberHash(n); + auto receipts = bc().receipts(h).receipts; + for (size_t i = 0; i < receipts.size(); i++) + { + TransactionReceipt receipt = receipts[i]; + if (_f.matches(receipt.bloom())) + { + auto info = bc().info(h); + auto th = transaction(info.hash, i).sha3(); + LogEntries le = _f.matches(receipt); + if (le.size()) + { + total += le.size(); + for (unsigned j = 0; j < le.size(); ++j) + ret.insert(ret.begin(), LocalisedLogEntry(le[j], n, th)); + } + } + + if (!total) + falsePos++; + } + } + + cdebug << matchingBlocks.size() << "searched from" << (end - begin) << "skipped; " << falsePos << "false +ves"; + return ret; +} + +unsigned ClientBase::installWatch(LogFilter const& _f, Reaping _r) +{ + h256 h = _f.sha3(); + { + Guard l(m_filterLock); + if (!m_filters.count(h)) + { + cwatch << "FFF" << _f << h.abridged(); + m_filters.insert(make_pair(h, _f)); + } + } + return installWatch(h, _r); +} + +unsigned ClientBase::installWatch(h256 _h, Reaping _r) +{ + unsigned ret; + { + Guard l(m_filterLock); + ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; + m_watches[ret] = ClientWatch(_h, _r); + cwatch << "+++" << ret << _h.abridged(); + } + auto ch = logs(ret); + if (ch.empty()) + ch.push_back(InitialChange); + { + Guard l(m_filterLock); + swap(m_watches[ret].changes, ch); + } + return ret; +} + +bool ClientBase::uninstallWatch(unsigned _i) +{ + cwatch << "XXX" << _i; + + Guard l(m_filterLock); + + auto it = m_watches.find(_i); + if (it == m_watches.end()) + return false; + auto id = it->second.id; + m_watches.erase(it); + + auto fit = m_filters.find(id); + if (fit != m_filters.end()) + if (!--fit->second.refCount) + { + cwatch << "*X*" << fit->first << ":" << fit->second.filter; + m_filters.erase(fit); + } + return true; +} + +LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const +{ + Guard l(m_filterLock); + + cwatch << "peekWatch" << _watchId; + auto& w = m_watches.at(_watchId); + cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + w.lastPoll = chrono::system_clock::now(); + return w.changes; +} + +LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) +{ + Guard l(m_filterLock); + LocalisedLogEntries ret; + + cwatch << "checkWatch" << _watchId; + auto& w = m_watches.at(_watchId); + cwatch << "lastPoll updated to " << chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); + std::swap(ret, w.changes); + w.lastPoll = chrono::system_clock::now(); + + return ret; +} + +h256 ClientBase::hashFromNumber(unsigned _number) const +{ + return bc().numberHash(_number); +} + +BlockInfo ClientBase::blockInfo(h256 _hash) const +{ + return BlockInfo(bc().block(_hash)); +} + +BlockDetails ClientBase::blockDetails(h256 _hash) const +{ + return bc().details(_hash); +} + +Transaction ClientBase::transaction(h256 _transactionHash) const +{ + return Transaction(bc().transaction(_transactionHash), CheckSignature::Range); +} + +Transaction ClientBase::transaction(h256 _blockHash, unsigned _i) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + if (_i < b[1].itemCount()) + return Transaction(b[1][_i].data(), CheckSignature::Range); + else + return Transaction(); +} + +Transactions ClientBase::transactions(h256 _blockHash) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + Transactions res; + for (unsigned i = 0; i < b[1].itemCount(); i++) + res.emplace_back(b[1][i].data(), CheckSignature::Range); + return res; +} + +TransactionHashes ClientBase::transactionHashes(h256 _blockHash) const +{ + return bc().transactionHashes(_blockHash); +} + +BlockInfo ClientBase::uncle(h256 _blockHash, unsigned _i) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + if (_i < b[2].itemCount()) + return BlockInfo::fromHeader(b[2][_i].data()); + else + return BlockInfo(); +} + +UncleHashes ClientBase::uncleHashes(h256 _blockHash) const +{ + return bc().uncleHashes(_blockHash); +} + +unsigned ClientBase::transactionCount(h256 _blockHash) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + return b[1].itemCount(); +} + +unsigned ClientBase::uncleCount(h256 _blockHash) const +{ + auto bl = bc().block(_blockHash); + RLP b(bl); + return b[2].itemCount(); +} + +unsigned ClientBase::number() const +{ + return bc().number(); +} + +Transactions ClientBase::pending() const +{ + return postMine().pending(); +} + + +StateDiff ClientBase::diff(unsigned _txi, h256 _block) const +{ + State st = asOf(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); +} + +StateDiff ClientBase::diff(unsigned _txi, BlockNumber _block) const +{ + State st = asOf(_block); + return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); +} + +Addresses ClientBase::addresses(BlockNumber _block) const +{ + Addresses ret; + for (auto const& i: asOf(_block).addresses()) + ret.push_back(i.first); + return ret; +} + +u256 ClientBase::gasLimitRemaining() const +{ + return postMine().gasLimitRemaining(); +} + +void ClientBase::setAddress(Address _us) +{ + preMine().setAddress(_us); +} + +Address ClientBase::address() const +{ + return preMine().address(); +} diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h new file mode 100644 index 000000000..662f170ba --- /dev/null +++ b/libethereum/ClientBase.h @@ -0,0 +1,164 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + */ +/** @file ClientBase.h + * @author Gav Wood + * @author Marek Kotewicz + * @date 2015 + */ + +#pragma once + +#include +#include "Interface.h" +#include "LogFilter.h" + +namespace dev { + +namespace eth { + +struct InstalledFilter +{ + InstalledFilter(LogFilter const& _f): filter(_f) {} + + LogFilter filter; + unsigned refCount = 1; + LocalisedLogEntries changes; +}; + +static const h256 PendingChangedFilter = u256(0); +static const h256 ChainChangedFilter = u256(1); + +static const LogEntry SpecialLogEntry = LogEntry(Address(), h256s(), bytes()); +static const LocalisedLogEntry InitialChange(SpecialLogEntry, 0); + +struct ClientWatch +{ + ClientWatch(): lastPoll(std::chrono::system_clock::now()) {} + explicit ClientWatch(h256 _id, Reaping _r): id(_id), lastPoll(_r == Reaping::Automatic ? std::chrono::system_clock::now() : std::chrono::system_clock::time_point::max()) {} + + h256 id; + LocalisedLogEntries changes = LocalisedLogEntries{ InitialChange }; + mutable std::chrono::system_clock::time_point lastPoll = std::chrono::system_clock::now(); +}; + +struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; +#define cwatch dev::LogOutputStream() +struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; +struct WorkOutChannel: public LogChannel { static const char* name() { return "() +#define cworkin dev::LogOutputStream() +#define cworkout dev::LogOutputStream() + +class ClientBase: public dev::eth::Interface +{ +public: + ClientBase() {} + virtual ~ClientBase() {} + + /// Submits the given message-call transaction. + virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; + + /// Submits a new contract-creation transaction. + /// @returns the new contract's address (assuming it all goes through). + virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; + + /// Makes the given call. Nothing is recorded into the state. + virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; + + virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override; + + using Interface::balanceAt; + using Interface::countAt; + using Interface::stateAt; + using Interface::codeAt; + using Interface::storageAt; + + virtual u256 balanceAt(Address _a, BlockNumber _block) const override; + virtual u256 countAt(Address _a, BlockNumber _block) const override; + virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const override; + virtual bytes codeAt(Address _a, BlockNumber _block) const override; + virtual std::map storageAt(Address _a, BlockNumber _block) const override; + + virtual LocalisedLogEntries logs(unsigned _watchId) const override; + virtual LocalisedLogEntries logs(LogFilter const& _filter) const override; + + /// Install, uninstall and query watches. + virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override; + virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) override; + virtual bool uninstallWatch(unsigned _watchId) override; + virtual LocalisedLogEntries peekWatch(unsigned _watchId) const override; + virtual LocalisedLogEntries checkWatch(unsigned _watchId) override; + + virtual h256 hashFromNumber(unsigned _number) const override; + virtual eth::BlockInfo blockInfo(h256 _hash) const override; + virtual eth::BlockDetails blockDetails(h256 _hash) const override; + virtual eth::Transaction transaction(h256 _transactionHash) const override; + virtual eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; + virtual eth::Transactions transactions(h256 _blockHash) const override; + virtual eth::TransactionHashes transactionHashes(h256 _blockHash) const override; + virtual eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; + virtual eth::UncleHashes uncleHashes(h256 _blockHash) const override; + virtual unsigned transactionCount(h256 _blockHash) const override; + virtual unsigned uncleCount(h256 _blockHash) const override; + virtual unsigned number() const override; + virtual eth::Transactions pending() const override; + + using Interface::diff; + virtual StateDiff diff(unsigned _txi, h256 _block) const override; + virtual StateDiff diff(unsigned _txi, BlockNumber _block) const override; + + using Interface::addresses; + virtual Addresses addresses(BlockNumber _block) const override; + virtual u256 gasLimitRemaining() const override; + + // Set the coinbase address + virtual void setAddress(Address _us) override; + + /// Get the coinbase address + virtual Address address() const override; + + /// TODO: consider moving it to a separate interface + + virtual void setMiningThreads(unsigned _threads) override { (void)_threads; BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::setMiningThreads")); } + virtual unsigned miningThreads() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningThreads")); } + virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); } + virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); } + virtual bool isMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); } + virtual eth::MineProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningProgress")); } + virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } + virtual bool submitWork(eth::ProofOfWork::Proof const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } + +protected: + + virtual BlockChain const& bc() const = 0; + virtual State asOf(BlockNumber _h) const = 0; + virtual State asOf(h256 _h) const = 0; + virtual State preMine() const = 0; + virtual State postMine() const = 0; + virtual void prepareForTransaction() = 0; + + TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. + + // filters + mutable Mutex m_filterLock; + std::map m_filters; + std::map m_watches; + +}; + +}} diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 68eb4b094..7252abb73 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -38,6 +38,7 @@ namespace eth { using TransactionHashes = h256s; +using UncleHashes = h256s; enum class Reaping { @@ -66,9 +67,6 @@ public: /// @returns the new contract's address (assuming it all goes through). virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) = 0; - /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. - virtual void inject(bytesConstRef _rlp) = 0; - /// Blocks until all pending transactions have been processed. virtual void flushTransactions() = 0; @@ -118,6 +116,7 @@ public: virtual Transaction transaction(h256 _transactionHash) const = 0; virtual Transaction transaction(h256 _blockHash, unsigned _i) const = 0; virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const = 0; + virtual UncleHashes uncleHashes(h256 _blockHash) const = 0; virtual unsigned transactionCount(h256 _blockHash) const = 0; virtual unsigned uncleCount(h256 _blockHash) const = 0; virtual Transactions transactions(h256 _blockHash) const = 0; diff --git a/libethereum/State.h b/libethereum/State.h index 46a111a9b..033942c12 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -41,7 +41,7 @@ namespace dev { -namespace test { class ImportTest; } +namespace test { class ImportTest; class StateLoader; } namespace eth { @@ -99,6 +99,7 @@ class State { friend class ExtVM; friend class dev::test::ImportTest; + friend class dev::test::StateLoader; friend class Executive; public: diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index fd7b2263d..b6a9a45b1 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -44,27 +44,18 @@ namespace mix const Secret c_defaultUserAccountSecret = Secret("cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074"); const u256 c_mixGenesisDifficulty = c_minimumDifficulty; //TODO: make it lower for Mix somehow - -class MixBlockChain: public dev::eth::BlockChain + +bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) { -public: - MixBlockChain(std::string const& _path, h256 _stateRoot): - BlockChain(createGenesisBlock(_stateRoot), _path, true) - { - } - - static bytes createGenesisBlock(h256 _stateRoot) - { - RLPStream block(3); - block.appendList(15) - << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie - << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 - << std::string() << h256() << h64(u64(42)); - block.appendRaw(RLPEmptyList); - block.appendRaw(RLPEmptyList); - return block.out(); - } -}; + RLPStream block(3); + block.appendList(15) + << h256() << EmptyListSHA3 << h160() << _stateRoot << EmptyTrie << EmptyTrie + << LogBloom() << c_mixGenesisDifficulty << 0 << c_genesisGasLimit << 0 << (unsigned)0 + << std::string() << h256() << h64(u64(42)); + block.appendRaw(RLPEmptyList); + block.appendRaw(RLPEmptyList); + return block.out(); +} MixClient::MixClient(std::string const& _dbPath): m_dbPath(_dbPath), m_minigThreads(0) @@ -243,15 +234,21 @@ ExecutionResult MixClient::execution(unsigned _index) const return m_executions.at(_index); } -State MixClient::asOf(int _block) const +State MixClient::asOf(BlockNumber _block) const { ReadGuard l(x_state); - if (_block == 0) + if (_block == PendingBlock) return m_state; - else if (_block == -1) + else if (_block == LatestBlock) return m_startState; - else - return State(m_stateDB, bc(), bc().numberHash(_block)); + + return State(m_stateDB, bc(), bc().numberHash(_block)); +} + +State MixClient::asOf(h256 _block) const +{ + ReadGuard l(x_state); + return State(m_stateDB, bc(), _block); } void MixClient::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) @@ -272,26 +269,11 @@ Address MixClient::submitTransaction(Secret _secret, u256 _endowment, bytes cons return address; } -void MixClient::inject(bytesConstRef _rlp) -{ - WriteGuard l(x_state); - eth::Transaction t(_rlp, CheckSignature::None); - executeTransaction(t, m_state, false); -} - -void MixClient::flushTransactions() -{ -} - dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber) { - u256 n; - State temp; - { - ReadGuard lr(x_state); - temp = asOf(_blockNumber); - n = temp.transactionsFrom(toAddress(_secret)); - } + + State temp = asOf(_blockNumber); + u256 n = temp.transactionsFrom(toAddress(_secret)); Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); bytes rlp = t.rlp(); WriteGuard lw(x_state); //TODO: lock is required only for last execution state @@ -315,122 +297,6 @@ dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes c return lastExecution().result; } -u256 MixClient::balanceAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).balance(_a); -} - -u256 MixClient::countAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).transactionsFrom(_a); -} - -u256 MixClient::stateAt(Address _a, u256 _l, BlockNumber _block) const -{ - return asOf(_block).storage(_a, _l); -} - -bytes MixClient::codeAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).code(_a); -} - -std::map MixClient::storageAt(Address _a, BlockNumber _block) const -{ - return asOf(_block).storage(_a); -} - -eth::LocalisedLogEntries MixClient::logs(unsigned _watchId) const -{ - Guard l(m_filterLock); - h256 h = m_watches.at(_watchId).id; - auto filterIter = m_filters.find(h); - if (filterIter != m_filters.end()) - return logs(filterIter->second.filter); - return eth::LocalisedLogEntries(); -} - -eth::LocalisedLogEntries MixClient::logs(eth::LogFilter const& _f) const -{ - LocalisedLogEntries ret; - unsigned lastBlock = bc().number(); - unsigned block = std::min(lastBlock, (unsigned)_f.latest()); - unsigned end = std::min(lastBlock, std::min(block, (unsigned)_f.earliest())); - // Pending transactions - if (block > bc().number()) - { - ReadGuard l(x_state); - for (unsigned i = 0; i < m_state.pending().size(); ++i) - { - // Might have a transaction that contains a matching log. - TransactionReceipt const& tr = m_state.receipt(i); - LogEntries logEntries = _f.matches(tr); - for (unsigned entry = 0; entry < logEntries.size(); ++entry) - ret.insert(ret.begin(), LocalisedLogEntry(logEntries[entry], block)); - } - block = bc().number(); - } - - // The rest - auto h = bc().numberHash(block); - for (; ret.size() != block && block != end; block--) - { - if (_f.matches(bc().info(h).logBloom)) - for (TransactionReceipt receipt: bc().receipts(h).receipts) - if (_f.matches(receipt.bloom())) - for (auto const& e: _f.matches(receipt)) - ret.insert(ret.begin(), LocalisedLogEntry(e, block)); - h = bc().details(h).parent; - } - return ret; -} - -unsigned MixClient::installWatch(h256 _h, eth::Reaping _r) -{ - unsigned ret; - { - Guard l(m_filterLock); - ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; - m_watches[ret] = ClientWatch(_h, _r); - } - auto ch = logs(ret); - if (ch.empty()) - ch.push_back(eth::InitialChange); - { - Guard l(m_filterLock); - swap(m_watches[ret].changes, ch); - } - return ret; -} - -unsigned MixClient::installWatch(eth::LogFilter const& _f, eth::Reaping _r) -{ - h256 h = _f.sha3(); - { - Guard l(m_filterLock); - m_filters.insert(std::make_pair(h, _f)); - } - return installWatch(h, _r); -} - -bool MixClient::uninstallWatch(unsigned _i) -{ - Guard l(m_filterLock); - - auto it = m_watches.find(_i); - if (it == m_watches.end()) - return false; - auto id = it->second.id; - m_watches.erase(it); - - auto fit = m_filters.find(id); - if (fit != m_filters.end()) - if (!--fit->second.refCount) - m_filters.erase(fit); - - return true; -} - void MixClient::noteChanged(h256Set const& _filters) { for (auto& i: m_watches) @@ -445,160 +311,18 @@ void MixClient::noteChanged(h256Set const& _filters) i.second.changes.clear(); } -LocalisedLogEntries MixClient::peekWatch(unsigned _watchId) const -{ - Guard l(m_filterLock); - if (_watchId < m_watches.size()) - return m_watches.at(_watchId).changes; - return LocalisedLogEntries(); -} - -LocalisedLogEntries MixClient::checkWatch(unsigned _watchId) -{ - Guard l(m_filterLock); - LocalisedLogEntries ret; - if (_watchId < m_watches.size()) - std::swap(ret, m_watches.at(_watchId).changes); - return ret; -} - -h256 MixClient::hashFromNumber(unsigned _number) const -{ - ReadGuard l(x_state); - return bc().numberHash(_number); -} - -eth::BlockInfo MixClient::blockInfo(h256 _hash) const -{ - ReadGuard l(x_state); - return BlockInfo(bc().block(_hash)); - -} - eth::BlockInfo MixClient::blockInfo() const { ReadGuard l(x_state); return BlockInfo(bc().block()); } -eth::BlockDetails MixClient::blockDetails(h256 _hash) const -{ - ReadGuard l(x_state); - return bc().details(_hash); -} - -Transaction MixClient::transaction(h256 _transactionHash) const -{ - ReadGuard l(x_state); - return Transaction(bc().transaction(_transactionHash), CheckSignature::Range); -} - -eth::Transaction MixClient::transaction(h256 _blockHash, unsigned _i) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - if (_i < b[1].itemCount()) - return Transaction(b[1][_i].data(), CheckSignature::Range); - else - return Transaction(); -} - -eth::BlockInfo MixClient::uncle(h256 _blockHash, unsigned _i) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - if (_i < b[2].itemCount()) - return BlockInfo::fromHeader(b[2][_i].data()); - else - return BlockInfo(); -} - -unsigned MixClient::transactionCount(h256 _blockHash) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - return b[1].itemCount(); -} - -unsigned MixClient::uncleCount(h256 _blockHash) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - return b[2].itemCount(); -} - -Transactions MixClient::transactions(h256 _blockHash) const -{ - ReadGuard l(x_state); - auto bl = bc().block(_blockHash); - RLP b(bl); - Transactions res; - for (unsigned i = 0; i < b[1].itemCount(); i++) - res.emplace_back(b[1][i].data(), CheckSignature::Range); - return res; -} - -TransactionHashes MixClient::transactionHashes(h256 _blockHash) const -{ - ReadGuard l(x_state); - return bc().transactionHashes(_blockHash); -} - -unsigned MixClient::number() const -{ - ReadGuard l(x_state); - return bc().number(); -} - -eth::Transactions MixClient::pending() const -{ - ReadGuard l(x_state); - return m_state.pending(); -} - -eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const -{ - ReadGuard l(x_state); - State st(m_stateDB, bc(), _block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -eth::StateDiff MixClient::diff(unsigned _txi, BlockNumber _block) const -{ - State st = asOf(_block); - return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); -} - -Addresses MixClient::addresses(BlockNumber _block) const -{ - Addresses ret; - for (auto const& i: asOf(_block).addresses()) - ret.push_back(i.first); - return ret; -} - -u256 MixClient::gasLimitRemaining() const -{ - ReadGuard l(x_state); - return m_state.gasLimitRemaining(); -} - void MixClient::setAddress(Address _us) { WriteGuard l(x_state); m_state.setAddress(_us); } -Address MixClient::address() const -{ - ReadGuard l(x_state); - return m_state.address(); -} - void MixClient::setMiningThreads(unsigned _threads) { m_minigThreads = _threads; diff --git a/mix/MixClient.h b/mix/MixClient.h index e1085a20e..1a81f10cc 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -25,8 +25,7 @@ #include #include -#include -#include +#include #include #include "MachineStates.h" @@ -35,9 +34,15 @@ namespace dev namespace mix { -class MixBlockChain; +class MixBlockChain: public dev::eth::BlockChain +{ +public: + MixBlockChain(std::string const& _path, h256 _stateRoot): BlockChain(createGenesisBlock(_stateRoot), _path, true) {} + + static bytes createGenesisBlock(h256 _stateRoot); +}; -class MixClient: public dev::eth::Interface +class MixClient: public dev::eth::ClientBase { public: MixClient(std::string const& _dbPath); @@ -48,43 +53,12 @@ public: ExecutionResult lastExecution() const; ExecutionResult execution(unsigned _index) const; - //dev::eth::Interface void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) override; Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override; - void inject(bytesConstRef _rlp) override; - void flushTransactions() override; - dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber) override; - dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber) override; - u256 balanceAt(Address _a, eth::BlockNumber _block) const override; - u256 countAt(Address _a, eth::BlockNumber _block) const override; - u256 stateAt(Address _a, u256 _l, eth::BlockNumber _block) const override; - bytes codeAt(Address _a, eth::BlockNumber _block) const override; - std::map storageAt(Address _a, eth::BlockNumber _block) const override; - eth::LocalisedLogEntries logs(unsigned _watchId) const override; - eth::LocalisedLogEntries logs(eth::LogFilter const& _filter) const override; - unsigned installWatch(eth::LogFilter const& _filter, eth::Reaping _r = eth::Reaping::Automatic) override; - unsigned installWatch(h256 _filterId, eth::Reaping _r = eth::Reaping::Automatic) override; - bool uninstallWatch(unsigned _watchId) override; - eth::LocalisedLogEntries peekWatch(unsigned _watchId) const override; - eth::LocalisedLogEntries checkWatch(unsigned _watchId) override; - h256 hashFromNumber(unsigned _number) const override; - eth::BlockInfo blockInfo(h256 _hash) const override; - eth::BlockDetails blockDetails(h256 _hash) const override; - eth::Transaction transaction(h256 _transactionHash) const override; - eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; - eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; - unsigned transactionCount(h256 _blockHash) const override; - unsigned uncleCount(h256 _blockHash) const override; - eth::Transactions transactions(h256 _blockHash) const override; - eth::TransactionHashes transactionHashes(h256 _blockHash) const override; - unsigned number() const override; - eth::Transactions pending() const override; - eth::StateDiff diff(unsigned _txi, h256 _block) const override; - eth::StateDiff diff(unsigned _txi, eth::BlockNumber _block) const override; - Addresses addresses(eth::BlockNumber _block) const override; - u256 gasLimitRemaining() const override; + dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber = eth::PendingBlock) override; + dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * eth::szabo, eth::BlockNumber _blockNumber = eth::PendingBlock) override; + void setAddress(Address _us) override; - Address address() const override; void setMiningThreads(unsigned _threads) override; unsigned miningThreads() const override; void startMining() override; @@ -93,16 +67,27 @@ public: eth::MineProgress miningProgress() const override; std::pair getWork() override { return std::pair(); } bool submitWork(eth::ProofOfWork::Proof const&) override { return false; } + virtual void flushTransactions() override {} + /// @returns the last mined block information + using Interface::blockInfo; // to remove warning about hiding virtual function eth::BlockInfo blockInfo() const; std::vector userAccounts() { return m_userAccounts; } +protected: + virtual dev::eth::BlockChain& bc() { return *m_bc; } + + /// InterfaceStub methods + virtual dev::eth::State asOf(eth::BlockNumber _block) const override; + virtual dev::eth::State asOf(h256 _block) const override; + virtual dev::eth::BlockChain const& bc() const override { return *m_bc; } + virtual dev::eth::State preMine() const override { ReadGuard l(x_state); return m_startState; } + virtual dev::eth::State postMine() const override { ReadGuard l(x_state); return m_state; } + virtual void prepareForTransaction() override {} + private: void executeTransaction(dev::eth::Transaction const& _t, eth::State& _state, bool _call); void noteChanged(h256Set const& _filters); - dev::eth::State asOf(int _block) const; - MixBlockChain& bc() { return *m_bc; } - MixBlockChain const& bc() const { return *m_bc; } std::vector m_userAccounts; eth::State m_state; @@ -111,9 +96,6 @@ private: std::auto_ptr m_bc; mutable boost::shared_mutex x_state; mutable boost::shared_mutex x_executions; - mutable std::mutex m_filterLock; - std::map m_filters; - std::map m_watches; ExecutionResults m_executions; std::string m_dbPath; unsigned m_minigThreads; From fc5b6de4952d541518029af15a987133af392082 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 25 Mar 2015 19:03:57 +0100 Subject: [PATCH 2/2] Remove asOf redundancy. Minor mix typo fix. --- libethereum/Client.cpp | 21 +++++---------------- libethereum/Client.h | 4 ++-- libethereum/ClientBase.cpp | 23 ++++++++++++++++------- libethereum/ClientBase.h | 19 +++++++++++-------- mix/MixClient.cpp | 23 ++++++----------------- mix/MixClient.h | 6 +++--- 6 files changed, 43 insertions(+), 53 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index f1ef6df71..5af2bebd3 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -246,7 +246,7 @@ void Client::clearPending() void Client::noteChanged(h256Set const& _filters) { - Guard l(m_filterLock); + Guard l(x_filtersWatches); if (_filters.size()) cnote << "noteChanged(" << _filters << ")"; // accrue all changes left in each filter into the watches. @@ -266,7 +266,7 @@ void Client::noteChanged(h256Set const& _filters) void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash) { - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (pair& i: m_filters) if (i.second.filter.envelops(RelativeBlock::Pending, m_bc.number() + 1)) { @@ -288,7 +288,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) auto d = m_bc.info(_block); auto br = m_bc.receipts(_block); - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (pair& i: m_filters) if (i.second.filter.envelops(RelativeBlock::Latest, d.number) && i.second.filter.matches(d.logBloom)) // acceptable number & looks like block may contain a matching log entry. @@ -535,7 +535,7 @@ void Client::doWork() // watches garbage collection vector toUninstall; { - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (auto key: keysOf(m_watches)) if (m_watches[key].lastPoll != chrono::system_clock::time_point::max() && chrono::system_clock::now() - m_watches[key].lastPoll > chrono::seconds(20)) { @@ -553,18 +553,7 @@ void Client::doWork() } } -State Client::asOf(BlockNumber _h) const -{ - ReadGuard l(x_stateDB); - if (_h == PendingBlock) - return m_postMine; - else if (_h == LatestBlock) - return m_preMine; - - return State(m_stateDB, bc(), bc().numberHash(_h)); -} - -State Client::asOf(h256 _block) const +State Client::asOf(h256 const& _block) const { ReadGuard l(x_stateDB); return State(m_stateDB, bc(), _block); diff --git a/libethereum/Client.h b/libethereum/Client.h index c49181a13..dad0ca664 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -254,8 +254,8 @@ protected: /// Returns the state object for the full block (i.e. the terminal state) for index _h. /// Works properly with LatestBlock and PendingBlock. - virtual State asOf(BlockNumber _h) const override; - virtual State asOf(h256 _block) const override; + using ClientBase::asOf; + virtual State asOf(h256 const& _block) const override; virtual State preMine() const override { ReadGuard l(x_stateDB); return m_preMine; } virtual State postMine() const override { ReadGuard l(x_stateDB); return m_postMine; } virtual void prepareForTransaction() override; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 03e74de58..f9caf98ac 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -29,6 +29,15 @@ using namespace std; using namespace dev; using namespace dev::eth; +State ClientBase::asOf(BlockNumber _h) const +{ + if (_h == PendingBlock) + return postMine(); + else if (_h == LatestBlock) + return preMine(); + return asOf(bc().numberHash(_h)); +} + void ClientBase::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice) { prepareForTransaction(); @@ -123,7 +132,7 @@ LocalisedLogEntries ClientBase::logs(unsigned _watchId) const LogFilter f; try { - Guard l(m_filterLock); + Guard l(x_filtersWatches); f = m_filters.at(m_watches.at(_watchId).id).filter; } catch (...) @@ -196,7 +205,7 @@ unsigned ClientBase::installWatch(LogFilter const& _f, Reaping _r) { h256 h = _f.sha3(); { - Guard l(m_filterLock); + Guard l(x_filtersWatches); if (!m_filters.count(h)) { cwatch << "FFF" << _f << h.abridged(); @@ -210,7 +219,7 @@ unsigned ClientBase::installWatch(h256 _h, Reaping _r) { unsigned ret; { - Guard l(m_filterLock); + Guard l(x_filtersWatches); ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0; m_watches[ret] = ClientWatch(_h, _r); cwatch << "+++" << ret << _h.abridged(); @@ -219,7 +228,7 @@ unsigned ClientBase::installWatch(h256 _h, Reaping _r) if (ch.empty()) ch.push_back(InitialChange); { - Guard l(m_filterLock); + Guard l(x_filtersWatches); swap(m_watches[ret].changes, ch); } return ret; @@ -229,7 +238,7 @@ bool ClientBase::uninstallWatch(unsigned _i) { cwatch << "XXX" << _i; - Guard l(m_filterLock); + Guard l(x_filtersWatches); auto it = m_watches.find(_i); if (it == m_watches.end()) @@ -249,7 +258,7 @@ bool ClientBase::uninstallWatch(unsigned _i) LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const { - Guard l(m_filterLock); + Guard l(x_filtersWatches); cwatch << "peekWatch" << _watchId; auto& w = m_watches.at(_watchId); @@ -260,7 +269,7 @@ LocalisedLogEntries ClientBase::peekWatch(unsigned _watchId) const LocalisedLogEntries ClientBase::checkWatch(unsigned _watchId) { - Guard l(m_filterLock); + Guard l(x_filtersWatches); LocalisedLogEntries ret; cwatch << "checkWatch" << _watchId; diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 662f170ba..9d9482277 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -126,7 +126,7 @@ public: virtual Addresses addresses(BlockNumber _block) const override; virtual u256 gasLimitRemaining() const override; - // Set the coinbase address + /// Set the coinbase address virtual void setAddress(Address _us) override; /// Get the coinbase address @@ -143,21 +143,24 @@ public: virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } virtual bool submitWork(eth::ProofOfWork::Proof const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } -protected: + State asOf(BlockNumber _h) const; +protected: + /// The interface that must be implemented in any class deriving this. + /// { virtual BlockChain const& bc() const = 0; - virtual State asOf(BlockNumber _h) const = 0; - virtual State asOf(h256 _h) const = 0; + virtual State asOf(h256 const& _h) const = 0; virtual State preMine() const = 0; virtual State postMine() const = 0; virtual void prepareForTransaction() = 0; + /// } - TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. + TransactionQueue m_tq; ///< Maintains a list of incoming transactions not yet in a block on the blockchain. // filters - mutable Mutex m_filterLock; - std::map m_filters; - std::map m_watches; + mutable Mutex x_filtersWatches; ///< Our lock. + std::map m_filters; ///< The dictionary of filters that are active. + std::map m_watches; ///< Each and every watch - these reference a filter. }; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index b6a9a45b1..0a2464b65 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -58,7 +58,7 @@ bytes MixBlockChain::createGenesisBlock(h256 _stateRoot) } MixClient::MixClient(std::string const& _dbPath): - m_dbPath(_dbPath), m_minigThreads(0) + m_dbPath(_dbPath), m_miningThreads(0) { std::map account; account.insert(std::make_pair(c_defaultUserAccountSecret, 1000000 * ether)); @@ -72,7 +72,7 @@ MixClient::~MixClient() void MixClient::resetState(std::map _accounts) { WriteGuard l(x_state); - Guard fl(m_filterLock); + Guard fl(x_filtersWatches); m_filters.clear(); m_watches.clear(); @@ -188,7 +188,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c BOOST_THROW_EXCEPTION(OutOfGas() << errinfo_comment("Not enough gas for contract deployment")); // collect watches h256Set changed; - Guard l(m_filterLock); + Guard l(x_filtersWatches); for (std::pair& i: m_filters) if ((unsigned)i.second.filter.latest() > bc().number()) { @@ -234,18 +234,7 @@ ExecutionResult MixClient::execution(unsigned _index) const return m_executions.at(_index); } -State MixClient::asOf(BlockNumber _block) const -{ - ReadGuard l(x_state); - if (_block == PendingBlock) - return m_state; - else if (_block == LatestBlock) - return m_startState; - - return State(m_stateDB, bc(), bc().numberHash(_block)); -} - -State MixClient::asOf(h256 _block) const +State MixClient::asOf(h256 const& _block) const { ReadGuard l(x_state); return State(m_stateDB, bc(), _block); @@ -325,12 +314,12 @@ void MixClient::setAddress(Address _us) void MixClient::setMiningThreads(unsigned _threads) { - m_minigThreads = _threads; + m_miningThreads = _threads; } unsigned MixClient::miningThreads() const { - return m_minigThreads; + return m_miningThreads; } void MixClient::startMining() diff --git a/mix/MixClient.h b/mix/MixClient.h index 1a81f10cc..179b445ac 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -78,8 +78,8 @@ protected: virtual dev::eth::BlockChain& bc() { return *m_bc; } /// InterfaceStub methods - virtual dev::eth::State asOf(eth::BlockNumber _block) const override; - virtual dev::eth::State asOf(h256 _block) const override; + virtual dev::eth::State asOf(h256 const& _block) const override; + using ClientBase::asOf; virtual dev::eth::BlockChain const& bc() const override { return *m_bc; } virtual dev::eth::State preMine() const override { ReadGuard l(x_state); return m_startState; } virtual dev::eth::State postMine() const override { ReadGuard l(x_state); return m_state; } @@ -98,7 +98,7 @@ private: mutable boost::shared_mutex x_executions; ExecutionResults m_executions; std::string m_dbPath; - unsigned m_minigThreads; + unsigned m_miningThreads; }; }